@nsxbet/playwright-orchestrator 0.8.1 → 1.1.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.
@@ -0,0 +1,123 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { Command, Flags } from '@oclif/core';
4
+ export default class FilterReport extends Command {
5
+ static description = 'Remove orchestrator-skipped tests from a Playwright JSON report';
6
+ static examples = [
7
+ '<%= config.bin %> filter-report --report-file ./results.json',
8
+ '<%= config.bin %> filter-report --report-file ./merged.json --output-file ./filtered.json',
9
+ ];
10
+ static flags = {
11
+ 'report-file': Flags.string({
12
+ char: 'r',
13
+ description: 'Path to Playwright JSON report file',
14
+ required: true,
15
+ }),
16
+ 'output-file': Flags.string({
17
+ char: 'o',
18
+ description: 'Path to write filtered report (defaults to overwriting input)',
19
+ }),
20
+ verbose: Flags.boolean({
21
+ char: 'v',
22
+ description: 'Show verbose output',
23
+ default: false,
24
+ }),
25
+ };
26
+ async run() {
27
+ const { flags } = await this.parse(FilterReport);
28
+ const reportPath = path.resolve(flags['report-file']);
29
+ if (!fs.existsSync(reportPath)) {
30
+ this.warn(`Report file not found: ${reportPath}`);
31
+ return;
32
+ }
33
+ let report;
34
+ try {
35
+ report = JSON.parse(fs.readFileSync(reportPath, 'utf-8'));
36
+ }
37
+ catch {
38
+ this.error(`Failed to parse report: ${reportPath}`);
39
+ }
40
+ if (!report.suites) {
41
+ this.warn('Report has no suites, nothing to filter');
42
+ return;
43
+ }
44
+ const suites = report.suites;
45
+ const beforeCount = this.countSpecs(suites);
46
+ this.filterSuites(suites);
47
+ const afterCount = this.countSpecs(suites);
48
+ const removed = beforeCount - afterCount;
49
+ const outputPath = flags['output-file']
50
+ ? path.resolve(flags['output-file'])
51
+ : reportPath;
52
+ fs.writeFileSync(outputPath, JSON.stringify(report));
53
+ if (flags.verbose || removed > 0) {
54
+ this.log(`Filtered report: ${beforeCount} → ${afterCount} specs (removed ${removed} orchestrator-skipped)`);
55
+ }
56
+ }
57
+ /**
58
+ * Check if a test has an orchestrator-skip annotation ("Not in shard").
59
+ */
60
+ isOrchestratorSkipped(test) {
61
+ const annotations = test.annotations;
62
+ return (annotations?.some((a) => a.type === 'skip' && a.description === 'Not in shard') ?? false);
63
+ }
64
+ /**
65
+ * Remove orchestrator-skipped entries at three levels:
66
+ * 1. Results: strip skipped results from tests with "Not in shard" annotation
67
+ * 2. Tests: remove tests with no remaining results
68
+ * 3. Specs/Suites: remove empty specs and prune empty suites
69
+ */
70
+ filterSuites(suites) {
71
+ for (let i = suites.length - 1; i >= 0; i--) {
72
+ const suite = suites[i];
73
+ if (!suite)
74
+ continue;
75
+ if (Array.isArray(suite.specs)) {
76
+ const specs = suite.specs;
77
+ for (const spec of specs) {
78
+ const tests = spec.tests;
79
+ if (!tests)
80
+ continue;
81
+ // Result-level: strip skipped results from orchestrator-skipped tests
82
+ for (const test of tests) {
83
+ if (!this.isOrchestratorSkipped(test))
84
+ continue;
85
+ const results = test.results;
86
+ if (!results)
87
+ continue;
88
+ test.results = results.filter((r) => r.status !== 'skipped');
89
+ }
90
+ // Test-level: remove tests with no remaining results
91
+ spec.tests = tests.filter((test) => {
92
+ const results = test.results;
93
+ return results && results.length > 0;
94
+ });
95
+ }
96
+ // Spec-level: remove specs with no remaining tests
97
+ suite.specs = specs.filter((spec) => {
98
+ const tests = spec.tests;
99
+ return tests && tests.length > 0;
100
+ });
101
+ }
102
+ if (Array.isArray(suite.suites)) {
103
+ this.filterSuites(suite.suites);
104
+ }
105
+ const hasSpecs = Array.isArray(suite.specs) && suite.specs.length > 0;
106
+ const hasSubSuites = Array.isArray(suite.suites) && suite.suites.length > 0;
107
+ if (!hasSpecs && !hasSubSuites) {
108
+ suites.splice(i, 1);
109
+ }
110
+ }
111
+ }
112
+ countSpecs(suites) {
113
+ let count = 0;
114
+ for (const suite of suites) {
115
+ if (Array.isArray(suite.specs))
116
+ count += suite.specs.length;
117
+ if (Array.isArray(suite.suites))
118
+ count += this.countSpecs(suite.suites);
119
+ }
120
+ return count;
121
+ }
122
+ }
123
+ //# sourceMappingURL=filter-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-report.js","sourceRoot":"","sources":["../../src/commands/filter-report.ts"],"names":[],"mappings":"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"}
@@ -29,7 +29,7 @@ export interface CKKResult {
29
29
  * @param timeoutMs - Maximum time to search for optimal solution
30
30
  * @returns Optimal (or near-optimal) shard assignments
31
31
  */
32
- export declare function assignWithCKK(tests: TestWithDuration[], numShards: number, timeoutMs?: number): CKKResult;
32
+ export declare function assignWithCKK(tests: TestWithDuration[], numShards: number, timeoutMs?: number, fileAffinityPenalty?: number): CKKResult;
33
33
  /**
34
34
  * Calculate theoretical lower bound for makespan
35
35
  * This is the best possible makespan if we could partition perfectly
@@ -1 +1 @@
1
- {"version":3,"file":"ckk-algorithm.d.ts","sourceRoot":"","sources":["../../src/core/ckk-algorithm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,wBAAwB;IACxB,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,gBAAgB,EAAE,EACzB,SAAS,EAAE,MAAM,EACjB,SAAS,GAAE,MAA4B,GACtC,SAAS,CA8HX;AA4ED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,gBAAgB,EAAE,EACzB,SAAS,EAAE,MAAM,GAChB,MAAM,CAQR"}
1
+ {"version":3,"file":"ckk-algorithm.d.ts","sourceRoot":"","sources":["../../src/core/ckk-algorithm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,wBAAwB;IACxB,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,gBAAgB,EAAE,EACzB,SAAS,EAAE,MAAM,EACjB,SAAS,GAAE,MAA4B,EACvC,mBAAmB,SAAI,GACtB,SAAS,CA8MX;AAmHD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,gBAAgB,EAAE,EACzB,SAAS,EAAE,MAAM,GAChB,MAAM,CAQR"}
@@ -17,7 +17,7 @@ export const DEFAULT_CKK_TIMEOUT = 500;
17
17
  * @param timeoutMs - Maximum time to search for optimal solution
18
18
  * @returns Optimal (or near-optimal) shard assignments
19
19
  */
20
- export function assignWithCKK(tests, numShards, timeoutMs = DEFAULT_CKK_TIMEOUT) {
20
+ export function assignWithCKK(tests, numShards, timeoutMs = DEFAULT_CKK_TIMEOUT, fileAffinityPenalty = 0) {
21
21
  if (tests.length === 0) {
22
22
  return {
23
23
  assignments: createEmptyAssignments(numShards),
@@ -32,18 +32,36 @@ export function assignWithCKK(tests, numShards, timeoutMs = DEFAULT_CKK_TIMEOUT)
32
32
  // More shards than tests - each test gets its own shard
33
33
  return assignOnePerShard(tests, numShards);
34
34
  }
35
- // Sort tests by duration descending for better pruning
36
- const sortedTests = [...tests].sort((a, b) => b.duration - a.duration);
37
- // Get LPT solution as upper bound
38
- const lptResult = assignWithLPTInternal(sortedTests, numShards);
35
+ // Sort tests by duration descending, then by file ascending for better
36
+ // file-affinity convergence (same-file tests appear adjacent)
37
+ const sortedTests = [...tests].sort((a, b) => b.duration - a.duration || a.file.localeCompare(b.file));
38
+ // Precompute per-file test counts for penalty amortization
39
+ const fileTestCounts = new Map();
40
+ for (const test of sortedTests) {
41
+ fileTestCounts.set(test.file, (fileTestCounts.get(test.file) ?? 0) + 1);
42
+ }
43
+ // Get LPT solution as upper bound (uses its own copy of fileRemaining)
44
+ const lptResult = assignWithLPTInternal(sortedTests, numShards, fileAffinityPenalty, fileTestCounts, new Map(fileTestCounts));
39
45
  let bestMakespan = lptResult.makespan;
40
46
  let bestAssignment = lptResult.assignments;
41
47
  let isOptimal = false;
48
+ // Track remaining unassigned tests per file for CKK search
49
+ const fileRemaining = new Map(fileTestCounts);
42
50
  // For small inputs, try to find optimal solution
43
51
  const startTime = Date.now();
44
52
  // Branch and bound search
45
- const shardLoads = new Array(numShards).fill(0);
53
+ // effectiveLoads include penalty; actualLoads are real durations for output
54
+ const effectiveLoads = new Array(numShards).fill(0);
55
+ const actualLoads = new Array(numShards).fill(0);
46
56
  const shardTests = Array.from({ length: numShards }, () => []);
57
+ const shardFiles = Array.from({ length: numShards }, () => new Set());
58
+ function computePenalty(shardIdx, file) {
59
+ if (fileAffinityPenalty <= 0 || shardFiles[shardIdx]?.has(file))
60
+ return 0;
61
+ const total = fileTestCounts.get(file) ?? 1;
62
+ const remaining = fileRemaining.get(file) ?? 1;
63
+ return Math.round(fileAffinityPenalty * (remaining / total));
64
+ }
47
65
  function search(testIndex) {
48
66
  // Check timeout
49
67
  if (Date.now() - startTime > timeoutMs) {
@@ -51,10 +69,10 @@ export function assignWithCKK(tests, numShards, timeoutMs = DEFAULT_CKK_TIMEOUT)
51
69
  }
52
70
  // All tests assigned
53
71
  if (testIndex >= sortedTests.length) {
54
- const currentMakespan = Math.max(...shardLoads);
72
+ const currentMakespan = Math.max(...effectiveLoads);
55
73
  if (currentMakespan < bestMakespan) {
56
74
  bestMakespan = currentMakespan;
57
- bestAssignment = shardLoads.map((load, i) => ({
75
+ bestAssignment = actualLoads.map((load, i) => ({
58
76
  shardIndex: i + 1,
59
77
  tests: [...(shardTests[i] ?? [])],
60
78
  expectedDuration: load,
@@ -67,49 +85,91 @@ export function assignWithCKK(tests, numShards, timeoutMs = DEFAULT_CKK_TIMEOUT)
67
85
  if (!test)
68
86
  return true;
69
87
  // Calculate lower bound: current max + remaining items distributed perfectly
70
- const remainingDuration = sortedTests
71
- .slice(testIndex)
72
- .reduce((sum, t) => sum + t.duration, 0);
73
- const currentMax = Math.max(...shardLoads);
74
- const totalAfter = shardLoads.reduce((sum, l) => sum + l, 0) + remainingDuration;
88
+ const remainingTests = sortedTests.slice(testIndex);
89
+ let remainingDuration = 0;
90
+ for (const t of remainingTests) {
91
+ remainingDuration += t.duration;
92
+ }
93
+ // Account for minimum penalties: each unique file among remaining tests
94
+ // that isn't on ANY shard yet will require at least one penalty payment
95
+ let minPenaltyCost = 0;
96
+ if (fileAffinityPenalty > 0) {
97
+ const allShardFiles = new Set();
98
+ for (const files of shardFiles) {
99
+ for (const f of files)
100
+ allShardFiles.add(f);
101
+ }
102
+ const newFiles = new Set();
103
+ for (const t of remainingTests) {
104
+ if (!allShardFiles.has(t.file))
105
+ newFiles.add(t.file);
106
+ }
107
+ // Each new file incurs at least one amortized penalty
108
+ for (const file of newFiles) {
109
+ const total = fileTestCounts.get(file) ?? 1;
110
+ const remaining = fileRemaining.get(file) ?? 1;
111
+ minPenaltyCost += Math.round(fileAffinityPenalty * (remaining / total));
112
+ }
113
+ }
114
+ const currentMax = Math.max(...effectiveLoads);
115
+ const totalAfter = effectiveLoads.reduce((sum, l) => sum + l, 0) +
116
+ remainingDuration +
117
+ minPenaltyCost;
75
118
  const lowerBound = Math.max(currentMax, Math.ceil(totalAfter / numShards));
76
119
  // Prune if lower bound exceeds best
77
120
  if (lowerBound >= bestMakespan) {
78
121
  return true;
79
122
  }
80
123
  // Try assigning to each shard, starting with least loaded
81
- const shardOrder = shardLoads
124
+ const shardOrder = effectiveLoads
82
125
  .map((load, i) => ({ load, index: i }))
83
126
  .sort((a, b) => a.load - b.load)
84
127
  .map((s) => s.index);
85
- // Skip duplicate loads to avoid redundant exploration
86
- const seenLoads = new Set();
128
+ // Skip duplicate states to avoid redundant exploration.
129
+ // When penalty > 0, key on load + whether shard has the file,
130
+ // since two shards with the same load but different file sets are NOT equivalent.
131
+ const seenStates = new Set();
87
132
  for (const shardIdx of shardOrder) {
88
- const load = shardLoads[shardIdx];
133
+ const load = effectiveLoads[shardIdx];
89
134
  if (load === undefined)
90
135
  continue;
91
- // Skip if we've already tried a shard with this load
92
- if (seenLoads.has(load)) {
136
+ const hasFile = shardFiles[shardIdx]?.has(test.file) ?? false;
137
+ const dedupKey = fileAffinityPenalty > 0 ? `${load}:${hasFile}` : `${load}`;
138
+ if (seenStates.has(dedupKey)) {
93
139
  continue;
94
140
  }
95
- seenLoads.add(load);
141
+ seenStates.add(dedupKey);
142
+ const penalty = computePenalty(shardIdx, test.file);
143
+ const effectiveCost = test.duration + penalty;
96
144
  // Prune: if adding to this shard exceeds best makespan, skip
97
- if (load + test.duration >= bestMakespan) {
145
+ if (load + effectiveCost >= bestMakespan) {
98
146
  continue;
99
147
  }
100
148
  // Assign test to shard
101
- shardLoads[shardIdx] = load + test.duration;
149
+ effectiveLoads[shardIdx] = load + effectiveCost;
150
+ actualLoads[shardIdx] = (actualLoads[shardIdx] ?? 0) + test.duration;
151
+ const isNewFile = !shardFiles[shardIdx]?.has(test.file);
152
+ shardFiles[shardIdx]?.add(test.file);
102
153
  shardTests[shardIdx]?.push(test.testId);
154
+ fileRemaining.set(test.file, (fileRemaining.get(test.file) ?? 1) - 1);
103
155
  const completed = search(testIndex + 1);
104
156
  if (!completed) {
105
157
  // Timeout - restore and return
106
- shardLoads[shardIdx] = load;
158
+ effectiveLoads[shardIdx] = load;
159
+ actualLoads[shardIdx] = (actualLoads[shardIdx] ?? 0) - test.duration;
160
+ if (isNewFile)
161
+ shardFiles[shardIdx]?.delete(test.file);
107
162
  shardTests[shardIdx]?.pop();
163
+ fileRemaining.set(test.file, (fileRemaining.get(test.file) ?? 0) + 1);
108
164
  return false;
109
165
  }
110
166
  // Restore state for backtracking
111
- shardLoads[shardIdx] = load;
167
+ effectiveLoads[shardIdx] = load;
168
+ actualLoads[shardIdx] = (actualLoads[shardIdx] ?? 0) - test.duration;
169
+ if (isNewFile)
170
+ shardFiles[shardIdx]?.delete(test.file);
112
171
  shardTests[shardIdx]?.pop();
172
+ fileRemaining.set(test.file, (fileRemaining.get(test.file) ?? 0) + 1);
113
173
  }
114
174
  return true;
115
175
  }
@@ -117,35 +177,69 @@ export function assignWithCKK(tests, numShards, timeoutMs = DEFAULT_CKK_TIMEOUT)
117
177
  if (tests.length <= 50) {
118
178
  search(0);
119
179
  }
180
+ // Return actual makespan (without penalties) for user-facing output
181
+ const actualMakespan = bestAssignment.length > 0
182
+ ? Math.max(...bestAssignment.map((a) => a.expectedDuration))
183
+ : 0;
120
184
  return {
121
185
  assignments: bestAssignment,
122
- makespan: bestMakespan,
186
+ makespan: actualMakespan,
123
187
  isOptimal,
124
188
  };
125
189
  }
126
190
  /**
127
- * Simple LPT algorithm for internal use
191
+ * Simple LPT algorithm for internal use, with optional file affinity penalty.
192
+ *
193
+ * When fileAffinityPenalty > 0, effective loads include the penalty for each
194
+ * new file introduced to a shard. The returned expectedDuration and makespan
195
+ * reflect effective loads so the CKK search can use them as an upper bound.
128
196
  */
129
- function assignWithLPTInternal(sortedTests, numShards) {
197
+ function assignWithLPTInternal(sortedTests, numShards, fileAffinityPenalty = 0, fileTestCounts = new Map(), fileRemaining = new Map()) {
130
198
  const shards = Array.from({ length: numShards }, (_, i) => ({
131
199
  shardIndex: i + 1,
132
200
  tests: [],
133
201
  expectedDuration: 0,
134
202
  }));
203
+ // Track effective loads (with penalty) separately from actual durations
204
+ const effectiveLoads = new Array(numShards).fill(0);
205
+ const actualLoads = new Array(numShards).fill(0);
206
+ const shardFiles = Array.from({ length: numShards }, () => new Set());
135
207
  for (const test of sortedTests) {
136
- // Find shard with minimum load
137
- let minShard = shards[0];
138
- for (const shard of shards) {
139
- if (minShard && shard.expectedDuration < minShard.expectedDuration) {
140
- minShard = shard;
208
+ // Find shard with minimum effective load after assignment (including penalty)
209
+ let minIdx = 0;
210
+ let minCost = Infinity;
211
+ for (let i = 0; i < numShards; i++) {
212
+ let penalty = 0;
213
+ if (fileAffinityPenalty > 0 && !shardFiles[i]?.has(test.file)) {
214
+ const total = fileTestCounts.get(test.file) ?? 1;
215
+ const remaining = fileRemaining.get(test.file) ?? 1;
216
+ penalty = Math.round(fileAffinityPenalty * (remaining / total));
141
217
  }
218
+ const cost = (effectiveLoads[i] ?? 0) + test.duration + penalty;
219
+ if (cost < minCost) {
220
+ minCost = cost;
221
+ minIdx = i;
222
+ }
223
+ }
224
+ let penalty = 0;
225
+ if (fileAffinityPenalty > 0 && !shardFiles[minIdx]?.has(test.file)) {
226
+ const total = fileTestCounts.get(test.file) ?? 1;
227
+ const remaining = fileRemaining.get(test.file) ?? 1;
228
+ penalty = Math.round(fileAffinityPenalty * (remaining / total));
142
229
  }
143
- if (minShard) {
144
- minShard.tests.push(test.testId);
145
- minShard.expectedDuration += test.duration;
230
+ const shard = shards[minIdx];
231
+ if (shard) {
232
+ shard.tests.push(test.testId);
233
+ effectiveLoads[minIdx] =
234
+ (effectiveLoads[minIdx] ?? 0) + test.duration + penalty;
235
+ actualLoads[minIdx] = (actualLoads[minIdx] ?? 0) + test.duration;
236
+ shard.expectedDuration = actualLoads[minIdx] ?? 0;
237
+ shardFiles[minIdx]?.add(test.file);
238
+ fileRemaining.set(test.file, (fileRemaining.get(test.file) ?? 1) - 1);
146
239
  }
147
240
  }
148
- const makespan = Math.max(...shards.map((s) => s.expectedDuration));
241
+ // Makespan uses effective loads so CKK can prune against it
242
+ const makespan = effectiveLoads.length > 0 ? Math.max(...effectiveLoads) : 0;
149
243
  return { assignments: shards, makespan };
150
244
  }
151
245
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ckk-algorithm.js","sourceRoot":"","sources":["../../src/core/ckk-algorithm.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ckk-algorithm.js","sourceRoot":"","sources":["../../src/core/ckk-algorithm.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,8 @@
1
1
  import type { DiscoveredTest, TimingData } from './types.js';
2
+ /**
3
+ * Default file affinity penalty when no timing data exists (30 seconds)
4
+ */
5
+ export declare const DEFAULT_FILE_AFFINITY_PENALTY = 30000;
2
6
  /**
3
7
  * Default milliseconds per line for duration estimation
4
8
  */
@@ -61,6 +65,13 @@ export declare function getTestDurations(tests: DiscoveredTest[], timingData: Ti
61
65
  * @returns Average duration in milliseconds, or DEFAULT_TEST_DURATION if no data
62
66
  */
63
67
  export declare function calculateAverageTestDuration(timingData: TimingData | null): number;
68
+ /**
69
+ * Calculate file affinity penalty from timing data.
70
+ *
71
+ * Computes the P25 (25th percentile) of per-file average durations.
72
+ * Falls back to DEFAULT_FILE_AFFINITY_PENALTY when no timing data exists.
73
+ */
74
+ export declare function calculateFileAffinityPenalty(timingData: TimingData | null): number;
64
75
  /**
65
76
  * Calculate average test duration for tests in a specific file
66
77
  *
@@ -1 +1 @@
1
- {"version":3,"file":"estimate.d.ts","sourceRoot":"","sources":["../../src/core/estimate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7D;;GAEG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAE3C;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQnD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAA4B,GACtC,MAAM,CAGR;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,EACf,SAAS,GAAE,MAA4B,GACtC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CASrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,GAAG,IAAI,GAC5B,MAAM,CA0BR;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,cAAc,EAAE,EACvB,UAAU,EAAE,UAAU,GAAG,IAAI,GAC5B,KAAK,CAAC;IACP,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CAoBD;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,UAAU,GAAG,IAAI,GAC5B,MAAM,CAQR;AAED;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,UAAU,GAAG,IAAI,GAC5B,MAAM,GAAG,IAAI,CAef"}
1
+ {"version":3,"file":"estimate.d.ts","sourceRoot":"","sources":["../../src/core/estimate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7D;;GAEG;AACH,eAAO,MAAM,6BAA6B,QAAQ,CAAC;AAEnD;;GAEG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAE3C;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQnD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAA4B,GACtC,MAAM,CAGR;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,EACf,SAAS,GAAE,MAA4B,GACtC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CASrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,GAAG,IAAI,GAC5B,MAAM,CA0BR;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,cAAc,EAAE,EACvB,UAAU,EAAE,UAAU,GAAG,IAAI,GAC5B,KAAK,CAAC;IACP,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CAoBD;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,UAAU,GAAG,IAAI,GAC5B,MAAM,CAQR;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,UAAU,GAAG,IAAI,GAC5B,MAAM,CAoCR;AAED;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,UAAU,GAAG,IAAI,GAC5B,MAAM,GAAG,IAAI,CAef"}
@@ -1,6 +1,10 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import { parseTestId } from './types.js';
4
+ /**
5
+ * Default file affinity penalty when no timing data exists (30 seconds)
6
+ */
7
+ export const DEFAULT_FILE_AFFINITY_PENALTY = 30000;
4
8
  /**
5
9
  * Default milliseconds per line for duration estimation
6
10
  */
@@ -121,6 +125,45 @@ export function calculateAverageTestDuration(timingData) {
121
125
  const sum = durations.reduce((acc, d) => acc + d, 0);
122
126
  return Math.round(sum / durations.length);
123
127
  }
128
+ /**
129
+ * Calculate file affinity penalty from timing data.
130
+ *
131
+ * Computes the P25 (25th percentile) of per-file average durations.
132
+ * Falls back to DEFAULT_FILE_AFFINITY_PENALTY when no timing data exists.
133
+ */
134
+ export function calculateFileAffinityPenalty(timingData) {
135
+ if (!timingData || Object.keys(timingData.tests).length === 0) {
136
+ return DEFAULT_FILE_AFFINITY_PENALTY;
137
+ }
138
+ // Group tests by file and compute average duration per file
139
+ const fileTests = new Map();
140
+ for (const test of Object.values(timingData.tests)) {
141
+ const durations = fileTests.get(test.file);
142
+ if (durations) {
143
+ durations.push(test.duration);
144
+ }
145
+ else {
146
+ fileTests.set(test.file, [test.duration]);
147
+ }
148
+ }
149
+ const fileAverages = [];
150
+ for (const durations of fileTests.values()) {
151
+ const sum = durations.reduce((acc, d) => acc + d, 0);
152
+ fileAverages.push(sum / durations.length);
153
+ }
154
+ if (fileAverages.length === 0) {
155
+ return DEFAULT_FILE_AFFINITY_PENALTY;
156
+ }
157
+ // P25 of per-file averages
158
+ fileAverages.sort((a, b) => a - b);
159
+ const index = (fileAverages.length - 1) * 0.25;
160
+ const lower = Math.floor(index);
161
+ const upper = Math.ceil(index);
162
+ const lowerVal = fileAverages[lower] ?? 0;
163
+ const upperVal = fileAverages[upper] ?? lowerVal;
164
+ const penalty = lowerVal + (upperVal - lowerVal) * (index - lower);
165
+ return Math.round(penalty);
166
+ }
124
167
  /**
125
168
  * Calculate average test duration for tests in a specific file
126
169
  *
@@ -1 +1 @@
1
- {"version":3,"file":"estimate.js","sourceRoot":"","sources":["../../src/core/estimate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"estimate.js","sourceRoot":"","sources":["../../src/core/estimate.ts"],"names":[],"mappings":"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"}
package/dist/fixture.d.ts CHANGED
@@ -56,16 +56,6 @@ import type { TestType } from '@playwright/test';
56
56
  export declare function withOrchestratorFilter<T extends object, W extends object>(test: TestType<T, W>): TestType<T & {
57
57
  _orchestratorFilter: undefined;
58
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
- *
63
- * Sets up the orchestrator filter as a beforeEach hook.
64
- * This will skip tests that are not in the current shard.
65
- *
66
- * @param test - The test object from @playwright/test
67
- */
68
- export declare function setupOrchestratorFilter<T extends object, W extends object>(test: TestType<T, W>): void;
69
59
  /**
70
60
  * Check if a test should run based on the shard file.
71
61
  * Can be called manually in individual tests.
@@ -1 +1 @@
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"}
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;;;;;;;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
@@ -127,42 +127,6 @@ export function withOrchestratorFilter(test) {
127
127
  ],
128
128
  });
129
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
- *
134
- * Sets up the orchestrator filter as a beforeEach hook.
135
- * This will skip tests that are not in the current shard.
136
- *
137
- * @param test - The test object from @playwright/test
138
- */
139
- export function setupOrchestratorFilter(test) {
140
- // biome-ignore lint/correctness/noEmptyPattern: Playwright requires empty destructuring for fixtures
141
- test.beforeEach(async ({}, testInfo) => {
142
- const allowedTestIds = loadShardFile();
143
- if (allowedTestIds) {
144
- // CRITICAL: Use project.testDir for consistent path resolution with test-discovery
145
- // No fallback to process.cwd() - this causes path mismatch bugs
146
- const testDir = testInfo.project.testDir;
147
- if (!testDir) {
148
- throw new Error('[Orchestrator Fixture] Could not determine project testDir. ' +
149
- 'Ensure your playwright.config.ts has projects configured with testDir.');
150
- }
151
- const testId = buildTestIdFromRuntime(testInfo.file, testInfo.titlePath, {
152
- projectName: testInfo.project.name,
153
- baseDir: testDir,
154
- });
155
- const isAllowed = allowedTestIds.has(testId);
156
- // Debug: Write to stderr for visibility in CI logs
157
- if (process.env.ORCHESTRATOR_DEBUG === '1') {
158
- process.stderr.write(`[Fixture] testDir=${testInfo.project.testDir} | testId=${testId} | allowed=${isAllowed}\n`);
159
- }
160
- if (!isAllowed) {
161
- test.skip(true, 'Not in shard');
162
- }
163
- }
164
- });
165
- }
166
130
  /**
167
131
  * Check if a test should run based on the shard file.
168
132
  * Can be called manually in individual tests.