@gjsify/unit 0.3.13 → 0.3.14

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 (3) hide show
  1. package/lib/esm/index.js +461 -571
  2. package/lib/esm/spy.js +135 -131
  3. package/package.json +7 -7
package/lib/esm/index.js CHANGED
@@ -1,7 +1,9 @@
1
+ import { spy } from "./spy.js";
1
2
  import "@girs/gjs";
2
- export * from "./spy.js";
3
3
  import nodeAssert from "node:assert";
4
4
  import { quitMainLoop } from "@gjsify/utils/main-loop";
5
+
6
+ //#region src/index.ts
5
7
  const mainloop = globalThis?.imports?.mainloop;
6
8
  let countTestsOverall = 0;
7
9
  let countTestsFailed = 0;
@@ -11,47 +13,48 @@ let runStartTime = 0;
11
13
  let currentSuite = "";
12
14
  let testErrors = [];
13
15
  const DEFAULT_TIMEOUT_CONFIG = {
14
- testTimeout: 5e3,
15
- suiteTimeout: 3e4,
16
- runTimeout: 12e4
16
+ testTimeout: 5e3,
17
+ suiteTimeout: 3e4,
18
+ runTimeout: 12e4
17
19
  };
18
20
  let timeoutConfig = { ...DEFAULT_TIMEOUT_CONFIG };
19
- class TimeoutError extends Error {
20
- constructor(label, timeoutMs) {
21
- super(`Timeout: "${label}" exceeded ${timeoutMs}ms`);
22
- this.name = "TimeoutError";
23
- }
24
- }
21
+ var TimeoutError = class extends Error {
22
+ constructor(label, timeoutMs) {
23
+ super(`Timeout: "${label}" exceeded ${timeoutMs}ms`);
24
+ this.name = "TimeoutError";
25
+ }
26
+ };
25
27
  async function withTimeout(fn, timeoutMs, label) {
26
- if (timeoutMs <= 0) return fn();
27
- let timeoutId;
28
- const timeoutPromise = new Promise((_, reject) => {
29
- timeoutId = setTimeout(() => reject(new TimeoutError(label, timeoutMs)), timeoutMs);
30
- });
31
- const fnPromise = Promise.resolve(fn());
32
- fnPromise.catch(() => {
33
- });
34
- try {
35
- return await Promise.race([fnPromise, timeoutPromise]);
36
- } finally {
37
- clearTimeout(timeoutId);
38
- }
28
+ if (timeoutMs <= 0) return fn();
29
+ let timeoutId;
30
+ const timeoutPromise = new Promise((_, reject) => {
31
+ timeoutId = setTimeout(() => reject(new TimeoutError(label, timeoutMs)), timeoutMs);
32
+ });
33
+ const fnPromise = Promise.resolve(fn());
34
+ fnPromise.catch(() => {});
35
+ try {
36
+ return await Promise.race([fnPromise, timeoutPromise]);
37
+ } finally {
38
+ clearTimeout(timeoutId);
39
+ }
39
40
  }
40
41
  const configure = (overrides) => {
41
- timeoutConfig = { ...timeoutConfig, ...overrides };
42
+ timeoutConfig = {
43
+ ...timeoutConfig,
44
+ ...overrides
45
+ };
42
46
  };
43
47
  function applyEnvOverrides() {
44
- try {
45
- const env = globalThis.process?.env;
46
- if (!env) return;
47
- const t = parseInt(env.GJSIFY_TEST_TIMEOUT, 10);
48
- if (!isNaN(t) && t >= 0) timeoutConfig.testTimeout = t;
49
- const s = parseInt(env.GJSIFY_SUITE_TIMEOUT, 10);
50
- if (!isNaN(s) && s >= 0) timeoutConfig.suiteTimeout = s;
51
- const r = parseInt(env.GJSIFY_RUN_TIMEOUT, 10);
52
- if (!isNaN(r) && r >= 0) timeoutConfig.runTimeout = r;
53
- } catch (_e) {
54
- }
48
+ try {
49
+ const env = globalThis.process?.env;
50
+ if (!env) return;
51
+ const t = parseInt(env.GJSIFY_TEST_TIMEOUT, 10);
52
+ if (!isNaN(t) && t >= 0) timeoutConfig.testTimeout = t;
53
+ const s = parseInt(env.GJSIFY_SUITE_TIMEOUT, 10);
54
+ if (!isNaN(s) && s >= 0) timeoutConfig.suiteTimeout = s;
55
+ const r = parseInt(env.GJSIFY_RUN_TIMEOUT, 10);
56
+ if (!isNaN(r) && r >= 0) timeoutConfig.runTimeout = r;
57
+ } catch (_e) {}
55
58
  }
56
59
  const RED = "\x1B[31m";
57
60
  const GREEN = "\x1B[32m";
@@ -60,585 +63,472 @@ const GRAY = "\x1B[90m";
60
63
  const RESET = "\x1B[39m";
61
64
  const now = () => globalThis.performance?.now?.() ?? Date.now();
62
65
  const formatDuration = (ms) => {
63
- if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
64
- if (ms >= 100) return `${Math.round(ms)}ms`;
65
- return `${ms.toFixed(1)}ms`;
66
+ if (ms >= 1e3) return `${(ms / 1e3).toFixed(2)}s`;
67
+ if (ms >= 100) return `${Math.round(ms)}ms`;
68
+ return `${ms.toFixed(1)}ms`;
66
69
  };
67
70
  const _isGjsProcess = typeof globalThis.process?.versions?.gjs === "string";
68
71
  const print = !_isGjsProcess && typeof globalThis.document !== "undefined" ? console.log : globalThis.print || console.log;
