@zohodesk/testinglibrary 0.0.85-n20-experimental → 0.0.86-n20-experimental

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.
@@ -3,17 +3,15 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.FAILURE_TAG_SUFFIX = exports.FAILURE_TAG_METADATA_SCHEMA_VERSION = exports.FAILURE_TAG_METADATA_ATTACHMENT = exports.FAILURE_TAGS = void 0;
6
+ exports.FAILURE_TAG_SUFFIX = exports.FAILURE_TAG_METADATA_ATTACHMENT = exports.FAILURE_TAGS = void 0;
7
7
  const FAILURE_TAGS = exports.FAILURE_TAGS = {
8
8
  CASE: 'CASE',
9
9
  CODE: 'CODE',
10
10
  ENVIRONMENT: 'ENVIRONMENT',
11
- FLAKY: 'FLAKY',
12
11
  TOOL: 'TOOL'
13
12
  };
14
13
 
15
14
  // Suffix appended to a category when merging into the existing spec.tags array,
16
- // e.g. "CODE" -> "CODE_failure". The flaky category is emitted as-is ("FLAKY").
15
+ // e.g. "CODE" -> "CODE_failure".
17
16
  const FAILURE_TAG_SUFFIX = exports.FAILURE_TAG_SUFFIX = '_failure';
18
- const FAILURE_TAG_METADATA_ATTACHMENT = exports.FAILURE_TAG_METADATA_ATTACHMENT = 'failure-tag-metadata';
19
- const FAILURE_TAG_METADATA_SCHEMA_VERSION = exports.FAILURE_TAG_METADATA_SCHEMA_VERSION = 1;
17
+ const FAILURE_TAG_METADATA_ATTACHMENT = exports.FAILURE_TAG_METADATA_ATTACHMENT = 'failure-tag-metadata';
@@ -16,19 +16,20 @@ var _failureTagConstants = require("../constants/failureTagConstants");
16
16
  var _patterns = require("../patterns");
17
17
  var _readConfigFile = require("../readConfigFile");
18
18
  var _runtimeLogReader = require("./runtimeLogReader");
19
- // A failing test is tagged purely on presence: if its runtime log contains an
20
- // environment pattern it gets `ENVIRONMENT`, if it contains a code pattern it
21
- // gets `CODE`, and if its stdout/stderr contains a tool pattern it gets `TOOL`
22
- // or a case pattern it gets `CASE` (any may apply). A `CASE` match also adds
23
- // `CODE` for now via case-implied metadata. `FLAKY` comes from the Playwright
24
- // retry outcome, not the log.
19
+ // A failing test resolves to a SINGLE category tag by strict priority:
20
+ // CODE > CASE > TOOL > ENVIRONMENT. If several categories match, only the
21
+ // highest-priority one is emitted (e.g. CODE + ENVIRONMENT -> CODE only).
22
+ // CODE/ENVIRONMENT patterns are matched against the runtime log; TOOL/CASE
23
+ // patterns against stdout/stderr. If a failure matches no category at all it
24
+ // defaults to CODE. Retry-passed (flaky) outcomes are not tagged.
25
25
 
26
26
  const FAILING_RESULT_STATUSES = new Set(['failed', 'timedOut', 'interrupted']);
27
27
  const UNEXPECTED_OUTCOME = 'unexpected';
28
- const FLAKY_OUTCOME = 'flaky';
29
28
  const RUNTIME_LOG_SOURCE = 'runtime-log';
30
29
  const MAX_MATCH_TEXT_LENGTH = 500;
