Skip to content

Commit 37e3d37

Browse files
committed
fix: do not crash component tests on stale cached files
1 parent 988bc82 commit 37e3d37

File tree

2 files changed

+65
-9
lines changed

2 files changed

+65
-9
lines changed

packages/playwright-ct-core/src/vitePlugin.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,25 @@
1717
import fs from 'fs';
1818
import path from 'path';
1919

20+
import { assert, calculateSha1, getPlaywrightVersion, isURLAvailable } from 'playwright-core/lib/utils';
21+
import { colors, debug } from 'playwright-core/lib/utilsBundle';
2022
import { setExternalDependencies } from 'playwright/lib/transform/compilationCache';
2123
import { resolveHook } from 'playwright/lib/transform/transform';
2224
import { removeDirAndLogToConsole } from 'playwright/lib/util';
2325
import { stoppable } from 'playwright/lib/utilsBundle';
24-
import { isURLAvailable } from 'playwright-core/lib/utils';
25-
import { assert, calculateSha1, getPlaywrightVersion } from 'playwright-core/lib/utils';
26-
import { colors, debug } from 'playwright-core/lib/utilsBundle';
2726

2827
import { runDevServer } from './devServer';
2928
import { source as injectedSource } from './generated/indexSource';
3029
import { createConfig, frameworkConfig, hasJSComponents, populateComponentsFromTests, resolveDirs, resolveEndpoint, transformIndexFile } from './viteUtils';
3130

32-
import type { ImportInfo } from './tsxTransform';
33-
import type { ComponentRegistry } from './viteUtils';
34-
import type { TestRunnerPlugin } from '../../playwright/src/plugins';
3531
import type http from 'http';
3632
import type { AddressInfo } from 'net';
3733
import type { FullConfig, Suite } from 'playwright/types/testReporter';
3834
import type { PluginContext } from 'rollup';
3935
import type { Plugin, ResolveFn, ResolvedConfig } from 'vite';
36+
import type { TestRunnerPlugin } from '../../playwright/src/plugins';
37+
import type { ImportInfo } from './tsxTransform';
38+
import type { ComponentRegistry } from './viteUtils';
4039

4140

4241
const log = debug('pw:vite');
@@ -235,8 +234,16 @@ async function checkNewComponents(buildInfo: BuildInfo, componentRegistry: Compo
235234
break;
236235
}
237236
}
238-
for (const c of oldComponents.values())
239-
componentRegistry.set(c.id, c);
237+
for (const c of oldComponents.values()) {
238+
try {
239+
if ((await fs.promises.stat(c.filename)))
240+
componentRegistry.set(c.id, c);
241+
} catch (e) {
242+
if (e.code !== 'ENOENT')
243+
throw e;
244+
log('non existent file, skipping component registry:', c.filename);
245+
}
246+
}
240247

241248
return hasNewComponents;
242249
}

tests/playwright-test/playwright.ct-build.spec.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { test, expect, playwrightCtConfigText } from './playwright-test-fixtures';
1817
import fs from 'fs';
1918
import path from 'path';
19+
import { expect, playwrightCtConfigText, test } from './playwright-test-fixtures';
2020

2121
test.describe.configure({ mode: 'parallel' });
2222

@@ -351,6 +351,55 @@ test('should grow cache', async ({ runInlineTest }, testInfo) => {
351351
});
352352
});
353353

354+
test('should not crash when cached component test file is deleted', async ({ runInlineTest }, testInfo) => {
355+
356+
await test.step('run first test to build the cache', async () => {
357+
const result = await runInlineTest({
358+
'playwright.config.ts': playwrightCtConfigText,
359+
'playwright/index.html': `<script type="module" src="./index.ts"></script>`,
360+
'playwright/index.ts': ``,
361+
'src/button.tsx': `
362+
export const Button = () => <button>Button</button>;
363+
`,
364+
'src/button.test.tsx': `
365+
import { test, expect } from '@playwright/experimental-ct-react';
366+
import { Button } from './button.tsx';
367+
test('pass', async ({ mount }) => {
368+
const component = await mount(<Button></Button>);
369+
await expect(component).toHaveText('Button');
370+
});
371+
`,
372+
'src/button2.tsx': `
373+
export const Button2 = () => <button>Button 2</button>;
374+
`,
375+
'src/button2.test.tsx': `
376+
import { test, expect } from '@playwright/experimental-ct-react';
377+
import { Button2 } from './button2.tsx';
378+
test('pass', async ({ mount }) => {
379+
const component = await mount(<Button2></Button2>);
380+
await expect(component).toHaveText('Button 2');
381+
});
382+
`,
383+
}, { workers: 1 });
384+
385+
expect(result.exitCode).toBe(0);
386+
expect(result.passed).toBe(2);
387+
388+
});
389+
390+
await test.step('remove the second test and component and run the tests again', async () => {
391+
392+
fs.unlinkSync(testInfo.outputPath('src/button2.tsx'));
393+
fs.unlinkSync(testInfo.outputPath('src/button2.test.tsx'));
394+
395+
const result2 = await runInlineTest({}, { workers: 1 });
396+
397+
expect(result2.exitCode).toBe(0);
398+
expect(result2.passed).toBe(1);
399+
});
400+
401+
});
402+
354403
test('should not use global config for preview', async ({ runInlineTest }) => {
355404
const result1 = await runInlineTest({
356405
'playwright.config.ts': playwrightCtConfigText,

0 commit comments

Comments
 (0)