Tests haphazardly fail when using graphql-request and MSW - node.js

I am trying to set up tests to test a Remix loader and noticed that the request function from graphql-request haphazardly fails when using MSW. If I replace this and use a simple fetch for the requests the tests pass.
Is there any configuration I need to change? I have created a sample repo that demonstrates the bug. The bug gets worse the more tests you have that are using the same mocked request.
Sample repo: https://github.com/charklewis/sb9or3
Here is a summary of the code I am using:
//modules/database.server
import { GraphQLClient } from "graphql-request";
const client = new GraphQLClient("http://some-graphql-api.com/api/graphql", {});
export const fetchQuery = async (query: any, variables: any) => {
try {
const response = await client.request(query, variables || {});
return response;
} catch (error) {
return {};
}
};
//routes/index
import type { LoaderFunction } from "remix";
import { json } from "remix";
import { gql } from "graphql-request";
import { fetchQuery } from "~/modules/database.server";
export const loader: LoaderFunction = async () => {
const query = gql`
query MyQuery {
demoQuery {
value
}
}
`;
const response = await fetchQuery(query, {});
return json({ value: response.demoQuery.value });
};
//routes/__tests__/index
import { graphql } from "msw";
import { setupServer } from "msw/node";
import { loader } from "~/routes/index";
const createServer = (handlers: any[] = []) => {
const server = setupServer(...handlers);
beforeAll(() => server.listen({ onUnhandledRequest: "bypass" }));
afterAll(() => server.close());
afterEach(() => server.resetHandlers());
return server;
};
const createDemoQueryHandler = ({ value = true } = {}) => {
return graphql.query("MyQuery", (req, res, ctx) => {
return res(ctx.data({ demoQuery: { value } }));
});
};
createServer([createDemoQueryHandler()]);
test("the loader returns data (round 1)", async () => {
const response = await loader({
request: new Request("/", { method: "GET" }),
params: {},
context: {},
});
const data = await response.json();
expect(data.value).toBe(true);
});
My vitest configuration is:
/// <reference types="vitest" />
/// <reference types="vite/client" />
import { defineConfig } from "vite";
import react from "#vitejs/plugin-react";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [react(), tsconfigPaths()],
test: {
globals: true,
environment: "jsdom",
setupFiles: "./app/test-utils.ts",
testTimeout: 20000
}
});

Related

Why when I tried to execute following test code, I got "The first character of a path should be `/` or `*`" error?

When I tried to execute following test code, I got error like "The first character of a path should be / or *"
archives.test.ts
import ...
describe('Get All Archived', () => {
test('it must return ok', async () => {
const app = init()
const response = await app.inject().get('/client/archives')
const body = JSON.parse(response.body)
expect(response.statusCode).toBe(200)
expect(body.status).toBe('ok')
expect(body.data.length).toBe(0)
})
})
My code:
api.ts
import fastify, { FastifyInstance } from 'fastify'
other imports...
export const init = (): FastifyInstance => {
const app = fastify()
app.addContentTypeParser('application/json', { parseAs: 'string' }, parseApplicationJson)
app.register(ArchivesPlugin)
return app
}
const proxy = awsLambdaFastify(init())
export const handler = proxy
ArchivesPlugin.ts
import fp from 'fastify-plugin'
import { FastifyInstance, FastifyPluginAsync } from 'fastify'
import { register } from '../controllers/archives'
const ArchivesPlugin: FastifyPluginAsync = async (app: FastifyInstance) => {
await register(app)
}
export default fp(ArchivesPlugin)
register.ts
import { FastifyInstance, FastifyRequest } from 'fastify'
other imports...
const handlers = {
code content
},
}
export async function register(app: FastifyInstance) {
app.route({
method: 'GET',
url: `${prefix}/archives`,
schema: { ...GetArchiveListResponse, tags: ['archives'] },
handler: handlers.index,
})
}
Appreciate any help.
The problem was caused by ${prefix} in register.ts which is given above. When called register.ts, prefix seemed undefined. That's why I was getting the error.

