Skip to content

Commit b987317

Browse files
ricokahlerd4rekanguok
authored andcommitted
add tests
1 parent 82fda59 commit b987317

File tree

11 files changed

+2925
-39
lines changed

11 files changed

+2925
-39
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ node_modules/
22
gatsby-node.js
33
gatsby-node.d.ts
44
graphql-codegen.config.js
5-
graphql-codegen.config.d.ts
5+
graphql-codegen.config.d.ts
6+
coverage
7+
.DS_Store

babel.config.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// babel is used purely for jest
2+
module.exports = {
3+
presets: [
4+
[
5+
'@babel/preset-env',
6+
{
7+
targets: { node: true },
8+
},
9+
],
10+
['@babel/preset-typescript'],
11+
],
12+
}

jest.config.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// For a detailed explanation regarding each configuration property, visit:
2+
// https://jestjs.io/docs/en/configuration.html
3+
4+
const fs = require('fs')
5+
const path = require('path')
6+
const packages = fs.readdirSync(path.join(__dirname, './packages'))
7+
8+
module.exports = {
9+
clearMocks: true,
10+
coverageDirectory: 'coverage',
11+
testEnvironment: 'node',
12+
// https://jestjs.io/docs/en/configuration.html#modulenamemapper-objectstring-string
13+
moduleNameMapper: packages
14+
.map(package => [
15+
`^${package}/(.*)$`,
16+
`<rootDir>/packages/${package}/src/$1`,
17+
])
18+
.reduce((acc, [key, value]) => {
19+
acc[key] = value
20+
return acc
21+
}, {}),
22+
}

