@w-lfpup/jackrabbit 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/.github/workflows/browsers.json +45 -0
  2. package/.github/workflows/browsers.macos.json +51 -0
  3. package/.github/workflows/browsers.windows.json +19 -0
  4. package/.github/workflows/tests.yml +42 -0
  5. package/README.md +151 -8
  6. package/browser/dist/logger.js +43 -0
  7. package/browser/dist/mod.js +26 -0
  8. package/browser/dist/queue.js +27 -0
  9. package/browser/dist/runner.js +20 -0
  10. package/{cli → browser}/package.json +1 -1
  11. package/browser/src/logger.ts +57 -0
  12. package/browser/src/mod.ts +30 -0
  13. package/browser/src/runner.ts +22 -0
  14. package/browser/tsconfig.json +11 -0
  15. package/browser/tsconfig.tsbuildinfo +1 -0
  16. package/browsers.json +38 -0
  17. package/core/dist/jackrabbit_types.d.ts +62 -28
  18. package/core/dist/mod.d.ts +2 -2
  19. package/core/dist/mod.js +1 -1
  20. package/core/dist/run_steps.d.ts +2 -2
  21. package/core/dist/run_steps.js +83 -67
  22. package/core/src/jackrabbit_types.ts +73 -29
  23. package/core/src/mod.ts +2 -8
  24. package/core/src/run_steps.ts +111 -80
  25. package/examples/hello_world/goodbye_world.ts +1 -1
  26. package/examples/hello_world/hello_world.ts +1 -1
  27. package/nodejs/dist/logger.js +161 -0
  28. package/nodejs/dist/mod.js +31 -0
  29. package/nodejs/dist/results.js +139 -0
  30. package/nodejs/dist/results_str.js +147 -0
  31. package/nodejs/dist/runner.js +17 -0
  32. package/nodejs/src/logger.ts +193 -0
  33. package/nodejs/src/mod.ts +37 -0
  34. package/nodejs/src/results_str.ts +234 -0
  35. package/{cli → nodejs}/tsconfig.json +2 -1
  36. package/nodejs/tsconfig.tsbuildinfo +1 -0
  37. package/package.json +9 -6
  38. package/tests/dist/mod.d.ts +14 -3
  39. package/tests/dist/mod.js +33 -13
  40. package/tests/dist/test_error.test.d.ts +9 -0
  41. package/tests/dist/test_error.test.js +27 -0
  42. package/tests/dist/test_errors.test.d.ts +9 -0
  43. package/tests/dist/test_errors.test.js +27 -0
  44. package/tests/dist/test_logger.d.ts +3 -2
  45. package/tests/dist/test_logger.js +5 -1
  46. package/tests/src/mod.ts +31 -15
  47. package/tests/src/test_error.test.ts +32 -0
  48. package/tests/src/test_logger.ts +6 -1
  49. package/tests/tsconfig.tsbuildinfo +1 -1
  50. package/tsconfig.json +1 -1
  51. package/webdriver/dist/config.js +57 -0
  52. package/webdriver/dist/eventbus.js +18 -0
  53. package/webdriver/dist/listeners.js +21 -0
  54. package/webdriver/dist/logger.js +203 -0
  55. package/webdriver/dist/mod.js +36 -0
  56. package/webdriver/dist/results_str.js +167 -0
  57. package/webdriver/dist/routes.js +172 -0
  58. package/webdriver/dist/routes2.js +163 -0
  59. package/webdriver/dist/test_hangar.js +20 -0
  60. package/webdriver/dist/webdriver.js +273 -0
  61. package/webdriver/package.json +8 -0
  62. package/webdriver/src/config.ts +89 -0
  63. package/webdriver/src/eventbus.ts +104 -0
  64. package/webdriver/src/logger.ts +247 -0
  65. package/webdriver/src/mod.ts +43 -0
  66. package/webdriver/src/results.ts +56 -0
  67. package/webdriver/src/results_str.ts +222 -0
  68. package/webdriver/src/routes.ts +211 -0
  69. package/webdriver/src/test_hangar.ts +25 -0
  70. package/webdriver/src/webdriver.ts +372 -0
  71. package/{nodejs_cli → webdriver}/tsconfig.json +1 -0
  72. package/webdriver/tsconfig.tsbuildinfo +1 -0
  73. package/.github/workflows/build_and_test.yml +0 -18
  74. package/cli/dist/cli.d.ts +0 -3
  75. package/cli/dist/cli.js +0 -8
  76. package/cli/dist/cli_types.d.ts +0 -7
  77. package/cli/dist/config.d.ts +0 -5
  78. package/cli/dist/config.js +0 -27
  79. package/cli/dist/importer.d.ts +0 -7
  80. package/cli/dist/importer.js +0 -16
  81. package/cli/dist/logger.d.ts +0 -7
  82. package/cli/dist/logger.js +0 -88
  83. package/cli/dist/mod.d.ts +0 -6
  84. package/cli/dist/mod.js +0 -4
  85. package/cli/src/cli.ts +0 -17
  86. package/cli/src/cli_types.ts +0 -9
  87. package/cli/src/config.ts +0 -36
  88. package/cli/src/importer.ts +0 -25
  89. package/cli/src/logger.ts +0 -126
  90. package/cli/src/mod.ts +0 -7
  91. package/cli/tsconfig.tsbuildinfo +0 -1
  92. package/nodejs_cli/dist/mod.d.ts +0 -2
  93. package/nodejs_cli/dist/mod.js +0 -20
  94. package/nodejs_cli/src/mod.ts +0 -25
  95. package/nodejs_cli/tsconfig.tsbuildinfo +0 -1
  96. package/test_guide.md +0 -114
  97. /package/{nodejs_cli → nodejs}/package.json +0 -0
  98. /package/{cli/dist/cli_types.js → webdriver/dist/results.js} +0 -0