69
- class MatcherFactory {
70
- constructor(actualValue, positive, negated) {
71
- this.actualValue = actualValue;
72
- this.positive = positive;
73
- if (negated) {
74
- this.not = negated;
75
- } else {
76
- this.not = new MatcherFactory(actualValue, !positive, this);
77
- }
78
- }
79
- actualValue;
80
- positive;
81
- not;
82
- triggerResult(success, msg) {
83
- if (success && !this.positive || !success && this.positive) {
84
- const error = new Error(msg);
85
- error.__testFailureCounted = true;
86
- ++countTestsFailed;
87
- throw error;
88
- }
89
- }
90
- to(callback) {
91
- this.triggerResult(
92
- callback(this.actualValue),
93
- ` Expected callback to validate`
94
- );
95
- }
96
- toBe(expectedValue) {
97
- this.triggerResult(
98
- this.actualValue === expectedValue,
99
- ` Expected values to match using ===
100
- Expected: ${expectedValue} (${typeof expectedValue})
101
- Actual: ${this.actualValue} (${typeof this.actualValue})`
102
- );
103
- }
104
- toEqual(expectedValue) {
105
- this.triggerResult(
106
- this.actualValue == expectedValue,
107
- ` Expected values to match using ==
108
- Expected: ${expectedValue} (${typeof expectedValue})
109
- Actual: ${this.actualValue} (${typeof this.actualValue})`
110
- );
111
- }
112
- toStrictEqual(expectedValue) {
113
- let success = true;
114
- let errorMessage = "";
115
- try {
116
- nodeAssert.deepStrictEqual(this.actualValue, expectedValue);
117
- } catch (e) {
118
- success = false;
119
- errorMessage = e.message || "";
120
- }
121
- this.triggerResult(
122
- success,
123
- ` Expected values to be deeply strictly equal
124
- Expected: ${JSON.stringify(expectedValue)}
125
- Actual: ${JSON.stringify(this.actualValue)}` + (errorMessage ? `
126
- ${errorMessage}` : "")
127
- );
128
- }
129
- toEqualArray(expectedValue) {
130
- let success = Array.isArray(this.actualValue) && Array.isArray(expectedValue) && this.actualValue.length === expectedValue.length;
131
- for (let i = 0; i < this.actualValue.length; i++) {
132
- const actualVal = this.actualValue[i];
133
- const expectedVal = expectedValue[i];
134
- success = actualVal == expectedVal;
135
- if (!success) break;
136
- }
137
- this.triggerResult(
138
- success,
139
- ` Expected array items to match using ==
140
- Expected: ${expectedValue} (${typeof expectedValue})
141
- Actual: ${this.actualValue} (${typeof this.actualValue})`
142
- );
143
- }
144
- toBeInstanceOf(expectedType) {
145
- this.triggerResult(
146
- this.actualValue instanceof expectedType,
147
- ` Expected value to be instance of ${expectedType.name || expectedType}
148
- Actual: ${this.actualValue?.constructor?.name || typeof this.actualValue}`
149
- );
150
- }
151
- toHaveLength(expectedLength) {
152
- const actualLength = this.actualValue?.length;
153
- this.triggerResult(
154
- actualLength === expectedLength,
155
- ` Expected length: ${expectedLength}
156
- Actual length: ${actualLength}`
157
- );
158
- }
159
- toMatch(expectedValue) {
160
- if (typeof this.actualValue.match !== "function") {
161
- throw new Error(`You can not use toMatch on type ${typeof this.actualValue}`);
162
- }
163
- this.triggerResult(
164
- !!this.actualValue.match(expectedValue),
165
- " Expected values to match using regular expression\n Expression: " + expectedValue + "\n Actual: " + this.actualValue
166
- );
167
- }
168
- toBeDefined() {
169
- this.triggerResult(
170
- typeof this.actualValue !== "undefined",
171
- ` Expected value to be defined`
172
- );
173
- }
174
- toBeUndefined() {
175
- this.triggerResult(
176
- typeof this.actualValue === "undefined",
177
- ` Expected value to be undefined`
178
- );
179
- }
180
- toBeNull() {
181
- this.triggerResult(
182
- this.actualValue === null,
183
- ` Expected value to be null`
184
- );
185
- }
186
- toBeTruthy() {
187
- this.triggerResult(
188
- this.actualValue,
189
- ` Expected value to be truthy`
190
- );
191
- }
192
- toBeFalsy() {
193
- this.triggerResult(
194
- !this.actualValue,
195
- ` Expected value to be falsy`
196
- );
197
- }
198
- toContain(needle) {
199
- const value = this.actualValue;
200
- let contains;
201
- if (typeof value === "string") {
202
- contains = value.includes(String(needle));
203
- } else if (value instanceof Array) {
204
- contains = value.indexOf(needle) !== -1;
205
- } else {
206
- contains = false;
207
- }
208
- this.triggerResult(
209
- contains,
210
- ` Expected ` + value + ` to contain ` + needle
211
- );
212
- }
213
- toBeLessThan(greaterValue) {
214
- this.triggerResult(
215
- this.actualValue < greaterValue,
216
- ` Expected ` + this.actualValue + ` to be less than ` + greaterValue
217
- );
218
- }
219
- toBeGreaterThan(smallerValue) {
220
- this.triggerResult(
221
- this.actualValue > smallerValue,
222
- ` Expected ` + this.actualValue + ` to be greater than ` + smallerValue
223
- );
224
- }
225
- toBeGreaterThanOrEqual(value) {
226
- this.triggerResult(
227
- this.actualValue >= value,
228
- ` Expected ${this.actualValue} to be greater than or equal to ${value}`
229
- );
230
- }
231
- toBeLessThanOrEqual(value) {
232
- this.triggerResult(
233
- this.actualValue <= value,
234
- ` Expected ${this.actualValue} to be less than or equal to ${value}`
235
- );
236
- }
237
- toBeCloseTo(expectedValue, precision) {
238
- const shiftHelper = Math.pow(10, precision);
239
- this.triggerResult(
240
- Math.round(this.actualValue * shiftHelper) / shiftHelper === Math.round(expectedValue * shiftHelper) / shiftHelper,
241
- ` Expected ` + this.actualValue + ` with precision ` + precision + ` to be close to ` + expectedValue
242
- );
243
- }
244
- toThrow(expected) {
245
- let errorMessage = "";
246
- let didThrow = false;
247
- let typeMatch = true;
248
- let messageMatch = true;
249
- try {
250
- this.actualValue();
251
- didThrow = false;
252
- } catch (e) {
253
- errorMessage = e.message || "";
254
- didThrow = true;
255
- if (typeof expected === "function") {
256
- typeMatch = e instanceof expected;
257
- } else if (typeof expected === "string") {
258
- messageMatch = errorMessage.includes(expected);
259
- } else if (expected instanceof RegExp) {
260
- messageMatch = expected.test(errorMessage);
261
- }
262
- }
263
- const functionName = this.actualValue.name || typeof this.actualValue === "function" ? "[anonymous function]" : this.actualValue.toString();
264
- this.triggerResult(
265
- didThrow,
266
- ` Expected ${functionName} to ${this.positive ? "throw" : "not throw"} an exception ${!this.positive && errorMessage ? `, but an error with the message "${errorMessage}" was thrown` : ""}`
267
- );
268
- if (typeof expected === "function") {
269
- this.triggerResult(
270
- typeMatch,
271
- ` Expected Error type '${expected.name}', but the error is not an instance of it`
272
- );
273
- } else if (expected !== void 0) {
274
- this.triggerResult(
275
- messageMatch,
276
- ` Expected error message to match ${expected}
277
- Actual message: "${errorMessage}"`
278
- );
279
- }
280
- }
281
- async toReject(expected) {
282
- let didReject = false;
283
- let errorMessage = "";
284
- let typeMatch = true;
285
- let messageMatch = true;
286
- try {
287
- await this.actualValue;
288
- didReject = false;
289
- } catch (e) {
290
- didReject = true;
291
- errorMessage = e?.message || String(e);
292
- if (typeof expected === "function") {
293
- typeMatch = e instanceof expected;
294
- } else if (typeof expected === "string") {
295
- messageMatch = errorMessage.includes(expected);
296
- } else if (expected instanceof RegExp) {
297
- messageMatch = expected.test(errorMessage);
298
- }
299
- }
300
- this.triggerResult(
301
- didReject,
302
- ` Expected promise to ${this.positive ? "reject" : "resolve"}${!this.positive && errorMessage ? `, but it rejected with "${errorMessage}"` : ""}`
303
- );
304
- if (didReject && typeof expected === "function") {
305
- this.triggerResult(
306
- typeMatch,
307
- ` Expected rejection type '${expected.name}', but the error is not an instance of it`
308
- );
309
- } else if (didReject && expected !== void 0) {
310
- this.triggerResult(
311
- messageMatch,
312
- ` Expected rejection message to match ${expected}
313
- Actual message: "${errorMessage}"`
314
- );
315
- }
316
- }
317
- async toResolve() {
318
- let didResolve = false;
319
- let errorMessage = "";
320
- try {
321
- await this.actualValue;
322
- didResolve = true;
323
- } catch (e) {
324
- didResolve = false;
325
- errorMessage = e?.message || String(e);
326
- }
327
- this.triggerResult(
328
- didResolve,
329
- ` Expected promise to ${this.positive ? "resolve" : "reject"}${!didResolve ? `, but it rejected with "${errorMessage}"` : ""}`
330
- );
331
- }
332
- }
72
+ var MatcherFactory = class MatcherFactory {
73
+ not;
74
+ constructor(actualValue, positive, negated) {
75
+ this.actualValue = actualValue;
76
+ this.positive = positive;
77
+ if (negated) {
78
+ this.not = negated;
79
+ } else {
80
+ this.not = new MatcherFactory(actualValue, !positive, this);
81
+ }
82
+ }
83
+ triggerResult(success, msg) {
84
+ if (success && !this.positive || !success && this.positive) {
85
+ const error = new Error(msg);
86
+ error.__testFailureCounted = true;
87
+ ++countTestsFailed;
88
+ throw error;
89
+ }
90
+ }
91
+ to(callback) {
92
+ this.triggerResult(callback(this.actualValue), ` Expected callback to validate`);
93
+ }
94
+ toBe(expectedValue) {
95
+ this.triggerResult(this.actualValue === expectedValue, ` Expected values to match using ===\n` + ` Expected: ${expectedValue} (${typeof expectedValue})\n` + ` Actual: ${this.actualValue} (${typeof this.actualValue})`);
96
+ }
97
+ toEqual(expectedValue) {
98
+ this.triggerResult(this.actualValue == expectedValue, ` Expected values to match using ==\n` + ` Expected: ${expectedValue} (${typeof expectedValue})\n` + ` Actual: ${this.actualValue} (${typeof this.actualValue})`);
99
+ }
100
+ toStrictEqual(expectedValue) {
101
+ let success = true;
102
+ let errorMessage = "";
103
+ try {
104
+ nodeAssert.deepStrictEqual(this.actualValue, expectedValue);
105
+ } catch (e) {
106
+ success = false;
107
+ errorMessage = e.message || "";
108
+ }
109
+ this.triggerResult(success, ` Expected values to be deeply strictly equal\n` + ` Expected: ${JSON.stringify(expectedValue)}\n` + ` Actual: ${JSON.stringify(this.actualValue)}` + (errorMessage ? `\n ${errorMessage}` : ""));
110
+ }
111
+ toEqualArray(expectedValue) {
112
+ let success = Array.isArray(this.actualValue) && Array.isArray(expectedValue) && this.actualValue.length === expectedValue.length;
113
+ for (let i = 0; i < this.actualValue.length; i++) {
114
+ const actualVal = this.actualValue[i];
115
+ const expectedVal = expectedValue[i];
116
+ success = actualVal == expectedVal;
117
+ if (!success) break;
118
+ }
119
+ this.triggerResult(success, ` Expected array items to match using ==\n` + ` Expected: ${expectedValue} (${typeof expectedValue})\n` + ` Actual: ${this.actualValue} (${typeof this.actualValue})`);
120
+ }
121
+ toBeInstanceOf(expectedType) {
122
+ this.triggerResult(this.actualValue instanceof expectedType, ` Expected value to be instance of ${expectedType.name || expectedType}\n` + ` Actual: ${this.actualValue?.constructor?.name || typeof this.actualValue}`);
123
+ }
124
+ toHaveLength(expectedLength) {
125
+ const actualLength = this.actualValue?.length;
126
+ this.triggerResult(actualLength === expectedLength, ` Expected length: ${expectedLength}\n` + ` Actual length: ${actualLength}`);
127
+ }
128
+ toMatch(expectedValue) {
129
+ if (typeof this.actualValue.match !== "function") {
130
+ throw new Error(`You can not use toMatch on type ${typeof this.actualValue}`);
131
+ }
132
+ this.triggerResult(!!this.actualValue.match(expectedValue), " Expected values to match using regular expression\n" + " Expression: " + expectedValue + "\n" + " Actual: " + this.actualValue);
133
+ }
134
+ toBeDefined() {
135
+ this.triggerResult(typeof this.actualValue !== "undefined", ` Expected value to be defined`);
136
+ }
137
+ toBeUndefined() {
138
+ this.triggerResult(typeof this.actualValue === "undefined", ` Expected value to be undefined`);
139
+ }
140
+ toBeNull() {
141
+ this.triggerResult(this.actualValue === null, ` Expected value to be null`);
142
+ }
143
+ toBeTruthy() {
144
+ this.triggerResult(this.actualValue, ` Expected value to be truthy`);
145
+ }
146
+ toBeFalsy() {
147
+ this.triggerResult(!this.actualValue, ` Expected value to be falsy`);
148
+ }
149
+ toContain(needle) {
150
+ const value = this.actualValue;
151
+ let contains;
152
+ if (typeof value === "string") {
153
+ contains = value.includes(String(needle));
154
+ } else if (value instanceof Array) {
155
+ contains = value.indexOf(needle) !== -1;
156
+ } else {
157
+ contains = false;
158
+ }
159
+ this.triggerResult(contains, ` Expected ` + value + ` to contain ` + needle);
160
+ }
161
+ toBeLessThan(greaterValue) {
162
+ this.triggerResult(this.actualValue < greaterValue, ` Expected ` + this.actualValue + ` to be less than ` + greaterValue);
163
+ }
164
+ toBeGreaterThan(smallerValue) {
165
+ this.triggerResult(this.actualValue > smallerValue, ` Expected ` + this.actualValue + ` to be greater than ` + smallerValue);
166
+ }
167
+ toBeGreaterThanOrEqual(value) {
168
+ this.triggerResult(this.actualValue >= value, ` Expected ${this.actualValue} to be greater than or equal to ${value}`);
169
+ }
170
+ toBeLessThanOrEqual(value) {
171
+ this.triggerResult(this.actualValue <= value, ` Expected ${this.actualValue} to be less than or equal to ${value}`);
172
+ }
173
+ toBeCloseTo(expectedValue, precision) {
174
+ const shiftHelper = Math.pow(10, precision);
175
+ this.triggerResult(Math.round(this.actualValue * shiftHelper) / shiftHelper === Math.round(expectedValue * shiftHelper) / shiftHelper, ` Expected ` + this.actualValue + ` with precision ` + precision + ` to be close to ` + expectedValue);
176
+ }
177
+ toThrow(expected) {
178
+ let errorMessage = "";
179
+ let didThrow = false;
180
+ let typeMatch = true;
181
+ let messageMatch = true;
182
+ try {
183
+ this.actualValue();
184
+ didThrow = false;
185
+ } catch (e) {
186
+ errorMessage = e.message || "";
187
+ didThrow = true;
188
+ if (typeof expected === "function") {
189
+ typeMatch = e instanceof expected;
190
+ } else if (typeof expected === "string") {
191
+ messageMatch = errorMessage.includes(expected);
192
+ } else if (expected instanceof RegExp) {
193
+ messageMatch = expected.test(errorMessage);
194
+ }
195
+ }
196
+ const functionName = this.actualValue.name || typeof this.actualValue === "function" ? "[anonymous function]" : this.actualValue.toString();
197
+ this.triggerResult(didThrow, ` Expected ${functionName} to ${this.positive ? "throw" : "not throw"} an exception ${!this.positive && errorMessage ? `, but an error with the message "${errorMessage}" was thrown` : ""}`);
198
+ if (typeof expected === "function") {
199
+ this.triggerResult(typeMatch, ` Expected Error type '${expected.name}', but the error is not an instance of it`);
200
+ } else if (expected !== undefined) {
201
+ this.triggerResult(messageMatch, ` Expected error message to match ${expected}\n` + ` Actual message: "${errorMessage}"`);
202
+ }
203
+ }
204
+ async toReject(expected) {
205
+ let didReject = false;
206
+ let errorMessage = "";
207
+ let typeMatch = true;
208
+ let messageMatch = true;
209
+ try {
210
+ await this.actualValue;
211
+ didReject = false;
212
+ } catch (e) {
213
+ didReject = true;
214
+ errorMessage = e?.message || String(e);
215
+ if (typeof expected === "function") {
216
+ typeMatch = e instanceof expected;
217
+ } else if (typeof expected === "string") {
218
+ messageMatch = errorMessage.includes(expected);
219
+ } else if (expected instanceof RegExp) {
220
+ messageMatch = expected.test(errorMessage);
221
+ }
222
+ }
223
+ this.triggerResult(didReject, ` Expected promise to ${this.positive ? "reject" : "resolve"}${!this.positive && errorMessage ? `, but it rejected with "${errorMessage}"` : ""}`);
224
+ if (didReject && typeof expected === "function") {
225
+ this.triggerResult(typeMatch, ` Expected rejection type '${expected.name}', but the error is not an instance of it`);
226
+ } else if (didReject && expected !== undefined) {
227
+ this.triggerResult(messageMatch, ` Expected rejection message to match ${expected}\n` + ` Actual message: "${errorMessage}"`);
228
+ }
229
+ }
230
+ async toResolve() {
231
+ let didResolve = false;
232
+ let errorMessage = "";
233
+ try {
234
+ await this.actualValue;
235
+ didResolve = true;
236
+ } catch (e) {
237
+ didResolve = false;
238
+ errorMessage = e?.message || String(e);
239
+ }
240
+ this.triggerResult(didResolve, ` Expected promise to ${this.positive ? "resolve" : "reject"}${!didResolve ? `, but it rejected with "${errorMessage}"` : ""}`);
241
+ }
242
+ };
333
243
  const describe = async function(moduleName, callback, options) {
334
- const suiteTimeoutMs = typeof options === "number" ? options : options?.timeout ?? timeoutConfig.suiteTimeout;
335
- print("\n" + moduleName);
336
- const prevSuite = currentSuite;
337
- currentSuite = moduleName;
338
- const t0 = now();
339
- try {
340
- await withTimeout(callback, suiteTimeoutMs, `describe: ${moduleName}`);
341
- } catch (e) {
342
- if (e instanceof TimeoutError) {
343
- ++countTestsFailed;
344
- print(` ${RED}\u23F1 Suite timed out: ${e.message}${RESET}`);
345
- } else {
346
- throw e;
347
- }
348
- }
349
- currentSuite = prevSuite;
350
- const duration = now() - t0;
351
- print(` ${GRAY}\u21B3 ${formatDuration(duration)}${RESET}`);
352
- beforeEachCb = null;
353
- afterEachCb = null;
244
+ const suiteTimeoutMs = typeof options === "number" ? options : options?.timeout ?? timeoutConfig.suiteTimeout;
245
+ print("\n" + moduleName);
246
+ const prevSuite = currentSuite;
247
+ currentSuite = moduleName;
248
+ const t0 = now();
249
+ try {
250
+ await withTimeout(callback, suiteTimeoutMs, `describe: ${moduleName}`);
251
+ } catch (e) {
252
+ if (e instanceof TimeoutError) {
253
+ ++countTestsFailed;
254
+ print(` ${RED} Suite timed out: ${e.message}${RESET}`);
255
+ } else {
256
+ throw e;
257
+ }
258
+ }
259
+ currentSuite = prevSuite;
260
+ const duration = now() - t0;
261
+ print(` ${GRAY} ${formatDuration(duration)}${RESET}`);
262
+ beforeEachCb = null;
263
+ afterEachCb = null;
354
264
  };