cannot mock a fetch call with `fetch-mock-jest 1.5.1` lib

I'm trying to mock a fetch call using thisfetch-mock-jest but it the code still trys to go to the remote address and eventually fail with error message FetchError: request to https://some.domain.io/app-config.yaml failed, reason: getaddrinfo ENOTFOUND some.domain.io].
Here the the test code
import { AppConfig } from '#backstage/config';
import { loadConfig } from './loader';
import mockFs from 'mock-fs';
import fetchMock from 'fetch-mock-jest';
describe('loadConfig', () => {
beforeEach(() => {
fetchMock.mock({
matcher: '*',
response: `app:
title: Example App
sessionKey: 'abc123'
`
});
});
afterEach(() => {
fetchMock.mockReset();
});
it('load config from remote path', async () => {
const configUrl = 'https://some.domain.io/app-config.yaml';
await expect(
loadConfig({
configRoot: '/root',
configTargets: [{ url: configUrl }],
env: 'production',
remote: {
reloadIntervalSeconds: 30,
},
})
).resolves.toEqual([
{
context: configUrl,
data: {
app: {
title: 'Example App',
sessionKey: 'abc123',
},
},
},
]);
expect(fetchMock).toHaveBeenCalledTimes(1);
});
function defer<T>() {
let resolve: (value: T) => void;
const promise = new Promise<T>(_resolve => {
resolve = _resolve;
});
return { promise, resolve: resolve! };
}
});
loadConfig has the fetch code that I'm trying to mock.
export async function loadConfig(
options: LoadConfigOptions,
): Promise<AppConfig[]> {
const loadRemoteConfigFiles = async () => {
const configs: AppConfig[] = [];
const readConfigFromUrl = async (remoteConfigProp: RemoteConfigProp) => {
const response = await fetch(remoteConfigProp.url);
if (!response.ok) {
throw new Error(
`Could not read config file at ${remoteConfigProp.url}`,
);
}
remoteConfigProp.oldETag = remoteConfigProp.newETag ?? undefined;
remoteConfigProp.newETag =
response.headers.get(HTTP_RESPONSE_HEADER_ETAG) ?? undefined;
remoteConfigProp.content = await response.text();
return remoteConfigProp;
};
.......
return configs;
}
let remoteConfigs: AppConfig[] = [];
if (remote) {
try {
remoteConfigs = await loadRemoteConfigFiles();
} catch (error) {
throw new Error(`Failed to read remote configuration file, ${error}`);
}
}
........ do some stuff with config then return
return remoteConfigs;
}
The config is a yaml file, that eventually gets parsed and converted into config object.
Any idea why is it failing to mock the fetch call?
replaced
import fetchMock from 'fetch-mock-jest';
with
const fetchMock = require('fetch-mock').sandbox();
const nodeFetch = require('node-fetch');
nodeFetch.default = fetchMock;
and fetchMock.mockReset(); with fetchMock.restore();

How to mock Prisma (via Proxy) in Jest 27

Our Prisma setup is as follows:
// server/database/prisma.ts
import { Prisma } from 'database/generated/prisma-client';
const client = new Prisma({
endpoint: PRISMA_ENDPOINT,
secret: PRISMA_SECRET,
debug: PRISMA_DEBUG,
});
// Add a proxy 'middleware' to the Prisma client to record query execution time
const prismaProxy = new Proxy(client, {
get(client, endpoint) {
const endTimer = metrics.prisma_call_duration_seconds.startTimer({ endpoint });
return function (...args) {
const result: Prisma = client[endpoint](...args);
endTimer();
return result;
};
},
});
export const prisma = prismaProxy;
I then have a utility that uses prisma that I would like to test:
// utils.ts
import { prisma } from 'database/prisma';
export const myFunc = async () => {
const result = await prisma.items();
if (items && items.length > 0) {
return true;
}
return false;
};
How do I mock prisma in a test? In Jest 26, this worked:
// utils.test.ts
jest.mock('server/database/prisma');
import { prisma } from 'server/database/prisma';
import { myFunc } from 'server/utils';
describe.only('my test', () => {
beforeAll(() => {
prisma.items.mockImplementation(() =>
Promise.resolve([
{
foo: 'bar',
},
])
);
});
test('it works', async () => {
const items = await myFunc();
expect(items).toBeTruthy();
});
});
This no longer works in Jest 27 it seems. Having made no changes except upgrading Jest from v26 -> v27 the mocks no longer seem to take hold.
What is the proper way to mock prisma here?

