testTree.js 11 KB

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