@nsxbet/playwright-orchestrator 0.6.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -24
- package/dist/fixture.d.ts +43 -3
- package/dist/fixture.d.ts.map +1 -1
- package/dist/fixture.js +75 -3
- package/dist/fixture.js.map +1 -1
- package/package.json +9 -1
- package/dist/commands/list-tests.d.ts +0 -16
- package/dist/commands/list-tests.d.ts.map +0 -1
- package/dist/commands/list-tests.js +0 -98
- package/dist/commands/list-tests.js.map +0 -1
package/README.md
CHANGED
|
@@ -25,29 +25,48 @@ This orchestrator:
|
|
|
25
25
|
|
|
26
26
|
Result: All shards finish at roughly the same time.
|
|
27
27
|
|
|
28
|
+
### Test-Level Distribution
|
|
29
|
+
|
|
30
|
+
Unlike other solutions that only distribute at the **file level**, this orchestrator supports **test-level distribution**. This matters when you have files with many tests of varying durations - distributing individual tests achieves much better balance than distributing entire files.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
File-level: login.spec.ts (50 tests, 10min) → all go to shard 1
|
|
34
|
+
Test-level: login.spec.ts tests → spread across shards 1-4
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Test-level distribution requires the reporter AND a test fixture to filter tests at runtime. See [Setup](#setup) below.
|
|
38
|
+
|
|
28
39
|
## Quick Start
|
|
29
40
|
|
|
30
41
|
```bash
|
|
31
42
|
# Install
|
|
32
43
|
bun add -D @nsxbet/playwright-orchestrator
|
|
33
44
|
|
|
34
|
-
#
|
|
35
|
-
playwright
|
|
45
|
+
# Generate test list
|
|
46
|
+
bunx playwright test --list --reporter=json --project "Mobile Chrome" > test-list.json
|
|
36
47
|
|
|
37
|
-
# Assign tests to shards
|
|
38
|
-
playwright-orchestrator assign \
|
|
39
|
-
--test-
|
|
48
|
+
# Assign tests to shards
|
|
49
|
+
bunx playwright-orchestrator assign \
|
|
50
|
+
--test-list ./test-list.json \
|
|
40
51
|
--timing-file ./timing-data.json \
|
|
41
|
-
--shards 4
|
|
42
|
-
|
|
52
|
+
--shards 4 > assignment.json
|
|
53
|
+
|
|
54
|
+
# Extract each shard's tests to separate files
|
|
55
|
+
jq '.shards."1"' assignment.json > shard-1.json
|
|
56
|
+
jq '.shards."2"' assignment.json > shard-2.json
|
|
57
|
+
jq '.shards."3"' assignment.json > shard-3.json
|
|
58
|
+
jq '.shards."4"' assignment.json > shard-4.json
|
|
43
59
|
|
|
44
|
-
#
|
|
45
|
-
|
|
60
|
+
# Run tests for a specific shard (fixture filters based on ORCHESTRATOR_SHARD_FILE)
|
|
61
|
+
ORCHESTRATOR_SHARD_FILE=shard-1.json bunx playwright test --project "Mobile Chrome"
|
|
62
|
+
|
|
63
|
+
# Extract timing from report after tests complete
|
|
64
|
+
bunx playwright-orchestrator extract-timing \
|
|
46
65
|
--report-file ./playwright-report/results.json \
|
|
47
66
|
--output-file ./shard-1-timing.json
|
|
48
67
|
|
|
49
|
-
# Merge timing data
|
|
50
|
-
playwright-orchestrator merge-timing \
|
|
68
|
+
# Merge timing data from all shards
|
|
69
|
+
bunx playwright-orchestrator merge-timing \
|
|
51
70
|
--existing ./timing-data.json \
|
|
52
71
|
--new ./shard-1-timing.json ./shard-2-timing.json \
|
|
53
72
|
--output ./timing-data.json
|
|
@@ -70,9 +89,11 @@ playwright-orchestrator merge-timing \
|
|
|
70
89
|
2. **Run Tests**: Each shard reads its files from `needs.orchestrate.outputs`
|
|
71
90
|
3. **Merge**: Collect timing from all shards, update history with EMA
|
|
72
91
|
|
|
73
|
-
##
|
|
92
|
+
## Setup
|
|
93
|
+
|
|
94
|
+
For test-level distribution to work, you need **two things**:
|
|
74
95
|
|
|
75
|
-
|
|
96
|
+
### 1. Reporter (in `playwright.config.ts`)
|
|
76
97
|
|
|
77
98
|
```typescript
|
|
78
99
|
import { defineConfig } from "@playwright/test";
|
|
@@ -82,7 +103,33 @@ export default defineConfig({
|
|
|
82
103
|
});
|
|
83
104
|
```
|
|
84
105
|
|
|
85
|
-
|
|
106
|
+
### 2. Test Fixture (in your test setup file)
|
|
107
|
+
|
|
108
|
+
Wrap your base test with `withOrchestratorFilter`:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// e2e/setup.ts
|
|
112
|
+
import { test as base } from "@playwright/test";
|
|
113
|
+
import { withOrchestratorFilter } from "@nsxbet/playwright-orchestrator/fixture";
|
|
114
|
+
|
|
115
|
+
export const test = withOrchestratorFilter(base);
|
|
116
|
+
export { expect } from "@playwright/test";
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Then use this `test` in your spec files:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// e2e/login.spec.ts
|
|
123
|
+
import { test, expect } from "./setup";
|
|
124
|
+
|
|
125
|
+
test("should login", async ({ page }) => {
|
|
126
|
+
// ...
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The reporter and fixture work together:
|
|
131
|
+
- **Reporter**: Reads `ORCHESTRATOR_SHARD_FILE` env var to know which tests belong to this shard
|
|
132
|
+
- **Fixture**: Skips tests that don't belong to the current shard at runtime
|
|
86
133
|
|
|
87
134
|
## Local Testing
|
|
88
135
|
|
|
@@ -143,9 +190,9 @@ jobs:
|
|
|
143
190
|
- uses: NSXBet/playwright-orchestrator/.github/actions/orchestrate@v0
|
|
144
191
|
id: orchestrate
|
|
145
192
|
with:
|
|
146
|
-
test-list: test-list.json #
|
|
193
|
+
test-list: test-list.json # Required: pre-generated list
|
|
194
|
+
timing-file: timing-data.json # Required: timing data
|
|
147
195
|
shards: 4
|
|
148
|
-
timing-file: timing-data.json
|
|
149
196
|
|
|
150
197
|
# Phase 2: Run tests (parallel matrix)
|
|
151
198
|
e2e:
|
|
@@ -176,17 +223,14 @@ See [docs/external-integration.md](./docs/external-integration.md) for complete
|
|
|
176
223
|
|
|
177
224
|
## CLI Commands
|
|
178
225
|
|
|
179
|
-
| Command | Description
|
|
180
|
-
| ---------------- |
|
|
181
|
-
| `
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
| `merge-timing` | Merge timing data with EMA smoothing |
|
|
226
|
+
| Command | Description |
|
|
227
|
+
| ---------------- | ---------------------------------------- |
|
|
228
|
+
| `assign` | Distribute tests across shards |
|
|
229
|
+
| `extract-timing` | Extract timing from Playwright report |
|
|
230
|
+
| `merge-timing` | Merge timing data with EMA smoothing |
|
|
185
231
|
|
|
186
232
|
Run `playwright-orchestrator <command> --help` for details.
|
|
187
233
|
|
|
188
|
-
**Important**: The `--project` flag is recommended for both `list-tests` and `assign` commands to ensure accurate test discovery, especially for parameterized tests (e.g., `test.each`).
|
|
189
|
-
|
|
190
234
|
## Development
|
|
191
235
|
|
|
192
236
|
```bash
|
|
@@ -235,6 +279,18 @@ Test scenarios covered in `examples/monorepo/`:
|
|
|
235
279
|
|
|
236
280
|
See [AGENTS.md](./AGENTS.md) for AI assistant instructions.
|
|
237
281
|
|
|
282
|
+
## Cache Strategy
|
|
283
|
+
|
|
284
|
+
GitHub Actions cache is branch-scoped, which creates challenges for sharing timing data between PRs and main. We recommend a **promote-on-merge** pattern:
|
|
285
|
+
|
|
286
|
+
1. Each PR branch saves to its own cache key
|
|
287
|
+
2. PRs restore from their own cache, falling back to main
|
|
288
|
+
3. When a PR is merged, a workflow promotes the PR's cache to main
|
|
289
|
+
|
|
290
|
+
This avoids race conditions between concurrent PRs while ensuring main always has the latest timing data.
|
|
291
|
+
|
|
292
|
+
See [Cache Strategy for PRs](./docs/external-integration.md#cache-strategy-for-prs) for implementation details.
|
|
293
|
+
|
|
238
294
|
## License
|
|
239
295
|
|
|
240
296
|
MIT
|
package/dist/fixture.d.ts
CHANGED
|
@@ -6,10 +6,21 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Usage in your test setup file (e.g., tests/setup.ts):
|
|
8
8
|
* ```typescript
|
|
9
|
-
* import { test } from '@playwright/test';
|
|
10
|
-
* import {
|
|
9
|
+
* import { test as base } from '@playwright/test';
|
|
10
|
+
* import { withOrchestratorFilter } from '@nsxbet/playwright-orchestrator/fixture';
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* // Create extended test with orchestrator filtering
|
|
13
|
+
* export const test = withOrchestratorFilter(base);
|
|
14
|
+
* export { expect } from '@playwright/test';
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Then in your test files:
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { test, expect } from './setup';
|
|
20
|
+
*
|
|
21
|
+
* test('my test', async ({ page }) => {
|
|
22
|
+
* // ...
|
|
23
|
+
* });
|
|
13
24
|
* ```
|
|
14
25
|
*
|
|
15
26
|
* Environment variables:
|
|
@@ -20,6 +31,35 @@
|
|
|
20
31
|
*/
|
|
21
32
|
import type { TestType } from '@playwright/test';
|
|
22
33
|
/**
|
|
34
|
+
* Creates an extended test with orchestrator filtering as an auto-fixture.
|
|
35
|
+
* This ensures tests not in the current shard are skipped.
|
|
36
|
+
*
|
|
37
|
+
* IMPORTANT: Use this function to create your test object, then export it.
|
|
38
|
+
* All test files should import the extended test, not the base test.
|
|
39
|
+
*
|
|
40
|
+
* @param test - The base test object from @playwright/test
|
|
41
|
+
* @returns Extended test with orchestrator filtering
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* // In setup.ts
|
|
46
|
+
* import { test as base } from '@playwright/test';
|
|
47
|
+
* import { withOrchestratorFilter } from '@nsxbet/playwright-orchestrator/fixture';
|
|
48
|
+
*
|
|
49
|
+
* export const test = withOrchestratorFilter(base);
|
|
50
|
+
*
|
|
51
|
+
* // In your.spec.ts
|
|
52
|
+
* import { test } from './setup';
|
|
53
|
+
* test('example', async ({ page }) => { ... });
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function withOrchestratorFilter<T extends object, W extends object>(test: TestType<T, W>): TestType<T & {
|
|
57
|
+
_orchestratorFilter: undefined;
|
|
58
|
+
}, W>;
|
|
59
|
+
/**
|
|
60
|
+
* @deprecated Use `withOrchestratorFilter` instead. This function uses beforeEach
|
|
61
|
+
* which only works for the first test file processed, not subsequent files.
|
|
62
|
+
*
|
|
23
63
|
* Sets up the orchestrator filter as a beforeEach hook.
|
|
24
64
|
* This will skip tests that are not in the current shard.
|
|
25
65
|
*
|
package/dist/fixture.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAuDjD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,EACvE,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GACnB,QAAQ,CAAC,CAAC,GAAG;IAAE,mBAAmB,EAAE,SAAS,CAAA;CAAE,EAAE,CAAC,CAAC,CAwDrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,EACxE,IAAI,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,GACnB,IAAI,CAoCN;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5C,GAAG,OAAO,CAUV"}
|
package/dist/fixture.js
CHANGED
|
@@ -6,10 +6,21 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Usage in your test setup file (e.g., tests/setup.ts):
|
|
8
8
|
* ```typescript
|
|
9
|
-
* import { test } from '@playwright/test';
|
|
10
|
-
* import {
|
|
9
|
+
* import { test as base } from '@playwright/test';
|
|
10
|
+
* import { withOrchestratorFilter } from '@nsxbet/playwright-orchestrator/fixture';
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* // Create extended test with orchestrator filtering
|
|
13
|
+
* export const test = withOrchestratorFilter(base);
|
|
14
|
+
* export { expect } from '@playwright/test';
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Then in your test files:
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { test, expect } from './setup';
|
|
20
|
+
*
|
|
21
|
+
* test('my test', async ({ page }) => {
|
|
22
|
+
* // ...
|
|
23
|
+
* });
|
|
13
24
|
* ```
|
|
14
25
|
*
|
|
15
26
|
* Environment variables:
|
|
@@ -59,6 +70,67 @@ function loadShardFile() {
|
|
|
59
70
|
}
|
|
60
71
|
}
|
|
61
72
|
/**
|
|
73
|
+
* Creates an extended test with orchestrator filtering as an auto-fixture.
|
|
74
|
+
* This ensures tests not in the current shard are skipped.
|
|
75
|
+
*
|
|
76
|
+
* IMPORTANT: Use this function to create your test object, then export it.
|
|
77
|
+
* All test files should import the extended test, not the base test.
|
|
78
|
+
*
|
|
79
|
+
* @param test - The base test object from @playwright/test
|
|
80
|
+
* @returns Extended test with orchestrator filtering
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* // In setup.ts
|
|
85
|
+
* import { test as base } from '@playwright/test';
|
|
86
|
+
* import { withOrchestratorFilter } from '@nsxbet/playwright-orchestrator/fixture';
|
|
87
|
+
*
|
|
88
|
+
* export const test = withOrchestratorFilter(base);
|
|
89
|
+
*
|
|
90
|
+
* // In your.spec.ts
|
|
91
|
+
* import { test } from './setup';
|
|
92
|
+
* test('example', async ({ page }) => { ... });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export function withOrchestratorFilter(test) {
|
|
96
|
+
return test.extend({
|
|
97
|
+
// @ts-expect-error - Playwright's auto fixture typing is complex
|
|
98
|
+
_orchestratorFilter: [
|
|
99
|
+
async (
|
|
100
|
+
// biome-ignore lint/correctness/noEmptyPattern: Playwright requires empty destructuring
|
|
101
|
+
{}, use, testInfo) => {
|
|
102
|
+
const allowedTestIds = loadShardFile();
|
|
103
|
+
if (allowedTestIds) {
|
|
104
|
+
// CRITICAL: Use project.testDir for consistent path resolution with test-discovery
|
|
105
|
+
// No fallback to process.cwd() - this causes path mismatch bugs
|
|
106
|
+
const testDir = testInfo.project.testDir;
|
|
107
|
+
if (!testDir) {
|
|
108
|
+
throw new Error('[Orchestrator Fixture] Could not determine project testDir. ' +
|
|
109
|
+
'Ensure your playwright.config.ts has projects configured with testDir.');
|
|
110
|
+
}
|
|
111
|
+
const testId = buildTestIdFromRuntime(testInfo.file, testInfo.titlePath, {
|
|
112
|
+
projectName: testInfo.project.name,
|
|
113
|
+
baseDir: testDir,
|
|
114
|
+
});
|
|
115
|
+
const isAllowed = allowedTestIds.has(testId);
|
|
116
|
+
// Debug: Write to stderr for visibility in CI logs
|
|
117
|
+
if (process.env.ORCHESTRATOR_DEBUG === '1') {
|
|
118
|
+
process.stderr.write(`[Fixture] testDir=${testInfo.project.testDir} | testId=${testId} | allowed=${isAllowed}\n`);
|
|
119
|
+
}
|
|
120
|
+
if (!isAllowed) {
|
|
121
|
+
test.skip(true, 'Not in shard');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
await use();
|
|
125
|
+
},
|
|
126
|
+
{ auto: true },
|
|
127
|
+
],
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* @deprecated Use `withOrchestratorFilter` instead. This function uses beforeEach
|
|
132
|
+
* which only works for the first test file processed, not subsequent files.
|
|
133
|
+
*
|
|
62
134
|
* Sets up the orchestrator filter as a beforeEach hook.
|
|
63
135
|
* This will skip tests that are not in the current shard.
|
|
64
136
|
*
|
package/dist/fixture.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixture.js","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fixture.js","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nsxbet/playwright-orchestrator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Intelligent Playwright test distribution across CI shards using historical timing data",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
],
|
|
13
13
|
"fixture": [
|
|
14
14
|
"./dist/fixture.d.ts"
|
|
15
|
+
],
|
|
16
|
+
"core": [
|
|
17
|
+
"./dist/core/index.d.ts"
|
|
15
18
|
]
|
|
16
19
|
}
|
|
17
20
|
},
|
|
@@ -30,6 +33,11 @@
|
|
|
30
33
|
"types": "./dist/fixture.d.ts",
|
|
31
34
|
"import": "./dist/fixture.js",
|
|
32
35
|
"require": "./dist/fixture.js"
|
|
36
|
+
},
|
|
37
|
+
"./core": {
|
|
38
|
+
"types": "./dist/core/index.d.ts",
|
|
39
|
+
"import": "./dist/core/index.js",
|
|
40
|
+
"require": "./dist/core/index.js"
|
|
33
41
|
}
|
|
34
42
|
},
|
|
35
43
|
"bin": {
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
2
|
-
export default class ListTests extends Command {
|
|
3
|
-
static description: string;
|
|
4
|
-
static examples: string[];
|
|
5
|
-
static flags: {
|
|
6
|
-
'test-dir': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
-
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
-
'output-format': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
-
'glob-pattern': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
-
'use-fallback': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
-
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
-
};
|
|
13
|
-
run(): Promise<void>;
|
|
14
|
-
private outputResult;
|
|
15
|
-
}
|
|
16
|
-
//# sourceMappingURL=list-tests.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"list-tests.d.ts","sourceRoot":"","sources":["../../src/commands/list-tests.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAQ7C,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,OAAO;IAC5C,OAAgB,WAAW,SACiD;IAE5E,OAAgB,QAAQ,WAGtB;IAEF,OAAgB,KAAK;;;;;;;MA6BnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC1B,OAAO,CAAC,YAAY;CA4BrB"}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import * as path from 'node:path';
|
|
2
|
-
import { Command, Flags } from '@oclif/core';
|
|
3
|
-
import { discoverTests, discoverTestsFromFiles, groupTestsByFile, } from '../core/index.js';
|
|
4
|
-
export default class ListTests extends Command {
|
|
5
|
-
static description = 'Discover all tests in a Playwright project using --list or file parsing';
|
|
6
|
-
static examples = [
|
|
7
|
-
'<%= config.bin %> list-tests --test-dir ./src/test/e2e',
|
|
8
|
-
'<%= config.bin %> list-tests --test-dir ./e2e --project "Mobile Chrome" --output-format json',
|
|
9
|
-
];
|
|
10
|
-
static flags = {
|
|
11
|
-
'test-dir': Flags.string({
|
|
12
|
-
char: 'd',
|
|
13
|
-
description: 'Path to test directory',
|
|
14
|
-
required: true,
|
|
15
|
-
}),
|
|
16
|
-
project: Flags.string({
|
|
17
|
-
char: 'p',
|
|
18
|
-
description: 'Playwright project name',
|
|
19
|
-
}),
|
|
20
|
-
'output-format': Flags.string({
|
|
21
|
-
char: 'f',
|
|
22
|
-
description: 'Output format',
|
|
23
|
-
default: 'json',
|
|
24
|
-
options: ['json', 'text'],
|
|
25
|
-
}),
|
|
26
|
-
'glob-pattern': Flags.string({
|
|
27
|
-
description: 'Glob pattern for test files (used for fallback discovery)',
|
|
28
|
-
default: '**/*.spec.ts',
|
|
29
|
-
}),
|
|
30
|
-
'use-fallback': Flags.boolean({
|
|
31
|
-
description: 'Use file parsing instead of Playwright --list',
|
|
32
|
-
default: false,
|
|
33
|
-
}),
|
|
34
|
-
verbose: Flags.boolean({
|
|
35
|
-
char: 'v',
|
|
36
|
-
description: 'Show verbose output',
|
|
37
|
-
default: false,
|
|
38
|
-
}),
|
|
39
|
-
};
|
|
40
|
-
async run() {
|
|
41
|
-
const { flags } = await this.parse(ListTests);
|
|
42
|
-
const testDir = path.resolve(flags['test-dir']);
|
|
43
|
-
if (flags.verbose) {
|
|
44
|
-
this.log(`Discovering tests in ${testDir}...`);
|
|
45
|
-
}
|
|
46
|
-
let tests;
|
|
47
|
-
try {
|
|
48
|
-
if (flags['use-fallback']) {
|
|
49
|
-
// Use file parsing
|
|
50
|
-
tests = discoverTestsFromFiles(testDir, flags['glob-pattern']);
|
|
51
|
-
if (flags.verbose) {
|
|
52
|
-
this.log('Using file parsing for test discovery');
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
// Use Playwright --list
|
|
57
|
-
tests = discoverTests(testDir, flags.project);
|
|
58
|
-
if (flags.verbose) {
|
|
59
|
-
this.log('Using Playwright --list for test discovery');
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
// Fallback to file parsing if Playwright --list fails
|
|
65
|
-
if (flags.verbose) {
|
|
66
|
-
this.warn('Playwright --list failed, falling back to file parsing');
|
|
67
|
-
}
|
|
68
|
-
tests = discoverTestsFromFiles(testDir, flags['glob-pattern']);
|
|
69
|
-
}
|
|
70
|
-
if (flags.verbose) {
|
|
71
|
-
this.log(`Discovered ${tests.length} tests`);
|
|
72
|
-
}
|
|
73
|
-
this.outputResult(tests, flags['output-format'], flags.verbose);
|
|
74
|
-
}
|
|
75
|
-
outputResult(tests, format, verbose = false) {
|
|
76
|
-
if (format === 'json') {
|
|
77
|
-
this.log(JSON.stringify(tests));
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
// Text format - group by file
|
|
81
|
-
const grouped = groupTestsByFile(tests);
|
|
82
|
-
this.log('\n=== Discovered Tests ===\n');
|
|
83
|
-
for (const [file, fileTests] of grouped) {
|
|
84
|
-
this.log(`${file}:`);
|
|
85
|
-
for (const test of fileTests) {
|
|
86
|
-
const titlePath = test.titlePath.join(' > ');
|
|
87
|
-
this.log(` - ${titlePath}`);
|
|
88
|
-
if (verbose) {
|
|
89
|
-
this.log(` ID: ${test.testId}`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
this.log('');
|
|
93
|
-
}
|
|
94
|
-
this.log(`Total: ${tests.length} tests in ${grouped.size} files`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
//# sourceMappingURL=list-tests.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"list-tests.js","sourceRoot":"","sources":["../../src/commands/list-tests.ts"],"names":[],"mappings":"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"}
|