31
- const TAG_ORDER = [_failureTagConstants.FAILURE_TAGS.FLAKY, _failureTagConstants.FAILURE_TAGS.ENVIRONMENT, _failureTagConstants.FAILURE_TAGS.CASE, _failureTagConstants.FAILURE_TAGS.CODE, _failureTagConstants.FAILURE_TAGS.TOOL];
30
+
31
+ // Single-tag priority: the highest-ranked matched category wins.
32
+ const CATEGORY_PRIORITY = [_failureTagConstants.FAILURE_TAGS.CODE, _failureTagConstants.FAILURE_TAGS.CASE, _failureTagConstants.FAILURE_TAGS.TOOL, _failureTagConstants.FAILURE_TAGS.ENVIRONMENT];
32
33
  const RUNTIME_LOG_RULES = [{
33
34
  tag: _failureTagConstants.FAILURE_TAGS.ENVIRONMENT,
34
35
  patterns: _patterns.ENVIRONMENT_PATTERNS
@@ -41,13 +42,9 @@ const STDIO_RULES = [{
41
42
  patterns: _patterns.TOOL_PATTERNS
42
43
  }, {
43
44
  tag: _failureTagConstants.FAILURE_TAGS.CASE,
44
- patterns: _patterns.CASE_PATTERNS,
45
- impliesCode: true
45
+ patterns: _patterns.CASE_PATTERNS
46
46
  }];
47
- function resolveLogsDir(logsDir) {
48
- if (logsDir) {
49
- return logsDir;
50
- }
47
+ function resolveLogsDir() {
51
48
  const {
52
49
  uatDirectory
53
50
  } = (0, _readConfigFile.generateConfigFromFile)();
@@ -147,33 +144,18 @@ function collectRuntimeLogMatches(events, matches) {
147
144
  function collectStdioMatches(stdio, matches) {
148
145
  for (const rule of STDIO_RULES) {
149
146
  const match = findStdioMatch(stdio, rule.patterns, rule.tag);
150
- if (!match) {
151
- continue;
152
- }
153
- matches.push(match);
154
- if (rule.impliesCode) {
155
- implyCodeFromCase(matches, match);
147
+ if (match) {
148
+ matches.push(match);
156
149
  }
157
150
  }
158
151
  }
159
- function implyCodeFromCase(matches, caseMatch) {
160
- if (matches.some(match => match.tag === _failureTagConstants.FAILURE_TAGS.CODE)) {
161
- return;
162
- }
163
- matches.push({
164
- tag: _failureTagConstants.FAILURE_TAGS.CODE,
165
- pattern: null,
166
- source: 'case-implied',
167
- text: caseMatch.text
168
- });
169
- }
170
152
  function classifyFailureDetailsFromEvents(events = []) {
171
153
  const matches = [];
172
154
  collectRuntimeLogMatches(events, matches);
173
155
  return buildFailureTagMetadata(matches);
174
156
  }
175
- function classifyFailureDetailsFromResult(result, testTitle, logsDir) {
176
- const resolvedLogsDir = resolveLogsDir(logsDir);
157
+ function classifyFailureDetailsFromResult(result, testTitle) {
158
+ const resolvedLogsDir = resolveLogsDir();
177
159
  const events = (0, _runtimeLogReader.readRuntimeLogEventsForResult)(result, testTitle, resolvedLogsDir);
178
160
  const matches = [];
179
161
  collectRuntimeLogMatches(events, matches);
@@ -181,26 +163,24 @@ function classifyFailureDetailsFromResult(result, testTitle, logsDir) {
181
163
  return buildFailureTagMetadata(matches);
182
164
  }
183
165
  function buildFailureTagMetadata(matches = []) {
184
- const tags = TAG_ORDER.filter(tag => matches.some(match => match.tag === tag));
166
+ const tags = [];
167
+
168
+ // Collapse the matched categories to the single highest-priority winner.
169
+ // When a failure matched no category, fall back to CODE.
170
+ const winningCategory = CATEGORY_PRIORITY.find(tag => matches.some(match => match.tag === tag));
171
+ if (winningCategory) {
172
+ tags.push(winningCategory);
173
+ } else {
174
+ tags.push(_failureTagConstants.FAILURE_TAGS.CODE);
175
+ }
185
176
  return {
186
- schemaVersion: _failureTagConstants.FAILURE_TAG_METADATA_SCHEMA_VERSION,
187
177
  tags,
188
178
  matches
189
179
  };
190
180
  }
191
- function classifyFailureDetails(testDetails = [], testTitle = 'test', logsDir) {
192
- const resolvedLogsDir = resolveLogsDir(logsDir);
181
+ function classifyFailureDetails(testDetails = [], testTitle = 'test') {
193
182
  const matches = [];
194
183
  for (const detail of testDetails) {
195
- if (detail.status === FLAKY_OUTCOME) {
196
- matches.push({
197
- tag: _failureTagConstants.FAILURE_TAGS.FLAKY,
198
- pattern: null,
199
- source: 'retry-outcome',
200
- text: null
201
- });
202
- continue;
203
- }
204
184
  if (detail.status !== UNEXPECTED_OUTCOME) {
205
185
  continue;
206
186
  }
@@ -208,7 +188,7 @@ function classifyFailureDetails(testDetails = [], testTitle = 'test', logsDir) {
208
188
  if (!failingResult) {
209
189
  continue;
210
190
  }
211
- const resultDetails = classifyFailureDetailsFromResult(failingResult, testTitle, resolvedLogsDir);
191
+ const resultDetails = classifyFailureDetailsFromResult(failingResult, testTitle);
212
192
  matches.push(...resultDetails.matches);
213
193
  }
214
194
  const uniqueMatches = [];
@@ -217,10 +197,16 @@ function classifyFailureDetails(testDetails = [], testTitle = 'test', logsDir) {
217
197
  uniqueMatches.push(match);
218
198
  }
219
199
  }
200
+ if (!testDetails.some(detail => detail.status === UNEXPECTED_OUTCOME)) {
201
+ return {
202
+ tags: [],
203
+ matches: uniqueMatches
204
+ };
205
+ }
220
206
  return buildFailureTagMetadata(uniqueMatches);
221
207
  }
222
- function classifyFailureTags(testDetails = [], testTitle = 'test', logsDir) {
223
- return classifyFailureDetails(testDetails, testTitle, logsDir).tags;
208
+ function classifyFailureTags(testDetails = [], testTitle = 'test') {
209
+ return classifyFailureDetails(testDetails, testTitle).tags;
224
210
  }
225
211
  function pickFailingResult(results = []) {
226
212
  const failing = results.filter(result => FAILING_RESULT_STATUSES.has(result.status));
@@ -230,9 +216,6 @@ function pickFailingResult(results = []) {
230
216
  return failing[failing.length - 1];
231
217
  }
232
218
  function formatFailureTag(tag) {
233
- if (tag === _failureTagConstants.FAILURE_TAGS.FLAKY) {
234
- return tag;
235
- }
236
219
  return `${tag}${_failureTagConstants.FAILURE_TAG_SUFFIX}`;
237
220
  }
238
221
  function mergeFailureTags(existingTags = [], failureTags = []) {
@@ -20,15 +20,7 @@ const TOOL_PATTERNS = exports.TOOL_PATTERNS = [/Error during login/i, /fetch fai
20
20
  /Error while parsing cookies/i,
21
21
  // FileMutex
22
22
  /Watch timeout exceeded/i,
23
- // Runner
24
- /Method 'run\(\)' must be implemented\./i, /Invalid runner type/i,
25
- // Process
26
- /Child Process Exited with Code/i, /Terminating Playwright Process\.\.\./i, /Cleaning up\.\.\./i,
27
- // TestData
28
- /Error appending or creating the test data file:/i, /Error\(err\)/i, /Error while deleting the test data file:/i,
29
- // FeatureFile
30
- /Error while parsing feature files\. Please fix the above issues before running test cases/i,
31
23
  // RPC
32
- /Error: HTTP /i, /Invalid payload\. It must be a non-null object\./i, /Invalid payload\.arguments\.entityId\./i,
24
+ /Invalid payload\. It must be a non-null object\./i, /Invalid payload\.arguments\.entityId\./i,
33
25
  // Search
34
26
  /Invalid or missing data in dataTable/i, /JSONPath query/i];
@@ -6,18 +6,25 @@ var _os = _interopRequireDefault(require("os"));
6
6
  var _path = _interopRequireDefault(require("path"));
7
7
  var toolPatterns = _interopRequireWildcard(require("../../../../../core/playwright/patterns/toolPatterns"));
8
8
  var casePatterns = _interopRequireWildcard(require("../../../../../core/playwright/patterns/casePatterns"));
9
+ var _readConfigFile = require("../../../../../core/playwright/readConfigFile");
9
10
  var _runtimeLogPath = require("../../../../../core/playwright/helpers/runtimeLogPath");
10
11
  var _failureTagClassifier = require("../../../../../core/playwright/helpers/failureTagClassifier");
11
12
  var _failureTagConstants = require("../../../../../core/playwright/constants/failureTagConstants");
12
13
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
14
+ jest.mock('../../../../../core/playwright/readConfigFile', () => ({
15
+ generateConfigFromFile: jest.fn()
16
+ }));
13
17
  const TEST_TITLE = 'environment-abort-test';
14
18
  let tempDir;
19
+ function runtimeLogsDir() {
20
+ return _path.default.join(tempDir, 'runtime-logs');
21
+ }
15
22
  function writeRuntimeLog(events, {
16
23
  workerIndex = 0,
17
24
  retry = 0,
18
25
  title = TEST_TITLE
19
26
  } = {}) {
20
- const file = (0, _runtimeLogPath.resolveRuntimeLogFilePath)(tempDir, {
27
+ const file = (0, _runtimeLogPath.resolveRuntimeLogFilePath)(runtimeLogsDir(), {
21
28
  workerIndex,
22
29
  retry,
23
30
  title
@@ -55,6 +62,9 @@ function failingDetailWithStdio({
55
62
  }
56
63
  beforeEach(() => {
57
64
  tempDir = _fs.default.mkdtempSync(_path.default.join(_os.default.tmpdir(), 'failure-tag-'));
65
+ _readConfigFile.generateConfigFromFile.mockReturnValue({
66
+ uatDirectory: tempDir
67
+ });
58
68
  });
59
69
  afterEach(() => {
60
70
  _fs.default.rmSync(tempDir, {
@@ -63,12 +73,12 @@ afterEach(() => {
63
73
  });
64
74
  });
65
75
  describe('classifyFailureTags', () => {
66
- test('returns flaky and skips log analysis for flaky outcome', () => {
76
+ test('leaves retry-passed (flaky) outcomes untagged', () => {
67
77
  const tags = (0, _failureTagClassifier.classifyFailureTags)([{
68
78
  status: 'flaky',
69
79
  results: []
70
- }], TEST_TITLE, tempDir);
71
- expect(tags).toEqual(['FLAKY']);
80
+ }], TEST_TITLE);
81
+ expect(tags).toEqual([]);
72
82
  });
73
83
  test('tags environment when a document abort is present', () => {
74
84
  const tags = (0, _failureTagClassifier.classifyFailureTags)([failingDetail([{
@@ -76,7 +86,7 @@ describe('classifyFailureTags', () => {
76
86
  url: 'https://app/events/list/commentsubtabevent',
77
87
  failure: 'net::ERR_ABORTED',
78
88
  resourceType: 'document'
79
- }])], TEST_TITLE, tempDir);
89
+ }])], TEST_TITLE);
80
90
  expect(tags).toEqual(['ENVIRONMENT']);
81
91
  });
82
92
  test('tags environment for a network/cert failure', () => {
@@ -87,7 +97,7 @@ describe('classifyFailureTags', () => {
87
97
  resourceType: 'script'
88
98
  }], {
89
99
  title: 'cert-test'
90
- })], 'cert-test', tempDir);
100
+ })], 'cert-test');
91
101
  expect(tags).toEqual(['ENVIRONMENT']);
92
102
  });
93
103
  test('tags code when an app undefined-property error is present', () => {
@@ -97,10 +107,10 @@ describe('classifyFailureTags', () => {
97
107
  message: "Cannot read properties of undefined (reading 'id')"
98
108
  }], {
99
109
  title: 'code-test'
100
- })], 'code-test', tempDir);
110
+ })], 'code-test');
101
111
  expect(tags).toEqual(['CODE']);
102
112
  });
103
- test('does not tag a non-document (xhr) abort as environment', () => {
113
+ test('defaults to code (not environment) for a non-document xhr abort', () => {
104
114
  const tags = (0, _failureTagClassifier.classifyFailureTags)([failingDetail([{
105
115
  kind: 'requestfailed',
106
116
  url: 'https://app/api/tickets',
@@ -108,17 +118,17 @@ describe('classifyFailureTags', () => {
108
118
  resourceType: 'xhr'
109
119
  }], {
110
120
  title: 'xhr-test'
111
- })], 'xhr-test', tempDir);
112
- expect(tags).toEqual([]);
121
+ })], 'xhr-test');
122
+ expect(tags).toEqual(['CODE']);
113
123
  });
114
- test('tags case and code when stderr reports DATA_GENERATION_ERROR', () => {
124
+ test('tags case when stderr reports DATA_GENERATION_ERROR', () => {
115
125
  const detail = failingDetailWithStdio({
116
126
  stderr: [{
117
127
  text: 'status : DATA_GENERATION_ERROR\n generator: comments'
118
128
  }]
119
129
  });
120
- const tags = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE, tempDir);
121
- expect(tags).toEqual(['CASE', 'CODE']);
130
+ const tags = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE);
131
+ expect(tags).toEqual(['CASE']);
122
132
  });
123
133
  test('tags tool for a known tool pattern in base64 stdout', () => {
124
134
  const detail = failingDetailWithStdio({
@@ -126,10 +136,10 @@ describe('classifyFailureTags', () => {
126
136
  buffer: Buffer.from('Error during login').toString('base64')
127
137
  }]
128
138
  });
129
- const tags = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE, tempDir);
139
+ const tags = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE);
130
140
  expect(tags).toEqual(['TOOL']);
131
141
  });
132
- test('tags case and code when stderr matches a case pattern', () => {
142
+ test('tags case when stderr matches a case pattern', () => {
133
143
  const originalCasePatterns = casePatterns.CASE_PATTERNS.slice();
134
144
  try {
135
145
  casePatterns.CASE_PATTERNS.push(/expect\(received\)\.toBe/i);
@@ -138,22 +148,37 @@ describe('classifyFailureTags', () => {
138
148
  text: 'Error: expect(received).toBe(expected)'
139
149
  }]
140
150
  });
141
- const tags = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE, tempDir);
142
- expect(tags).toEqual(['CASE', 'CODE']);
151
+ const tags = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE);
152
+ expect(tags).toEqual(['CASE']);
143
153
  } finally {
144
154
  casePatterns.CASE_PATTERNS.length = 0;
145
155
  casePatterns.CASE_PATTERNS.push(...originalCasePatterns);
146
156
  }
147
157
  });