355
265
  describe.skip = async function(moduleName, _callback) {
356
- ++countTestsIgnored;
357
- print(`
358
- ${BLUE}- ${moduleName} (skipped)${RESET}`);
266
+ ++countTestsIgnored;
267
+ print(`\n${BLUE}- ${moduleName} (skipped)${RESET}`);
359
268
  };
360
269
  const hasDisplay = () => {
361
- const env = globalThis.process?.env;
362
- if (env) {
363
- return !!(env.DISPLAY || env.WAYLAND_DISPLAY);
364
- }
365
- try {
366
- const GLib = globalThis?.imports?.gi?.GLib;
367
- if (GLib) {
368
- return !!(GLib.getenv("DISPLAY") || GLib.getenv("WAYLAND_DISPLAY"));
369
- }
370
- } catch (_) {
371
- }
372
- return false;
270
+ const env = globalThis.process?.env;
271
+ if (env) {
272
+ return !!(env.DISPLAY || env.WAYLAND_DISPLAY);
273
+ }
274
+ try {
275
+ const GLib = globalThis?.imports?.gi?.GLib;
276
+ if (GLib) {
277
+ return !!(GLib.getenv("DISPLAY") || GLib.getenv("WAYLAND_DISPLAY"));
278
+ }
279
+ } catch (_) {}
280
+ return false;
373
281
  };
