@decohere/testing 0.0.1-dev.126 → 0.0.1-dev.5

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.
package/README.md CHANGED
@@ -31,4 +31,4 @@ beforeEach(() => {
31
31
  });
32
32
  ```
33
33
 
34
- `vitest` and `memfs` are peer dependencies: install them in the project that runs the tests ([ADR 0017](../../docs/adr/0017-dependency-manifests-from-imports.md)).
34
+ `vitest` and `memfs` should be peer dependencies; the dependency sync can't declare peers yet ([#683](https://github.com/typesmiths/decohere/issues/683)).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decohere/testing",
3
- "version": "0.0.1-dev.126",
3
+ "version": "0.0.1-dev.5",
4
4
  "description": "Test helpers for Decohere code under Vitest: memfs file systems, a mock logger, constructor spies that keep DI metadata.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -9,15 +9,6 @@
9
9
  "memfs",
10
10
  "decohere"
11
11
  ],
12
- "homepage": "https://github.com/typesmiths/decohere/tree/main/projects/testing#readme",
13
- "bugs": {
14
- "url": "https://github.com/typesmiths/decohere/issues"
15
- },
16
- "repository": {
17
- "type": "git",
18
- "url": "git+https://github.com/typesmiths/decohere.git",
19
- "directory": "projects/testing"
20
- },
21
12
  "license": "ISC",
22
13
  "author": "Typesmiths",
23
14
  "type": "module",
@@ -33,10 +24,9 @@
33
24
  "test": "echo \"Error: no test specified\" && exit 1"
34
25
  },
35
26
  "dependencies": {
36
- "@decohere/core": "0.0.1-dev.126"
27
+ "@decohere/core": "0.0.1-dev.5"
37
28
  },
38
- "devDependencies": {},
39
- "peerDependencies": {
29
+ "devDependencies": {
40
30
  "memfs": "^4.17.0",
41
31
  "vitest": "^3.2.6"
42
32
  }
@@ -1,6 +1,7 @@
1
1
  var scheduler = typeof setImmediate === "function" ? setImmediate : setTimeout;
2
+
2
3
  export function flushPromises() {
3
- return new Promise((resolve)=>{
4
- scheduler(resolve);
5
- });
4
+ return new Promise((resolve) => {
5
+ scheduler(resolve);
6
+ });
6
7
  }
@@ -1,4 +1,5 @@
1
1
  import { fs } from "memfs";
2
+
2
3
  // Wraps a real `glob` module so all file-system access is routed through memfs.
3
4
  //
4
5
  // Why this exists:
@@ -7,14 +8,18 @@ import { fs } from "memfs";
7
8
  // `glob` inside helper modules can trigger initialization-order issues.
8
9
  // - Passing the already-loaded `actual` module keeps the helper pure and
9
10
  // hoist-safe while still allowing us to inject memfs.
10
- export function withMemfsFsForGlob(actual) {
11
- // Preserve all module exports, overriding only `glob` to force `{ fs: memfs }`.
12
- const glob = (pattern, options)=>actual.glob(pattern, {
13
- ...options,
14
- fs
15
- });
16
- return {
17
- ...actual,
18
- glob
19
- };
11
+ export function withMemfsFsForGlob<
12
+ TGlobModule extends { glob: (...args: unknown[]) => unknown },
13
+ >(actual: TGlobModule): TGlobModule {
14
+ // Preserve all module exports, overriding only `glob` to force `{ fs: memfs }`.
15
+ const glob = ((pattern: string | string[], options?: unknown) =>
16
+ (actual.glob as (...args: unknown[]) => unknown)(pattern, {
17
+ ...(options as Record<string, unknown> | undefined),
18
+ fs,
19
+ })) as TGlobModule["glob"];
20
+
21
+ return {
22
+ ...actual,
23
+ glob,
24
+ };
20
25
  }
package/src/memfs.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { fs, vol } from "memfs";
2
+
3
+ export function resetMemfs(): void {
4
+ vol.reset();
5
+ }
6
+
7
+ export function seedMemfs(files: Record<string, string>): void {
8
+ vol.fromJSON(files);
9
+ }
10
+
11
+ export function createTestFile(filePath: string, content: string | Buffer): void {
12
+ fs.mkdirSync(filePath.substring(0, filePath.lastIndexOf("/")), { recursive: true });
13
+ fs.writeFileSync(filePath, content);
14
+ }
15
+
16
+ export async function readFile(filePath: string): Promise<string> {
17
+ const response = (await fs.promises.readFile(filePath, "utf8")) as string;
18
+ return response;
19
+ }
20
+
21
+ export function fileExists(filePath: string): boolean {
22
+ try {
23
+ vol.statSync(filePath);
24
+ return true;
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
@@ -0,0 +1,42 @@
1
+ import { vi } from "vitest";
2
+
3
+ export type LoggerMethod = (msg: string | number | object, ...args: unknown[]) => void;
4
+
5
+ export type TestLogger = {
6
+ fatal: LoggerMethod;
7
+ error: LoggerMethod;
8
+ warn: LoggerMethod;
9
+ info: LoggerMethod;
10
+ debug: LoggerMethod;
11
+ trace: LoggerMethod;
12
+ isLevelEnabled(level: string): boolean;
13
+ readonly currentLevel: string;
14
+ readonly logLevels: Record<string, number>;
15
+ };
16
+
17
+ const DefaultLogLevels: Record<string, number> = {
18
+ fatal: 60,
19
+ error: 50,
20
+ warn: 40,
21
+ info: 30,
22
+ debug: 20,
23
+ trace: 10,
24
+ };
25
+
26
+ export function createMockLogger(): TestLogger {
27
+ return {
28
+ error: vi.fn(),
29
+ info: vi.fn(),
30
+ warn: vi.fn(),
31
+ debug: vi.fn(),
32
+ trace: vi.fn(),
33
+ fatal: vi.fn(),
34
+ isLevelEnabled: vi.fn().mockReturnValue(true),
35
+ get currentLevel() {
36
+ return "info";
37
+ },
38
+ get logLevels() {
39
+ return DefaultLogLevels;
40
+ },
41
+ };
42
+ }
@@ -1,5 +1,6 @@
1
1
  import { vi } from "vitest";
2
2
  import type { Constructable } from "@decohere/core/reflection";
3
+
3
4
  /**
4
5
  * Creates a Proxy around a class constructor that intercepts `new` calls,
5
6
  * records arguments via a spy, and delegates to the real constructor.
@@ -15,7 +16,18 @@ import type { Constructable } from "@decohere/core/reflection";
15
16
  * // use SpiedService in place of MyService when wiring the container
16
17
  * expect(spy).toHaveBeenCalledWith(expect.any(ExpectedDependency));
17
18
  */
18
- export declare function spyOnConstructor<T extends Constructable>(Target: T): {
19
- spy: ReturnType<typeof vi.fn>;
20
- Proxy: T;
21
- };
19
+ export function spyOnConstructor<T extends Constructable>(
20
+ Target: T,
21
+ ): { spy: ReturnType<typeof vi.fn>; Proxy: T } {
22
+ const spy = vi.fn();
23
+ const ProxiedTarget = new Proxy(Target, {
24
+ construct(Ctor, args) {
25
+ spy(...args);
26
+ return Reflect.construct(Ctor, args);
27
+ },
28
+ });
29
+ for (const key of Reflect.getMetadataKeys(Target)) {
30
+ Reflect.defineMetadata(key, Reflect.getMetadata(key, Target), ProxiedTarget);
31
+ }
32
+ return { spy, Proxy: ProxiedTarget as T };
33
+ }
package/LICENSE DELETED
@@ -1,15 +0,0 @@
1
- ISC License
2
-
3
- Copyright (c) 2025-2026 Typesmiths
4
-
5
- Permission to use, copy, modify, and/or distribute this software for any
6
- purpose with or without fee is hereby granted, provided that the above
7
- copyright notice and this permission notice appear in all copies.
8
-
9
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -1,16 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "flushPromises", {
6
- enumerable: true,
7
- get: function() {
8
- return flushPromises;
9
- }
10
- });
11
- var scheduler = typeof setImmediate === "function" ? setImmediate : setTimeout;
12
- function flushPromises() {
13
- return new Promise((resolve)=>{
14
- scheduler(resolve);
15
- });
16
- }
@@ -1,22 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "withMemfsFsForGlob", {
6
- enumerable: true,
7
- get: function() {
8
- return withMemfsFsForGlob;
9
- }
10
- });
11
- const _memfs = require("memfs");
12
- function withMemfsFsForGlob(actual) {
13
- // Preserve all module exports, overriding only `glob` to force `{ fs: memfs }`.
14
- const glob = (pattern, options)=>actual.glob(pattern, {
15
- ...options,
16
- fs: _memfs.fs
17
- });
18
- return {
19
- ...actual,
20
- glob
21
- };
22
- }
@@ -1,22 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- _export_star(require("./flush-promises.js"), exports);
6
- _export_star(require("./glob-with-memfs.js"), exports);
7
- _export_star(require("./memfs.js"), exports);
8
- _export_star(require("./mock-logger.js"), exports);
9
- _export_star(require("./spy-on-constructor.js"), exports);
10
- function _export_star(from, to) {
11
- Object.keys(from).forEach(function(k) {
12
- if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
13
- Object.defineProperty(to, k, {
14
- enumerable: true,
15
- get: function() {
16
- return from[k];
17
- }
18
- });
19
- }
20
- });
21
- return from;
22
- }
@@ -1,52 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- function _export(target, all) {
6
- for(var name in all)Object.defineProperty(target, name, {
7
- enumerable: true,
8
- get: Object.getOwnPropertyDescriptor(all, name).get
9
- });
10
- }
11
- _export(exports, {
12
- get createTestFile () {
13
- return createTestFile;
14
- },
15
- get fileExists () {
16
- return fileExists;
17
- },
18
- get readFile () {
19
- return readFile;
20
- },
21
- get resetMemfs () {
22
- return resetMemfs;
23
- },
24
- get seedMemfs () {
25
- return seedMemfs;
26
- }
27
- });
28
- const _memfs = require("memfs");
29
- function resetMemfs() {
30
- _memfs.vol.reset();
31
- }
32
- function seedMemfs(files) {
33
- _memfs.vol.fromJSON(files);
34
- }
35
- function createTestFile(filePath, content) {
36
- _memfs.fs.mkdirSync(filePath.substring(0, filePath.lastIndexOf("/")), {
37
- recursive: true
38
- });
39
- _memfs.fs.writeFileSync(filePath, content);
40
- }
41
- async function readFile(filePath) {
42
- const response = await _memfs.fs.promises.readFile(filePath, "utf8");
43
- return response;
44
- }
45
- function fileExists(filePath) {
46
- try {
47
- _memfs.vol.statSync(filePath);
48
- return true;
49
- } catch {
50
- return false;
51
- }
52
- }
@@ -1,38 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "createMockLogger", {
6
- enumerable: true,
7
- get: function() {
8
- return createMockLogger;
9
- }
10
- });
11
- const _vitest = require("vitest");
12
- const DefaultLogLevels = {
13
- fatal: 60,
14
- error: 50,
15
- warn: 40,
16
- info: 30,
17
- debug: 20,
18
- trace: 10
19
- };
20
- function createMockLogger() {
21
- const logger = {
22
- error: _vitest.vi.fn(),
23
- info: _vitest.vi.fn(),
24
- warn: _vitest.vi.fn(),
25
- debug: _vitest.vi.fn(),
26
- trace: _vitest.vi.fn(),
27
- fatal: _vitest.vi.fn(),
28
- isLevelEnabled: _vitest.vi.fn().mockReturnValue(true),
29
- child: _vitest.vi.fn(()=>logger),
30
- get currentLevel () {
31
- return "info";
32
- },
33
- get logLevels () {
34
- return DefaultLogLevels;
35
- }
36
- };
37
- return logger;
38
- }
@@ -1,27 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "spyOnConstructor", {
6
- enumerable: true,
7
- get: function() {
8
- return spyOnConstructor;
9
- }
10
- });
11
- const _vitest = require("vitest");
12
- function spyOnConstructor(Target) {
13
- const spy = _vitest.vi.fn();
14
- const ProxiedTarget = new Proxy(Target, {
15
- construct (Ctor, args) {
16
- spy(...args);
17
- return Reflect.construct(Ctor, args);
18
- }
19
- });
20
- for (const key of Reflect.getMetadataKeys(Target)){
21
- Reflect.defineMetadata(key, Reflect.getMetadata(key, Target), ProxiedTarget);
22
- }
23
- return {
24
- spy,
25
- Proxy: ProxiedTarget
26
- };
27
- }
package/es6/src/memfs.js DELETED
@@ -1,25 +0,0 @@
1
- import { fs, vol } from "memfs";
2
- export function resetMemfs() {
3
- vol.reset();
4
- }
5
- export function seedMemfs(files) {
6
- vol.fromJSON(files);
7
- }
8
- export function createTestFile(filePath, content) {
9
- fs.mkdirSync(filePath.substring(0, filePath.lastIndexOf("/")), {
10
- recursive: true
11
- });
12
- fs.writeFileSync(filePath, content);
13
- }
14
- export async function readFile(filePath) {
15
- const response = await fs.promises.readFile(filePath, "utf8");
16
- return response;
17
- }
18
- export function fileExists(filePath) {
19
- try {
20
- vol.statSync(filePath);
21
- return true;
22
- } catch {
23
- return false;
24
- }
25
- }
@@ -1,28 +0,0 @@
1
- import { vi } from "vitest";
2
- const DefaultLogLevels = {
3
- fatal: 60,
4
- error: 50,
5
- warn: 40,
6
- info: 30,
7
- debug: 20,
8
- trace: 10
9
- };
10
- /** `child` returns the same mock, so calls on a child are asserted on the parent. */ export function createMockLogger() {
11
- const logger = {
12
- error: vi.fn(),
13
- info: vi.fn(),
14
- warn: vi.fn(),
15
- debug: vi.fn(),
16
- trace: vi.fn(),
17
- fatal: vi.fn(),
18
- isLevelEnabled: vi.fn().mockReturnValue(true),
19
- child: vi.fn(()=>logger),
20
- get currentLevel () {
21
- return "info";
22
- },
23
- get logLevels () {
24
- return DefaultLogLevels;
25
- }
26
- };
27
- return logger;
28
- }
@@ -1,31 +0,0 @@
1
- import { vi } from "vitest";
2
- /**
3
- * Creates a Proxy around a class constructor that intercepts `new` calls,
4
- * records arguments via a spy, and delegates to the real constructor.
5
- *
6
- * Use this to assert what was injected into a class by a DI container
7
- * (e.g. Container) without changing actual behavior.
8
- *
9
- * The proxy copies all `reflect-metadata` entries from the original class
10
- * so that DI metadata (`design:paramtypes`) remains intact.
11
- *
12
- * @example
13
- * const { spy, Proxy: SpiedService } = spyOnConstructor(MyService);
14
- * // use SpiedService in place of MyService when wiring the container
15
- * expect(spy).toHaveBeenCalledWith(expect.any(ExpectedDependency));
16
- */ export function spyOnConstructor(Target) {
17
- const spy = vi.fn();
18
- const ProxiedTarget = new Proxy(Target, {
19
- construct (Ctor, args) {
20
- spy(...args);
21
- return Reflect.construct(Ctor, args);
22
- }
23
- });
24
- for (const key of Reflect.getMetadataKeys(Target)){
25
- Reflect.defineMetadata(key, Reflect.getMetadata(key, Target), ProxiedTarget);
26
- }
27
- return {
28
- spy,
29
- Proxy: ProxiedTarget
30
- };
31
- }
@@ -1 +0,0 @@
1
- export declare function flushPromises(): Promise<unknown>;
@@ -1,3 +0,0 @@
1
- export declare function withMemfsFsForGlob<TGlobModule extends {
2
- glob: (...args: unknown[]) => unknown;
3
- }>(actual: TGlobModule): TGlobModule;
@@ -1,5 +0,0 @@
1
- export * from "./flush-promises.js";
2
- export * from "./glob-with-memfs.js";
3
- export * from "./memfs.js";
4
- export * from "./mock-logger.js";
5
- export * from "./spy-on-constructor.js";
@@ -1,5 +0,0 @@
1
- export declare function resetMemfs(): void;
2
- export declare function seedMemfs(files: Record<string, string>): void;
3
- export declare function createTestFile(filePath: string, content: string | Buffer): void;
4
- export declare function readFile(filePath: string): Promise<string>;
5
- export declare function fileExists(filePath: string): boolean;
@@ -1,15 +0,0 @@
1
- export type LoggerMethod = (msg: string | number | object, ...args: unknown[]) => void;
2
- export type TestLogger = {
3
- fatal: LoggerMethod;
4
- error: LoggerMethod;
5
- warn: LoggerMethod;
6
- info: LoggerMethod;
7
- debug: LoggerMethod;
8
- trace: LoggerMethod;
9
- isLevelEnabled(level: string): boolean;
10
- child(fields: Record<string, unknown>): TestLogger;
11
- readonly currentLevel: string;
12
- readonly logLevels: Record<string, number>;
13
- };
14
- /** `child` returns the same mock, so calls on a child are asserted on the parent. */
15
- export declare function createMockLogger(): TestLogger;
File without changes