ScuttleBot

Blame History Raw 330 lines
1
"use strict";
2
var __defProp = Object.defineProperty;
3
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
var __getOwnPropNames = Object.getOwnPropertyNames;
5
var __hasOwnProp = Object.prototype.hasOwnProperty;
6
var __export = (target, all) => {
7
for (var name in all)
8
__defProp(target, name, { get: all[name], enumerable: true });
9
};
10
var __copyProps = (to, from, except, desc) => {
11
if (from && typeof from === "object" || typeof from === "function") {
12
for (let key of __getOwnPropNames(from))
13
if (!__hasOwnProp.call(to, key) && key !== except)
14
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
}
16
return to;
17
};
18
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
var testTree_exports = {};
20
__export(testTree_exports, {
21
TestTree: () => TestTree,
22
sortAndPropagateStatus: () => sortAndPropagateStatus,
23
statusEx: () => statusEx
24
});
25
module.exports = __toCommonJS(testTree_exports);
26
class TestTree {
27
constructor(rootFolder, rootSuite, loadErrors, projectFilters, pathSeparator, hideFiles) {
28
this._treeItemById = /* @__PURE__ */ new Map();
29
this._treeItemByTestId = /* @__PURE__ */ new Map();
30
const filterProjects = projectFilters && [...projectFilters.values()].some(Boolean);
31
this.pathSeparator = pathSeparator;
32
this.rootItem = {
33
kind: "group",
34
subKind: "folder",
35
id: rootFolder,
36
title: "",
37
location: { file: "", line: 0, column: 0 },
38
duration: 0,
39
parent: void 0,
40
children: [],
41
status: "none",
42
hasLoadErrors: false
43
};
44
this._treeItemById.set(rootFolder, this.rootItem);
45
const visitSuite = (project, parentSuite, parentGroup, mode) => {
46
for (const suite of mode === "tests" ? [] : parentSuite.suites) {
47
if (!suite.title) {
48
visitSuite(project, suite, parentGroup, "all");
49
continue;
50
}
51
let group = parentGroup.children.find((item) => item.kind === "group" && item.title === suite.title);
52
if (!group) {
53
group = {
54
kind: "group",
55
subKind: "describe",
56
id: "suite:" + parentSuite.titlePath().join("") + "" + suite.title,
57
// account for anonymous suites
58
title: suite.title,
59
location: suite.location,
60
duration: 0,
61
parent: parentGroup,
62
children: [],
63
status: "none",
64
hasLoadErrors: false
65
};
66
this._addChild(parentGroup, group);
67
}
68
visitSuite(project, suite, group, "all");
69
}
70
for (const test of mode === "suites" ? [] : parentSuite.tests) {
71
const title = test.title;
72
let testCaseItem = parentGroup.children.find((t) => t.kind !== "group" && t.title === title);
73
if (!testCaseItem) {
74
testCaseItem = {
75
kind: "case",
76
id: "test:" + test.titlePath().join(""),
77
title,
78
parent: parentGroup,
79
children: [],
80
tests: [],
81
location: test.location,
82
duration: 0,
83
status: "none",
84
project: void 0,
85
test: void 0,
86
tags: test.tags
87
};
88
this._addChild(parentGroup, testCaseItem);
89
}
90
const result = test.results[0];
91
let status = "none";
92
if (result?.[statusEx] === "scheduled")
93
status = "scheduled";
94
else if (result?.[statusEx] === "running")
95
status = "running";
96
else if (result?.status === "skipped")
97
status = "skipped";
98
else if (result?.status === "interrupted")
99
status = "none";
100
else if (result && test.outcome() !== "expected")
101
status = "failed";
102
else if (result && test.outcome() === "expected")
103
status = "passed";
104
testCaseItem.tests.push(test);
105
const testItem = {
106
kind: "test",
107
id: test.id,
108
title: project.name,
109
location: test.location,
110
test,
111
parent: testCaseItem,
112
children: [],
113
status,
114
duration: test.results.length ? Math.max(0, test.results[0].duration) : 0,
115
project
116
};
117
this._addChild(testCaseItem, testItem);
118
this._treeItemByTestId.set(test.id, testItem);
119
testCaseItem.duration = testCaseItem.children.reduce((a, b) => a + b.duration, 0);
120
}
121
};
122
for (const projectSuite of rootSuite?.suites || []) {
123
if (filterProjects && !projectFilters.get(projectSuite.title))
124
continue;
125
for (const fileSuite of projectSuite.suites) {
126
if (hideFiles) {
127
visitSuite(projectSuite.project(), fileSuite, this.rootItem, "suites");
128
if (fileSuite.tests.length) {
129
const defaultDescribeItem = this._defaultDescribeItem();
130
visitSuite(projectSuite.project(), fileSuite, defaultDescribeItem, "tests");
131
}
132
} else {
133
const fileItem = this._fileItem(fileSuite.location.file.split(pathSeparator), true);
134
visitSuite(projectSuite.project(), fileSuite, fileItem, "all");
135
}
136
}
137
}
138
for (const loadError of loadErrors) {
139
if (!loadError.location)
140
continue;
141
const fileItem = this._fileItem(loadError.location.file.split(pathSeparator), true);
142
fileItem.hasLoadErrors = true;
143
}
144
}
145
_addChild(parent, child) {
146
parent.children.push(child);
147
child.parent = parent;
148
this._treeItemById.set(child.id, child);
149
}
150
filterTree(filterText, statusFilters, runningTestIds) {
151
const tokens = filterText.trim().toLowerCase().split(" ");
152
const filtersStatuses = [...statusFilters.values()].some(Boolean);
153
const filter = (testCase) => {
154
const titleWithTags = [...testCase.tests[0].titlePath(), ...testCase.tests[0].tags].join(" ").toLowerCase();
155
if (!tokens.every((token) => titleWithTags.includes(token)) && !testCase.tests.some((t) => runningTestIds?.has(t.id)))
156
return false;
157
testCase.children = testCase.children.filter((test) => {
158
return !filtersStatuses || runningTestIds?.has(test.test.id) || statusFilters.get(test.status);
159
});
160
testCase.tests = testCase.children.map((c) => c.test);
161
return !!testCase.children.length;
162
};
163
const visit = (treeItem) => {
164
const newChildren = [];
165
for (const child of treeItem.children) {
166
if (child.kind === "case") {
167
if (filter(child))
168
newChildren.push(child);
169
} else {
170
visit(child);
171
if (child.children.length || child.hasLoadErrors)
172
newChildren.push(child);
173
}
174
}
175
treeItem.children = newChildren;
176
};
177
visit(this.rootItem);
178
}
179
_fileItem(filePath, isFile) {
180
if (filePath.length === 0)
181
return this.rootItem;
182
const fileName = filePath.join(this.pathSeparator);
183
const existingFileItem = this._treeItemById.get(fileName);
184
if (existingFileItem)
185
return existingFileItem;
186
const parentFileItem = this._fileItem(filePath.slice(0, filePath.length - 1), false);
187
const fileItem = {
188
kind: "group",
189
subKind: isFile ? "file" : "folder",
190
id: fileName,
191
title: filePath[filePath.length - 1],
192
location: { file: fileName, line: 0, column: 0 },
193
duration: 0,
194
parent: parentFileItem,
195
children: [],
196
status: "none",
197
hasLoadErrors: false
198
};
199
this._addChild(parentFileItem, fileItem);
200
return fileItem;
201
}
202
_defaultDescribeItem() {
203
let defaultDescribeItem = this._treeItemById.get("<anonymous>");
204
if (!defaultDescribeItem) {
205
defaultDescribeItem = {
206
kind: "group",
207
subKind: "describe",
208
id: "<anonymous>",
209
title: "<anonymous>",
210
location: { file: "", line: 0, column: 0 },
211
duration: 0,
212
parent: this.rootItem,
213
children: [],
214
status: "none",
215
hasLoadErrors: false
216
};
217
this._addChild(this.rootItem, defaultDescribeItem);
218
}
219
return defaultDescribeItem;
220
}
221
sortAndPropagateStatus() {
222
sortAndPropagateStatus(this.rootItem);
223
}
224
flattenForSingleProject() {
225
const visit = (treeItem) => {
226
if (treeItem.kind === "case" && treeItem.children.length === 1) {
227
treeItem.project = treeItem.children[0].project;
228
treeItem.test = treeItem.children[0].test;
229
treeItem.children = [];
230
this._treeItemByTestId.set(treeItem.test.id, treeItem);
231
} else {
232
treeItem.children.forEach(visit);
233
}
234
};
235
visit(this.rootItem);
236
}
237
shortenRoot() {
238
let shortRoot = this.rootItem;
239
while (shortRoot.children.length === 1 && shortRoot.children[0].kind === "group" && shortRoot.children[0].subKind === "folder")
240
shortRoot = shortRoot.children[0];
241
shortRoot.location = this.rootItem.location;
242
this.rootItem = shortRoot;
243
}
244
fileNames() {
245
const result = /* @__PURE__ */ new Set();
246
const visit = (treeItem) => {
247
if (treeItem.kind === "group" && treeItem.subKind === "file")
248
result.add(treeItem.id);
249
else
250
treeItem.children.forEach(visit);
251
};
252
visit(this.rootItem);
253
return [...result];
254
}
255
flatTreeItems() {
256
const result = [];
257
const visit = (treeItem) => {
258
result.push(treeItem);
259
treeItem.children.forEach(visit);
260
};
261
visit(this.rootItem);
262
return result;
263
}
264
treeItemById(id) {
265
return this._treeItemById.get(id);
266
}
267
collectTestIds(treeItem) {
268
return collectTestIds(treeItem);
269
}
270
}
271
function sortAndPropagateStatus(treeItem) {
272
for (const child of treeItem.children)
273
sortAndPropagateStatus(child);
274
if (treeItem.kind === "group") {
275
treeItem.children.sort((a, b) => {
276
const fc = a.location.file.localeCompare(b.location.file);
277
return fc || a.location.line - b.location.line;
278
});
279
}
280
let allPassed = treeItem.children.length > 0;
281
let allSkipped = treeItem.children.length > 0;
282
let hasFailed = false;
283
let hasRunning = false;
284
let hasScheduled = false;
285
for (const child of treeItem.children) {
286
allSkipped = allSkipped && child.status === "skipped";
287
allPassed = allPassed && (child.status === "passed" || child.status === "skipped");
288
hasFailed = hasFailed || child.status === "failed";
289
hasRunning = hasRunning || child.status === "running";
290
hasScheduled = hasScheduled || child.status === "scheduled";
291
}
292
if (hasRunning)
293
treeItem.status = "running";
294
else if (hasScheduled)
295
treeItem.status = "scheduled";
296
else if (hasFailed)
297
treeItem.status = "failed";
298
else if (allSkipped)
299
treeItem.status = "skipped";
300
else if (allPassed)
301
treeItem.status = "passed";
302
}
303
function collectTestIds(treeItem) {
304
const testIds = /* @__PURE__ */ new Set();
305
const locations = /* @__PURE__ */ new Set();
306
const visit = (treeItem2) => {
307
if (treeItem2.kind !== "test" && treeItem2.kind !== "case") {
308
treeItem2.children.forEach(visit);
309
return;
310
}
311
let fileItem = treeItem2;
312
while (fileItem && fileItem.parent && !(fileItem.kind === "group" && fileItem.subKind === "file"))
313
fileItem = fileItem.parent;
314
locations.add(fileItem.location.file);
315
if (treeItem2.kind === "case")
316
treeItem2.tests.forEach((test) => testIds.add(test.id));
317
else
318
testIds.add(treeItem2.id);
319
};
320
visit(treeItem);
321
return { testIds, locations };
322
}
323
const statusEx = Symbol("statusEx");
324
// Annotate the CommonJS export names for ESM import in node:
325
0 && (module.exports = {
326
TestTree,
327
sortAndPropagateStatus,
328
statusEx
329
});
330

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button