148
- test('does not tag tool for benign stdout/stderr', () => {
158
+ test('emits only the highest-priority tag (CODE) when CODE and ENVIRONMENT both match', () => {
159
+ const tags = (0, _failureTagClassifier.classifyFailureTags)([failingDetail([{
160
+ kind: 'requestfailed',
161
+ url: 'https://app/events/list/commentsubtabevent',
162
+ failure: 'net::ERR_ABORTED',
163
+ resourceType: 'document'
164
+ }, {
165
+ kind: 'pageerror',
166
+ name: 'TypeError',
167
+ message: "Cannot read properties of undefined (reading 'id')"
168
+ }], {
169
+ title: 'code-over-env-test'
170
+ })], 'code-over-env-test');
171
+ expect(tags).toEqual(['CODE']);
172
+ });
173
+ test('defaults to code (not tool) for benign stdout/stderr', () => {
149
174
  const detail = failingDetailWithStdio({
150
175
  stdout: [{
151
176
  text: 'Generated 5 records successfully'
152
177
  }],
153
178
  stderr: []
154
179
  });
155
- const tags = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE, tempDir);
156
- expect(tags).toEqual([]);
180
+ const tags = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE);
181
+ expect(tags).toEqual(['CODE']);
157
182
  });
158
183
  test('does not throw for malformed stdio chunk entries', () => {
159
184
  const detail = failingDetailWithStdio({
@@ -162,10 +187,10 @@ describe('classifyFailureTags', () => {
162
187
  }],
163
188
  stderr: [false]
164
189
  });
165
- expect(() => (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE, tempDir)).not.toThrow();
166
- expect((0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE, tempDir)).toEqual([]);
190
+ expect(() => (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE)).not.toThrow();
191
+ expect((0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE)).toEqual(['CODE']);
167
192
  });
