Skip to content

Commit 7d0c249

Browse files
authored
Await dev server plugin callback and refactor tests (#1570)
* Await dev server plugin callback and refactor tests * Clarify mock import order * Improve mock import order comment * Update devServerPlugin.ts * Clean up needless unassignment * Update startDevServer.ts
1 parent 69b7640 commit 7d0c249

File tree

11 files changed

+206
-296
lines changed

11 files changed

+206
-296
lines changed

packages/react-cosmos/src/cosmosConfig/__tests__/cosmosConfigFile.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// Import mocks first
1+
// WARNING: Module mocks need to be imported before the mocked modules are
2+
// imported, which are sometimes imported indirectly by the modules being
3+
// tested. Otherwise the mocks will be applied too late and the tests will run
4+
// against the unmocked original modules instead.
25
import { mockCosmosConfig, resetFsMock } from '../../testHelpers/mockFs.js';
36
import { mockCliArgs, unmockCliArgs } from '../../testHelpers/mockYargs.js';
47

packages/react-cosmos/src/cosmosConfig/__tests__/exposeImports.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// Import mocks first
1+
// WARNING: Module mocks need to be imported before the mocked modules are
2+
// imported, which are sometimes imported indirectly by the modules being
3+
// tested. Otherwise the mocks will be applied too late and the tests will run
4+
// against the unmocked original modules instead.
25
import { mockCliArgs, unmockCliArgs } from '../../testHelpers/mockYargs.js';
36

47
import { getCwdPath } from '../../testHelpers/cwd.js';

packages/react-cosmos/src/cosmosConfig/__tests__/lazy.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// Import mocks first
1+
// WARNING: Module mocks need to be imported before the mocked modules are
2+
// imported, which are sometimes imported indirectly by the modules being
3+
// tested. Otherwise the mocks will be applied too late and the tests will run
4+
// against the unmocked original modules instead.
25
import { mockCliArgs, unmockCliArgs } from '../../testHelpers/mockYargs.js';
36

47
import { createCosmosConfig } from '../createCosmosConfig.js';

packages/react-cosmos/src/cosmosConfig/__tests__/port.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// Import mocks first
1+
// WARNING: Module mocks need to be imported before the mocked modules are
2+
// imported, which are sometimes imported indirectly by the modules being
3+
// tested. Otherwise the mocks will be applied too late and the tests will run
4+
// against the unmocked original modules instead.
25
import { mockCliArgs, unmockCliArgs } from '../../testHelpers/mockYargs.js';
36

47
import { createCosmosConfig } from '../createCosmosConfig.js';
Lines changed: 58 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
// Import mocks first
2-
import { jestWorkerId } from '../../testHelpers/jestProcessUtils.js';
3-
import { mockConsole } from '../../testHelpers/mockConsole.js';
1+
// WARNING: Module mocks need to be imported before the mocked modules are
2+
// imported, which are sometimes imported indirectly by the modules being
3+
// tested. Otherwise the mocks will be applied too late and the tests will run
4+
// against the unmocked original modules instead.
45
import { mockCosmosPlugins } from '../../testHelpers/mockCosmosPlugins.js';
5-
import {
6-
mockCosmosConfig,
7-
mockFile,
8-
resetFsMock,
9-
} from '../../testHelpers/mockFs.js';
10-
import { mockCliArgs, unmockCliArgs } from '../../testHelpers/mockYargs.js';
6+
import { mockCosmosConfig, mockFile } from '../../testHelpers/mockFs.js';
7+
import { mockCliArgs } from '../../testHelpers/mockYargs.js';
118

129
import retry from '@skidding/async-retry';
1310
import 'isomorphic-fetch';
1411
import * as http from 'node:http';
1512
import path from 'node:path';
13+
import { setTimeout } from 'node:timers/promises';
1614
import { ServerMessage, SocketMessage } from 'react-cosmos-core';
1715
import { DevServerPluginArgs } from '../../cosmosPlugin/types.js';
16+
import { jestWorkerId } from '../../testHelpers/jestProcessUtils.js';
17+
import { mockConsole } from '../../testHelpers/mockConsole.js';
1818
import { startDevServer } from '../startDevServer.js';
1919

2020
const testCosmosPlugin = {
@@ -36,123 +36,83 @@ const testServerPlugin = {
3636
}),
3737

3838
devServer: jest.fn(async () => {
39-
await new Promise(resolve => setTimeout(resolve, 50));
40-
return () => devServerCleanup();
39+
await setTimeout(50);
40+
return async () => {
41+
await setTimeout(50);
42+
devServerCleanup();
43+
};
4144
}),
4245
};
4346

