@nsxbet/playwright-orchestrator 0.2.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.
Files changed (59) hide show
  1. package/README.md +161 -0
  2. package/bin/run.js +5 -0
  3. package/dist/commands/assign.d.ts +23 -0
  4. package/dist/commands/assign.d.ts.map +1 -0
  5. package/dist/commands/assign.js +256 -0
  6. package/dist/commands/assign.js.map +1 -0
  7. package/dist/commands/extract-timing.d.ts +40 -0
  8. package/dist/commands/extract-timing.d.ts.map +1 -0
  9. package/dist/commands/extract-timing.js +196 -0
  10. package/dist/commands/extract-timing.js.map +1 -0
  11. package/dist/commands/list-tests.d.ts +16 -0
  12. package/dist/commands/list-tests.d.ts.map +1 -0
  13. package/dist/commands/list-tests.js +98 -0
  14. package/dist/commands/list-tests.js.map +1 -0
  15. package/dist/commands/merge-timing.d.ts +19 -0
  16. package/dist/commands/merge-timing.d.ts.map +1 -0
  17. package/dist/commands/merge-timing.js +217 -0
  18. package/dist/commands/merge-timing.js.map +1 -0
  19. package/dist/core/ckk-algorithm.d.ts +38 -0
  20. package/dist/core/ckk-algorithm.d.ts.map +1 -0
  21. package/dist/core/ckk-algorithm.js +192 -0
  22. package/dist/core/ckk-algorithm.js.map +1 -0
  23. package/dist/core/estimate.d.ts +72 -0
  24. package/dist/core/estimate.d.ts.map +1 -0
  25. package/dist/core/estimate.js +142 -0
  26. package/dist/core/estimate.js.map +1 -0
  27. package/dist/core/grep-pattern.d.ts +61 -0
  28. package/dist/core/grep-pattern.d.ts.map +1 -0
  29. package/dist/core/grep-pattern.js +104 -0
  30. package/dist/core/grep-pattern.js.map +1 -0
  31. package/dist/core/index.d.ts +9 -0
  32. package/dist/core/index.d.ts.map +1 -0
  33. package/dist/core/index.js +9 -0
  34. package/dist/core/index.js.map +1 -0
  35. package/dist/core/lpt-algorithm.d.ts +28 -0
  36. package/dist/core/lpt-algorithm.d.ts.map +1 -0
  37. package/dist/core/lpt-algorithm.js +80 -0
  38. package/dist/core/lpt-algorithm.js.map +1 -0
  39. package/dist/core/slugify.d.ts +13 -0
  40. package/dist/core/slugify.d.ts.map +1 -0
  41. package/dist/core/slugify.js +19 -0
  42. package/dist/core/slugify.js.map +1 -0
  43. package/dist/core/test-discovery.d.ts +46 -0
  44. package/dist/core/test-discovery.d.ts.map +1 -0
  45. package/dist/core/test-discovery.js +192 -0
  46. package/dist/core/test-discovery.js.map +1 -0
  47. package/dist/core/timing-store.d.ts +90 -0
  48. package/dist/core/timing-store.d.ts.map +1 -0
  49. package/dist/core/timing-store.js +280 -0
  50. package/dist/core/timing-store.js.map +1 -0
  51. package/dist/core/types.d.ts +241 -0
  52. package/dist/core/types.d.ts.map +1 -0
  53. package/dist/core/types.js +54 -0
  54. package/dist/core/types.js.map +1 -0
  55. package/dist/index.d.ts +9 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +9 -0
  58. package/dist/index.js.map +1 -0
  59. package/package.json +70 -0