374
282
  const runtimeMatch = async function(onRuntime, version) {
375
- if (onRuntime.includes("Display")) {
376
- return { matched: hasDisplay() };
377
- }
378
- const currRuntime = await getRuntime();
379
- const foundRuntime = onRuntime.find((r) => currRuntime.includes(r));
380
- if (!foundRuntime) {
381
- return {
382
- matched: false
383
- };
384
- }
385
- if (typeof version === "string") {
386
- if (!currRuntime.includes(version)) {
387
- return {
388
- matched: false
389
- };
390
- }
391
- }
392
- return {
393
- matched: true,
394
- runtime: foundRuntime,
395
- version
396
- };
283
+ if (onRuntime.includes("Display")) {
284
+ return { matched: hasDisplay() };
285
+ }
286
+ const currRuntime = await getRuntime();
287
+ const foundRuntime = onRuntime.find((r) => currRuntime.includes(r));
288
+ if (!foundRuntime) {
289
+ return { matched: false };
290
+ }
291
+ if (typeof version === "string") {
292
+ if (!currRuntime.includes(version)) {
293
+ return { matched: false };
294
+ }
295
+ }
296
+ return {
297
+ matched: true,
298
+ runtime: foundRuntime,
299
+ version
300
+ };
397
301
  };
302
+ /** E.g on('Deno', () { it(...) }) */
398
303
  const on = async function(onRuntime, version, callback) {
399
- if (typeof onRuntime === "string") {
400
- onRuntime = [onRuntime];
401
- }
402
- if (typeof version === "function") {
403
- callback = version;
404
- version = void 0;
405
- }
406
- const { matched } = await runtimeMatch(onRuntime, version);
407
- if (!matched) {
408
- ++countTestsIgnored;
409
- return;
410
- }
411
- print(`
412
- On ${onRuntime.join(", ")}${version ? " " + version : ""}`);
413
- await callback();
304
+ if (typeof onRuntime === "string") {
305
+ onRuntime = [onRuntime];
306
+ }
307
+ if (typeof version === "function") {
308
+ callback = version;
309
+ version = undefined;
310
+ }
311
+ const { matched } = await runtimeMatch(onRuntime, version);
312
+ if (!matched) {
313
+ ++countTestsIgnored;
314
+ return;
315
+ }
316
+ print(`\nOn ${onRuntime.join(", ")}${version ? " " + version : ""}`);
317
+ await callback();
414
318
  };
