diff --git a/docs/src/test-cli-js.md b/docs/src/test-cli-js.md index 422e5bb16e..f91a0b6864 100644 --- a/docs/src/test-cli-js.md +++ b/docs/src/test-cli-js.md @@ -29,6 +29,11 @@ Here are the most common options available in the command line. npx playwright test my-spec my-spec-2 ``` +- Run files that are in line 42 in my-spec.ts + ```bash + npx playwright test my-spec.ts:42 + ``` + - Run the test with the title ```bash npx playwright test -g "add a todo item" diff --git a/packages/playwright-test/src/cli.ts b/packages/playwright-test/src/cli.ts index 4d8e7d7e68..52491be8e0 100644 --- a/packages/playwright-test/src/cli.ts +++ b/packages/playwright-test/src/cli.ts @@ -74,6 +74,7 @@ Arguments [test-filter...]: Examples: $ npx playwright test my.spec.ts + $ npx playwright test some.spec.ts:42 $ npx playwright test --headed $ npx playwright test --browser=webkit`); } @@ -156,10 +157,11 @@ async function runTests(args: string[], opts: { [key: string]: any }) { throw new Error(`Cannot use --browser option when configuration file defines projects. Specify browserName in the projects instead.`); const filePatternFilter: FilePatternFilter[] = args.map(arg => { - const match = /^(.*):(\d+)$/.exec(arg); + const match = /^(.*?):(\d+):?(\d+)?$/.exec(arg); return { re: forceRegExp(match ? match[1] : arg), line: match ? parseInt(match[2], 10) : null, + column: match?.[3] ? parseInt(match[3], 10) : null, }; }); diff --git a/packages/playwright-test/src/runner.ts b/packages/playwright-test/src/runner.ts index aa08e79fa7..0241117df0 100644 --- a/packages/playwright-test/src/runner.ts +++ b/packages/playwright-test/src/runner.ts @@ -260,7 +260,7 @@ export class Runner { preprocessRoot._addSuite(fileSuite); } - // 2. Filter tests to respect column filter. + // 2. Filter tests to respect line/column filter. filterByFocusedLine(preprocessRoot, testFileReFilters); // 3. Complain about only. @@ -487,14 +487,14 @@ function filterByFocusedLine(suite: Suite, focusedTestFileLines: FilePatternFilt if (!filterWithLine) return; - const testFileLineMatches = (testFileName: string, testLine: number) => focusedTestFileLines.some(({ re, line }) => { + const testFileLineMatches = (testFileName: string, testLine: number, testColumn: number) => focusedTestFileLines.some(({ re, line, column }) => { re.lastIndex = 0; - return re.test(testFileName) && (line === testLine || line === null); + return re.test(testFileName) && (line === testLine || line === null) && (column === testColumn || column === null); }); const suiteFilter = (suite: Suite) => { - return !!suite.location && testFileLineMatches(suite.location.file, suite.location.line); + return !!suite.location && testFileLineMatches(suite.location.file, suite.location.line, suite.location.column); }; - const testFilter = (test: TestCase) => testFileLineMatches(test.location.file, test.location.line); + const testFilter = (test: TestCase) => testFileLineMatches(test.location.file, test.location.line, test.location.column); return filterSuite(suite, suiteFilter, testFilter); } diff --git a/packages/playwright-test/src/util.ts b/packages/playwright-test/src/util.ts index 15f9407fd8..c4c23264fe 100644 --- a/packages/playwright-test/src/util.ts +++ b/packages/playwright-test/src/util.ts @@ -107,6 +107,7 @@ export type Matcher = (value: string) => boolean; export type FilePatternFilter = { re: RegExp; line: number | null; + column: number | null; }; export function createFileMatcher(patterns: string | RegExp | (string | RegExp)[]): Matcher { diff --git a/tests/playwright-test/command-line-filter.spec.ts b/tests/playwright-test/command-line-filter.spec.ts index c0a9fe3e72..503c47662c 100644 --- a/tests/playwright-test/command-line-filter.spec.ts +++ b/tests/playwright-test/command-line-filter.spec.ts @@ -53,6 +53,21 @@ test('should filter by line', async ({ runInlineTest }) => { expect(result.output).toMatch(/x\.spec\.ts.*two/); }); +test('should filter by line and column', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'foo/x.spec.js': ` + pwt.test('yes-full-match', () => { expect(1).toBe(1); }); + pwt.test('no-wrong-column', () => { expect(1).toBe(2); }); + pwt.test('yes-no-column-specified', () => { expect(1).toBe(1); }); + pwt.test('no-match', () => { expect(1).toBe(1); }); + `, + }, undefined, undefined, { additionalArgs: ['x.spec.js:5:11', 'x.spec.js:6:99999', 'x.spec.js:7'] }); + expect(result.exitCode).toBe(0); + expect(result.skipped).toBe(0); + expect(result.passed).toBe(2); + expect(result.report.suites[0].specs.map(spec => spec.title)).toEqual(['yes-full-match', 'yes-no-column-specified']); +}); + test('line should override focused test', async ({ runInlineTest }) => { const result = await runInlineTest({ 'foo/x.spec.ts': `