@matter/testing 0.14.1-alpha.0-20250607-a93593303 → 0.15.0-alpha.0-20250613-a55f991d4

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 (84) hide show
  1. package/dist/cjs/chip/config.d.ts.map +1 -1
  2. package/dist/cjs/chip/config.js +2 -6
  3. package/dist/cjs/chip/config.js.map +1 -1
  4. package/dist/cjs/chip/container-command-pipe.d.ts +1 -0
  5. package/dist/cjs/chip/container-command-pipe.d.ts.map +1 -1
  6. package/dist/cjs/chip/container-command-pipe.js +2 -1
  7. package/dist/cjs/chip/container-command-pipe.js.map +1 -1
  8. package/dist/cjs/cli.d.ts.map +1 -1
  9. package/dist/cjs/cli.js +10 -0
  10. package/dist/cjs/cli.js.map +1 -1
  11. package/dist/cjs/global-declarations.d.ts +9 -2
  12. package/dist/cjs/global-declarations.d.ts.map +1 -1
  13. package/dist/cjs/global-definitions.js +5 -1
  14. package/dist/cjs/global-definitions.js.map +1 -1
  15. package/dist/cjs/mocha.d.ts.map +1 -1
  16. package/dist/cjs/mocha.js +10 -6
  17. package/dist/cjs/mocha.js.map +1 -1
  18. package/dist/cjs/mocks/boot.d.ts +4 -1
  19. package/dist/cjs/mocks/boot.d.ts.map +1 -1
  20. package/dist/cjs/mocks/boot.js +5 -5
  21. package/dist/cjs/mocks/boot.js.map +1 -1
  22. package/dist/cjs/mocks/crypto.d.ts +27 -1
  23. package/dist/cjs/mocks/crypto.d.ts.map +1 -1
  24. package/dist/cjs/mocks/crypto.js +56 -43
  25. package/dist/cjs/mocks/crypto.js.map +1 -1
  26. package/dist/cjs/mocks/logging.d.ts +1 -1
  27. package/dist/cjs/mocks/logging.d.ts.map +1 -1
  28. package/dist/cjs/mocks/logging.js +5 -5
  29. package/dist/cjs/mocks/logging.js.map +1 -1
  30. package/dist/cjs/mocks/time.d.ts +16 -1
  31. package/dist/cjs/mocks/time.d.ts.map +1 -1
  32. package/dist/cjs/mocks/time.js +48 -9
  33. package/dist/cjs/mocks/time.js.map +2 -2
  34. package/dist/cjs/nodejs.d.ts.map +1 -1
  35. package/dist/cjs/nodejs.js +2 -0
  36. package/dist/cjs/nodejs.js.map +1 -1
  37. package/dist/esm/chip/config.d.ts.map +1 -1
  38. package/dist/esm/chip/config.js +2 -6
  39. package/dist/esm/chip/config.js.map +1 -1
  40. package/dist/esm/chip/container-command-pipe.d.ts +1 -0
  41. package/dist/esm/chip/container-command-pipe.d.ts.map +1 -1
  42. package/dist/esm/chip/container-command-pipe.js +2 -1
  43. package/dist/esm/chip/container-command-pipe.js.map +1 -1
  44. package/dist/esm/cli.d.ts.map +1 -1
  45. package/dist/esm/cli.js +11 -1
  46. package/dist/esm/cli.js.map +1 -1
  47. package/dist/esm/global-declarations.d.ts +9 -2
  48. package/dist/esm/global-declarations.d.ts.map +1 -1
  49. package/dist/esm/global-definitions.js +7 -3
  50. package/dist/esm/global-definitions.js.map +1 -1
  51. package/dist/esm/mocha.d.ts.map +1 -1
  52. package/dist/esm/mocha.js +10 -6
  53. package/dist/esm/mocha.js.map +1 -1
  54. package/dist/esm/mocks/boot.d.ts +4 -1
  55. package/dist/esm/mocks/boot.d.ts.map +1 -1
  56. package/dist/esm/mocks/boot.js +5 -5
  57. package/dist/esm/mocks/boot.js.map +1 -1
  58. package/dist/esm/mocks/crypto.d.ts +27 -1
  59. package/dist/esm/mocks/crypto.d.ts.map +1 -1
  60. package/dist/esm/mocks/crypto.js +56 -43
  61. package/dist/esm/mocks/crypto.js.map +1 -1
  62. package/dist/esm/mocks/logging.d.ts +1 -1
  63. package/dist/esm/mocks/logging.d.ts.map +1 -1
  64. package/dist/esm/mocks/logging.js +5 -5
  65. package/dist/esm/mocks/logging.js.map +1 -1
  66. package/dist/esm/mocks/time.d.ts +16 -1
  67. package/dist/esm/mocks/time.d.ts.map +1 -1
  68. package/dist/esm/mocks/time.js +48 -9
  69. package/dist/esm/mocks/time.js.map +2 -2
  70. package/dist/esm/nodejs.d.ts.map +1 -1
  71. package/dist/esm/nodejs.js +2 -0
  72. package/dist/esm/nodejs.js.map +1 -1
  73. package/package.json +4 -4
  74. package/src/chip/config.ts +2 -6
  75. package/src/chip/container-command-pipe.ts +1 -1
  76. package/src/cli.ts +13 -1
  77. package/src/global-declarations.ts +10 -2
  78. package/src/global-definitions.ts +10 -3
  79. package/src/mocha.ts +14 -9
  80. package/src/mocks/boot.ts +15 -5
  81. package/src/mocks/crypto.ts +95 -49
  82. package/src/mocks/logging.ts +4 -4
  83. package/src/mocks/time.ts +56 -11
  84. package/src/nodejs.ts +3 -0