415
319
  let beforeEachCb;
416
320
  let afterEachCb;
417
321
  const beforeEach = function(callback) {
418
- beforeEachCb = callback;
322
+ beforeEachCb = callback;
419
323
  };
420
324
  const afterEach = function(callback) {
421
- afterEachCb = callback;
325
+ afterEachCb = callback;
422
326
  };
423
327
  const it = async function(expectation, callback, options) {
424
- const timeoutMs = typeof options === "number" ? options : options?.timeout ?? timeoutConfig.testTimeout;
425
- const t0 = now();
426
- try {
427
- if (typeof beforeEachCb === "function") {
428
- await beforeEachCb();
429
- }
430
- await withTimeout(callback, timeoutMs, expectation);
431
- if (typeof afterEachCb === "function") {
432
- await afterEachCb();
433
- }
434
- const duration = now() - t0;
435
- print(` ${GREEN}\u2714${RESET} ${GRAY}${expectation} (${formatDuration(duration)})${RESET}`);
436
- } catch (e) {
437
- const duration = now() - t0;
438
- if (!e.__testFailureCounted) {
439
- ++countTestsFailed;
440
- }
441
- testErrors.push({ suite: currentSuite, test: expectation, message: e.message ?? String(e) });
442
- const icon = e instanceof TimeoutError ? "\u23F1" : "\u274C";
443
- print(` ${RED}${icon}${RESET} ${GRAY}${expectation} (${formatDuration(duration)})${RESET}`);
444
- print(`${RED}${e.message}${RESET}`);
445
- if (e.stack) print(e.stack);
446
- }
328
+ const timeoutMs = typeof options === "number" ? options : options?.timeout ?? timeoutConfig.testTimeout;
329
+ const t0 = now();
330
+ try {
331
+ if (typeof beforeEachCb === "function") {
332
+ await beforeEachCb();
333
+ }
334
+ await withTimeout(callback, timeoutMs, expectation);
335
+ if (typeof afterEachCb === "function") {
336
+ await afterEachCb();
337
+ }
338
+ const duration = now() - t0;
339
+ print(` ${GREEN}✔${RESET} ${GRAY}${expectation} (${formatDuration(duration)})${RESET}`);
340
+ } catch (e) {
341
+ const duration = now() - t0;
342
+ if (!e.__testFailureCounted) {
343
+ ++countTestsFailed;
344
+ }
345
+ testErrors.push({
346
+ suite: currentSuite,
347
+ test: expectation,
348
+ message: e.message ?? String(e)
349
+ });
350
+ const icon = e instanceof TimeoutError ? "⏱" : "❌";
351
+ print(` ${RED}${icon}${RESET} ${GRAY}${expectation} (${formatDuration(duration)})${RESET}`);
352
+ print(`${RED}${e.message}${RESET}`);
353
+ if (e.stack) print(e.stack);
354
+ }
447
355
  };
