@zohodesk/testinglibrary 0.0.84-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.
- package/build/core/playwright/constants/failureTagConstants.js +3 -5
- package/build/core/playwright/helpers/failureTagClassifier.js +35 -52
- package/build/core/playwright/patterns/toolPatterns.js +1 -9
- package/build/test/core/playwright/helpers/__tests__/failureTagClassifier.test.js +83 -62
- package/npm-shrinkwrap.json +1199 -1187
- package/package.json +1 -1
- package/unit_reports/unit-report.html +1 -1
|
@@ -3,17 +3,15 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.FAILURE_TAG_SUFFIX = exports.
|
|
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".
|
|
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
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
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
|
-
|
|
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(
|
|
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 (
|
|
151
|
-
|
|
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
|
|
176
|
-
const resolvedLogsDir = resolveLogsDir(
|
|
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 =
|
|
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'
|
|
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
|
|
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'
|
|
223
|
-
return classifyFailureDetails(testDetails, testTitle
|
|
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
|
-
/
|
|
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)(
|
|
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('
|
|
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
|
|
71
|
-
expect(tags).toEqual([
|
|
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
|
|
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'
|
|
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'
|
|
110
|
+
})], 'code-test');
|
|
101
111
|
expect(tags).toEqual(['CODE']);
|
|
102
112
|
});
|
|
103
|
-
test('
|
|
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'
|
|
112
|
-
expect(tags).toEqual([]);
|
|
121
|
+
})], 'xhr-test');
|
|
122
|
+
expect(tags).toEqual(['CODE']);
|
|
113
123
|
});
|
|
114
|
-
test('tags case
|
|
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
|
|
121
|
-
expect(tags).toEqual(['CASE'
|
|
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
|
|
139
|
+
const tags = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE);
|
|
130
140
|
expect(tags).toEqual(['TOOL']);
|
|
131
141
|
});
|
|
132
|
-
test('tags case
|
|
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
|
|
142
|
-
expect(tags).toEqual(['CASE'
|
|
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('
|
|
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
|
|
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
|
|
166
|
-
expect((0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE
|
|
190
|
+
expect(() => (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE)).not.toThrow();
|
|
191
|
+
expect((0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE)).toEqual(['CODE']);
|
|
167
192
|
});
|
|
168
|
-
test('
|
|
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'
|
|
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
|
|
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('
|
|
218
|
-
const details = (0, _failureTagClassifier.
|
|
219
|
-
|
|
220
|
-
|
|
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
|
|
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
|
|
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
|
|
252
|
-
expect(details.tags).toEqual(['CASE'
|
|
253
|
-
expect(details.matches).toHaveLength(
|
|
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('
|
|
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'
|
|
292
|
-
|
|
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('
|
|
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
|
|
315
|
-
expect(details.tags).toEqual(['CASE'
|
|
316
|
-
expect(details.matches.find(match => match.tag === 'CODE')).
|
|
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('
|
|
380
|
-
expect((0, _failureTagClassifier.mergeFailureTags)([], ['
|
|
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
|
|
403
|
-
const second = (0, _failureTagClassifier.classifyFailureTags)([detail], TEST_TITLE
|
|
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 {
|