168
- test('leaves untagged when no environment or code pattern is present', () => {
193
+ test('defaults to code when a failure matches no pattern', () => {
169
194
  const tags = (0, _failureTagClassifier.classifyFailureTags)([failingDetail([{
170
195
  kind: 'response',
171
196
  status: 200,
@@ -180,7 +205,18 @@ describe('classifyFailureTags', () => {
180
205
  }
181
206
  }], {
182
207
  title: 'neutral-test'
183
- })], 'neutral-test', tempDir);
208
+ })], 'neutral-test');
209
+ expect(tags).toEqual(['CODE']);
210
+ });
211
+ test('leaves a passing spec untagged (no default code)', () => {
212
+ const tags = (0, _failureTagClassifier.classifyFailureTags)([{
213
+ status: 'expected',
214
+ results: [{
215
+ status: 'passed',
216
+ workerIndex: 0,
217
+ retry: 0
218
+ }]
219
+ }], 'passing-test');
184
220
  expect(tags).toEqual([]);
185
221
  });
186
222
  });
@@ -191,7 +227,7 @@ describe('classifyFailureDetails', () => {
191
227
  url: 'https://app/events/list/commentsubtabevent',
192
228
  failure: 'net::ERR_ABORTED',
193
229
  resourceType: 'document'
194
- }])], TEST_TITLE, tempDir);
230
+ }])], TEST_TITLE);
195
231
  expect(details.tags).toEqual(['ENVIRONMENT']);
196
232
  expect(details.matches).toHaveLength(1);
197
233
  expect(details.matches[0]).toMatchObject({
@@ -214,25 +250,17 @@ describe('classifyFailureDetails', () => {
214
250
  source: 'runtime-log'
215
251
  });
216
252
  });
217
- test('records flaky outcome without a regex pattern', () => {
218
- const details = (0, _failureTagClassifier.classifyFailureDetails)([{
219
- status: 'flaky',
220
- results: []
221
- }], TEST_TITLE, tempDir);
222
- expect(details.tags).toEqual(['FLAKY']);
223
- expect(details.matches[0]).toEqual({
224
- tag: 'FLAKY',
225
- pattern: null,
226
- source: 'retry-outcome',
227
- text: null
228
- });
253
+ test('defaults to code with no matches when events carry no recognised pattern', () => {
254
+ const details = (0, _failureTagClassifier.classifyFailureDetailsFromEvents)([]);
255
+ expect(details.tags).toEqual(['CODE']);
256
+ expect(details.matches).toEqual([]);
229
257
  });
230
258
  test('records tool pattern from stdio', () => {
231
259
  const details = (0, _failureTagClassifier.classifyFailureDetails)([failingDetailWithStdio({
232
260
  stderr: [{
233
261
  text: 'Error during login for actor admin'
234
262
  }]
235
- })], TEST_TITLE, tempDir);
263
+ })], TEST_TITLE);
236
264
  expect(details.tags).toEqual(['TOOL']);
237
265
  expect(details.matches[0]).toMatchObject({
238
266
  tag: 'TOOL',
@@ -240,7 +268,7 @@ describe('classifyFailureDetails', () => {
240
268
  source: 'stdio'
241
269
  });
242
270
  });
243
- test('records case pattern from stdio and implies code', () => {
271
+ test('records case pattern from stdio without implying code', () => {
244
272
  const originalCasePatterns = casePatterns.CASE_PATTERNS.slice();
245
273
  try {
246
274
  casePatterns.CASE_PATTERNS.push(/expect\(received\)\.toBe/i);
@@ -248,26 +276,20 @@ describe('classifyFailureDetails', () => {
248
276
  stderr: [{
249
277
  text: 'Error: expect(received).toBe(expected)'
250
278
  }]
251
- })], TEST_TITLE, tempDir);
252
- expect(details.tags).toEqual(['CASE', 'CODE']);
253
- expect(details.matches).toHaveLength(2);
279
+ })], TEST_TITLE);
280
+ expect(details.tags).toEqual(['CASE']);
281
+ expect(details.matches).toHaveLength(1);
254
282
  expect(details.matches[0]).toMatchObject({
255
283
  tag: 'CASE',
256
284
  pattern: 'expect\\(received\\)\\.toBe',
257
285
  source: 'stdio'
258
286
  });
259
- expect(details.matches[1]).toMatchObject({
260
- tag: 'CODE',
261
- pattern: null,
262
- source: 'case-implied'
263
- });
264
- expect(details.matches[1].text).toContain('expect(received).toBe(expected)');
265
287
  } finally {
266
288
  casePatterns.CASE_PATTERNS.length = 0;
267
289
  casePatterns.CASE_PATTERNS.push(...originalCasePatterns);
268
290
  }
269
291
  });
270
- test('keeps runtime-log code match when case also matches', () => {
292
+ test('resolves to CODE when a runtime-log code match outranks a stdio case match', () => {
271
293
  const originalCasePatterns = casePatterns.CASE_PATTERNS.slice();
272
294
  try {
273
295
  casePatterns.CASE_PATTERNS.push(/expect\(received\)\.toBe/i);
@@ -288,8 +310,10 @@ describe('classifyFailureDetails', () => {
288
310
  text: 'Error: expect(received).toBe(expected)'
289
311
  }]
290
312
  }]
291
- }], 'case-with-runtime-code', tempDir);
292
- expect(details.tags).toEqual(['CASE', 'CODE']);
313
+ }], 'case-with-runtime-code');
314
+
315
+ // CODE outranks CASE, so only CODE surfaces as the tag; both remain as evidence.
316
+ expect(details.tags).toEqual(['CODE']);
293
317
  expect(details.matches).toHaveLength(2);