448
356
  it.skip = async function(expectation, _callback) {
449
- ++countTestsIgnored;
450
- print(` ${BLUE}-${RESET} ${GRAY}${expectation} (skipped)${RESET}`);
357
+ ++countTestsIgnored;
358
+ print(` ${BLUE}-${RESET} ${GRAY}${expectation} (skipped)${RESET}`);
451
359
  };
452
360
  const expect = function(actualValue) {
453
- ++countTestsOverall;
454
- const expecter = new MatcherFactory(actualValue, true);
455
- return expecter;
361
+ ++countTestsOverall;
362
+ const expecter = new MatcherFactory(actualValue, true);
363
+ return expecter;
456
364
  };
457
365
  const assert = function(success, message) {
458
- ++countTestsOverall;
459
- if (!success) {
460
- ++countTestsFailed;
461
- }
462
- try {
463
- nodeAssert(success, message);
464
- } catch (error) {
465
- error.__testFailureCounted = true;
466
- throw error;
467
- }
366
+ ++countTestsOverall;
367
+ if (!success) {
368
+ ++countTestsFailed;
369
+ }
370
+ try {
371
+ nodeAssert(success, message);
372
+ } catch (error) {
373
+ error.__testFailureCounted = true;
374
+ throw error;
375
+ }
468
376
  };