package.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,20 @@
88
"license": "MIT",
99
"scripts": {
1010
"bootstrap": "lerna bootstrap --use-workspaces",
11-
"build": "lerna run build"
11+
"build": "lerna run build",
12+
"test": "jest"
1213
},
1314
"private": true,
1415
"workspaces": [
1516
"packages/*"
1617
],
1718
"devDependencies": {
18-
"lerna": "^3.16.4"
19+
"@babel/core": "^7.8.4",
20+
"@babel/preset-env": "^7.8.4",
21+
"@babel/preset-typescript": "^7.8.3",
22+
"@types/jest": "^25.1.2",
23+
"jest": "^25.1.0",
24+
"lerna": "^3.16.4",
25+
"prettier": "^1.19.1"
1926
}
2027
}
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
import { onPostBootstrap } from './gatsby-node'
2+
import { generateWithConfig } from './graphql-codegen.config'
3+
4+
jest.mock('./graphql-codegen.config', () => ({ generateWithConfig: jest.fn() }))
5+
6+
const delay = (milliseconds) =>
7+
new Promise((resolve) => setTimeout(resolve, milliseconds))
8+
9+
it('early returns if the codegen option is false', async () => {
10+
const mockGetState = jest.fn()
11+
const mockGatsbyArgs = {
12+
store: {
13+
getState: mockGetState,
14+
},
15+
}
16+
17+
const pluginOptions = {
18+
plugins: [],
19+
codegen: false,
20+
}
21+
22+
await onPostBootstrap(mockGatsbyArgs, pluginOptions)
23+
24+
expect(mockGetState).not.toHaveBeenCalled()
25+
})
26+
27+
it('calls `generateWithConfig` from `graphql-codegen.config.ts`', async () => {
28+
const mockGatsbyArgs = {
29+
store: {
30+
getState: () => ({
31+
schema: 'mock-schema',
32+
program: { directory: 'mock-directory' },
33+
}),
34+
subscribe: jest.fn(),
35+
},
36+
reporter: {
37+
info: jest.fn(),
38+
panic: jest.fn(),
39+
},
40+
}
41+
42+
const pluginOptions = {
43+
documentPaths: ['./example-document-paths'],
44+
fileName: './example-filename.ts',
45+
plugins: [],
46+
}
47+
48+
const mockGenerateFromSchema = jest.fn()
49+
generateWithConfig.mockReturnValueOnce(mockGenerateFromSchema)
50+
51+
await onPostBootstrap(mockGatsbyArgs, pluginOptions)
52+
53+
expect(generateWithConfig).toHaveBeenCalledTimes(1)
54+
expect(generateWithConfig.mock.calls[0][0]).toMatchInlineSnapshot(`
55+
Object {
56+
"directory": "mock-directory",
57+
"documentPaths": Array [
58+
"./example-document-paths",
59+
],
60+
"fileName": "./example-filename.ts",
61+
"reporter": Object {
62+
"info": [MockFunction] {
63+
"calls": Array [
64+
Array [
65+
"[gatsby-plugin-graphql-codegen] definition for queries has been updated at ./example-filename.ts",
66+
],
67+
],
68+
"results": Array [
69+
Object {
70+
"type": "return",
71+
"value": undefined,
72+
},
73+
],
74+
},
75+
"panic": [MockFunction],
76+
},
77+
}
78+
`)
79+
80+
expect(mockGenerateFromSchema).toMatchInlineSnapshot(`
81+
[MockFunction] {
82+
"calls": Array [
83+
Array [
84+
"mock-schema",
85+
],
86+
],
87+
"results": Array [
88+
Object {
89+
"type": "return",
90+
"value": undefined,
91+
},
92+
],
93+
}
94+
`)
95+
})
96+
97+
it('calls `reporter.panic` if `generateWithConfig` throws', async () => {
98+
const mockGatsbyArgs = {
99+
store: {
100+
getState: () => ({
101+
schema: 'mock-schema',
102+
program: { directory: 'mock-directory' },
103+
}),
104+
subscribe: jest.fn(),
105+
},
106+
reporter: {
107+
info: jest.fn(),
108+
panic: jest.fn(),
109+
},
110+
}
111+
112+
const pluginOptions = {
113+
documentPaths: ['./example-document-paths'],
114+
fileName: './example-filename.ts',
115+
plugins: [],
116+
}
117+
118+
const mockGenerateFromSchema = () => {
119+
throw new Error('mock error')
120+
}
121+
generateWithConfig.mockReturnValueOnce(mockGenerateFromSchema)
122+
123+
await onPostBootstrap(mockGatsbyArgs, pluginOptions)
124+
125+
expect(mockGatsbyArgs.reporter.panic).toMatchInlineSnapshot(`
126+
[MockFunction] {
127+
"calls": Array [
128+
Array [
129+
[Error: mock error],
130+
],
131+
],
132+
"results": Array [
133+
Object {
134+
"type": "return",
135+
"value": undefined,
136+
},
137+
],
138+
}
139+
`)
140+
})
141+
142+
it('subscribes to the store and debounces the `build` function', async () => {
143+
// this variable is assigned when the `.subscribe` function is called.
144+
// it allows us to invoke the listener and mock "notify" its state
145+
let notify
146+
147+
const codegenDelay = 50
148+
149+
const mockState = {
150+
schema: 'mock-schema',
151+
program: { directory: 'mock-directory' },
152+
// this type has to be either `QUERY_EXTRACTED` or `REPLACE_STATIC_QUERY`
153+
lastAction: { type: 'QUERY_EXTRACTED' },
154+
}
155+
156+
const mockGatsbyArgs = {
157+
store: {
158+
getState: () => mockState,
159+
subscribe: (listener) => {
160+
notify = listener
161+
},
162+
},
163+
reporter: {
164+
info: jest.fn(),
165+
panic: jest.fn(),
166+
},
167+
}
168+
169+
const pluginOptions = {
170+
documentPaths: ['./example-document-paths'],
171+
fileName: './example-filename.ts',
172+
codegenDelay,
173+
plugins: [],
174+
}
175+
176+
const mockGenerateFromSchema = jest.fn()
177+
generateWithConfig.mockReturnValue(mockGenerateFromSchema)
178+
179+
await onPostBootstrap(mockGatsbyArgs, pluginOptions)
180+
expect(mockGenerateFromSchema).toHaveBeenCalledTimes(1)
181+
182+
// notify the subscriber 5 times
183+
for (let i = 0; i < 5; i += 1) {
184+
notify()
185+
}
186+
187+
// wait 2x the amount of the codegen delay to ensure everything has flushed
188+
await delay(codegenDelay * 2)
189+
// because of the debounce, we should expect it to have only been called
190+
// twice instead of 6 times
191+
expect(mockGenerateFromSchema).toHaveBeenCalledTimes(2)
192+
193+
expect(mockGatsbyArgs.reporter.info).toHaveBeenCalled()
194+
expect(mockGatsbyArgs.reporter.panic).not.toHaveBeenCalled()
195+
})
196+
197+
it("doesn't call build if the `lastAction.type` isn't 'REPLACE_STATIC_QUERY' or 'QUERY_EXTRACTED'", async () => {
198+
// this variable is assigned when the `.subscribe` function is called.
199+
// it allows us to invoke the listener and mock "notify" its state
200+
let notify
201+
202+
const codegenDelay = 50
203+
204+
const mockState = {
205+
schema: 'mock-schema',
206+
program: { directory: 'mock-directory' },
207+
lastAction: { type: 'NOT_THE_CORRECT_ACTION' },
208+
}
209+
210+
const mockGatsbyArgs = {
211+
store: {
212+
getState: () => mockState,
213+
subscribe: (listener) => {
214+
notify = listener
215+
},
216+
},
217+
reporter: {
218+
info: jest.fn(),
219+
panic: jest.fn(),
220+
},
221+
}
222+
223+
const pluginOptions = {
224+
documentPaths: ['./example-document-paths'],
225+
fileName: './example-filename.ts',
226+
codegenDelay,
227+
plugins: [],
228+
}
229+
230+
const mockGenerateFromSchema = jest.fn()
231+
generateWithConfig.mockReturnValue(mockGenerateFromSchema)
232+
233+
await onPostBootstrap(mockGatsbyArgs, pluginOptions)
234+
expect(mockGenerateFromSchema).toHaveBeenCalledTimes(1)
235+
236+
// notify the subscriber 5 times
237+
for (let i = 0; i < 5; i += 1) {
238+
notify()
239+
}
240+
241+
// wait 2x the amount of the codegen delay to ensure everything has flushed
242+
await delay(codegenDelay * 2)
243+
// because the lastAction.type above isn't the 'REPLACE_STATIC_QUERY' or
244+
// 'QUERY_EXTRACTED', this will only be called once
245+
expect(mockGenerateFromSchema).toHaveBeenCalledTimes(1)
246+
})

packages/gatsby-plugin-graphql-codegen/src/gatsby-node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const getOptions: GetOptions = (pluginOptions) => ({
2727
...pluginOptions,
2828
})
2929

30-
export const onPostBootstrap: GatsbyNode["onPostBootstrap"] = async (
30+
export const onPostBootstrap: NonNullable<GatsbyNode["onPostBootstrap"]> = async (
3131
{ store, reporter }, pluginOptions: TsOptions
3232
) => {
3333
const options = getOptions(pluginOptions)

0 commit comments

Comments
 (0)