@@ -4,67 +4,113 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- const subtle = globalThis.crypto.subtle;
7
+ import { Boot } from "./boot.js";
8
8
 
9
9
  /**
10
- * An extremely minimal Crypto mock.
11
- *
12
- * Only supports those subsets of crypto required to complete matter.js tests.
10
+ * An arbitrary fill byte for mock random data. At some point a pseudo-random function may make sense but for now this
11
+ * suffices. Do not choose 0 or 0xff because that will interfere with logic that disallows 0 or -1.
13
12
  */
14
- const TheCrypto = {
15
- getRandomData: (length: number) => {
16
- // Make random data deterministic
17
- const bytes = new Uint8Array(length);
13
+ const FILL_BYTE = 0x80;
18
14
 
19
- return bytes;
15
+ interface CryptoInstance {
16
+ getRandomData(length: number): Uint8Array;
17
+ }
18
+
19
+ interface CryptoNamespace {
20
+ default: CryptoInstance;
21
+ }
22
+
23
+ let RealCrypto: undefined | CryptoNamespace;
24
+
25
+ let restoreRandomness: undefined | (() => void);
26
+
27
+ export const MockCrypto: MockCrypto = {
28
+ set random(value: boolean) {
29
+ if (RealCrypto === undefined) {
30
+ return;
31
+ }
32
+
33
+ if (value === this.random) {
34
+ return;
35
+ }
36
+
37
+ restoreRandomness?.();
38
+ if (!value) {
39
+ const instance = RealCrypto.default;
40
+
41
+ const realGetRandomData = instance.getRandomData;
42
+ instance.getRandomData = length => {
43
+ const result = new Uint8Array(length);
44
+ result.fill(FILL_BYTE);
45
+ return result;
46
+ };
47
+
48
+ restoreRandomness = () => {
49
+ instance.getRandomData = realGetRandomData;
50
+ restoreRandomness = undefined;
51
+ };
52
+ }
20
53
  },
21
54
 
22
- async pbkdf2(secret: Uint8Array, salt: Uint8Array, iteration: number, keyLength: number) {
23
- const key = await subtle.importKey("raw", secret, "PBKDF2", false, ["deriveBits"]);
24
- const bits = await subtle.deriveBits(
25
- {
26
- name: "PBKDF2",
27
- hash: "SHA-256",
28
- salt: salt,
29
- iterations: iteration,
30
- },
31
- key,
32
- keyLength * 8,
33
- );
34
- return new Uint8Array(bits);
55
+ get random() {
56
+ return !restoreRandomness;
35
57
  },
36
58
 
37
- async hkdf(secret: Uint8Array, salt: Uint8Array, info: Uint8Array, length: number = 16) {
38
- const key = await subtle.importKey("raw", secret, "HKDF", false, ["deriveBits"]);
39
- return new Uint8Array(
40
- await subtle.deriveBits(
41
- {
42
- name: "HKDF",
43
- hash: "SHA-256",
44
- salt: salt,
45
- info: info,
46
- },
47
- key,
48
- length,
49
- ),
50
- );
59
+ withRandom<T>(value: boolean, actor: () => T): T {
60
+ const revertTo = !!restoreRandomness;
61
+ let isAsync = false;
62
+ try {
63
+ MockCrypto.random = value;
64
+ const result = actor();
65
+ if (typeof (result as any)?.then === "function") {
66
+ isAsync = true;
67
+ return Promise.resolve(result).finally(() => {
68
+ MockCrypto.random = revertTo;
69
+ }) as T;
70
+ }
71
+ return result;
72
+ } finally {
73
+ if (!isAsync) {
74
+ MockCrypto.random = revertTo;
75
+ }
76
+ }
51
77
  },
52
78
 
53
- verify() {
54
- // We do not really verify anything
55
- return;
79
+ enable() {
80
+ MockCrypto.random = false;
56
81
  },
57
82
 
58
- mock: true,
83
+ init() {
84
+ if (MockCrypto.random) {
85
+ MockCrypto.enable();
86
+ }
87
+ },
59
88
  };
60
89
 
61
- export function cryptoSetup(Crypto: any) {
62
- try {
63
- Crypto.get();
64
- } catch (e) {
65
- if ((e as Error).constructor.name !== "NoProviderError") {
66
- throw e;
67
- }
68
- Crypto.get = () => TheCrypto;
69
- }
90
+ export function cryptoSetup(Crypto: CryptoNamespace) {
91
+ RealCrypto = Crypto;
92
+ }
93
+
94
+ Boot.init(() => (MockCrypto.random = true));
95
+
96
+ export interface MockCrypto {
97
+ /**
98
+ * If false, crypto functions return all zeros instead of random data. Resets to true for each test file.
99
+ */
100
+ random: boolean;
101
+
102
+ /**
103
+ * Set {@link random} to false.
104
+ */
105
+ enable(): void;
106
+
107
+ /**
108
+ * Enabled if not already enabled.
109
+ */
110
+ init(): void;
111
+
112
+ /**
113
+ * Perform an operation with altered {@link random} then revert.
114
+ */
115
+ withRandom<T>(value: boolean, actor: () => T): T;
70
116
  }
@@ -25,7 +25,7 @@ export interface MockLogger {
25
25
  injectExternalMessage: (source: string, text: string) => void;
26
26
  }
27
27
 
28
- export const TheMockLogger: MockLogger = {
28
+ export const MockLogger: MockLogger = {
29
29
  emitAll: false,
30
30
  injectExternalMessage: (source: string, text: string) => console.log(formatExternalMessage(source, text)),
31
31
  };
@@ -67,7 +67,7 @@ export function loggerSetup(Logger: LoggerLike) {
67
67
  }
68
68
 
69
69
  function interceptingWriter(...args: [string, DiagnosticMessageLike]) {
70
- let emitAll = TheMockLogger.emitAll;
70
+ let emitAll = MockLogger.emitAll;
71
71
  if (MatterHooks?.loggerSink) {
72
72
  MatterHooks.loggerSink(...args);
73
73
  } else if (!emitAll) {
@@ -83,14 +83,14 @@ export function loggerSetup(Logger: LoggerLike) {
83
83
  }
84
84
  }
85
85
 
86
- TheMockLogger.injectExternalMessage = (source, text) =>
86
+ MockLogger.injectExternalMessage = (source, text) =>
87
87
  interceptingWriter(formatExternalMessage(source, text), { level: 0 });
88
88
 
89
89
  Logger.destinations.default.write = interceptingWriter;
90
90
 
91
91
  // Divert log messages for test duration
92
92
  LoggerHooks.beforeEach.push(function () {
93
- if (!TheMockLogger.emitAll) {
93
+ if (!MockLogger.emitAll) {
94
94
  messageBuffer = [];
95
95
  }
96
96
  });
package/src/mocks/time.ts CHANGED
@@ -83,30 +83,74 @@ function isAsync(fn: (...args: any) => any): fn is (...args: any) => Promise<any
83
83
  let callbacks = new Array<{ atMs: number; callback: TimerCallback }>();
84
84
  let nowMs = 0;
85
85
  let real = undefined as unknown;
86
- let enabled = true;
86
+ let enabled = false;
87
87
 
88
- // Must match matter.js Time interface
88
+ /**
89
+ * An arbitrary start for our mock timeline. Starting at zero causes problems with Matter dates that cannot encode back
90
+ * to the UNIX epoch
91
+ */
92
+ const epoch = new Date("2025-01-01 12:34:56Z");
93
+
94
+ // Must match matter.js Time interface (with extensions)
89
95
  export const MockTime = {
96
+ epoch,
97
+
90
98
  get activeImplementation(): unknown {
91
99
  return enabled ? this : (real ?? this);
92
100
  },
93
101
 
102
+ /**
103
+ * Revert to standard time implementation.
104
+ */
94
105
  disable() {
95
106
  enabled = false;
96
- reinstallTime?.();
107
+ installActiveImplementation?.();
97
108
  },
98
109
 
110
+ /**
111
+ * Enable time mocking. Reverts to disabled for each test file.
112
+ */
99
113
  enable() {
100
114
  enabled = true;
101
- reinstallTime?.();
115
+ installActiveImplementation?.();
102
116
  },
103
117
 
104
- reset(time = 0) {
118
+ /**
119
+ * Sets mock time to specific time and enable the mock.
120
+ */
121
+ reset(time: ConstructorParameters<typeof Date>[0] = epoch) {
105
122
  callbacks = [];
106
- nowMs = time;
123
+ nowMs = new Date(time).getTime();
124
+ MockTime.enable();
125
+ },
107
126
 
108
- // Ensure time reverts to correct implementation across suites
109
- this.enable();
127
+ /**
128
+ * Enable and reset if not already enabled.
129
+ */
130
+ init() {
131
+ if (!enabled) {
132
+ MockTime.enable();
133
+ }
134
+ },
135
+
136
+ atTime<T>(time: number | Date, actor: () => T): T {
137
+ const revertTo = nowMs;
138
+ let isAsync = false;
139
+ try {
140
+ nowMs = typeof time === "number" ? time : time.getTime();
141
+ const result = actor();
142
+ if (typeof (result as any)?.then === "function") {
143
+ isAsync = true;
144
+ return Promise.resolve(result).finally(() => {
145
+ nowMs = revertTo;
146
+ }) as T;
147
+ }
148
+ return result;
149
+ } finally {
150
+ if (!isAsync) {
151
+ nowMs = revertTo;
152
+ }
153
+ }
110
154
  },
111
155
 
112
156
  now(): Date {
@@ -305,7 +349,7 @@ export const MockTime = {
305
349
  },
306
350
  };
307
351
 
308
- let reinstallTime: undefined | (() => void);
352
+ let installActiveImplementation: undefined | (() => void);
309
353
 
310
354
  export function timeSetup(Time: {
311
355
  startup: { systemMs: number; processMs: number };
@@ -320,12 +364,13 @@ export function timeSetup(Time: {
320
364
  Time.startup.systemMs = Time.startup.processMs = 0;
321
365
  real = Time.get();
322
366
  (MockTime as any).sleep = (real as any).sleep;
323
- reinstallTime = () => (Time.get = () => MockTime.activeImplementation);
324
- reinstallTime();
367
+ installActiveImplementation = () => (Time.get = () => MockTime.activeImplementation);
368
+ installActiveImplementation();
325
369
  }
326
370
 
327
371
  Object.assign(globalThis, { MockTime });
328
372
 
329
373
  Boot.init(() => {
330
374
  MockTime.reset();
375
+ MockTime.disable();
331
376
  });
package/src/nodejs.ts CHANGED
@@ -15,11 +15,14 @@ import type { TestRunner } from "./runner.js";
15
15
  // Load globals so settings get applied
16
16
  import { FailureDetail } from "./failure-detail.js";
17
17
  import "./global-definitions.js";
18
+ import { Boot } from "./mocks/boot.js";
18
19
  import { TestDescriptor } from "./test-descriptor.js";
19
20
 
20
21
  extendApi(Mocha);
21
22
 
22
23
  export async function testNodejs(runner: TestRunner, format: "cjs" | "esm") {
24
+ Boot.format = format;
25
+
23
26
  // Grr Mocha (as of 10.2.0) classifies certain unhandled rejections as "mocha". For others, it uninstalls its
24
27
  // unhandled rejection handler and re-emits the "unhandledRejection" event. But since it already handled the event,
25
28
  // Node knows nothing about this and the event disappears silently.