4447
const port = 5000 + jestWorkerId();
4548

4649
let _stopServer: (() => Promise<unknown>) | undefined;
4750

48-
async function stopServer() {
49-
if (_stopServer) {
50-
await _stopServer();
51-
_stopServer = undefined;
52-
}
53-
}
54-
55-
beforeEach(() => {
51+
beforeAll(async () => {
5652
mockCliArgs({});
5753
mockCosmosConfig('cosmos.config.json', { port });
5854
mockFile(testCosmosPlugin.server, { default: testServerPlugin });
5955

60-
devServerCleanup.mockClear();
61-
testServerPlugin.config.mockClear();
62-
testServerPlugin.devServer.mockClear();
63-
});
64-
65-
afterEach(async () => {
66-
await stopServer();
67-
unmockCliArgs();
68-
resetFsMock();
69-
});
70-
71-
it('calls config hook', async () => {
72-
return mockConsole(async ({ expectLog }) => {
56+
await mockConsole(async ({ expectLog }) => {
7357
expectLog('[Cosmos] Using cosmos config found at cosmos.config.json');
7458
expectLog('[Cosmos] Found 1 plugin: Test Cosmos plugin');
7559
expectLog(`[Cosmos] See you at http://localhost:${port}`);
76-
7760
_stopServer = await startDevServer('web');
61+
});
62+
});
7863

79-
expect(testServerPlugin.config).toBeCalledWith({
80-
cosmosConfig: expect.objectContaining({ port }),
81-
command: 'dev',
82-
platform: 'web',
83-
});
64+
it('calls config hook', async () => {
65+
expect(testServerPlugin.config).toBeCalledWith({
66+
cosmosConfig: expect.objectContaining({ port }),
67+
command: 'dev',
68+
platform: 'web',
8469
});
8570
});
8671

8772
it('calls dev server hook (with updated config)', async () => {
88-
return mockConsole(async ({ expectLog }) => {
89-
expectLog('[Cosmos] Using cosmos config found at cosmos.config.json');
90-
expectLog('[Cosmos] Found 1 plugin: Test Cosmos plugin');
91-
expectLog(`[Cosmos] See you at http://localhost:${port}`);
92-
93-
_stopServer = await startDevServer('web');
94-
95-
expect(testServerPlugin.devServer).toBeCalledWith({
96-
cosmosConfig: expect.objectContaining({
97-
port,
98-
ignore: ['**/ignored.fixture.js'],
99-
}),
100-
platform: 'web',
101-
expressApp: expect.any(Function),
102-
httpServer: expect.any(http.Server),
103-
sendMessage: expect.any(Function),
104-
});
105-
106-
await stopServer();
107-
108-
expect(devServerCleanup).toBeCalled();
73+
expect(testServerPlugin.devServer).toBeCalledWith({
74+
cosmosConfig: expect.objectContaining({
75+
port,
76+
ignore: ['**/ignored.fixture.js'],
77+
}),
78+
platform: 'web',
79+
expressApp: expect.any(Function),
80+
httpServer: expect.any(http.Server),
81+
sendMessage: expect.any(Function),
10982
});
11083
});
11184

11285
it('calls dev server hook with send message API', async () => {
113-
return mockConsole(async ({ expectLog }) => {
114-
expectLog('[Cosmos] Using cosmos config found at cosmos.config.json');
115-
expectLog('[Cosmos] Found 1 plugin: Test Cosmos plugin');
116-
expectLog(`[Cosmos] See you at http://localhost:${port}`);
117-
118-
_stopServer = await startDevServer('web');
119-
120-
const client = new WebSocket(`ws://localhost:${port}`);
121-
122-
const message: SocketMessage<ServerMessage> = {
123-
channel: 'server',
124-
message: {
125-
type: 'buildStart',
126-
},
127-
};
128-
129-
const onMessage = jest.fn();
130-
client.addEventListener('open', () => {
131-
client.addEventListener('message', msg => onMessage(msg.data));
132-
133-
const [args] = testServerPlugin.devServer.mock
134-
.calls[0] as DevServerPluginArgs[];
135-
args.sendMessage(message.message);
136-
});
137-
138-
await retry(() =>
139-
expect(onMessage).toBeCalledWith(JSON.stringify(message))
140-
);
86+
const client = new WebSocket(`ws://localhost:${port}`);
87+
88+
const message: SocketMessage<ServerMessage> = {
89+
channel: 'server',
90+
message: {
91+
type: 'buildStart',
92+
},
93+
};
94+
95+
const onMessage = jest.fn();
96+
client.addEventListener('open', () => {
97+
client.addEventListener('message', msg => onMessage(msg.data));
98+
99+
const [args] = testServerPlugin.devServer.mock
100+
.calls[0] as DevServerPluginArgs[];
101+
args.sendMessage(message.message);
141102
});
103+
104+
await retry(() => expect(onMessage).toBeCalledWith(JSON.stringify(message)));
142105
});
143106

144107
it('embeds plugin in playground HTML', async () => {
145-
return mockConsole(async ({ expectLog }) => {
146-
expectLog('[Cosmos] Using cosmos config found at cosmos.config.json');
147-
expectLog('[Cosmos] Found 1 plugin: Test Cosmos plugin');
148-
expectLog(`[Cosmos] See you at http://localhost:${port}`);
149-
150-
_stopServer = await startDevServer('web');
108+
const res = await fetch(`http://localhost:${port}`);
109+
expect(res.status).toBe(200);
151110

152-
const res = await fetch(`http://localhost:${port}`);
153-
expect(res.status).toBe(200);
111+
const html = await res.text();
112+
expect(html).toContain(JSON.stringify([testCosmosPlugin]));
113+
});
154114

155-
const html = await res.text();
156-
expect(html).toContain(JSON.stringify([testCosmosPlugin]));
157-
});
115+
it('calls dev server cleanup hook', async () => {
116+
await _stopServer!();
117+
expect(devServerCleanup).toBeCalled();
158118
});
Lines changed: 30 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
// Import mocks first
2-
import { jestWorkerId } from '../../testHelpers/jestProcessUtils.js';
3-
import { mockConsole } from '../../testHelpers/mockConsole.js';
1+
// WARNING: Module mocks need to be imported before the mocked modules are
2+
// imported, which are sometimes imported indirectly by the modules being
3+
// tested. Otherwise the mocks will be applied too late and the tests will run
4+
// against the unmocked original modules instead.
45
import { mockCosmosPlugins } from '../../testHelpers/mockCosmosPlugins.js';
5-
import { mockCosmosConfig, resetFsMock } from '../../testHelpers/mockFs.js';
6-
import { mockCliArgs, unmockCliArgs } from '../../testHelpers/mockYargs.js';
6+
import { mockCosmosConfig } from '../../testHelpers/mockFs.js';
7+
import { mockCliArgs } from '../../testHelpers/mockYargs.js';
78

89
import 'isomorphic-fetch';
910
import fs from 'node:fs/promises';
1011
import path from 'node:path';
12+
import { jestWorkerId } from '../../testHelpers/jestProcessUtils.js';
13+
import { mockConsole } from '../../testHelpers/mockConsole.js';
1114
import { startDevServer } from '../startDevServer.js';
1215

1316
const port = 5000 + jestWorkerId();
@@ -24,14 +27,7 @@ mockCosmosPlugins([testCosmosPlugin]);
2427

2528
let _stopServer: (() => Promise<unknown>) | undefined;
2629

27-
async function stopServer() {
28-
if (_stopServer) {
29-
await _stopServer();
30-
_stopServer = undefined;
31-
}
32-
}
33-
34-
beforeEach(async () => {
30+
beforeAll(async () => {
3531
mockCliArgs({});
3632
mockCosmosConfig('cosmos.config.json', {
3733
rootDir: testFsPath,
@@ -40,47 +36,37 @@ beforeEach(async () => {
4036

4137
await fs.mkdir(testCosmosPlugin.rootDir, { recursive: true });
4238
await fs.writeFile(testCosmosPlugin.ui, 'export {}', 'utf8');
43-
});
4439

45-
afterEach(async () => {
46-
await stopServer();
47-
unmockCliArgs();
48-
resetFsMock();
49-
await fs.rm(pluginPath, { recursive: true, force: true });
50-
});
51-
52-
it('embeds plugin in playground HTML', async () => {
53-
return mockConsole(async ({ expectLog }) => {
40+
await mockConsole(async ({ expectLog }) => {
5441
expectLog('[Cosmos] Using cosmos config found at cosmos.config.json');
5542
expectLog('[Cosmos] Found 1 plugin: Test Cosmos plugin');
5643
expectLog(`[Cosmos] See you at http://localhost:${port}`);
5744

5845
_stopServer = await startDevServer('web');
59-
60-
const res = await fetch(`http://localhost:${port}`);
61-
expect(res.status).toBe(200);
62-
63-
const html = await res.text();
64-
expect(html).toContain(JSON.stringify([testCosmosPlugin]));
6546
});
6647
});
6748

68-
it('serves plugin JS files', async () => {
69-
return mockConsole(async ({ expectLog }) => {
70-
expectLog('[Cosmos] Using cosmos config found at cosmos.config.json');
71-
expectLog('[Cosmos] Found 1 plugin: Test Cosmos plugin');
72-
expectLog(`[Cosmos] See you at http://localhost:${port}`);
49+
afterAll(async () => {
50+
await _stopServer!();
51+
await fs.rm(pluginPath, { recursive: true, force: true });
52+
});
7353

74-
_stopServer = await startDevServer('web');
54+
it('embeds plugin in playground HTML', async () => {
55+
const res = await fetch(`http://localhost:${port}`);
56+
expect(res.status).toBe(200);
7557

76-
// Windows paths don't start with a slash (e.g. C:\foo\bar.js)
77-
const uiPath = testCosmosPlugin.ui.startsWith('/')
78-
? testCosmosPlugin.ui
79-
: `/${testCosmosPlugin.ui}`;
80-
const res = await fetch(`http://localhost:${port}/_plugin${uiPath}`);
81-
expect(res.status).toBe(200);
58+
const html = await res.text();
59+
expect(html).toContain(JSON.stringify([testCosmosPlugin]));
60+
});
8261

83-
const uiJs = await res.text();
84-
expect(uiJs).toBe('export {}');
85-
});
62+
it('serves plugin JS files', async () => {
63+
// Windows paths don't start with a slash (e.g. C:\foo\bar.js)
64+
const uiPath = testCosmosPlugin.ui.startsWith('/')
65+
? testCosmosPlugin.ui
66+
: `/${testCosmosPlugin.ui}`;
67+
const res = await fetch(`http://localhost:${port}/_plugin${uiPath}`);
68+
expect(res.status).toBe(200);
69+
70+
const uiJs = await res.text();
71+
expect(uiJs).toBe('export {}');
8672
});

0 commit comments

Comments
 (0)