469
377
  assert.strictEqual = function(actual, expected, message) {
470
- ++countTestsOverall;
471
- try {
472
- nodeAssert.strictEqual(actual, expected, message);
473
- } catch (error) {
474
- ++countTestsFailed;
475
- error.__testFailureCounted = true;
476
- throw error;
477
- }
378
+ ++countTestsOverall;
379
+ try {
380
+ nodeAssert.strictEqual(actual, expected, message);
381
+ } catch (error) {
382
+ ++countTestsFailed;
383
+ error.__testFailureCounted = true;
384
+ throw error;
385
+ }
478
386
  };
479
387
  assert.throws = function(promiseFn, ...args) {
480
- ++countTestsOverall;
481
- let error;
482
- try {
483
- promiseFn();
484
- } catch (e) {
485
- error = e;
486
- }
487
- if (!error) ++countTestsFailed;
488
- nodeAssert.throws(() => {
489
- if (error) throw error;
490
- }, args[0], args[1]);
388
+ ++countTestsOverall;
389
+ let error;
390
+ try {
391
+ promiseFn();
392
+ } catch (e) {
393
+ error = e;
394
+ }
395
+ if (!error) ++countTestsFailed;
396
+ nodeAssert.throws(() => {
397
+ if (error) throw error;
398
+ }, args[0], args[1]);
491
399
  };
492
400
  assert.deepStrictEqual = function(actual, expected, message) {
493
- ++countTestsOverall;
494
- try {
495
- nodeAssert.deepStrictEqual(actual, expected, message);
496
- } catch (error) {
497
- ++countTestsFailed;
498
- error.__testFailureCounted = true;
499
- throw error;
500
- }
401
+ ++countTestsOverall;
402
+ try {
403
+ nodeAssert.deepStrictEqual(actual, expected, message);
404
+ } catch (error) {
405
+ ++countTestsFailed;
406
+ error.__testFailureCounted = true;
407
+ throw error;
408
+ }
501
409
  };
502
410
  const runTests = async function(namespaces) {
503
- for (const subNamespace in namespaces) {
504
- const namespace = namespaces[subNamespace];
505
- if (typeof namespace === "function") {
506
- await namespace();
507
- } else if (typeof namespace === "object") {
508
- await runTests(namespace);
509
- }
510
- }
411
+ for (const subNamespace in namespaces) {
412
+ const namespace = namespaces[subNamespace];
413
+ if (typeof namespace === "function") {
414
+ await namespace();
415
+ } else if (typeof namespace === "object") {
416
+ await runTests(namespace);
417
+ }
418
+ }
511
419
  };
512
420
  const browserSignalDone = () => {
513
- const doc = globalThis.document;
514
- if (!doc) return;
515
- globalThis.__gjsify_test_results = {
516
- passed: countTestsOverall - countTestsFailed,
517
- failed: countTestsFailed,
518
- total: countTestsOverall,
519
- errors: testErrors
520
- };
521
- doc.documentElement.dataset.testsDone = "true";
421
+ const doc = globalThis.document;
422
+ if (!doc) return;
423
+ globalThis.__gjsify_test_results = {
424
+ passed: countTestsOverall - countTestsFailed,
425
+ failed: countTestsFailed,
426
+ total: countTestsOverall,
427
+ errors: testErrors
428
+ };
429
+ doc.documentElement.dataset.testsDone = "true";
522
430
  };
523
431
  const printResult = () => {
524
- const totalMs = runStartTime > 0 ? now() - runStartTime : 0;
525
- const durationStr = totalMs > 0 ? ` ${GRAY}(${formatDuration(totalMs)})` : "";
526
- if (countTestsIgnored) {
527
- print(`
528
- ${BLUE}\u2714 ${countTestsIgnored} ignored test${countTestsIgnored > 1 ? "s" : ""}${RESET}`);
529
- }
530
- if (countTestsFailed) {
531
- print(`
532
- ${RED}\u274C ${countTestsFailed} of ${countTestsOverall} tests failed${durationStr}${RESET}`);
533
- } else {
534
- print(`
535
- ${GREEN}\u2714 ${countTestsOverall} completed${durationStr}${RESET}`);
536
- }
432
+ const totalMs = runStartTime > 0 ? now() - runStartTime : 0;
433
+ const durationStr = totalMs > 0 ? ` ${GRAY}(${formatDuration(totalMs)})` : "";
434
+ if (countTestsIgnored) {
435
+ print(`\n${BLUE}✔ ${countTestsIgnored} ignored test${countTestsIgnored > 1 ? "s" : ""}${RESET}`);
436
+ }
437
+ if (countTestsFailed) {
438
+ print(`\n${RED}❌ ${countTestsFailed} of ${countTestsOverall} tests failed${durationStr}${RESET}`);
439
+ } else {
440
+ print(`\n${GREEN} ${countTestsOverall} completed${durationStr}${RESET}`);
441
+ }
537
442
  };
