diff --git a/packages/playwright/src/program.ts b/packages/playwright/src/program.ts index 801c88bc98aea..fe09d6f1a6eaf 100644 --- a/packages/playwright/src/program.ts +++ b/packages/playwright/src/program.ts @@ -37,6 +37,7 @@ import { runAllTestsWithConfig, TestRunner } from './runner/testRunner'; import { createErrorCollectingReporter } from './runner/reporters'; import { TestServerBackend, testServerBackendTools } from './mcp/test/testBackend'; import { ClaudeGenerator, OpencodeGenerator, VSCodeGenerator, CopilotGenerator } from './agents/generateAgents'; +import { loadTestList } from './runner/loadUtils'; import type { ConfigCLIOverrides } from './common/ipc'; import type { TraceMode } from '../types/test'; @@ -208,6 +209,12 @@ async function runTests(args: string[], opts: { [key: string]: any }) { config.cliTestList = opts.testList ? path.resolve(process.cwd(), opts.testList) : undefined; config.cliTestListInvert = opts.testListInvert ? path.resolve(process.cwd(), opts.testListInvert) : undefined; + if (config.cliTestList) { + const { files } = await loadTestList(config, config.cliTestList); + for (const project of config.projects) + project.project.testMatch = files; + } + // Evaluate project filters against config before starting execution. This enables a consistent error message across run modes filterProjects(config.projects, config.cliProjectFilter); diff --git a/packages/playwright/src/runner/loadUtils.ts b/packages/playwright/src/runner/loadUtils.ts index f97066c229192..eafc9388f1d34 100644 --- a/packages/playwright/src/runner/loadUtils.ts +++ b/packages/playwright/src/runner/loadUtils.ts @@ -350,7 +350,7 @@ function sourceMapSources(file: string, cache: Map): string[] } } -export async function loadTestList(config: FullConfigInternal, filePath: string): Promise { +export async function loadTestList(config: FullConfigInternal, filePath: string): Promise { try { const content = await fs.promises.readFile(filePath, 'utf-8'); const lines = content.split('\n').map(line => line.trim()).filter(line => line && !line.startsWith('#')); @@ -366,8 +366,7 @@ export async function loadTestList(config: FullConfigInternal, filePath: string) } return { project, file: toPosixPath(parseLocationArg(tokens[0]).file), titlePath: tokens.slice(1) }; }); - return (test: TestCase) => descriptions.some(d => { - // Note: there is no root yet at the time of filtering. + const filter = (test: TestCase) => descriptions.some(d => { const [projectName, , ...titles] = test.titlePath(); if (d.project !== undefined && d.project !== projectName) return false; @@ -376,6 +375,8 @@ export async function loadTestList(config: FullConfigInternal, filePath: string) return false; return d.titlePath.length <= titles.length && d.titlePath.every((_, index) => titles[index] === d.titlePath[index]); }); + filter.files = [...new Set(descriptions.map(d => d.file))]; + return filter; } catch (e) { throw errorWithFile(filePath, 'Cannot read test list file: ' + e.message); } diff --git a/tests/playwright-test/test-list.spec.ts b/tests/playwright-test/test-list.spec.ts index f57d502a676d9..3c77abc80ce68 100644 --- a/tests/playwright-test/test-list.spec.ts +++ b/tests/playwright-test/test-list.spec.ts @@ -226,3 +226,28 @@ test('--test-list with nested group entry should run only tests in that nested g 'd-test2-p2', ]); }); + +test('--test-list should not load files not in the list', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'playwright.config.ts': ` + module.exports = { testDir: '.' }; + `, + 'test.list': ` + good.test.ts + `, + 'good.test.ts': ` + import { test, expect } from '@playwright/test'; + test('good test', async () => { console.log('\\n%%good-test'); }); + `, + 'broken.test.ts': ` + // This file uses invalid syntax that would crash if loaded + describe('broken', () => { + it('should fail', () => {}); + }); + `, + }, { 'workers': 1, 'test-list': 'test.list' }); + // Should succeed without loading broken.test.ts + expect(result.exitCode).toBe(0); + expect(result.passed).toBe(1); + expect(result.outputLines).toEqual(['good-test']); +});