jaydenseric / graphql-upload

Middleware and an Upload scalar to add support for GraphQL multipart requests (file uploads via queries and mutations) to various Node.js GraphQL servers.
https://npm.im/graphql-upload
MIT License
1.43k stars 131 forks source link

Unit test jest upload issue #238

Closed alejocg20 closed 3 years ago

alejocg20 commented 3 years ago

I am having the following error

{
      errors: [
        GraphQLError [Object]: Variable "$file" got invalid value {}; Upload value invalid.
            at C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\execution\values.js:116:15
            at coerceInputValueImpl (C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\utilities\coerceInputValue.js:132:9)
            at coerceInputValueImpl (C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\utilities\coerceInputValue.js:56:14)
            at coerceInputValue (C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\utilities\coerceInputValue.js:39:10)
            at _loop (C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\execution\values.js:109:69)
            at coerceVariableValues (C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\execution\values.js:121:16)
            at getVariableValues (C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\execution\values.js:50:19)
            at buildExecutionContext (C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\execution\execute.js:190:61)
            at executeImpl (C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\execution\execute.js:87:20)
            at execute (C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\execution\execute.js:62:35)
            at graphqlImpl (C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\graphql.js:108:31)
            at C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\graphql.js:28:31
            at new Promise (<anonymous>)
            at Object.graphql (C:\Users\alejo\WebstormProjects\cm-appointments-bed\node_modules\graphql\graphql.js:26:10)
            at Object.exports.graphqlTestCall (C:\Users\alejo\WebstormProjects\cm-appointments-bed\src\utils\test\graphqlTestCall.ts:30:10)
            at processTicksAndRejections (internal/process/task_queues.js:93:5)
            at Object.<anonymous> (C:\Users\alejo\WebstormProjects\cm-appointments-bed\src\test\user.spec.ts:498:24) {
          locations: [Array]
        }
      ]
    }

Right now is only failing in the unit test, with the productive app it is working the uploading file functionality. I had the same error message with the productive app and I solved it adding the following to the package.json

 "resolutions": {
    "graphql-upload": "^11.0.0"
  },

I will share with you the unit test code

    test('Add profile picture', async () => {
      const fileName = 'logo.png';
      const file = fs.createReadStream(
        path.resolve(__dirname, `../utils/test/factory/${fileName}`)
      );
      const fileStream = new Promise((resolve) =>
        resolve({
          createReadStream: () => file,
          stream: file,
          filename: fileName,
          mimetype: 'application/png',
        })
      );
      const { id } = await createCoach({
        ...userData,
        userType: UserType.US,
      });
      await createRefreshToken({
        ...userData,
        valid: true,
        id,
      });
      const response = await graphqlTestCall({
        source: addProfilePictureMutation,
        variableValues: {
          id: { id },
          file: fileStream,
        },
      });
      console.log(response);
      expect(response.data.addProfilePicture).toEqual({
        id,
        profilePicture: true,
      });
      expect(sharp.mock.calls.length).toBe(resolutions.length);
    });

In addition this is the graphqlTestCall code

import { graphql, GraphQLSchema } from 'graphql';
import { buildSchema, Maybe } from 'type-graphql';
import { allResolvers } from '../../resolvers';
import { customAuthChecker } from '../../middleware/auth';
import { mockShortToken } from './shortToken';

interface Options {
  source: string;
  variableValues?: Maybe<{
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    [key: string]: any;
  }>;
  shortToken?: string;
}

let schema: GraphQLSchema;

export const graphqlTestCall = async ({
  source,
  variableValues,
  shortToken = mockShortToken(),
}: Options) => {
  if (!schema) {
    schema = await buildSchema({
      resolvers: allResolvers,
      validate: true,
      authChecker: customAuthChecker,
    });
  }
  return graphql({
    schema,
    source,
    contextValue: {
      ...(shortToken && {
        req: {
          headers: {
            authorization: `Bearer ${shortToken}`,
          },
        },
      }),
    },
    variableValues,
  });
};
elisalimli commented 3 years ago

You should send file,that's why it's throw that error.You just have filename

jaydenseric commented 3 years ago

Your problem is because the Upload Scalar is expecting the value coming in to be an Upload instance:

https://github.com/jaydenseric/graphql-upload#class-upload

Maybe you could update your approach to use that class, but I've personally never considered testing resolvers like that. I've only ever made full GraphQL multipart requests in tests.

Closing because this is more of a request for help with your project using Jest and apparently Apollo Server; whereas this issue tracker is for raising specific and reproducible graphql-upload bugs. Feel free to share your solution here though, whatever it ends up being :)

alejocg20 commented 3 years ago

You should send file,that's why it's throw that error.You just have filename

Hi @alisalim17 thanks for your reply. But I didnt understand, what do you mean ?? Could you be a little be more explicit, because I am sending the FileStream promise.

Thanks

jaimepater commented 3 years ago

I had the same issue, I resolved changing

      const fileStream = new Promise((resolve) =>
        resolve({
          createReadStream: () => file,
          stream: file,
          filename: fileName,
          mimetype: 'application/png',
        })
      );

for

  const upload = new Upload();
      upload.resolve({
        createReadStream: () => file,
        stream: file,
        filename: fileName,
        encoding: '7bit',
        mimetype: 'application/png',
      });
alejocg20 commented 3 years ago

I had the same issue, I resolved changing

      const fileStream = new Promise((resolve) =>
        resolve({
          createReadStream: () => file,
          stream: file,
          filename: fileName,
          mimetype: 'application/png',
        })
      );

for

  const upload = new Upload();
      upload.resolve({
        createReadStream: () => file,
        stream: file,
        filename: fileName,
        encoding: '7bit',
        mimetype: 'application/png',
      });

Thanks @jaimepater worked