@@ -2,11 +2,22 @@ import type {
2
2
  Assertions,
3
3
  LoggerInterface,
4
4
  TestModule,
5
+ Test,
6
+ TestOptions,
5
7
  } from "./jackrabbit_types.ts";
6
8
 
9
+ interface ExecTestParams {
10
+ logger: LoggerInterface;
11
+ options: TestOptions | undefined;
12
+ jrTest: Test;
13
+ collection_id: number;
14
+ module_id: number;
15
+ test_id: number;
16
+ }
17
+
7
18
  const TIMEOUT_INTERVAL_MS = 10000;
8
19
 
9
- function sleep(time: number): Promise<void> {
20
+ export function sleep(time: number): Promise<void> {
10
21
  return new Promise((resolve) => {
11
22
  setTimeout(() => {
12
23
  resolve();
@@ -14,125 +25,145 @@ function sleep(time: number): Promise<void> {
14
25
  });
15
26
  }
16
27
 
17
- async function createTimeout(
18
- timeoutMs: number = TIMEOUT_INTERVAL_MS,
19
- ): Promise<Assertions> {
28
+ async function failAfterTimeout(timeoutMs: number): Promise<Assertions> {
20
29
  await sleep(timeoutMs);
21
30
 
22
31
  return `timed out at ${performance.now()} after ${timeoutMs} ms.`;
23
32
  }
24
33
 
25
- async function execTest(
26
- testModules: TestModule[],
27
- logger: LoggerInterface,
28
- moduleId: number,
29
- testId: number,
30
- ) {
31
- if (logger.cancelled) return;
34
+ async function execTest(params: ExecTestParams) {
35
+ const { logger, options, jrTest, test_id, collection_id, module_id } = params;
36
+
37
+ const test_name = jrTest.name ?? test_id.toString();
32
38
 
33
- logger.log(testModules, {
39
+ logger.log({
40
+ collection_id,
41
+ module_id,
42
+ test_id,
43
+ test_name,
34
44
  type: "start_test",
35
- moduleId,
36
- testId,
37
45
  });
38
46
 
39
- const { tests, options } = testModules[moduleId];
40
-
41
- const testFunc = tests[testId];
42
- const startTime = performance.now();
43
- const assertions = await Promise.race([
44
- createTimeout(options.timeoutMs),
45
- testFunc(),
46
- ]);
47
-
48
- if (logger.cancelled) return;
49
-
50
- const endTime = performance.now();
51
-
52
- logger.log(testModules, {
53
- type: "end_test",
54
- assertions,
55
- endTime,
56
- moduleId,
57
- startTime,
58
- testId,
59
- });
47
+ let start_time = performance.now();
48
+
49
+ let assertions: Assertions;
50
+ try {
51
+ assertions = await Promise.race([
52
+ failAfterTimeout(options?.timeoutMs ?? TIMEOUT_INTERVAL_MS),
53
+ jrTest(),
54
+ ]);
55
+
56
+ let end_time = performance.now();
57
+
58
+ logger.log({
59
+ assertions,
60
+ collection_id,
61
+ start_time,
62
+ end_time,
63
+ module_id,
64
+ test_id,
65
+ type: "end_test",
66
+ });
67
+ } catch (e: unknown) {
68
+ return logger.log({
69
+ collection_id,
70
+ error: e?.toString() ?? "wild test error",
71
+ module_id,
72
+ test_id,
73
+ type: "test_error",
74
+ });
75
+ }
60
76
  }
61
77
 
62
78
  async function execCollection(
63
- testModules: TestModule[],
64
79
  logger: LoggerInterface,
65
- moduleId: number,
80
+ testModule: TestModule,
81
+ collection_id: number,
82
+ module_id: number,
66
83
  ) {
67
- if (logger.cancelled) return;
68
-
69
- const { tests } = testModules[moduleId];
84
+ const { tests, options } = testModule;
70
85
 
71
86
  const wrappedTests = [];
72
- for (let [testID] of tests.entries()) {
73
- wrappedTests.push(execTest(testModules, logger, moduleId, testID));
87
+ for (let [test_id, jrTest] of tests.entries()) {
88
+ wrappedTests.push(function () {
89
+ return execTest({
90
+ logger,
91
+ options,
92
+ jrTest,
93
+ collection_id,
94
+ module_id,
95
+ test_id,
96
+ });
97
+ });
74
98
  }
75
99
 
76
- if (logger.cancelled) return;
77
-
78
100
  await Promise.all(wrappedTests);
79
101
  }
80
102
 
81
103
  async function execCollectionOrdered(
82
- testModules: TestModule[],
83
104
  logger: LoggerInterface,
84
- moduleId: number,
105
+ testModule: TestModule,
106
+ collection_id: number,
107
+ module_id: number,
85
108
  ) {
86
- const { tests } = testModules[moduleId];
87
-
88
- for (let [testID] of tests.entries()) {
89
- if (logger.cancelled) return;
90
-
91
- await execTest(testModules, logger, moduleId, testID);
109
+ const { tests, options } = testModule;
110
+
111
+ for (let [test_id, jrTest] of tests.entries()) {
112
+ await execTest({
113
+ logger,
114
+ options,
115
+ jrTest,
116
+ collection_id,
117
+ module_id,
118
+ test_id,
119
+ });
92
120
  }
93
121
  }
94
122
 
95
- export async function startRun(
123
+ export async function runCollection(
96
124
  logger: LoggerInterface,
97
125
  testModules: TestModule[],
126
+ collection_id: number,
127
+ collection_url: string,
98
128
  ) {
99
- logger.log(testModules, {
100
- type: "start_run",
101
- time: performance.now(),
129
+ logger.log({
130
+ collection_id,
131
+ collection_url,
132
+ expected_module_count: testModules.length,
133
+ type: "start_collection",
102
134
  });
103
135
 
104
- for (let [moduleId, testModule] of testModules.entries()) {
105
- if (logger.cancelled) return;
136
+ for (let [module_id, testModule] of testModules.entries()) {
137
+ const { options } = testModule;
138
+
139
+ const module_name = options?.title ?? module_id.toString();
106
140
 
107
- logger.log(testModules, {
141
+ logger.log({
108
142
  type: "start_module",
109
- moduleId,
143
+ module_id,
144
+ module_name,
145
+ collection_id,
146
+ expected_test_count: testModule.tests.length,
110
147
  });
111
148
 
112
- const { options } = testModule;
113
149
  options?.runAsynchronously
114
- ? await execCollection(testModules, logger, moduleId)
115
- : await execCollectionOrdered(testModules, logger, moduleId);
116
-
117
- if (logger.cancelled) return;
118
-
119
- logger.log(testModules, {
150
+ ? await execCollection(logger, testModule, collection_id, module_id)
151
+ : await execCollectionOrdered(
152
+ logger,
153
+ testModule,
154
+ collection_id,
155
+ module_id,
156
+ );
157
+
158
+ logger.log({
120
159
  type: "end_module",
121
- moduleId,
160
+ collection_id,
161
+ module_id,
122
162
  });
123
163
  }
124
164
 
125
- logger.log(testModules, {
126
- type: "end_run",
127
- time: performance.now(),
128
- });
129
- }
130
-
131
- export function cancelRun(logger: LoggerInterface, testModules: TestModule[]) {
132
- if (logger.cancelled) return;
133
-
134
- logger.log(testModules, {
135
- type: "cancel_run",
136
- time: performance.now(),
165
+ logger.log({
166
+ type: "end_collection",
167
+ collection_id,
137
168
  });
138
169
  }
@@ -2,7 +2,7 @@ async function test_that_passes() {
2
2
  return [];
3
3
  }
4
4
 
5
- async function test_that_fails() {
5
+ function test_that_fails() {
6
6
  return ["this test will fail"];
7
7
  }
8
8
 
@@ -2,7 +2,7 @@ function test_will_pass() {
2
2
  return;
3
3
  }
4
4
 
5
- function test_will_fail() {
5
+ async function test_will_fail() {
6
6
  return "this test will fail";
7
7
  }
8
8
 
@@ -0,0 +1,161 @@
1
+ import { getResultsAsString } from "./results_str.js";
2
+ export class Logger {
3
+ #results = {
4
+ startTime: 0,
5
+ fails: 0,
6
+ errors: 0,
7
+ expectedTests: 0,
8
+ endTime: 0,
9
+ testTime: 0,
10
+ expectedModules: 0,
11
+ expectedCollections: 0,
12
+ completedModules: 0,
13
+ completedCollections: 0,
14
+ completedTests: 0,
15
+ errorLogs: [],
16
+ collections: [],
17
+ };
18
+ get failed() {
19
+ return this.#results.fails !== 0;
20
+ }
21
+ get errored() {
22
+ return this.#results.errors !== 0;
23
+ }
24
+ get results() {
25
+ return getResultsAsString(this.#results);
26
+ }
27
+ log(action) {
28
+ if ("start_run" === action.type) {
29
+ this.#results.startTime = action.time;
30
+ this.#results.expectedCollections = action.expected_collection_count;
31
+ }
32
+ if ("end_run" === action.type) {
33
+ this.#results.endTime = action.time;
34
+ }
35
+ if ("run_error" === action.type) {
36
+ this.#results.errors += 1;
37
+ this.#results.errorLogs.push(action);
38
+ }
39
+ if ("start_collection" === action.type) {
40
+ this.#results.collections[action.collection_id] = {
41
+ completedModules: 0,
42
+ completedTests: 0,
43
+ errorLogs: [],
44
+ errors: 0,
45
+ expectedModules: action.expected_module_count,
46
+ expectedTests: 0,
47
+ fails: 0,
48
+ loggerAction: action,
49
+ modules: [],
50
+ };
51
+ this.#results.expectedModules += action.expected_module_count;
52
+ }
53
+ if ("end_collection" === action.type) {
54
+ let collection = this.#results.collections[action.collection_id];
55
+ if (!collection)
56
+ return;
57
+ this.#results.completedCollections += 1;
58
+ }
59
+ if ("collection_error" === action.type) {
60
+ let collection = this.#results.collections[action.collection_id];
61
+ if (!collection)
62
+ return;
63
+ this.#results.errors += 1;
64
+ collection.errors += 1;
65
+ collection.errorLogs.push(action);
66
+ }
67
+ if ("start_module" === action.type) {
68
+ let collection = this.#results.collections[action.collection_id];
69
+ if (!collection)
70
+ return;
71
+ collection.modules[action.module_id] = {
72
+ completedTests: 0,
73
+ errorLogs: [],
74
+ errors: 0,
75
+ expectedTests: action.expected_test_count,
76
+ fails: 0,
77
+ loggerAction: action,
78
+ testResults: [],
79
+ };
80
+ collection.expectedTests += action.expected_test_count;
81
+ this.#results.expectedTests += action.expected_test_count;
82
+ }
83
+ if ("end_module" === action.type) {
84
+ let collection = this.#results.collections[action.collection_id];
85
+ if (!collection)
86
+ return;
87
+ let module = collection.modules[action.module_id];
88
+ if (!module)
89
+ return;
90
+ this.#results.completedModules += 1;
91
+ collection.completedModules += 1;
92
+ }
93
+ if ("module_error" === action.type) {
94
+ let collection = this.#results.collections[action.collection_id];
95
+ if (!collection)
96
+ return;
97
+ let module = collection.modules[action.module_id];
98
+ if (!module)
99
+ return;
100
+ this.#results.errors += 1;
101
+ collection.errors += 1;
102
+ module.errors += 1;
103
+ module.errorLogs.push(action);
104
+ }
105
+ if ("start_test" === action.type) {
106
+ let collection = this.#results.collections[action.collection_id];
107
+ if (!collection)
108
+ return;
109
+ let module = collection.modules[action.module_id];
110
+ if (!module)
111
+ return;
112
+ module.testResults[action.test_id] = {
113
+ loggerStartAction: action,
114
+ loggerEndAction: undefined,
115
+ };
116
+ }
117
+ if ("end_test" === action.type) {
118
+ endTest(this.#results, action);
119
+ }
120
+ if ("test_error" === action.type) {
121
+ let collection = this.#results.collections[action.collection_id];
122
+ if (!collection)
123
+ return;
124
+ let module = collection.modules[action.module_id];
125
+ if (!module)
126
+ return;
127
+ let testResult = module.testResults[action.test_id];
128
+ if (!testResult)
129
+ return;
130
+ testResult.loggerEndAction = action;
131
+ this.#results.errors += 1;
132
+ collection.errors += 1;
133
+ module.errors += 1;
134
+ }
135
+ }
136
+ }
137
+ function endTest(runResults, loggerAction) {
138
+ let collection = runResults.collections[loggerAction.collection_id];
139
+ if (!collection)
140
+ return;
141
+ let module = collection.modules[loggerAction.module_id];
142
+ if (!module)
143
+ return;
144
+ let testResult = module.testResults[loggerAction.test_id];
145
+ if (!testResult)
146
+ return;
147
+ testResult.loggerEndAction = loggerAction;
148
+ runResults.completedTests += 1;
149
+ collection.completedTests += 1;
150
+ module.completedTests += 1;
151
+ let { assertions } = loggerAction;
152
+ const isAssertionArray = Array.isArray(assertions) && assertions.length;
153
+ // might be worth just sticking with language standard "none" like "" or 0 or false
154
+ const isAssertion = !Array.isArray(assertions) && undefined !== assertions;
155
+ if (isAssertion || isAssertionArray) {
156
+ runResults.fails += 1;
157
+ collection.fails += 1;
158
+ module.fails += 1;
159
+ }
160
+ runResults.testTime += Math.max(0, loggerAction.end_time - loggerAction.start_time);
161
+ }
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ import { Logger } from "./logger.js";
3
+ import * as path from "path";
4
+ import { runCollection } from "../../core/dist/mod.js";
5
+ let filepaths = process.argv.slice(2);
6
+ const logger = new Logger();
7
+ logger.log({
8
+ type: "start_run",
9
+ time: performance.now(),
10
+ expected_collection_count: filepaths.length,
11
+ });
12
+ for (const [collection_id, file] of filepaths.entries()) {
13
+ try {
14
+ let filepath = path.join(process.cwd(), file);
15
+ const { testModules } = await import(filepath);
16
+ await runCollection(logger, testModules, collection_id, filepath);
17
+ }
18
+ catch (e) {
19
+ logger.log({
20
+ type: "collection_error",
21
+ collection_id,
22
+ error: e?.toString() ?? "wild horses error",
23
+ });
24
+ }
25
+ }
26
+ logger.log({
27
+ type: "end_run",
28
+ time: performance.now(),
29
+ });
30
+ console.log(logger.results);
31
+ logger.failed || logger.errored ? process.exit(1) : process.exit(0);
@@ -0,0 +1,139 @@
1
+ const SPACE = " ";
2
+ export function getResultsAsString(runResults) {
3
+ const output = [];
4
+ for (let errorAction of runResults.errorLogs) {
5
+ if ("run_error" !== errorAction.type)
6
+ continue;
7
+ output.push(`${SPACE}[session_error]\n${errorAction.error}`);
8
+ }
9
+ if (!logRunResults(output, runResults))
10
+ for (const collection of runResults.collections) {
11
+ if (logCollectionResult(output, collection))
12
+ continue;
13
+ if (collection)
14
+ for (const moduleResult of collection.modules) {
15
+ if (logModuleResult(output, moduleResult))
16
+ continue;
17
+ if (moduleResult)
18
+ for (const testResult of moduleResult.testResults) {
19
+ logTest(output, testResult);
20
+ }
21
+ }
22
+ }
23
+ logSummary(output, runResults);
24
+ return output.join("\n");
25
+ }
26
+ function logRunResults(output, result) {
27
+ if (!result.fails &&
28
+ !result.errors &&
29
+ result.expectedTests === result.completedTests &&
30
+ result.expectedModules === result.completedModules &&
31
+ result.expectedCollections === result.completedCollections) {
32
+ output.push(`${result.completedTests} tests
33
+ ${result.completedModules} modules
34
+ ${result.completedCollections} collections`);
35
+ return true;
36
+ }
37
+ for (let errorAction of result.errorLogs) {
38
+ if ("run_error" !== errorAction.type)
39
+ continue;
40
+ output.push(`[run_error] ${errorAction.error}`);
41
+ }
42
+ return false;
43
+ }
44
+ function logCollectionResult(output, collection) {
45
+ if (!collection)
46
+ return true;
47
+ let { loggerAction } = collection;
48
+ if ("start_collection" !== loggerAction.type)
49
+ return true;
50
+ output.push(`${SPACE}${loggerAction.collection_url}`);
51
+ if (!collection.fails &&
52
+ !collection.errors &&
53
+ collection.expectedTests === collection.completedTests &&
54
+ collection.expectedModules === collection.completedModules) {
55
+ output.push(`${collection.expectedTests} tests
56
+ ${loggerAction.expected_module_count} modules`);
57
+ return true;
58
+ }
59
+ for (let errorAction of collection.errorLogs) {
60
+ if ("collection_error" !== errorAction.type)
61
+ continue;
62
+ output.push(`[collection_error] ${errorAction.error}`);
63
+ }
64
+ return false;
65
+ }
66
+ function logModuleResult(output, module) {
67
+ if (!module)
68
+ return true;
69
+ let { loggerAction } = module;
70
+ if ("start_module" !== loggerAction.type)
71
+ return true;
72
+ output.push(`${SPACE}${loggerAction.module_name}`);
73
+ if (!module.fails &&
74
+ !module.errors &&
75
+ module.expectedTests === module.completedTests) {
76
+ output.push(`${SPACE.repeat(2)}${module.expectedTests} tests`);
77
+ return true;
78
+ }
79
+ for (let errorAction of module.errorLogs) {
80
+ if ("collection_error" !== errorAction.type)
81
+ continue;
82
+ output.push(`${SPACE}[module_error] ${errorAction.error}`);
83
+ }
84
+ return false;
85
+ }
86
+ function logTest(output, test) {
87
+ if (!test)
88
+ return;
89
+ let { loggerStartAction, loggerEndAction } = test;
90
+ if ("start_test" !== loggerStartAction.type)
91
+ return;
92
+ if ("test_error" === loggerEndAction?.type) {
93
+ let { test_name } = loggerStartAction;
94
+ output.push(`${SPACE.repeat(2)}${test_name}
95
+ ${SPACE.repeat(3)}[error] ${loggerEndAction.error}`);
96
+ }
97
+ if ("end_test" === loggerEndAction?.type) {
98
+ let { assertions } = loggerEndAction;
99
+ const isAssertionArray = Array.isArray(assertions) && assertions.length;
100
+ const isAssertion = !Array.isArray(assertions) &&
101
+ undefined !== assertions &&
102
+ null !== assertions;
103
+ if (isAssertion || isAssertionArray) {
104
+ let { test_name } = loggerStartAction;
105
+ output.push(`${SPACE.repeat(2)}${test_name}`);
106
+ }
107
+ if (isAssertion) {
108
+ output.push(`${SPACE.repeat(3)}- ${assertions}`);
109
+ }
110
+ if (isAssertionArray) {
111
+ for (const assertion of assertions) {
112
+ output.push(`${SPACE.repeat(3)}- ${assertion}`);
113
+ }
114
+ }
115
+ }
116
+ }
117
+ function logSummary(output, runResults) {
118
+ let status_with_color = runResults.fails
119
+ ? yellow("\u{2717} failed")
120
+ : blue("\u{2714} passed");
121
+ if (runResults.errors) {
122
+ status_with_color = gray("\u{2717} errored");
123
+ }
124
+ let totalTime = runResults.endTime - runResults.startTime;
125
+ output.push(`
126
+ ${status_with_color}
127
+ duration: ${runResults.testTime.toFixed(4)} mS
128
+ total: ${totalTime.toFixed(4)} mS
129
+ `);
130
+ }
131
+ function blue(text) {
132
+ return `\x1b[44m\x1b[97m${text}\x1b[0m`;
133
+ }
134
+ function yellow(text) {
135
+ return `\x1b[43m\x1b[97m${text}\x1b[0m`;
136
+ }
137
+ function gray(text) {
138
+ return `\x1b[100m\x1b[97m${text}\x1b[0m`;
139
+ }