538
443
  const getRuntime = async () => {
539
- if (runtime && runtime !== "Unknown") {
540
- return runtime;
541
- }
542
- if (globalThis.Deno?.version?.deno) {
543
- return "Deno " + globalThis.Deno?.version?.deno;
544
- }
545
- {
546
- let process = globalThis.process;
547
- if (!process) {
548
- try {
549
- process = await import("process");
550
- } catch (_e) {
551
- }
552
- }
553
- if (process?.versions?.gjs) {
554
- runtime = "Gjs " + process.versions.gjs;
555
- return runtime;
556
- } else if (process?.versions?.node) {
557
- runtime = "Node.js " + process.versions.node;
558
- return runtime;
559
- }
560
- }
561
- if (typeof globalThis.document !== "undefined") {
562
- runtime = "Browser";
563
- return runtime;
564
- }
565
- return runtime || "Unknown";
444
+ if (runtime && runtime !== "Unknown") {
445
+ return runtime;
446
+ }
447
+ if (globalThis.Deno?.version?.deno) {
448
+ return "Deno " + globalThis.Deno?.version?.deno;
449
+ }
450
+ {
451
+ let process = globalThis.process;
452
+ if (!process) {
453
+ try {
454
+ process = await import("process");
455
+ } catch (_e) {}
456
+ }
457
+ if (process?.versions?.gjs) {
458
+ runtime = "Gjs " + process.versions.gjs;
459
+ return runtime;
460
+ } else if (process?.versions?.node) {
461
+ runtime = "Node.js " + process.versions.node;
462
+ return runtime;
463
+ }
464
+ }
465
+ if (typeof globalThis.document !== "undefined") {
466
+ runtime = "Browser";
467
+ return runtime;
468
+ }
469
+ return runtime || "Unknown";
566
470
  };
567
471
  const printRuntime = async () => {
568
- const runtime2 = await getRuntime();
569
- print(`
570
- Running on ${runtime2}`);
472
+ const runtime = await getRuntime();
473
+ print(`\nRunning on ${runtime}`);
571
474
  };
572
475
  const run = async (namespaces, options) => {
573
- applyEnvOverrides();
574
- runStartTime = now();
575
- if (options) {
576
- if (typeof options === "number") {
577
- timeoutConfig.runTimeout = options;
578
- } else {
579
- if (options.timeout !== void 0) timeoutConfig.runTimeout = options.timeout;
580
- if (options.testTimeout !== void 0) timeoutConfig.testTimeout = options.testTimeout;
581
- if (options.suiteTimeout !== void 0) timeoutConfig.suiteTimeout = options.suiteTimeout;
582
- }
583
- }
584
- printRuntime().then(async () => {
585
- try {
586
- await withTimeout(() => runTests(namespaces), timeoutConfig.runTimeout, "entire test run");
587
- } catch (e) {
588
- if (e instanceof TimeoutError) {
589
- print(`
590
- ${RED}\u23F1 ${e.message}${RESET}`);
591
- ++countTestsFailed;
592
- } else {
593
- throw e;
594
- }
595
- }
596
- }).then(async () => {
597
- printResult();
598
- browserSignalDone();
599
- print();
600
- quitMainLoop();
601
- mainloop?.quit();
602
- if (!mainloop) {
603
- const exitCode = countTestsFailed > 0 ? 1 : 0;
604
- try {
605
- const process = globalThis.process || await import("process");
606
- process.exit(exitCode);
607
- } catch (_e) {
608
- }
609
- }
610
- });
611
- mainloop?.run();
612
- if (mainloop) {
613
- const exitCode = countTestsFailed > 0 ? 1 : 0;
614
- try {
615
- globalThis.imports.system.exit(exitCode);
616
- } catch (_e) {
617
- }
618
- }
619
- };
620
- var index_default = {
621
- run,
622
- assert,
623
- expect,
624
- it,
625
- afterEach,
626
- beforeEach,
627
- on,
628
- describe,
629
- configure,
630
- print
476
+ applyEnvOverrides();
477
+ runStartTime = now();
478
+ if (options) {
479
+ if (typeof options === "number") {
480
+ timeoutConfig.runTimeout = options;
481
+ } else {
482
+ if (options.timeout !== undefined) timeoutConfig.runTimeout = options.timeout;
483
+ if (options.testTimeout !== undefined) timeoutConfig.testTimeout = options.testTimeout;
484
+ if (options.suiteTimeout !== undefined) timeoutConfig.suiteTimeout = options.suiteTimeout;
485
+ }
486
+ }
487
+ printRuntime().then(async () => {
488
+ try {
489
+ await withTimeout(() => runTests(namespaces), timeoutConfig.runTimeout, "entire test run");
490
+ } catch (e) {
491
+ if (e instanceof TimeoutError) {
492
+ print(`\n${RED}⏱ ${e.message}${RESET}`);
493
+ ++countTestsFailed;
494
+ } else {
495
+ throw e;
496
+ }
497
+ }
498
+ }).then(async () => {
499
+ printResult();
500
+ browserSignalDone();
501
+ print();
502
+ quitMainLoop();
503
+ mainloop?.quit();
504
+ if (!mainloop) {
505
+ const exitCode = countTestsFailed > 0 ? 1 : 0;
506
+ try {
507
+ const process = globalThis.process || await import("process");
508
+ process.exit(exitCode);
509
+ } catch (_e) {}
510
+ }
511
+ });
512
+ mainloop?.run();
513
+ if (mainloop) {
514
+ const exitCode = countTestsFailed > 0 ? 1 : 0;
515
+ try {
516
+ globalThis.imports.system.exit(exitCode);
517
+ } catch (_e) {}
518
+ }
631
519
  };
632
- export {
633
- afterEach,
634
- assert,
635
- beforeEach,
636
- configure,
637
- index_default as default,
638
- describe,
639
- expect,
640
- it,
641
- on,
642
- print,
643
- run
520
+ var src_default = {
521
+ run,
522
+ assert,
523
+ expect,
524
+ it,
525
+ afterEach,
526
+ beforeEach,
527
+ on,
528
+ describe,
529
+ configure,
530
+ print
644
531
  };
532
+
533
+ //#endregion
534
+ export { afterEach, assert, beforeEach, configure, src_default as default, describe, expect, it, on, print, run, spy };