@digitaldefiance/express-suite-test-utils 1.0.0 → 1.0.2
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 +8 -0
- package/package.json +2 -2
- package/src/index.d.ts +2 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +7 -0
- package/src/lib/console.js +52 -0
- package/src/lib/localStorage-mock.d.ts +13 -0
- package/src/lib/localStorage-mock.d.ts.map +1 -0
- package/src/lib/localStorage-mock.js +37 -0
- package/src/lib/to-throw-type.js +134 -0
- package/src/lib/types.js +3 -0
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@digitaldefiance/express-suite-test-utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Test utilities for Digital Defiance Express Suite",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "npx nx build digitaldefiance-express-suite-test-utils",
|
|
8
|
+
"build": "npx nx build digitaldefiance-express-suite-test-utils && npx nx run digitaldefiance-express-suite-test-utils:postbuild",
|
|
9
9
|
"build:stream": "npx nx build --outputStyle=stream digitaldefiance-express-suite-test-utils",
|
|
10
10
|
"build:logged": "npx nx build --outputStyle=stream digitaldefiance-express-suite-test-utils 2>&1 | ansifilter -o build.log",
|
|
11
11
|
"test": "npx nx test digitaldefiance-express-suite-test-utils",
|
package/src/index.d.ts
CHANGED
package/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../packages/digitaldefiance-express-suite-test-utils/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../packages/digitaldefiance-express-suite-test-utils/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAA"}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./lib/to-throw-type"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./lib/console"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./lib/localStorage-mock"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./lib/localStorage-mock"), exports);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* Reusable console mock helpers for e2e tests */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.withConsoleMocks = withConsoleMocks;
|
|
5
|
+
exports.spyContains = spyContains;
|
|
6
|
+
/**
|
|
7
|
+
* Wrap a test body with console spies that are always restored.
|
|
8
|
+
* By default mutes console output to keep test logs clean.
|
|
9
|
+
*/
|
|
10
|
+
async function withConsoleMocks(options, fn) {
|
|
11
|
+
const mute = options?.mute !== false; // default true
|
|
12
|
+
const noop = () => undefined;
|
|
13
|
+
const spies = {
|
|
14
|
+
log: jest
|
|
15
|
+
.spyOn(console, 'log')
|
|
16
|
+
.mockImplementation(mute ? noop : console.log),
|
|
17
|
+
info: jest
|
|
18
|
+
.spyOn(console, 'info')
|
|
19
|
+
.mockImplementation(mute ? noop : console.info),
|
|
20
|
+
warn: jest
|
|
21
|
+
.spyOn(console, 'warn')
|
|
22
|
+
.mockImplementation(mute ? noop : console.warn),
|
|
23
|
+
error: jest
|
|
24
|
+
.spyOn(console, 'error')
|
|
25
|
+
.mockImplementation(mute ? noop : console.error),
|
|
26
|
+
debug: jest
|
|
27
|
+
.spyOn(console, 'debug')
|
|
28
|
+
.mockImplementation(mute
|
|
29
|
+
? noop
|
|
30
|
+
: console.debug ??
|
|
31
|
+
noop),
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
return await fn(spies);
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
spies.log.mockRestore();
|
|
38
|
+
spies.info.mockRestore();
|
|
39
|
+
spies.warn.mockRestore();
|
|
40
|
+
spies.error.mockRestore();
|
|
41
|
+
spies.debug.mockRestore();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* True if any call to the spy contains all provided substrings, in order-agnostic check.
|
|
46
|
+
*/
|
|
47
|
+
function spyContains(spy, ...needles) {
|
|
48
|
+
return spy.mock.calls.some((args) => {
|
|
49
|
+
const text = args.map((a) => (typeof a === 'string' ? a : '')).join(' ');
|
|
50
|
+
return needles.every((n) => text.includes(n));
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple localStorage mock for Node.js test environment
|
|
3
|
+
*/
|
|
4
|
+
export declare class LocalStorageMock implements Storage {
|
|
5
|
+
private store;
|
|
6
|
+
get length(): number;
|
|
7
|
+
clear(): void;
|
|
8
|
+
getItem(key: string): string | null;
|
|
9
|
+
key(index: number): string | null;
|
|
10
|
+
removeItem(key: string): void;
|
|
11
|
+
setItem(key: string, value: string): void;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=localStorage-mock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localStorage-mock.d.ts","sourceRoot":"","sources":["../../../../../packages/digitaldefiance-express-suite-test-utils/src/lib/localStorage-mock.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,gBAAiB,YAAW,OAAO;IAC9C,OAAO,CAAC,KAAK,CAAkC;IAE/C,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAInC,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKjC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI7B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;CAG1C"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalStorageMock = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Simple localStorage mock for Node.js test environment
|
|
6
|
+
*/
|
|
7
|
+
class LocalStorageMock {
|
|
8
|
+
store = new Map();
|
|
9
|
+
get length() {
|
|
10
|
+
return this.store.size;
|
|
11
|
+
}
|
|
12
|
+
clear() {
|
|
13
|
+
this.store.clear();
|
|
14
|
+
}
|
|
15
|
+
getItem(key) {
|
|
16
|
+
return this.store.get(key) ?? null;
|
|
17
|
+
}
|
|
18
|
+
key(index) {
|
|
19
|
+
const keys = Array.from(this.store.keys());
|
|
20
|
+
return keys[index] ?? null;
|
|
21
|
+
}
|
|
22
|
+
removeItem(key) {
|
|
23
|
+
this.store.delete(key);
|
|
24
|
+
}
|
|
25
|
+
setItem(key, value) {
|
|
26
|
+
this.store.set(key, value);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.LocalStorageMock = LocalStorageMock;
|
|
30
|
+
// Set up global localStorage mock
|
|
31
|
+
if (typeof globalThis.localStorage === 'undefined') {
|
|
32
|
+
Object.defineProperty(globalThis, 'localStorage', {
|
|
33
|
+
value: new LocalStorageMock(),
|
|
34
|
+
writable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toThrowType = void 0;
|
|
4
|
+
const globals_1 = require("@jest/globals");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
function isMatcherError(error) {
|
|
7
|
+
return error instanceof Error && 'matcherResult' in error;
|
|
8
|
+
}
|
|
9
|
+
function extractTestInfo(stackTrace) {
|
|
10
|
+
const stackLines = stackTrace.split('\n');
|
|
11
|
+
const anonymousLine = stackLines.find((line) => line.includes('Object.<anonymous>'));
|
|
12
|
+
const match = anonymousLine?.match(/\((.+?\.spec\.ts):(\d+):(\d+)\)/);
|
|
13
|
+
if (!match) {
|
|
14
|
+
return { testHierarchy: ['Unknown Test'], location: '' };
|
|
15
|
+
}
|
|
16
|
+
const fullTestPath = match[1];
|
|
17
|
+
const lineNumber = parseInt(match[2]);
|
|
18
|
+
const testFile = fullTestPath.split('/').pop() || '';
|
|
19
|
+
try {
|
|
20
|
+
const fileContent = (0, fs_1.readFileSync)(fullTestPath, 'utf8');
|
|
21
|
+
const lines = fileContent.split('\n');
|
|
22
|
+
const testLineContent = lines[lineNumber - 1];
|
|
23
|
+
const testNameMatch = testLineContent.match(/it\(['"](.+?)['"]/);
|
|
24
|
+
const testName = testNameMatch?.[1];
|
|
25
|
+
const testHierarchy = ['Test'];
|
|
26
|
+
if (testName) {
|
|
27
|
+
testHierarchy.push(testName);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
testHierarchy,
|
|
31
|
+
location: ` (${testFile}:${lineNumber})`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return {
|
|
36
|
+
testHierarchy: ['Test'],
|
|
37
|
+
location: ` (${testFile}:${lineNumber})`,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const toThrowType = async function (received, errorType, validator) {
|
|
42
|
+
const matcherName = 'toThrowType';
|
|
43
|
+
const options = {
|
|
44
|
+
isNot: this.isNot,
|
|
45
|
+
promise: this.promise,
|
|
46
|
+
};
|
|
47
|
+
let error;
|
|
48
|
+
let pass = false;
|
|
49
|
+
try {
|
|
50
|
+
if (this.promise) {
|
|
51
|
+
try {
|
|
52
|
+
await received;
|
|
53
|
+
pass = false;
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
error = e;
|
|
57
|
+
if (error instanceof Error &&
|
|
58
|
+
(error instanceof errorType || error.constructor === errorType)) {
|
|
59
|
+
pass = true;
|
|
60
|
+
if (validator) {
|
|
61
|
+
await validator(error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
if (typeof received !== 'function') {
|
|
68
|
+
throw new Error(this.utils.matcherHint(matcherName, undefined, undefined, options) +
|
|
69
|
+
'\n\n' +
|
|
70
|
+
'Received value must be a function');
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
await received();
|
|
74
|
+
pass = false;
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
error = e;
|
|
78
|
+
if (error instanceof Error &&
|
|
79
|
+
(error instanceof errorType || error.constructor === errorType)) {
|
|
80
|
+
pass = true;
|
|
81
|
+
if (validator) {
|
|
82
|
+
await validator(error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (validatorError) {
|
|
89
|
+
const error = validatorError instanceof Error
|
|
90
|
+
? validatorError
|
|
91
|
+
: new Error(String(validatorError));
|
|
92
|
+
const message = error.message;
|
|
93
|
+
const stack = error.stack || '';
|
|
94
|
+
let diffString;
|
|
95
|
+
if (isMatcherError(error) && error.matcherResult) {
|
|
96
|
+
diffString =
|
|
97
|
+
this.utils.diff(error.matcherResult.expected, error.matcherResult.received) || '';
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
diffString = this.utils.diff('Error to match assertions', message) || '';
|
|
101
|
+
}
|
|
102
|
+
const { testHierarchy, location } = extractTestInfo(stack);
|
|
103
|
+
return {
|
|
104
|
+
pass: false,
|
|
105
|
+
message: () => `\n\n${this.utils.RECEIVED_COLOR(`● ${testHierarchy.join(' › ')}${location ? ` ${location}` : ''}`)}\n\n` +
|
|
106
|
+
this.utils.matcherHint(matcherName, undefined, undefined, options) +
|
|
107
|
+
'\n\n' +
|
|
108
|
+
diffString +
|
|
109
|
+
'\n\n' +
|
|
110
|
+
(stack
|
|
111
|
+
? this.utils.RECEIVED_COLOR(stack.split('\n').slice(1).join('\n'))
|
|
112
|
+
: ''),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const testHeader = error instanceof Error && error.stack
|
|
116
|
+
? (() => {
|
|
117
|
+
const { testHierarchy, location } = extractTestInfo(error.stack);
|
|
118
|
+
return `\n\n${this.utils.RECEIVED_COLOR(`● ${testHierarchy.join(' › ')}${location}`)}\n\n`;
|
|
119
|
+
})()
|
|
120
|
+
: '\n';
|
|
121
|
+
return {
|
|
122
|
+
pass,
|
|
123
|
+
message: () => testHeader +
|
|
124
|
+
this.utils.matcherHint(matcherName, undefined, undefined, options) +
|
|
125
|
+
'\n\n' +
|
|
126
|
+
(pass
|
|
127
|
+
? `Expected function not to throw ${this.utils.printExpected(errorType.name)}`
|
|
128
|
+
: this.promise
|
|
129
|
+
? this.utils.matcherErrorMessage(this.utils.matcherHint(matcherName, undefined, undefined, options), 'Expected promise to reject', 'Promise resolved successfully')
|
|
130
|
+
: this.utils.matcherErrorMessage(this.utils.matcherHint(matcherName, undefined, undefined, options), `Expected function to throw ${this.utils.printExpected(errorType.name)}`, `Received: ${this.utils.printReceived(error instanceof Error ? error.constructor.name : typeof error)}`)),
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
exports.toThrowType = toThrowType;
|
|
134
|
+
globals_1.expect.extend({ toThrowType: exports.toThrowType });
|
package/src/lib/types.js
ADDED