Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File upload receiving buffer instead of promise with createTestClient #2372

Closed
JonathanRoyGagnon opened this issue Feb 25, 2019 · 9 comments
Closed

Comments

@JonathanRoyGagnon
Copy link

Calling a resolver using createTestClient and a file gives a buffer instead of a promise like you would normally receiving from a client.

// test.js
it('UPLOAD', async () => {
  const file = fs.readFileSync(path.join(__dirname, './test.txt'))

  const UPLOAD = gql`
    mutation upload($files: [Upload!]!) {
      upload(files: $files) {
        location
      }
    }
  `

  const { mutate } = createTestClient(server)

  const res = await mutate({ query: UPLOAD, variables: { files: [file] } })
})
// resolver.js
  Mutation: {
    async upload (root, { files }) {
      console.log('Files', files)
      const medias = await Promise.all(files.map(async file => {
        const { stream, mimetype } = await file
        console.log('Info', stream, mimetype)
        // ...
      })).catch(console.error)
      return medias
    }
  }
console.log resolver.js
  Files [ <Buffer 49 20 61 6d 20 61 20 74 65 73 74 20 66 69 6c 65> ]
  Info undefined undefined

Is the Upload scalar supported by createTestClient?

@WoLfulus
Copy link

I'm also receiving undefined in stream. Did you solve this?

@JonathanRoyGagnon
Copy link
Author

No, I had to leave it out until this is fixed

@joemckie
Copy link

joemckie commented Nov 3, 2019

It feels a little hacky, but I got this to work by doing the following:

import { createReadStream } from 'fs';

const fileName = `${__dirname}/...`;

const response = await mutate({
  mutation: UPLOAD_PRODUCT_IMAGE,
  variables: {
    file: {
      createReadStream: () => createReadStream(fileName),
      filename: 'image.jpg',
      mimetype: 'image/jpeg',
    },
  },
});

Instead of using readFileSync, pass your own createReadStream function that creates a stream with the path to the file you want.

@loremaps
Copy link

loremaps commented Apr 9, 2020

@joemckie does this still works for you?

I am getting the following error:

 Received: [[GraphQLError: Variable "$file" got invalid value { createReadStream: [function createReadStream], filename: "image.jpg", mimetype: "image/jpeg" }; Expected type Upload. Upload value invalid.]]

Any ideas?

@joemckie
Copy link

joemckie commented Apr 9, 2020

@loremaps I think I removed this code in a refactor when I moved to NestJS unfortunately so I wouldn't be able to test it out any more 😞

FWIW, I remember this being an issue during the refactor, and I found it was easier to test it by using supertest and using a multipart request

@Nabrok
Copy link

Nabrok commented Mar 20, 2021

@loremaps I am getting the same error as you, did you find a solution?

EDIT: Okay, so digging into it I found graphql-upload expects to receive an instance of the Upload class. I got around it with ...

const fs = require('fs');
const path = require('path');
const Upload = require('graphql-upload/public/Upload');

let test_client;

beforeEach(async () => {
  // Setup test_client
  ...
});

test('upload file', async () => {
  const filename = 'test.txt';
  const file = new Upload();
  file.promise = new Promise(resolve => resolve({
    createReadStream: () => fs.createReadStream(path.join(__dirname, '__fixtures__', filename)),
    filename,
    mimetype: 'text/plain'
  }));
  const upload_file = await test_client.mutate({ query: QUERY, variables: { file } });
  expect(upload_file.errors).toBeUndefined();
});

@glasser
Copy link
Member

glasser commented Mar 22, 2021

We are removing the built-in graphql-upload integration in Apollo Server 3 and are also considering removing apollo-server-testing as a standalone package so I am going to close this issue.

createTestClient is a very thin wrapper around server.executeOperation, a function that lets you hook directly in to the Apollo Server request pipeline bypassing all HTTP-based processing. This does make doing things like graphql-upload which involve a pre-processing step in the HTTP process challenging. I think we should keep executeOperation around as a simple hook for running operations when we don't care about HTTP stuff, but also make it easier (through better documentation or improvements) to test your full HTTP server.

@glasser glasser closed this as completed Mar 22, 2021
@electerious
Copy link

The solution of @Nabrok still works. Even when using server.executeOperation.

It's also possible to make testing easier by not using a real file. Simply create a variable and a Readable from an iterable and pass it to server.executeOperation.

import { Readable } from 'stream'
import Upload from 'graphql-upload/Upload.mjs'

const file = new Upload()
file.promise = Promise.resolve({
    createReadStream: () => Readable.from(['Content']),
    filename: 'file.txt',
    mimetype: 'text/plain',
})

server.executeOperation(
    {
        query: QUERY,
        variables: { file },
    },
)

@allestaire
Copy link

Tried the solutions commented on this thread but still no luck,

graphql-upload@11.0.0

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 19, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

9 participants