package/README.md ADDED
@@ -0,0 +1,161 @@
1
+ # @nsxbet/playwright-orchestrator
2
+
3
+ Intelligent Playwright test distribution across CI shards using historical timing data.
4
+
5
+ ## The Problem
6
+
7
+ Default Playwright sharding (`--shard=N/M`) distributes tests by **file count**, not by duration. This creates significant imbalance:
8
+
9
+ | Shard | Duration | vs Fastest |
10
+ |-------|----------|------------|
11
+ | Shard 1 | ~31 min | +182% |
12
+ | Shard 2 | ~15 min | +36% |
13
+ | Shard 3 | ~22 min | +100% |
14
+ | Shard 4 | ~11 min | baseline |
15
+
16
+ Your CI is bottlenecked by the slowest shard, wasting runner time.
17
+
18
+ ## The Solution
19
+
20
+ This orchestrator:
21
+
22
+ 1. **Learns** test durations from previous runs
23
+ 2. **Distributes** tests optimally using the CKK algorithm
24
+ 3. **Balances** shards to within 10-15% of each other
25
+
26
+ Result: All shards finish at roughly the same time.
27
+
28
+ ## Quick Start
29
+
30
+ ```bash
31
+ # Install
32
+ bun add -D @nsxbet/playwright-orchestrator
33
+
34
+ # Discover tests
35
+ playwright-orchestrator list-tests --test-dir ./e2e
36
+
37
+ # Assign tests to shards (with timing data)
38
+ playwright-orchestrator assign \
39
+ --test-dir ./e2e \
40
+ --timing-file ./timing-data.json \
41
+ --shards 4
42
+
43
+ # Extract timing from report
44
+ playwright-orchestrator extract-timing \
45
+ --report-file ./playwright-report/results.json \
46
+ --output-file ./shard-1-timing.json
47
+
48
+ # Merge timing data
49
+ playwright-orchestrator merge-timing \
50
+ --existing ./timing-data.json \
51
+ --new ./shard-1-timing.json ./shard-2-timing.json \
52
+ --output ./timing-data.json
53
+ ```
54
+
55
+ ## How It Works
56
+
57
+ ```
58
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
59
+ │ Orchestrate │────▶│ Run Tests │────▶│ Merge Timing │
60
+ │ (1 job) │ │ (N parallel) │ │ (1 job) │
61
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
62
+ │ │ │
63
+ ▼ ▼ ▼
64
+ Run CKK once Read shard-files Merge all shards
65
+ Output all shards from job outputs Update cache
66
+ ```
67
+
68
+ 1. **Orchestrate**: Run once, compute assignments for ALL shards
69
+ 2. **Run Tests**: Each shard reads its files from `needs.orchestrate.outputs`
70
+ 3. **Merge**: Collect timing from all shards, update history with EMA
71
+
72
+ ## GitHub Actions (External Repositories)
73
+
74
+ Use the orchestrator in your own repository. The recommended pattern runs orchestration **once** before matrix jobs:
75
+
76
+ ```yaml
77
+ jobs:
78
+ # Phase 1: Orchestrate (runs once)
79
+ orchestrate:
80
+ runs-on: ubuntu-24.04
81
+ outputs:
82
+ shard-files: ${{ steps.orchestrate.outputs.shard-files }}
83
+ steps:
84
+ - uses: actions/checkout@v4
85
+ - uses: NSXBet/playwright-orchestrator/.github/actions/setup-orchestrator@v1
86
+
87
+ # YOU control cache location
88
+ - uses: actions/cache/restore@v4
89
+ with:
90
+ path: timing-data.json
91
+ key: playwright-timing-${{ github.ref_name }}
92
+ restore-keys: playwright-timing-
93
+
94
+ # Action handles all orchestration logic
95
+ - uses: NSXBet/playwright-orchestrator/.github/actions/orchestrate@v1
96
+ id: orchestrate
97
+ with:
98
+ test-dir: ./e2e
99
+ shards: 4
100
+ timing-file: timing-data.json
101
+ # No shard-index = outputs ALL shards
102
+
103
+ # Phase 2: Run tests (parallel matrix)
104
+ e2e:
105
+ needs: [orchestrate]
106
+ runs-on: ubuntu-24.04
107
+ strategy:
108
+ fail-fast: false
109
+ matrix:
110
+ shard: [1, 2, 3, 4]
111
+ steps:
112
+ - uses: actions/checkout@v4
113
+
114
+ # Action handles parsing + fallback
115
+ - uses: NSXBet/playwright-orchestrator/.github/actions/get-shard@v1
116
+ id: shard
117
+ with:
118
+ shard-files: ${{ needs.orchestrate.outputs.shard-files }}
119
+ shard-index: ${{ matrix.shard }}
120
+ shards: 4
121
+
122
+ # Just works - either files or --shard=N/M
123
+ - run: npx playwright test ${{ steps.shard.outputs.test-args }}
124
+ ```
125
+
126
+ See [docs/external-integration.md](./docs/external-integration.md) for complete workflow with timing data persistence.
127
+
128
+ ## CLI Commands
129
+
130
+ | Command | Description |
131
+ |---------|-------------|
132
+ | `list-tests` | Discover tests in a project |
133
+ | `assign` | Distribute tests across shards |
134
+ | `extract-timing` | Extract timing from Playwright report |
135
+ | `merge-timing` | Merge timing data with EMA smoothing |
136
+
137
+ Run `playwright-orchestrator <command> --help` for details.
138
+
139
+ ## Development
140
+
141
+ ```bash
142
+ # Install dependencies
143
+ make install
144
+
145
+ # Run quality checks
146
+ make lint # Biome linter
147
+ make typecheck # TypeScript
148
+ make test # Bun test
149
+
150
+ # Build
151
+ make build
152
+
153
+ # Run CI locally (requires Act)
154
+ make act-test
155
+ ```
156
+
157
+ See [AGENTS.md](./AGENTS.md) for AI assistant instructions.
158
+
159
+ ## License
160
+
161
+ MIT
package/bin/run.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execute } from '@oclif/core';
4
+
5
+ await execute({ dir: import.meta.url });
@@ -0,0 +1,23 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Assign 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
+ 'timing-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ shards: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'output-format': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'fallback-ms-per-line': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
11
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ 'glob-pattern': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ level: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
15
+ };
16
+ run(): Promise<void>;
17
+ private runTestLevel;
18
+ private runFileLevel;
19
+ private outputTestResult;
20
+ private outputResult;
21
+ private formatDuration;
22
+ }
23
+ //# sourceMappingURL=assign.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assign.d.ts","sourceRoot":"","sources":["../../src/commands/assign.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAqB7C,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,OAAO;IACzC,OAAgB,WAAW,SAC8C;IAEzE,OAAgB,QAAQ,WAItB;IAEF,OAAgB,KAAK;;;;;;;;;;MA4CnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;YAaZ,YAAY;YAyGZ,YAAY;IAuF1B,OAAO,CAAC,gBAAgB;IA0CxB,OAAO,CAAC,YAAY;IA6BpB,OAAO,CAAC,cAAc;CAQvB"}
@@ -0,0 +1,256 @@
1
+ import * as path from 'node:path';
2
+ import { Command, Flags } from '@oclif/core';
3
+ import { glob } from 'glob';
4
+ import { assignWithCKK, assignWithLPT, DEFAULT_CKK_TIMEOUT, DEFAULT_MS_PER_LINE, discoverTestsFromFiles, estimateDuration, formatAssignResult, generateGrepPatterns, getFileDuration, getTestDurations, isTimingDataV2, loadTimingData, } from '../core/index.js';
5
+ export default class Assign extends Command {
6
+ static description = 'Assign test files or tests to shards based on historical timing data';
7
+ static examples = [
8
+ '<%= config.bin %> assign --test-dir ./src/test/e2e --shards 4',
9
+ '<%= config.bin %> assign --test-dir ./e2e --timing-file ./timing.json --shards 4 --output-format json',
10
+ '<%= config.bin %> assign --test-dir ./e2e --shards 4 --level test --timeout 500',
11
+ ];
12
+ static flags = {
13
+ 'test-dir': Flags.string({
14
+ char: 'd',
15
+ description: 'Path to test directory containing spec files',
16
+ required: true,
17
+ }),
18
+ 'timing-file': Flags.string({
19
+ char: 't',
20
+ description: 'Path to timing data JSON file (optional)',
21
+ }),
22
+ shards: Flags.integer({
23
+ char: 's',
24
+ description: 'Number of shards to distribute tests across',
25
+ required: true,
26
+ }),
27
+ 'output-format': Flags.string({
28
+ char: 'f',
29
+ description: 'Output format',
30
+ default: 'json',
31
+ options: ['json', 'text'],
32
+ }),
33
+ 'fallback-ms-per-line': Flags.integer({
34
+ description: 'Milliseconds per line for duration estimation',
35
+ default: DEFAULT_MS_PER_LINE,
36
+ }),
37
+ verbose: Flags.boolean({
38
+ char: 'v',
39
+ description: 'Show verbose output',
40
+ default: false,
41
+ }),
42
+ 'glob-pattern': Flags.string({
43
+ description: 'Glob pattern for test files',
44
+ default: '**/*.spec.ts',
45
+ }),
46
+ level: Flags.string({
47
+ char: 'l',
48
+ description: 'Distribution level: file or test',
49
+ default: 'test',
50
+ options: ['file', 'test'],
51
+ }),
52
+ timeout: Flags.integer({
53
+ description: 'CKK algorithm timeout in milliseconds (test-level only)',
54
+ default: DEFAULT_CKK_TIMEOUT,
55
+ }),
56
+ };
57
+ async run() {
58
+ const { flags } = await this.parse(Assign);
59
+ const testDir = path.resolve(flags['test-dir']);
60
+ const pattern = flags['glob-pattern'];
61
+ if (flags.level === 'test') {
62
+ await this.runTestLevel(testDir, pattern, flags);
63
+ }
64
+ else {
65
+ await this.runFileLevel(testDir, pattern, flags);
66
+ }
67
+ }
68
+ async runTestLevel(testDir, pattern, flags) {
69
+ // Discover tests
70
+ const tests = discoverTestsFromFiles(testDir, pattern);
71
+ if (tests.length === 0) {
72
+ this.warn(`No tests found in ${testDir} matching ${pattern}`);
73
+ this.outputTestResult({
74
+ shards: Object.fromEntries(Array.from({ length: flags.shards }, (_, i) => [i + 1, []])),
75
+ grepPatterns: Object.fromEntries(Array.from({ length: flags.shards }, (_, i) => [i + 1, ''])),
76
+ expectedDurations: Object.fromEntries(Array.from({ length: flags.shards }, (_, i) => [i + 1, 0])),
77
+ totalTests: 0,
78
+ estimatedTests: [],
79
+ isOptimal: true,
80
+ }, flags['output-format']);
81
+ return;
82
+ }
83
+ if (flags.verbose) {
84
+ this.log(`Found ${tests.length} tests in ${testDir}`);
85
+ }
86
+ // Load timing data if available
87
+ let timingData = null;
88
+ if (flags['timing-file']) {
89
+ const loadedData = loadTimingData(flags['timing-file']);
90
+ if (isTimingDataV2(loadedData)) {
91
+ timingData = loadedData;
92
+ }
93
+ else if (flags.verbose) {
94
+ this.warn('Timing data is v1 (file-level), will estimate all tests');
95
+ }
96
+ }
97
+ // Get test durations
98
+ const testsWithDurations = getTestDurations(tests, timingData);
99
+ const estimatedTests = testsWithDurations
100
+ .filter((t) => t.estimated)
101
+ .map((t) => t.testId);
102
+ if (flags.verbose && estimatedTests.length > 0) {
103
+ this.log(`Estimated duration for ${estimatedTests.length} tests (no historical data)`);
104
+ }
105
+ // Convert to TestWithDuration format
106
+ const testInputs = testsWithDurations.map((t) => ({
107
+ testId: t.testId,
108
+ file: t.file,
109
+ duration: t.duration,
110
+ estimated: t.estimated,
111
+ }));
112
+ // Run CKK algorithm
113
+ const ckkResult = assignWithCKK(testInputs, flags.shards, flags.timeout);
114
+ if (flags.verbose) {
115
+ this.log(`Assignment ${ckkResult.isOptimal ? 'optimal' : 'near-optimal (LPT fallback)'}`);
116
+ this.log(`Makespan: ${this.formatDuration(ckkResult.makespan)}`);
117
+ }
118
+ // Generate grep patterns
119
+ const shardTests = {};
120
+ for (const assignment of ckkResult.assignments) {
121
+ shardTests[assignment.shardIndex] = assignment.tests;
122
+ }
123
+ const grepPatterns = generateGrepPatterns(shardTests);
124
+ // Build result
125
+ const result = {
126
+ shards: shardTests,
127
+ grepPatterns,
128
+ expectedDurations: Object.fromEntries(ckkResult.assignments.map((a) => [a.shardIndex, a.expectedDuration])),
129
+ totalTests: tests.length,
130
+ estimatedTests,
131
+ isOptimal: ckkResult.isOptimal,
132
+ };
133
+ this.outputTestResult(result, flags['output-format'], flags.verbose);
134
+ }
135
+ async runFileLevel(testDir, pattern, flags) {
136
+ // Find all test files
137
+ const testFiles = await glob(pattern, {
138
+ cwd: testDir,
139
+ nodir: true,
140
+ });
141
+ if (testFiles.length === 0) {
142
+ this.warn(`No test files found in ${testDir} matching ${pattern}`);
143
+ this.outputResult({
144
+ shards: Object.fromEntries(Array.from({ length: flags.shards }, (_, i) => [i + 1, []])),
145
+ expectedDurations: Object.fromEntries(Array.from({ length: flags.shards }, (_, i) => [i + 1, 0])),
146
+ totalFiles: 0,
147
+ estimatedFiles: [],
148
+ }, flags['output-format']);
149
+ return;
150
+ }
151
+ if (flags.verbose) {
152
+ this.log(`Found ${testFiles.length} test files in ${testDir}`);
153
+ }
154
+ // Load timing data if available
155
+ const timingData = flags['timing-file']
156
+ ? loadTimingData(flags['timing-file'])
157
+ : { version: 1, updatedAt: '', files: {} };
158
+ // Build file list with durations
159
+ const filesWithDurations = [];
160
+ const estimatedFiles = [];
161
+ for (const file of testFiles) {
162
+ const historicalDuration = getFileDuration(timingData, file);
163
+ if (historicalDuration !== undefined) {
164
+ filesWithDurations.push({
165
+ file,
166
+ duration: historicalDuration,
167
+ estimated: false,
168
+ });
169
+ }
170
+ else {
171
+ // Estimate based on line count
172
+ const fullPath = path.join(testDir, file);
173
+ const estimated = estimateDuration(fullPath, flags['fallback-ms-per-line']);
174
+ filesWithDurations.push({
175
+ file,
176
+ duration: estimated,
177
+ estimated: true,
178
+ });
179
+ estimatedFiles.push(file);
180
+ }
181
+ }
182
+ if (flags.verbose && estimatedFiles.length > 0) {
183
+ this.log(`Estimated duration for ${estimatedFiles.length} files (no historical data)`);
184
+ }
185
+ // Run LPT algorithm
186
+ const assignments = assignWithLPT(filesWithDurations, flags.shards);
187
+ const result = formatAssignResult(assignments, estimatedFiles);
188
+ // Output result
189
+ this.outputResult(result, flags['output-format'], flags.verbose);
190
+ }
191
+ outputTestResult(result, format, verbose = false) {
192
+ if (format === 'json') {
193
+ this.log(JSON.stringify(result));
194
+ }
195
+ else {
196
+ // Text format
197
+ this.log('\n=== Shard Assignments (Test-Level) ===\n');
198
+ for (const [shard, tests] of Object.entries(result.shards)) {
199
+ const duration = result.expectedDurations[Number(shard)];
200
+ const durationStr = this.formatDuration(duration ?? 0);
201
+ this.log(`Shard ${shard} (${durationStr}, ${tests.length} tests):`);
202
+ if (verbose) {
203
+ for (const testId of tests) {
204
+ const isEstimated = result.estimatedTests.includes(testId);
205
+ this.log(` - ${testId}${isEstimated ? ' (estimated)' : ''}`);
206
+ }
207
+ }
208
+ const grepPattern = result.grepPatterns[Number(shard)];
209
+ if (grepPattern && grepPattern.length < 100) {
210
+ this.log(` grep: "${grepPattern}"`);
211
+ }
212
+ else if (grepPattern) {
213
+ this.log(` grep: (${grepPattern.length} chars, use --grep-file)`);
214
+ }
215
+ this.log('');
216
+ }
217
+ this.log(`Total tests: ${result.totalTests}`);
218
+ this.log(`Optimal solution: ${result.isOptimal ? 'Yes' : 'No (LPT fallback)'}`);
219
+ if (result.estimatedTests.length > 0) {
220
+ this.log(`Tests with estimated duration: ${result.estimatedTests.length}`);
221
+ }
222
+ }
223
+ }
224
+ outputResult(result, format, _verbose = false) {
225
+ if (format === 'json') {
226
+ this.log(JSON.stringify(result));
227
+ }
228
+ else {
229
+ // Text format
230
+ this.log('\n=== Shard Assignments (File-Level) ===\n');
231
+ for (const [shard, files] of Object.entries(result.shards)) {
232
+ const duration = result.expectedDurations[Number(shard)];
233
+ const durationStr = this.formatDuration(duration ?? 0);
234
+ this.log(`Shard ${shard} (${durationStr}):`);
235
+ for (const file of files) {
236
+ const isEstimated = result.estimatedFiles.includes(file);
237
+ this.log(` - ${file}${isEstimated ? ' (estimated)' : ''}`);
238
+ }
239
+ this.log('');
240
+ }
241
+ this.log(`Total files: ${result.totalFiles}`);
242
+ if (result.estimatedFiles.length > 0) {
243
+ this.log(`Files with estimated duration: ${result.estimatedFiles.length}`);
244
+ }
245
+ }
246
+ }
247
+ formatDuration(ms) {
248
+ const seconds = Math.round(ms / 1000);
249
+ const minutes = Math.floor(seconds / 60);
250
+ const remainingSeconds = seconds % 60;
251
+ return minutes > 0
252
+ ? `${minutes}m ${remainingSeconds}s`
253
+ : `${remainingSeconds}s`;
254
+ }
255
+ }
256
+ //# sourceMappingURL=assign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assign.js","sourceRoot":"","sources":["../../src/commands/assign.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ExtractTiming extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'report-file': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'output-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ shard: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
9
+ project: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ level: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ /**
15
+ * Extract test-level durations from Playwright report
16
+ *
17
+ * Each test is identified by: file::describe::testTitle
18
+ */
19
+ private extractTestDurations;
20
+ /**
21
+ * Recursively extract test durations from a suite
22
+ */
23
+ private extractTestsFromSuite;
24
+ /**
25
+ * Extract file durations from Playwright report (legacy file-level)
26
+ *
27
+ * The report structure has nested suites where the top-level suite
28
+ * represents the file. We sum up all test durations within each file.
29
+ */
30
+ private extractFileDurations;
31
+ /**
32
+ * Normalize file path to be relative and consistent
33
+ */
34
+ private normalizeFilePath;
35
+ /**
36
+ * Calculate total duration for a suite (recursively including nested suites)
37
+ */
38
+ private calculateSuiteDuration;
39
+ }
40
+ //# sourceMappingURL=extract-timing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-timing.d.ts","sourceRoot":"","sources":["../../src/commands/extract-timing.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAQ7C,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,OAAO;IAChD,OAAgB,WAAW,SACoD;IAE/E,OAAgB,QAAQ,WAGtB;IAEF,OAAgB,KAAK;;;;;;;MA+BnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAiE1B;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAY5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA8C7B;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAc5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkBzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;CAuB/B"}