Test ApolloClient API with Jest in Vue3 (Composition API)

I am using Vue3 (typescript) with Composition API for my application. I am using ApolloClient grapghql for API calls. I have created a separate service file for API calls. (PFB files)
Service file
import { ApolloClient, InMemoryCache, HttpLink } from "#apollo/client/core"
import { gql } from "#apollo/client/core"
import fetch from 'cross-fetch';
const httpLink = new HttpLink({
uri: process.env.VUE_APP_BACKEND_GRAPHQL_URI,
fetch
})
const apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
})
export const getAplloAPIdata = async (reqQuery: any) => {
const query = gql `${reqQuery}`
try {
return await apolloClient.query({ query })
}catch {
console.log('API error')
}
}
Home.vue
setup() {
const threatList = ref([])
const threat = ref(null)
// get all threats
const getThreats = async () => {
const getThreatsQuery = `
query {
threats {
short_description
threat_level
}
}
`
try {
const result = await getAplloAPIdata(getThreatsQuery)
if (result) {
threatList.value = result.data.threats
}
} catch {
console.log('Error receiving threats data')
}
}
Can you please tell me how can I write test cases to mock this API in jest? Thank you!
I would mock getAplloAPIdata to return mock data, and verify that data in your test. The key is to make sure the mock path is the same as that imported in your component:
// Home.vue
import { getAplloAPIdata } from '#/service'
/*...*/
// Home.spec.js
jest.mock('#/service', () => {
return {
getAplloAPIdata: () => ({
data: {
threats: [{ id: 123456 }]
}
})
}
})
describe('Home.vue', () => {
it('gets threats', async () => {
const wrapper = shallowMount(Home)
await wrapper.vm.getThreats()
expect(wrapper.vm.threatList).toContainEqual({ id: 123456 })
})
})
GitHub demo

Unit test for customPollingHook which uses apollo useLazyQuery

So I have written a custom polling hook which uses useContext and useLazyQuery hooks. I want to write a unit test for this, which should cover its returned values state and side effect.
So far I have managed to do this much but I'm not so sure how to proceed ahead. Any tips?
export const useUploadActivityPolling = (
teId: TeIdType
): UploadActivityPollingResult => {
const { dispatch, uploadActivityId }: StoreContextType = useAppContext();
const [fetchActivityStatus, { error: UploadActivityError, data: UploadActivityData, stopPolling }] = useLazyQuery(
GET_UPLOAD_ACTIVITY,
{
pollInterval: 3000,
fetchPolicy: 'network-only',
variables: { teId, activityId: uploadActivityId },
}
);
useEffect(() => {
if (UploadActivityData) {
setUploadActivityId(
UploadActivityData.getUploadActivityStatus.activity_id,
dispatch
);
updateActivityStateAction(UploadActivityData.getExcelUploadActivityStatus.status, dispatch);
}
}, [UploadActivityData]);
return { fetchActivityStatus, stopPolling, UploadActivityError };
};
import React from 'react';
import { mount } from 'enzyme';
const TestCustomHook = ({ callback }) => {
callback();
return null;
};
export const testCustomHook = callback => {
mount(<TestCustomHook callback={callback} />);
};
describe('useUploadActivityPolling', () => {
let pollingResult;
const teId = 'some id';
beforeEach(() => {
testCustomHook(() => {
pollingResult = useUploadActivityPolling(teId);
});
});
test('should have an fetchActivityStatus function', () => {
expect(pollingResult.fetchActivityStatus).toBeInstanceOf(Function);
});
});

Resources