294
318
  expect(details.matches.find(match => match.tag === 'CASE')).toMatchObject({
295
319
  source: 'stdio'
@@ -303,7 +327,7 @@ describe('classifyFailureDetails', () => {
303
327
  casePatterns.CASE_PATTERNS.push(...originalCasePatterns);
304
328
  }
305
329
  });
306
- test('implies code from case when no runtime log is present', () => {
330
+ test('keeps case as the tag and does not add code when no runtime log is present', () => {
307
331
  const originalCasePatterns = casePatterns.CASE_PATTERNS.slice();
308
332
  try {
309
333
  casePatterns.CASE_PATTERNS.push(/Timed out \d+ms waiting for/i);
@@ -311,12 +335,9 @@ describe('classifyFailureDetails', () => {
311
335
  stderr: [{
312
336
  text: 'Timed out 5000ms waiting for locator("#save")'
313
337
  }]
314
- })], TEST_TITLE, tempDir);
315
- expect(details.tags).toEqual(['CASE', 'CODE']);
316
- expect(details.matches.find(match => match.tag === 'CODE')).toMatchObject({
317
- source: 'case-implied',
318
- pattern: null
319
- });
338
+ })], TEST_TITLE);
339
+ expect(details.tags).toEqual(['CASE']);
340
+ expect(details.matches.find(match => match.tag === 'CODE')).toBeUndefined();
320
341
  } finally {
321
342
  casePatterns.CASE_PATTERNS.length = 0;
322
343
  casePatterns.CASE_PATTERNS.push(...originalCasePatterns);
@@ -376,8 +397,8 @@ describe('mergeFailureTags', () => {
376
397
  test('places suffixed failure tags first, then existing tags', () => {
377
398
  expect((0, _failureTagClassifier.mergeFailureTags)(['@comments', '@calls'], ['CODE', 'ENVIRONMENT'])).toEqual(['CODE_failure', 'ENVIRONMENT_failure', '@comments', '@calls']);
378
399
  });
379
- test('emits the flaky category as FLAKY without a suffix', () => {
380
- expect((0, _failureTagClassifier.mergeFailureTags)([], ['FLAKY', 'CODE'])).toEqual(['FLAKY', 'CODE_failure']);
400
+ test('formats all failure tags with suffix', () => {
401
+ expect((0, _failureTagClassifier.mergeFailureTags)([], ['CODE', 'TOOL'])).toEqual(['CODE_failure', 'TOOL_failure']);
381
402
  });
382
403
  test('does not duplicate an already-present failure tag', () => {
383
404
  expect((0, _failureTagClassifier.mergeFailureTags)(['CODE_failure'], ['CODE'])).toEqual(['CODE_failure']);
@@ -399,8 +420,8 @@ describe('regex safety', () => {
399
420
  text: 'status : DATA_GENERATION_ERROR'
400
421
  }]
401
422
  });
402
- const first = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE, tempDir);
403
- const second = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE, tempDir);
423
+ const first = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE);
424
+ const second = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE);
404
425
  expect(first).toEqual(['TOOL']);
405
426
  expect(second).toEqual(['TOOL']);
406
427
  } finally {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@zohodesk/testinglibrary",
3
- "version": "0.0.85-n20-experimental",
3
+ "version": "0.0.86-n20-experimental",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@zohodesk/testinglibrary",
9
- "version": "0.0.85-n20-experimental",
9
+ "version": "0.0.86-n20-experimental",
10
10
  "hasInstallScript": true,
11
11
  "license": "ISC",
12
12
  "dependencies": {
@@ -3970,9 +3970,9 @@
3970
3970
  }
3971
3971
  },
3972
3972
  "node_modules/@sinclair/typebox": {
3973
- "version": "0.34.49",
3974
- "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz",
3975
- "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==",
3973
+ "version": "0.34.52",
3974
+ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz",
3975
+ "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==",
3976
3976
  "license": "MIT"
3977
3977
  },
3978
3978
  "node_modules/@sinonjs/commons": {
@@ -4345,9 +4345,9 @@
4345
4345
  "license": "MIT"
4346
4346
  },
4347
4347
  "node_modules/@ungap/structured-clone": {
4348
- "version": "1.3.2",
4349
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.2.tgz",
4350
- "integrity": "sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA==",
4348
+ "version": "1.3.3",
4349
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz",
4350
+ "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==",
4351
4351
  "license": "ISC"
4352
4352
  },
4353
4353
  "node_modules/@unrs/resolver-binding-android-arm-eabi": {
@@ -5518,9 +5518,9 @@
5518
5518
  }
5519
5519
  },
5520
5520
  "node_modules/baseline-browser-mapping": {
5521
- "version": "2.10.42",
5522
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz",
5523
- "integrity": "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==",
5521
+ "version": "2.10.43",
5522
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz",
5523
+ "integrity": "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==",
5524
5524
  "license": "Apache-2.0",
5525
5525
  "bin": {
5526
5526
  "baseline-browser-mapping": "dist/cli.cjs"
@@ -5569,9 +5569,9 @@
5569
5569
  }
5570
5570
  },
