Skip to content

Commit c87b779

Browse files
authored
[CLI] Create watcher.run.spec to test actual running behaviour (#10474)
* Create watcher.run.spec to test actual running behaviour * Rename watcher.spec to watcher.patterns.spec to clarify scenarios it is testing * Split watcher tests until there is Node 22+ support
1 parent 76a71d9 commit c87b779

File tree

3 files changed

+172
-59
lines changed

3 files changed

+172
-59
lines changed

.github/workflows/main.yml

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,17 +148,46 @@ jobs:
148148
run: node ./scripts/match-graphql.js ${{matrix.graphql_version}}
149149
- name: Install Dependencies
150150
run: yarn
151-
- name: Cache Vitest
152-
uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4
151+
- name: Build
152+
run: yarn build
153+
- name: Test
154+
# FIXME: Do not test `watcher.run.spec.ts` here because it is currently not working for Node22+ in Ubuntu
155+
run: yarn test --exclude="**/tests/watcher.run.spec.ts"
156+
env:
157+
CI: true
158+
159+
test-watcher:
160+
name: Watcher Unit Test on Node ${{matrix.node_version}} (${{matrix.os}}) and GraphQL v${{matrix.graphql_version}}
161+
runs-on: ubuntu-latest
162+
needs:
163+
- lint
164+
- prettier-check
165+
- esm
166+
strategy:
167+
matrix:
168+
os: [ubuntu-latest] # remove windows to speed up the tests
169+
node_version: [20]
170+
graphql_version: [15, 16]
171+
include:
172+
- node-version: 20
173+
os: windows-latest
174+
graphql_version: 16
175+
steps:
176+
- name: Checkout
177+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
178+
- name: Setup env
179+
uses: the-guild-org/shared-config/setup@main
153180
with:
154-
path: node_modules/.vite
155-
key: ${{runner.os}}-${{matrix.node_version}}-${{matrix.graphql_version}}-vitest-${{hashFiles('yarn.lock')}}
156-
restore-keys: |
157-
${{runner.os}}-${{matrix.node_version}}-${{matrix.graphql_version}}-vitest-
181+
nodeVersion: ${{matrix.node_version}}
182+
- name: Use GraphQL v${{matrix.graphql_version}}
183+
run: node ./scripts/match-graphql.js ${{matrix.graphql_version}}
184+
- name: Install Dependencies
185+
run: yarn
158186
- name: Build
159187
run: yarn build
160188
- name: Test
161-
run: yarn test
189+
# FIXME: Merge `watcher.run.spec.ts` test to the above when it works for Node22+ in Ubuntu
190+
run: yarn test packages/graphql-codegen-cli/tests/watcher.run.spec.ts
162191
env:
163192
CI: true
164193

packages/graphql-codegen-cli/tests/watcher.spec.ts renamed to packages/graphql-codegen-cli/tests/watcher.patterns.spec.ts

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ const unsubscribeMock = vi.fn();
1010
const subscribeMock = vi.fn();
1111
let subscribeCallbackMock: Mock<SubscribeCallback>;
1212

13-
// FIXME: this mocks out the main functionality which is triggering the codegen
14-
// This is not great because we cannot test the actual watch functionality
1513
vi.mock('@parcel/watcher', () => ({
1614
subscribe: subscribeMock.mockImplementation((watchDirectory: string, subscribeCallback: SubscribeCallback) => {
1715
subscribeCallbackMock = vi.fn(subscribeCallback);
@@ -35,7 +33,7 @@ const setupMockWatcher = async (
3533
return { stopWatching, dispatchChange };
3634
};
3735

38-
describe('Watch targets', () => {
36+
describe('Watch patterns', () => {
3937
beforeEach(() => {
4038
vi.clearAllMocks();
4139

@@ -776,53 +774,4 @@ describe('Watch targets', () => {
776774
}
777775
);
778776
});
779-
780-
test('it does not call onNext on error', async () => {
781-
vi.spyOn(fs, 'access').mockImplementation(() => Promise.resolve());
782-
const onNextMock = vi.fn();
783-
784-
const schema = /* GraphQL */ `
785-
type Query {
786-
me: User
787-
}
788-
789-
type User {
790-
id: ID
791-
}
792-
`;
793-
const document = /* GraphQL */ `
794-
query {
795-
me {
796-
id
797-
zzz # Error here
798-
}
799-
}
800-
`;
801-
802-
const { stopWatching } = await setupMockWatcher(
803-
{
804-
filepath: './foo/some-config.ts',
805-
config: {
806-
hooks: { onWatchTriggered: vi.fn() },
807-
schema,
808-
documents: document,
809-
generates: {
810-
['./foo/some-output.ts']: {
811-
plugins: ['typescript'],
812-
},
813-
},
814-
},
815-
},
816-
onNextMock
817-
);
818-
819-
// Because document has error, onNext shouldn't be called
820-
expect(onNextMock).not.toHaveBeenCalled();
821-
822-
// Wait a tick for stopWatch to be set up correctly, before calling it
823-
await new Promise(resolve => setTimeout(resolve, 100));
824-
await stopWatching();
825-
});
826-
827-
test.todo('on watcher subsequent codegen run, it does not call onNext on error');
828777
});
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import type { Mock } from 'vitest';
2+
import * as path from 'path';
3+
import { mkdtempSync, mkdirSync, writeFileSync } from 'fs';
4+
import { createWatcher } from '../src/utils/watcher.js';
5+
import { CodegenContext } from '../src/config.js';
6+
7+
/**
8+
* waitForNextEvent
9+
* @description This function waits for a short amount of time to let async things run
10+
* e.g. watcher subscription setup, watcher to react to change/create events, etc.
11+
*/
12+
const waitForNextEvent = async () => {
13+
return await new Promise(resolve => setTimeout(resolve, 500));
14+
};
15+
16+
type TestFilePaths = { absolute: string; relative: string };
17+
const setupTestFiles = (): { testDir: string; schemaFile: TestFilePaths; documentFile: TestFilePaths } => {
18+
const tempDir = path.join(__dirname, '..', 'temp');
19+
mkdirSync(tempDir, { recursive: true });
20+
21+
const testDir = mkdtempSync(path.join(tempDir, 'watcher-run-spec-'));
22+
23+
const schemaFileAbsolute = path.join(testDir, 'schema.graphql');
24+
const schemaFile = {
25+
absolute: schemaFileAbsolute,
26+
relative: path.relative(process.cwd(), schemaFileAbsolute),
27+
};
28+
29+
const documentFileAbsolute = path.join(testDir, 'document.graphql');
30+
const documentFile = {
31+
absolute: documentFileAbsolute,
32+
relative: path.relative(process.cwd(), documentFileAbsolute),
33+
};
34+
35+
return {
36+
testDir,
37+
schemaFile,
38+
documentFile,
39+
};
40+
};
41+
42+
const onNextMock = vi.fn();
43+
44+
const setupMockWatcher = async (
45+
codegenContext: ConstructorParameters<typeof CodegenContext>[0],
46+
onNext: Mock = vi.fn().mockResolvedValue([])
47+
) => {
48+
const { stopWatching } = createWatcher(new CodegenContext(codegenContext), onNext);
49+
// After creating watcher, wait for a tick for subscription to be completely set up
50+
await waitForNextEvent();
51+
return { stopWatching };
52+
};
53+
54+
describe('Watch runs', () => {
55+
test('calls onNext correctly on initial runs and subsequent runs', async () => {
56+
const { testDir, schemaFile, documentFile } = setupTestFiles();
57+
writeFileSync(
58+
schemaFile.absolute,
59+
/* GraphQL */ `
60+
type Query {
61+
me: User
62+
}
63+
64+
type User {
65+
id: ID!
66+
name: String!
67+
}
68+
`
69+
);
70+
writeFileSync(
71+
documentFile.absolute,
72+
/* GraphQL */ `
73+
query {
74+
me {
75+
id
76+
}
77+
}
78+
`
79+
);
80+
await waitForNextEvent();
81+
const { stopWatching } = await setupMockWatcher(
82+
{
83+
filepath: path.join(testDir, 'codegen.ts'),
84+
config: {
85+
schema: schemaFile.relative,
86+
documents: documentFile.relative,
87+
generates: {
88+
[path.join(testDir, 'types.ts')]: {
89+
plugins: ['typescript'],
90+
},
91+
},
92+
},
93+
},
94+
onNextMock
95+
);
96+
97+
// 1. Initial setup: onNext in initial run should be called because no errors
98+
expect(onNextMock).toHaveBeenCalledTimes(1);
99+
100+
// 2. Subsequent run 1: correct document file, so `onNext` is called again because no errors
101+
writeFileSync(
102+
documentFile.absolute,
103+
/* GraphQL */ `
104+
query {
105+
me {
106+
id
107+
name
108+
}
109+
}
110+
`
111+
);
112+
await waitForNextEvent();
113+
expect(onNextMock).toHaveBeenCalledTimes(2);
114+
115+
// 3. Subsequent run 2: incorrect document file, so `onNext` is NOT called
116+
writeFileSync(
117+
documentFile.absolute,
118+
/* GraphQL */ `
119+
query {
120+
me {
121+
id
122+
name
123+
zzzz # should throw error
124+
}
125+
}
126+
`
127+
);
128+
await waitForNextEvent();
129+
expect(onNextMock).toHaveBeenCalledTimes(2);
130+
131+
await stopWatching();
132+
133+
await waitForNextEvent();
134+
});
135+
});

0 commit comments

Comments
 (0)