5571
5571
  "node_modules/browserslist": {
5572
- "version": "4.28.5",
5573
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.5.tgz",
5574
- "integrity": "sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==",
5572
+ "version": "4.28.6",
5573
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz",
5574
+ "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==",
5575
5575
  "funding": [
5576
5576
  {
5577
5577
  "type": "opencollective",
@@ -5589,9 +5589,9 @@
5589
5589
  "license": "MIT",
5590
5590
  "dependencies": {
5591
5591
  "baseline-browser-mapping": "^2.10.42",
5592
- "caniuse-lite": "^1.0.30001800",
5593
- "electron-to-chromium": "^1.5.387",
5594
- "node-releases": "^2.0.50",
5592
+ "caniuse-lite": "^1.0.30001803",
5593
+ "electron-to-chromium": "^1.5.389",
5594
+ "node-releases": "^2.0.51",
5595
5595
  "update-browserslist-db": "^1.2.3"
5596
5596
  },
5597
5597
  "bin": {
@@ -5695,9 +5695,9 @@
5695
5695
  }
5696
5696
  },
5697
5697
  "node_modules/caniuse-lite": {
5698
- "version": "1.0.30001803",
5699
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001803.tgz",
5700
- "integrity": "sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==",
5698
+ "version": "1.0.30001805",
5699
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001805.tgz",
5700
+ "integrity": "sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==",
5701
5701
  "funding": [
5702
5702
  {
5703
5703
  "type": "opencollective",
@@ -7265,9 +7265,9 @@
7265
7265
  }
7266
7266
  },
7267
7267
  "node_modules/eslint": {
7268
- "version": "10.6.0",
7269
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz",
7270
- "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==",
7268
+ "version": "10.7.0",
7269
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.7.0.tgz",
7270
+ "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==",
7271
7271
  "license": "MIT",
7272
7272
  "peer": true,
7273
7273
  "workspaces": [
@@ -12385,21 +12385,21 @@
12385
12385
  }
12386
12386
  },
12387
12387
  "node_modules/msw/node_modules/tldts": {
12388
- "version": "7.4.7",
12389
- "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.7.tgz",
12390
- "integrity": "sha512-56L0/9HELHSsG1bFCzay8UoLxzRL7kpFf7Wl5q/kSYwiSJGACvro61xnKzPNM+SadxllzdtXsKDSXE7HPeqIAw==",
12388
+ "version": "7.4.8",
12389
+ "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.8.tgz",
12390
+ "integrity": "sha512-htwgN/8KRB3z3vnC0BOETVh2m499g5GmyTK9Wq5JBLX3FNz6tSBveAd+fQhzy9hkjif8vy2jwDMR1sGhLtZl2A==",
12391
12391
  "license": "MIT",
12392
12392
  "dependencies": {
12393
- "tldts-core": "^7.4.7"
12393
+ "tldts-core": "^7.4.8"
12394
12394
  },
12395
12395
  "bin": {
12396
12396
  "tldts": "bin/cli.js"
12397
12397
  }
12398
12398
  },
12399
12399
  "node_modules/msw/node_modules/tldts-core": {
12400
- "version": "7.4.7",
12401
- "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.7.tgz",
12402
- "integrity": "sha512-rNlAI8fKn/JckBMUSbNL/ES2kmDiurWaE49l+ikwEc9A6lFR7gMx9AhgQMQKBK4H5w4pKLH64JzZfB99uRsGNQ==",
12400
+ "version": "7.4.8",
12401
+ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.8.tgz",
12402
+ "integrity": "sha512-c1P7u0EhACHj7lPy4MJm8iTFEU8+nB0LCtddH0fhP7noaVoXAqafMtOOeX+ulpuPBqnrRgRhw494RICT3mbhnw==",
12403
12403
  "license": "MIT"
12404
12404
  },
12405
12405
  "node_modules/msw/node_modules/tough-cookie": {
@@ -12484,9 +12484,9 @@
12484
12484
  "license": "MIT"
12485
12485
  },
12486
12486
  "node_modules/node-releases": {
12487
- "version": "2.0.50",
12488
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz",
12489
- "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==",
12487
+ "version": "2.0.51",
12488
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz",
12489
+ "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==",
12490
12490
  "license": "MIT",
12491
12491
  "engines": {
12492
12492
  "node": ">=18"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zohodesk/testinglibrary",
3
- "version": "0.0.85-n20-experimental",
3
+ "version": "0.0.86-n20-experimental",
4
4
  "main": "./build/index.js",
5
5
  "scripts": {
6
6
  "postinstall": "node bin/postinstall.js",
@@ -274,4 +274,4 @@ header {
274
274
  font-size: 1rem;
275
275
  padding: 0 0.5rem;
276
276
  }
277
- </style></head><body><main class="jesthtml-content"><header><h1 id="title">Unit Report</h1></header><section id="metadata-container"><div id="timestamp">Started: 2026-07-05 14:02:44</div><div id="summary"><div id="suite-summary"><div class="summary-total">Suites (3)</div><div class="summary-passed ">3 passed</div><div class="summary-failed summary-empty">0 failed</div><div class="summary-pending summary-empty">0 pending</div></div><div id="test-summary"><div class="summary-total">Tests (41)</div><div class="summary-passed ">41 passed</div><div class="summary-failed summary-empty">0 failed</div><div class="summary-pending summary-empty">0 pending</div></div></div></section><details id="suite-1" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/failureTagClassifier.test.js</div><div class="suite-time">0.428s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">returns flaky and skips log analysis for flaky outcome</div><div class="test-status">passed</div><div class="test-duration">0.002s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">tags environment when a document abort is present</div><div class="test-status">passed</div><div class="test-duration">0.002s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">tags environment for a network/cert failure</div><div class="test-status">passed</div><div class="test-duration">0.002s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">tags code when an app undefined-property error is present</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">does not tag a non-document (xhr) abort as environment</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">tags case and code when stderr reports DATA_GENERATION_ERROR</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">tags tool for a known tool pattern in base64 stdout</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">tags case and code when stderr matches a case pattern</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">does not tag tool for benign stdout/stderr</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">does not throw for malformed stdio chunk entries</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">leaves untagged when no environment or code pattern is present</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">records the matching pattern for environment failures</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">records the matching pattern for code failures from events</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">records flaky outcome without a regex pattern</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">records tool pattern from stdio</div><div class="test-status">passed</div><div class="test-duration">0.002s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">records case pattern from stdio and implies code</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">keeps runtime-log code match when case also matches</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">implies code from case when no runtime log is present</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">failure tag metadata attachment</div><div class="test-title">builds a small JSON attachment when tags are present</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">failure tag metadata attachment</div><div class="test-title">returns null when no tags are present</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">failure tag metadata attachment</div><div class="test-title">replaces an existing metadata attachment</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">mergeFailureTags</div><div class="test-title">places suffixed failure tags first, then existing tags</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">mergeFailureTags</div><div class="test-title">emits the flaky category as FLAKY without a suffix</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">mergeFailureTags</div><div class="test-title">does not duplicate an already-present failure tag</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">mergeFailureTags</div><div class="test-title">formats case and code failure tags with suffix</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">regex safety</div><div class="test-title">resets stateful regex for tool pattern checks</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div></div></details><details id="suite-2" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/logCollector.test.js</div><div class="suite-time">0.049s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">drops ORB-blocked request failures (message pattern)</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">drops known noisy third-party URLs (url pattern)</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">drops noisy 4xx responses by URL (e.g. getHamburgerMenu)</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">drops CORS console errors (message pattern)</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">drops baseline pageerrors (Access is denied / enable* not a function)</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">keeps a real application pageerror</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">flushes captured events to a jsonl file</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">returns null from flushToFile when no events were captured</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">keeps a document-navigation abort (real environment signal)</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-3" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/runtimeLogReader.test.js</div><div class="suite-time">0.043s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">readRuntimeLogEventsForResult</div><div class="test-title">reads project-side runtime log for a failing result</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">readRuntimeLogEventsForResult</div><div class="test-title">skips malformed jsonl lines</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">readRuntimeLogEventsForResult</div><div class="test-title">returns an empty array when the runtime log file is missing</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">readRuntimeLogEventsForResult</div><div class="test-title">returns an empty array when logsDir is missing</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">readRuntimeLogEvents</div><div class="test-title">reads events from a jsonl file path</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">readRuntimeLogEvents</div><div class="test-title">returns an empty array for a missing file</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details></main></body></html>
277
+ </style></head><body><main class="jesthtml-content"><header><h1 id="title">Unit Report</h1></header><section id="metadata-container"><div id="timestamp">Started: 2026-07-13 22:11:22</div><div id="summary"><div id="suite-summary"><div class="summary-total">Suites (21)</div><div class="summary-passed ">21 passed</div><div class="summary-failed summary-empty">0 failed</div><div class="summary-pending summary-empty">0 pending</div></div><div id="test-summary"><div class="summary-total">Tests (114)</div><div class="summary-passed ">114 passed</div><div class="summary-failed summary-empty">0 failed</div><div class="summary-pending summary-empty">0 pending</div></div></div></section><details id="suite-1" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/__tests__/tagProcessor.test.js</div><div class="suite-time">1.35s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">TagProcessor</div><div class="test-title">should return tagArgs if no edition is provided</div><div class="test-status">passed</div><div class="test-duration">0.003s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">TagProcessor</div><div class="test-title">should handle a single edition with &lt;= operator</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">TagProcessor</div><div class="test-title">should handle a single edition with &gt;= operator</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">TagProcessor</div><div class="test-title">should handle a single edition with &lt; operator</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">TagProcessor</div><div class="test-title">should handle a single edition with &gt; operator</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">TagProcessor</div><div class="test-title">should handle a single edition with no operator</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">TagProcessor</div><div class="test-title">should log a message if edition is not found</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">TagProcessor</div><div class="test-title">should handle multiple editions</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">TagProcessor</div><div class="test-title">should build tags correctly when tags are empty</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-2" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/buildInFixtures/__tests__/caseTimeout.test.js</div><div class="suite-time">1.563s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">resolveCaseTimeoutMs</div><div class="test-title">returns null when no timeout tag is present</div><div class="test-status">passed</div><div class="test-duration">0.003s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">resolveCaseTimeoutMs</div><div class="test-title">returns null when tags is not an array</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">resolveCaseTimeoutMs</div><div class="test-title">parses @timeout_&lt;n&gt; as seconds for Chrome (factor 1)</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">resolveCaseTimeoutMs</div><div class="test-title">applies Firefox 2x multiplier</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">resolveCaseTimeoutMs</div><div class="test-title">returns 0 (no timeout) when tag is @timeout_0, regardless of browser</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">resolveCaseTimeoutMs</div><div class="test-title">uses the first matching tag and logs a warning when multiple are present</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">resolveCaseTimeoutMs</div><div class="test-title">ignores malformed timeout-like tags</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">resolveCaseTimeoutMs</div><div class="test-title">ignores non-string entries in tags</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">caseTimeout fixture</div><div class="test-title">is registered as an auto fixture</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">caseTimeout fixture</div><div class="test-title">calls testInfo.setTimeout with scaled value when tag matches</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">caseTimeout fixture</div><div class="test-title">does not call testInfo.setTimeout when no tag is present</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">caseTimeout fixture</div><div class="test-title">passes 0 through to testInfo.setTimeout for @timeout_0</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-3" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/dataGenerator/authenticator/__tests__/JWTauthenticator.test.js</div><div class="suite-time">1.673s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">JWTauthenticator</div><div class="test-title">should throw when jwt-oauth config is missing</div><div class="test-status">passed</div><div class="test-duration">0.011s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">JWTauthenticator</div><div class="test-title">should fetch jwt access token and set it in authHeaders</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">JWTauthenticator</div><div class="test-title">should throw a descriptive error when authenticator API returns non-ok response</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">JWTauthenticator</div><div class="test-title">should throw when authenticator response has no access_token</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div></div></details><details id="suite-4" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/runtimeLogReader.test.js</div><div class="suite-time">1.511s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">readRuntimeLogEventsForResult</div><div class="test-title">reads project-side runtime log for a failing result</div><div class="test-status">passed</div><div class="test-duration">0.003s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">readRuntimeLogEventsForResult</div><div class="test-title">skips malformed jsonl lines</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">readRuntimeLogEventsForResult</div><div class="test-title">returns an empty array when the runtime log file is missing</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">readRuntimeLogEventsForResult</div><div class="test-title">returns an empty array when logsDir is missing</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">readRuntimeLogEvents</div><div class="test-title">reads events from a jsonl file path</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">readRuntimeLogEvents</div><div class="test-title">returns an empty array for a missing file</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-5" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/logCollector.test.js</div><div class="suite-time">1.813s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">drops ORB-blocked request failures (message pattern)</div><div class="test-status">passed</div><div class="test-duration">0.004s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">drops known noisy third-party URLs (url pattern)</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">drops noisy 4xx responses by URL (e.g. getHamburgerMenu)</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">drops CORS console errors (message pattern)</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">drops baseline pageerrors (Access is denied / enable* not a function)</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">keeps a real application pageerror</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">flushes captured events to a jsonl file</div><div class="test-status">passed</div><div class="test-duration">0.003s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">returns null from flushToFile when no events were captured</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">LogCollector baseline-noise stripping</div><div class="test-title">keeps a document-navigation abort (real environment signal)</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-6" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/customFixturesHelper.test.js</div><div class="suite-time">1.623s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">getCustomAccountDetails</div><div class="test-title">returns selected user when any tag info is present</div><div class="test-status">passed</div><div class="test-duration">0.002s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getCustomAccountDetails</div><div class="test-title">logs and returns undefined if getCustomAccountDetails function throws</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getCustomAccountDetails</div><div class="test-title">returns default actor when no tag info is not provided</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div></div></details><details id="suite-7" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/configuration/__tests__/Configuration.test.js</div><div class="suite-time">0.174s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">Configuration Class</div><div class="test-title">should add new key-value pair to the configuration</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">Configuration Class</div><div class="test-title">should combine configurations correctly using addAll</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">Configuration Class</div><div class="test-title">should return correct value for a given key</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-8" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/dataGenerator/authenticator/__tests__/Authenticator.test.js</div><div class="suite-time">1.736s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">Authenticator</div><div class="test-title">should throw an error when an unknown authentication type is present</div><div class="test-status">passed</div><div class="test-duration">0.008s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">Authenticator</div><div class="test-title">should route oauth2 authentication and merge oauth2 config into payload</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">Authenticator</div><div class="test-title">should treat authentication values case-insensitively and deduplicate them</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">Authenticator</div><div class="test-title">should apply only the first supported authentication type</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-9" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/configPathResolver.test.js</div><div class="suite-time">0.194s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">getConfigPath</div><div class="test-title">should return config path when properties file exists and has mapping</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getConfigPath</div><div class="test-title">should log info and use default mapping if file does not exist</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getConfigPath</div><div class="test-title">should log failure if config mapping not found for stage</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div></div></details><details id="suite-10" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/additionalProfiles.test.js</div><div class="suite-time">0.264s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">additionalProfiles</div><div class="test-title">should return empty object when no additional profile tags are present</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">additionalProfiles</div><div class="test-title">should return additional profile actors when additional profile tags and editionInfo are present</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">additionalProfiles</div><div class="test-title">should return additional profile actors when all actor details are present</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-11" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/__tests__/validateFeature.test.js</div><div class="suite-time">1.716s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">validateFeatureFiles</div><div class="test-title">runPreprocessing with correct arguments and log success</div><div class="test-status">passed</div><div class="test-duration">0.003s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">validateFeatureFiles</div><div class="test-title">runPreprocessing with playwright conf</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">validateFeatureFiles</div><div class="test-title">error when runPreprocessing fails</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div></div></details><details id="suite-12" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/configFileNameProvider.test.js</div><div class="suite-time">1.767s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">getUATFileName</div><div class="test-title">should return pipeline-matched config path when file exists</div><div class="test-status">passed</div><div class="test-duration">0.008s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getUATFileName</div><div class="test-title">should use default stage and mode when not provided</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getUATFileName</div><div class="test-title">should return the default config files for pipeline matched files not exists</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getEnvConfigFilePath</div><div class="test-title">should return conf file path when file exists</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getEnvConfigFilePath</div><div class="test-title">should return default path when file does not exist</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-13" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/getUsers_ListOfActors.test.js</div><div class="suite-time">1.9s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">getListOfActors</div><div class="test-title">throws an error when config file cannot be loaded</div><div class="test-status">passed</div><div class="test-duration">0.015s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getListOfActors</div><div class="test-title">throws an error when beta feature config does not exist</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getListOfActors</div><div class="test-title">loads main configuration when betaFeature is not provided and main config exists</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getListOfActors</div><div class="test-title">falls back to default configuration if main config file does not exist</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getListOfActors</div><div class="test-title">loads beta feature configuration when betaFeature is provided</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-14" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/dataGenerator/authenticator/__tests__/OrgOAuthAuthenticator.test.js</div><div class="suite-time">0.133s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">OrgOAuthAuthenticator</div><div class="test-title">should assign org_oauth from actor info into payload account</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">OrgOAuthAuthenticator</div><div class="test-title">should throw when org_oauth is absent in actor configuration</div><div class="test-status">passed</div><div class="test-duration">0.004s</div></div></div></div></details><details id="suite-15" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/examples/src/__tests__/App.test.js</div><div class="suite-time">0.106s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">example functions</div><div class="test-title">add function adds two numbers correctly</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">example functions</div><div class="test-title">greet function greets a person with their name</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-16" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/dataGenerator/authenticator/__tests__/OAuthAuthenticator.test.js</div><div class="suite-time">0.153s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">OAuthAuthenticator</div><div class="test-title">should assign oauth2 from actor info into payload account</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">OAuthAuthenticator</div><div class="test-title">should throw when oauth2 is absent in actor configuration</div><div class="test-status">passed</div><div class="test-duration">0.015s</div></div></div></div></details><details id="suite-17" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/buildInFixtures/__tests__/executionContext.test.js</div><div class="suite-time">0.181s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">executionContext</div><div class="test-title">should pass actorInfo with details from getCustomAccountDetails to use</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-18" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/runner/__tests__/SpawnRunner.test.js</div><div class="suite-time">0.407s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">SpawnRunner &gt; run</div><div class="test-title">should call runPreprocessing when bddMode is true</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div></div></details><details id="suite-19" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/runner/__tests__/RunnerHelper.test.js</div><div class="suite-time">0.251s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">RunnerHelper &gt; createRunner</div><div class="test-title">should throw error on invalid runner type</div><div class="test-status">passed</div><div class="test-duration">0.005s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">RunnerHelper &gt; createRunner</div><div class="test-title">should create a valid runner class</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-20" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/failureTagClassifier.test.js</div><div class="suite-time">0.254s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">leaves retry-passed (flaky) outcomes untagged</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">tags environment when a document abort is present</div><div class="test-status">passed</div><div class="test-duration">0.002s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">tags environment for a network/cert failure</div><div class="test-status">passed</div><div class="test-duration">0.002s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">tags code when an app undefined-property error is present</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">defaults to code (not environment) for a non-document xhr abort</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">tags case when stderr reports DATA_GENERATION_ERROR</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">tags tool for a known tool pattern in base64 stdout</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">tags case when stderr matches a case pattern</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">emits only the highest-priority tag (CODE) when CODE and ENVIRONMENT both match</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">defaults to code (not tool) for benign stdout/stderr</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">does not throw for malformed stdio chunk entries</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">defaults to code when a failure matches no pattern</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureTags</div><div class="test-title">leaves a passing spec untagged (no default code)</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">records the matching pattern for environment failures</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">records the matching pattern for code failures from events</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">defaults to code with no matches when events carry no recognised pattern</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">records tool pattern from stdio</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">records case pattern from stdio without implying code</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">resolves to CODE when a runtime-log code match outranks a stdio case match</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">classifyFailureDetails</div><div class="test-title">keeps case as the tag and does not add code when no runtime log is present</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">failure tag metadata attachment</div><div class="test-title">builds a small JSON attachment when tags are present</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">failure tag metadata attachment</div><div class="test-title">returns null when no tags are present</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">failure tag metadata attachment</div><div class="test-title">replaces an existing metadata attachment</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">mergeFailureTags</div><div class="test-title">places suffixed failure tags first, then existing tags</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">mergeFailureTags</div><div class="test-title">formats all failure tags with suffix</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">mergeFailureTags</div><div class="test-title">does not duplicate an already-present failure tag</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">mergeFailureTags</div><div class="test-title">formats case and code failure tags with suffix</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">regex safety</div><div class="test-title">resets stateful regex for tool pattern checks</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details><details id="suite-21" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/fileMutex.test.js</div><div class="suite-time">2.519s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">FileMutex &gt; acquire</div><div class="test-title">should create the lock file if it does not exist</div><div class="test-status">passed</div><div class="test-duration">0.004s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">FileMutex &gt; acquire</div><div class="test-title">should wait for lock file deletion if it exists</div><div class="test-status">passed</div><div class="test-duration">0.003s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">FileMutex &gt; acquire</div><div class="test-title">should reject if watch timeout exceeds</div><div class="test-status">passed</div><div class="test-duration">1.007s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">FileMutex &gt; release</div><div class="test-title">should delete the lock file if it exists</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">FileMutex &gt; release</div><div class="test-title">should release lock by deleting lock file</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">FileMutex &gt; release</div><div class="test-title">should not attempt to delete the lock file if it does not exist</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">FileMutex &gt; release</div><div class="test-title">should log an error if deleting the lock file fails</div><div class="test-status">passed</div><div class="test-duration">0.002s</div></div></div></div></details></main></body></html>