@vercel/kv2 0.0.1
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 +87 -0
- package/SKILL.md +65 -0
- package/dist/blob-format.d.ts +35 -0
- package/dist/blob-format.d.ts.map +1 -0
- package/dist/blob-format.js +91 -0
- package/dist/blob-format.js.map +1 -0
- package/dist/blob-store.d.ts +11 -0
- package/dist/blob-store.d.ts.map +1 -0
- package/dist/blob-store.js +32 -0
- package/dist/blob-store.js.map +1 -0
- package/dist/cache.d.ts +33 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +146 -0
- package/dist/cache.js.map +1 -0
- package/dist/cached-kv.d.ts +63 -0
- package/dist/cached-kv.d.ts.map +1 -0
- package/dist/cached-kv.js +891 -0
- package/dist/cached-kv.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +342 -0
- package/dist/cli.js.map +1 -0
- package/dist/create-kv.d.ts +86 -0
- package/dist/create-kv.d.ts.map +1 -0
- package/dist/create-kv.js +125 -0
- package/dist/create-kv.js.map +1 -0
- package/dist/disk-cache.d.ts.map +1 -0
- package/dist/disk-cache.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/indexed-kv.d.ts +44 -0
- package/dist/indexed-kv.d.ts.map +1 -0
- package/dist/indexed-kv.js +373 -0
- package/dist/indexed-kv.js.map +1 -0
- package/dist/manifest-log.d.ts +57 -0
- package/dist/manifest-log.d.ts.map +1 -0
- package/dist/manifest-log.js +128 -0
- package/dist/manifest-log.js.map +1 -0
- package/dist/memory-cache.d.ts +22 -0
- package/dist/memory-cache.d.ts.map +1 -0
- package/dist/memory-cache.js +90 -0
- package/dist/memory-cache.js.map +1 -0
- package/dist/proxy-cache.d.ts +40 -0
- package/dist/proxy-cache.d.ts.map +1 -0
- package/dist/proxy-cache.js +124 -0
- package/dist/proxy-cache.js.map +1 -0
- package/dist/readme.test.d.ts +9 -0
- package/dist/readme.test.d.ts.map +1 -0
- package/dist/readme.test.js +285 -0
- package/dist/readme.test.js.map +1 -0
- package/dist/schema/define-schema.d.ts +35 -0
- package/dist/schema/define-schema.d.ts.map +1 -0
- package/dist/schema/define-schema.js +70 -0
- package/dist/schema/define-schema.js.map +1 -0
- package/dist/schema/index.d.ts +4 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/key-builders.d.ts +40 -0
- package/dist/schema/key-builders.d.ts.map +1 -0
- package/dist/schema/key-builders.js +124 -0
- package/dist/schema/key-builders.js.map +1 -0
- package/dist/schema/schema-kv.d.ts +48 -0
- package/dist/schema/schema-kv.d.ts.map +1 -0
- package/dist/schema/schema-kv.js +96 -0
- package/dist/schema/schema-kv.js.map +1 -0
- package/dist/schema/tree.d.ts +14 -0
- package/dist/schema/tree.d.ts.map +1 -0
- package/dist/schema/tree.js +135 -0
- package/dist/schema/tree.js.map +1 -0
- package/dist/schema/types.d.ts +135 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +2 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/testing/core-tests.d.ts +30 -0
- package/dist/testing/core-tests.d.ts.map +1 -0
- package/dist/testing/core-tests.js +383 -0
- package/dist/testing/core-tests.js.map +1 -0
- package/dist/testing/create-kv-test-setup.d.ts +21 -0
- package/dist/testing/create-kv-test-setup.d.ts.map +1 -0
- package/dist/testing/create-kv-test-setup.js +25 -0
- package/dist/testing/create-kv-test-setup.js.map +1 -0
- package/dist/testing/debug-manifest.d.ts +2 -0
- package/dist/testing/debug-manifest.d.ts.map +1 -0
- package/dist/testing/debug-manifest.js +14 -0
- package/dist/testing/debug-manifest.js.map +1 -0
- package/dist/testing/fake-blob-store.d.ts +23 -0
- package/dist/testing/fake-blob-store.d.ts.map +1 -0
- package/dist/testing/fake-blob-store.js +158 -0
- package/dist/testing/fake-blob-store.js.map +1 -0
- package/dist/testing/fake-cache.d.ts +54 -0
- package/dist/testing/fake-cache.d.ts.map +1 -0
- package/dist/testing/fake-cache.js +137 -0
- package/dist/testing/fake-cache.js.map +1 -0
- package/dist/testing/index.d.ts +34 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +101 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/manifest-test-setup.d.ts +22 -0
- package/dist/testing/manifest-test-setup.d.ts.map +1 -0
- package/dist/testing/manifest-test-setup.js +43 -0
- package/dist/testing/manifest-test-setup.js.map +1 -0
- package/dist/testing/perf-test.d.ts +13 -0
- package/dist/testing/perf-test.d.ts.map +1 -0
- package/dist/testing/perf-test.js +101 -0
- package/dist/testing/perf-test.js.map +1 -0
- package/dist/testing/run-tests.d.ts +2 -0
- package/dist/testing/run-tests.d.ts.map +1 -0
- package/dist/testing/run-tests.js +141 -0
- package/dist/testing/run-tests.js.map +1 -0
- package/dist/testing/setup.d.ts +2 -0
- package/dist/testing/setup.d.ts.map +1 -0
- package/dist/testing/setup.js +3 -0
- package/dist/testing/setup.js.map +1 -0
- package/dist/testing/test-index.d.ts +28 -0
- package/dist/testing/test-index.d.ts.map +1 -0
- package/dist/testing/test-index.js +35 -0
- package/dist/testing/test-index.js.map +1 -0
- package/dist/testing/test-setup.d.ts +32 -0
- package/dist/testing/test-setup.d.ts.map +1 -0
- package/dist/testing/test-setup.js +72 -0
- package/dist/testing/test-setup.js.map +1 -0
- package/dist/testing/upstream-kv-test-setup.d.ts +30 -0
- package/dist/testing/upstream-kv-test-setup.d.ts.map +1 -0
- package/dist/testing/upstream-kv-test-setup.js +66 -0
- package/dist/testing/upstream-kv-test-setup.js.map +1 -0
- package/dist/testing/vitest-compat.d.ts +92 -0
- package/dist/testing/vitest-compat.d.ts.map +1 -0
- package/dist/testing/vitest-compat.js +601 -0
- package/dist/testing/vitest-compat.js.map +1 -0
- package/dist/tracing.d.ts +71 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +232 -0
- package/dist/tracing.js.map +1 -0
- package/dist/typed-kv.d.ts +120 -0
- package/dist/typed-kv.d.ts.map +1 -0
- package/dist/typed-kv.js +565 -0
- package/dist/typed-kv.js.map +1 -0
- package/dist/typed-upstream-kv.d.ts +17 -0
- package/dist/typed-upstream-kv.d.ts.map +1 -0
- package/dist/typed-upstream-kv.js +38 -0
- package/dist/typed-upstream-kv.js.map +1 -0
- package/dist/types.d.ts +199 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/dist/upstream-kv.d.ts +84 -0
- package/dist/upstream-kv.d.ts.map +1 -0
- package/dist/upstream-kv.js +375 -0
- package/dist/upstream-kv.js.map +1 -0
- package/docs/api-reference.md +222 -0
- package/docs/caching.md +60 -0
- package/docs/cli.md +123 -0
- package/docs/copy-on-write-branches.md +98 -0
- package/docs/getting-started.md +61 -0
- package/docs/indexes.md +122 -0
- package/docs/iterating-and-pagination.md +93 -0
- package/docs/metadata.md +82 -0
- package/docs/optimistic-locking.md +72 -0
- package/docs/schema-and-trees.md +222 -0
- package/docs/streaming.md +61 -0
- package/docs/testing-and-tracing.md +141 -0
- package/docs/typed-stores.md +68 -0
- package/package.json +63 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal vitest compatibility layer for running tests outside vitest.
|
|
3
|
+
* Provides describe, it, expect, beforeEach, afterEach, beforeAll, afterAll, vi
|
|
4
|
+
*
|
|
5
|
+
* Supports test context: each test gets an isolated context object that hooks
|
|
6
|
+
* can populate. This enables concurrent test execution without shared state.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Test context - an isolated object for each test execution.
|
|
10
|
+
* Hooks populate this, tests consume it.
|
|
11
|
+
*/
|
|
12
|
+
export interface TestContext {
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
type TestFn = (ctx: TestContext) => Promise<void> | void;
|
|
16
|
+
type HookFn = (ctx: TestContext) => Promise<void> | void;
|
|
17
|
+
interface TestCase {
|
|
18
|
+
name: string;
|
|
19
|
+
fn: TestFn;
|
|
20
|
+
suite: string[];
|
|
21
|
+
}
|
|
22
|
+
export declare function describe(name: string, fn: () => void): void;
|
|
23
|
+
export declare function it(name: string, fn: TestFn): void;
|
|
24
|
+
export declare const test: typeof it;
|
|
25
|
+
export declare function beforeEach(fn: HookFn): void;
|
|
26
|
+
export declare function afterEach(fn: HookFn): void;
|
|
27
|
+
export declare function beforeAll(fn: HookFn): void;
|
|
28
|
+
export declare function afterAll(fn: HookFn): void;
|
|
29
|
+
type Matchers<T> = {
|
|
30
|
+
toBe(expected: T, message?: string): void;
|
|
31
|
+
toEqual(expected: unknown, message?: string): void;
|
|
32
|
+
toBeTruthy(message?: string): void;
|
|
33
|
+
toBeFalsy(message?: string): void;
|
|
34
|
+
toBeNull(message?: string): void;
|
|
35
|
+
toBeUndefined(message?: string): void;
|
|
36
|
+
toBeDefined(message?: string): void;
|
|
37
|
+
toBeInstanceOf(expected: new (...args: any[]) => unknown, message?: string): void;
|
|
38
|
+
toBeGreaterThan(expected: number, message?: string): void;
|
|
39
|
+
toBeGreaterThanOrEqual(expected: number, message?: string): void;
|
|
40
|
+
toBeLessThan(expected: number, message?: string): void;
|
|
41
|
+
toBeLessThanOrEqual(expected: number, message?: string): void;
|
|
42
|
+
toContain(expected: unknown, message?: string): void;
|
|
43
|
+
toHaveLength(expected: number, message?: string): void;
|
|
44
|
+
toMatch(expected: RegExp | string, message?: string): void;
|
|
45
|
+
toThrow(expected?: string | RegExp | Error): void;
|
|
46
|
+
toHaveBeenCalled(): void;
|
|
47
|
+
toHaveBeenCalledTimes(expected: number): void;
|
|
48
|
+
toHaveBeenCalledWith(...args: unknown[]): void;
|
|
49
|
+
resolves: Matchers<Awaited<T>>;
|
|
50
|
+
rejects: Matchers<unknown>;
|
|
51
|
+
not: Matchers<T>;
|
|
52
|
+
};
|
|
53
|
+
export declare function expect<T>(actual: T, message?: string): Matchers<T>;
|
|
54
|
+
export declare namespace expect {
|
|
55
|
+
var any: (ctor: new (...args: any[]) => unknown) => {
|
|
56
|
+
asymmetricMatch: (actual: unknown) => boolean;
|
|
57
|
+
toString: () => string;
|
|
58
|
+
};
|
|
59
|
+
var objectContaining: (expected: Record<string, unknown>) => {
|
|
60
|
+
asymmetricMatch: (actual: unknown) => boolean;
|
|
61
|
+
toString: () => string;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export interface TestResult {
|
|
65
|
+
name: string;
|
|
66
|
+
suite: string[];
|
|
67
|
+
passed: boolean;
|
|
68
|
+
error?: string;
|
|
69
|
+
duration: number;
|
|
70
|
+
}
|
|
71
|
+
export interface RunResult {
|
|
72
|
+
passed: number;
|
|
73
|
+
failed: number;
|
|
74
|
+
total: number;
|
|
75
|
+
duration: number;
|
|
76
|
+
results: TestResult[];
|
|
77
|
+
}
|
|
78
|
+
export interface RunTestsOptions {
|
|
79
|
+
filter?: string | RegExp;
|
|
80
|
+
onProgress?: (result: TestResult) => void;
|
|
81
|
+
/** Number of tests to run concurrently (default: 1) */
|
|
82
|
+
concurrency?: number;
|
|
83
|
+
/** Callback for keep-alive messages during slow tests */
|
|
84
|
+
onKeepAlive?: (testName: string, elapsedMs: number) => void;
|
|
85
|
+
/** Interval in ms for keep-alive messages (default: 5000) */
|
|
86
|
+
keepAliveInterval?: number;
|
|
87
|
+
}
|
|
88
|
+
export declare function runTests(filterOrOptions?: string | RegExp | RunTestsOptions, onProgress?: (result: TestResult) => void): Promise<RunResult>;
|
|
89
|
+
export declare function resetTestState(): void;
|
|
90
|
+
export declare function getRegisteredTests(): TestCase[];
|
|
91
|
+
export {};
|
|
92
|
+
//# sourceMappingURL=vitest-compat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest-compat.d.ts","sourceRoot":"","sources":["../../src/testing/vitest-compat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal vitest compatibility layer for running tests outside vitest.
|
|
3
|
+
* Provides describe, it, expect, beforeEach, afterEach, beforeAll, afterAll, vi
|
|
4
|
+
*
|
|
5
|
+
* Supports test context: each test gets an isolated context object that hooks
|
|
6
|
+
* can populate. This enables concurrent test execution without shared state.
|
|
7
|
+
*/
|
|
8
|
+
// Global state
|
|
9
|
+
const tests = [];
|
|
10
|
+
const suites = new Map();
|
|
11
|
+
let currentSuite = [];
|
|
12
|
+
function getSuiteKey(path) {
|
|
13
|
+
return path.join(" > ");
|
|
14
|
+
}
|
|
15
|
+
function getOrCreateSuite(path) {
|
|
16
|
+
const key = getSuiteKey(path);
|
|
17
|
+
const existing = suites.get(key);
|
|
18
|
+
if (existing) {
|
|
19
|
+
return existing;
|
|
20
|
+
}
|
|
21
|
+
const newSuite = {
|
|
22
|
+
name: path[path.length - 1] || "root",
|
|
23
|
+
parent: path.slice(0, -1),
|
|
24
|
+
beforeAll: [],
|
|
25
|
+
afterAll: [],
|
|
26
|
+
beforeEach: [],
|
|
27
|
+
afterEach: [],
|
|
28
|
+
};
|
|
29
|
+
suites.set(key, newSuite);
|
|
30
|
+
return newSuite;
|
|
31
|
+
}
|
|
32
|
+
export function describe(name, fn) {
|
|
33
|
+
const previousSuite = currentSuite;
|
|
34
|
+
currentSuite = [...currentSuite, name];
|
|
35
|
+
getOrCreateSuite(currentSuite);
|
|
36
|
+
fn();
|
|
37
|
+
currentSuite = previousSuite;
|
|
38
|
+
}
|
|
39
|
+
export function it(name, fn) {
|
|
40
|
+
tests.push({
|
|
41
|
+
name,
|
|
42
|
+
fn,
|
|
43
|
+
suite: [...currentSuite],
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
export const test = it;
|
|
47
|
+
export function beforeEach(fn) {
|
|
48
|
+
const suite = getOrCreateSuite(currentSuite);
|
|
49
|
+
suite.beforeEach.push(fn);
|
|
50
|
+
}
|
|
51
|
+
export function afterEach(fn) {
|
|
52
|
+
const suite = getOrCreateSuite(currentSuite);
|
|
53
|
+
suite.afterEach.push(fn);
|
|
54
|
+
}
|
|
55
|
+
export function beforeAll(fn) {
|
|
56
|
+
const suite = getOrCreateSuite(currentSuite);
|
|
57
|
+
suite.beforeAll.push(fn);
|
|
58
|
+
}
|
|
59
|
+
export function afterAll(fn) {
|
|
60
|
+
const suite = getOrCreateSuite(currentSuite);
|
|
61
|
+
suite.afterAll.push(fn);
|
|
62
|
+
}
|
|
63
|
+
function deepEqual(a, b) {
|
|
64
|
+
if (a === b)
|
|
65
|
+
return true;
|
|
66
|
+
if (a === null || b === null)
|
|
67
|
+
return a === b;
|
|
68
|
+
if (typeof a !== typeof b)
|
|
69
|
+
return false;
|
|
70
|
+
if (Buffer.isBuffer(a) && Buffer.isBuffer(b)) {
|
|
71
|
+
return a.equals(b);
|
|
72
|
+
}
|
|
73
|
+
if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
|
|
74
|
+
const aArr = new Uint8Array(a.buffer, a.byteOffset, a.byteLength);
|
|
75
|
+
const bArr = new Uint8Array(b.buffer, b.byteOffset, b.byteLength);
|
|
76
|
+
if (aArr.length !== bArr.length)
|
|
77
|
+
return false;
|
|
78
|
+
for (let i = 0; i < aArr.length; i++) {
|
|
79
|
+
if (aArr[i] !== bArr[i])
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
85
|
+
if (a.length !== b.length)
|
|
86
|
+
return false;
|
|
87
|
+
for (let i = 0; i < a.length; i++) {
|
|
88
|
+
if (!deepEqual(a[i], b[i]))
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
94
|
+
const aKeys = Object.keys(a);
|
|
95
|
+
const bKeys = Object.keys(b);
|
|
96
|
+
if (aKeys.length !== bKeys.length)
|
|
97
|
+
return false;
|
|
98
|
+
for (const key of aKeys) {
|
|
99
|
+
if (!deepEqual(a[key], b[key]))
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
function formatValue(value) {
|
|
107
|
+
if (Buffer.isBuffer(value)) {
|
|
108
|
+
return `Buffer<${value.toString("hex").slice(0, 20)}${value.length > 10 ? "..." : ""}>`;
|
|
109
|
+
}
|
|
110
|
+
if (typeof value === "string" && value.length > 100) {
|
|
111
|
+
return JSON.stringify(`${value.slice(0, 100)}...`);
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
return JSON.stringify(value);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return String(value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function createMatchers(actual, isNot = false, isAsync = null, customMessage) {
|
|
121
|
+
const assert = (condition, message, inlineMsg) => {
|
|
122
|
+
const pass = isNot ? !condition : condition;
|
|
123
|
+
if (!pass) {
|
|
124
|
+
const msg = customMessage || inlineMsg || message;
|
|
125
|
+
throw new Error(isNot ? `Expected NOT: ${msg}` : msg);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const matchers = {
|
|
129
|
+
toBe(expected, message) {
|
|
130
|
+
assert(actual === expected, `Expected ${formatValue(expected)}, got ${formatValue(actual)}`, message);
|
|
131
|
+
},
|
|
132
|
+
toEqual(expected, message) {
|
|
133
|
+
assert(deepEqual(actual, expected), `Expected ${formatValue(expected)}, got ${formatValue(actual)}`, message);
|
|
134
|
+
},
|
|
135
|
+
toBeTruthy() {
|
|
136
|
+
assert(!!actual, `Expected truthy, got ${formatValue(actual)}`);
|
|
137
|
+
},
|
|
138
|
+
toBeFalsy() {
|
|
139
|
+
assert(!actual, `Expected falsy, got ${formatValue(actual)}`);
|
|
140
|
+
},
|
|
141
|
+
toBeNull() {
|
|
142
|
+
assert(actual === null, `Expected null, got ${formatValue(actual)}`);
|
|
143
|
+
},
|
|
144
|
+
toBeUndefined() {
|
|
145
|
+
assert(actual === undefined, `Expected undefined, got ${formatValue(actual)}`);
|
|
146
|
+
},
|
|
147
|
+
toBeDefined() {
|
|
148
|
+
assert(actual !== undefined, `Expected defined, got ${formatValue(actual)}`);
|
|
149
|
+
},
|
|
150
|
+
toBeInstanceOf(expected) {
|
|
151
|
+
assert(actual instanceof expected, `Expected instance of ${expected.name}, got ${formatValue(actual)}`);
|
|
152
|
+
},
|
|
153
|
+
toBeGreaterThan(expected) {
|
|
154
|
+
assert(actual > expected, `Expected ${formatValue(actual)} > ${expected}`);
|
|
155
|
+
},
|
|
156
|
+
toBeGreaterThanOrEqual(expected) {
|
|
157
|
+
assert(actual >= expected, `Expected ${formatValue(actual)} >= ${expected}`);
|
|
158
|
+
},
|
|
159
|
+
toBeLessThan(expected) {
|
|
160
|
+
assert(actual < expected, `Expected ${formatValue(actual)} < ${expected}`);
|
|
161
|
+
},
|
|
162
|
+
toBeLessThanOrEqual(expected) {
|
|
163
|
+
assert(actual <= expected, `Expected ${formatValue(actual)} <= ${expected}`);
|
|
164
|
+
},
|
|
165
|
+
toContain(expected) {
|
|
166
|
+
if (typeof actual === "string") {
|
|
167
|
+
assert(actual.includes(expected), `Expected "${actual}" to contain "${expected}"`);
|
|
168
|
+
}
|
|
169
|
+
else if (Array.isArray(actual)) {
|
|
170
|
+
assert(actual.some((item) => deepEqual(item, expected)), `Expected array to contain ${formatValue(expected)}`);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
throw new Error("toContain only works with strings and arrays");
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
toHaveLength(expected) {
|
|
177
|
+
const len = actual.length;
|
|
178
|
+
assert(len === expected, `Expected length ${expected}, got ${len}`);
|
|
179
|
+
},
|
|
180
|
+
toMatch(expected) {
|
|
181
|
+
const regex = typeof expected === "string" ? new RegExp(expected) : expected;
|
|
182
|
+
assert(regex.test(actual), `Expected "${actual}" to match ${regex}`);
|
|
183
|
+
},
|
|
184
|
+
toThrow(expected) {
|
|
185
|
+
let threw = false;
|
|
186
|
+
let error;
|
|
187
|
+
try {
|
|
188
|
+
actual();
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
threw = true;
|
|
192
|
+
error = e;
|
|
193
|
+
}
|
|
194
|
+
assert(threw, "Expected function to throw");
|
|
195
|
+
if (expected !== undefined && error instanceof Error) {
|
|
196
|
+
if (typeof expected === "string") {
|
|
197
|
+
assert(error.message.includes(expected), `Expected error message to include "${expected}", got "${error.message}"`);
|
|
198
|
+
}
|
|
199
|
+
else if (expected instanceof RegExp) {
|
|
200
|
+
assert(expected.test(error.message), `Expected error message to match ${expected}, got "${error.message}"`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
toHaveBeenCalled() {
|
|
205
|
+
const mock = actual;
|
|
206
|
+
assert(mock.mock?.calls?.length > 0, "Expected function to have been called");
|
|
207
|
+
},
|
|
208
|
+
toHaveBeenCalledTimes(expected) {
|
|
209
|
+
const mock = actual;
|
|
210
|
+
const calls = mock.mock?.calls?.length ?? 0;
|
|
211
|
+
assert(calls === expected, `Expected ${expected} calls, got ${calls}`);
|
|
212
|
+
},
|
|
213
|
+
toHaveBeenCalledWith(...args) {
|
|
214
|
+
const mock = actual;
|
|
215
|
+
const calls = mock.mock?.calls ?? [];
|
|
216
|
+
const found = calls.some((call) => deepEqual(call, args));
|
|
217
|
+
assert(found, `Expected function to have been called with ${formatValue(args)}`);
|
|
218
|
+
},
|
|
219
|
+
get resolves() {
|
|
220
|
+
return {
|
|
221
|
+
async toBe(expected) {
|
|
222
|
+
const result = await actual;
|
|
223
|
+
createMatchers(result, isNot).toBe(expected);
|
|
224
|
+
},
|
|
225
|
+
async toEqual(expected) {
|
|
226
|
+
const result = await actual;
|
|
227
|
+
createMatchers(result, isNot).toEqual(expected);
|
|
228
|
+
},
|
|
229
|
+
async toBeUndefined() {
|
|
230
|
+
const result = await actual;
|
|
231
|
+
createMatchers(result, isNot).toBeUndefined();
|
|
232
|
+
},
|
|
233
|
+
async not() {
|
|
234
|
+
return createMatchers(actual, true, "resolves");
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
},
|
|
238
|
+
get rejects() {
|
|
239
|
+
return {
|
|
240
|
+
async toThrow(expected) {
|
|
241
|
+
let threw = false;
|
|
242
|
+
let error;
|
|
243
|
+
try {
|
|
244
|
+
await actual;
|
|
245
|
+
}
|
|
246
|
+
catch (e) {
|
|
247
|
+
threw = true;
|
|
248
|
+
error = e;
|
|
249
|
+
}
|
|
250
|
+
assert(threw, "Expected promise to reject");
|
|
251
|
+
if (expected !== undefined && error instanceof Error) {
|
|
252
|
+
if (typeof expected === "string") {
|
|
253
|
+
assert(error.message.includes(expected), `Expected error to include "${expected}"`);
|
|
254
|
+
}
|
|
255
|
+
else if (expected instanceof RegExp) {
|
|
256
|
+
assert(expected.test(error.message), `Expected error to match ${expected}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
},
|
|
262
|
+
get not() {
|
|
263
|
+
return createMatchers(actual, !isNot, isAsync, customMessage);
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
return matchers;
|
|
267
|
+
}
|
|
268
|
+
export function expect(actual, message) {
|
|
269
|
+
return createMatchers(actual, false, null, message);
|
|
270
|
+
}
|
|
271
|
+
// Add expect.any() helper
|
|
272
|
+
// biome-ignore lint/suspicious/noExplicitAny: Constructor types require any for compatibility
|
|
273
|
+
expect.any = (ctor) => ({
|
|
274
|
+
asymmetricMatch: (actual) => actual instanceof ctor,
|
|
275
|
+
toString: () => `Any<${ctor.name}>`,
|
|
276
|
+
});
|
|
277
|
+
expect.objectContaining = (expected) => ({
|
|
278
|
+
asymmetricMatch: (actual) => {
|
|
279
|
+
if (typeof actual !== "object" || actual === null)
|
|
280
|
+
return false;
|
|
281
|
+
for (const [key, value] of Object.entries(expected)) {
|
|
282
|
+
if (!deepEqual(actual[key], value)) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return true;
|
|
287
|
+
},
|
|
288
|
+
toString: () => `ObjectContaining(${JSON.stringify(expected)})`,
|
|
289
|
+
});
|
|
290
|
+
function createMockFn(impl) {
|
|
291
|
+
let defaultImpl = impl;
|
|
292
|
+
let returnValue;
|
|
293
|
+
const returnValueOnce = [];
|
|
294
|
+
const implOnce = [];
|
|
295
|
+
const mock = {
|
|
296
|
+
calls: [],
|
|
297
|
+
results: [],
|
|
298
|
+
};
|
|
299
|
+
const fn = ((...args) => {
|
|
300
|
+
mock.calls.push(args);
|
|
301
|
+
try {
|
|
302
|
+
let result;
|
|
303
|
+
if (implOnce.length > 0) {
|
|
304
|
+
result = implOnce.shift()?.(...args);
|
|
305
|
+
}
|
|
306
|
+
else if (returnValueOnce.length > 0) {
|
|
307
|
+
result = returnValueOnce[0];
|
|
308
|
+
returnValueOnce.shift();
|
|
309
|
+
}
|
|
310
|
+
else if (returnValue !== undefined) {
|
|
311
|
+
result = returnValue;
|
|
312
|
+
}
|
|
313
|
+
else if (defaultImpl) {
|
|
314
|
+
result = defaultImpl(...args);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
result = undefined;
|
|
318
|
+
}
|
|
319
|
+
mock.results.push({ type: "return", value: result });
|
|
320
|
+
return result;
|
|
321
|
+
}
|
|
322
|
+
catch (e) {
|
|
323
|
+
mock.results.push({ type: "throw", value: e });
|
|
324
|
+
throw e;
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
fn.mock = mock;
|
|
328
|
+
fn.mockClear = () => {
|
|
329
|
+
mock.calls = [];
|
|
330
|
+
mock.results = [];
|
|
331
|
+
};
|
|
332
|
+
fn.mockReset = () => {
|
|
333
|
+
fn.mockClear();
|
|
334
|
+
defaultImpl = undefined;
|
|
335
|
+
returnValue = undefined;
|
|
336
|
+
returnValueOnce.length = 0;
|
|
337
|
+
implOnce.length = 0;
|
|
338
|
+
};
|
|
339
|
+
fn.mockReturnValue = (value) => {
|
|
340
|
+
returnValue = value;
|
|
341
|
+
return fn;
|
|
342
|
+
};
|
|
343
|
+
fn.mockReturnValueOnce = (value) => {
|
|
344
|
+
returnValueOnce.push(value);
|
|
345
|
+
return fn;
|
|
346
|
+
};
|
|
347
|
+
fn.mockResolvedValue = (value) => {
|
|
348
|
+
returnValue = Promise.resolve(value);
|
|
349
|
+
return fn;
|
|
350
|
+
};
|
|
351
|
+
fn.mockResolvedValueOnce = (value) => {
|
|
352
|
+
returnValueOnce.push(Promise.resolve(value));
|
|
353
|
+
return fn;
|
|
354
|
+
};
|
|
355
|
+
fn.mockRejectedValue = (value) => {
|
|
356
|
+
returnValue = Promise.reject(value);
|
|
357
|
+
return fn;
|
|
358
|
+
};
|
|
359
|
+
fn.mockRejectedValueOnce = (value) => {
|
|
360
|
+
returnValueOnce.push(Promise.reject(value));
|
|
361
|
+
return fn;
|
|
362
|
+
};
|
|
363
|
+
fn.mockImplementation = (newImpl) => {
|
|
364
|
+
defaultImpl = newImpl;
|
|
365
|
+
return fn;
|
|
366
|
+
};
|
|
367
|
+
fn.mockImplementationOnce = (newImpl) => {
|
|
368
|
+
implOnce.push(newImpl);
|
|
369
|
+
return fn;
|
|
370
|
+
};
|
|
371
|
+
return fn;
|
|
372
|
+
}
|
|
373
|
+
const vi = {
|
|
374
|
+
fn: createMockFn,
|
|
375
|
+
clearAllMocks() {
|
|
376
|
+
// No-op in this simple implementation
|
|
377
|
+
},
|
|
378
|
+
mock(_module, _factory) {
|
|
379
|
+
// Module mocking not supported in this simple implementation
|
|
380
|
+
console.warn("vi.mock() is not supported in vitest-compat");
|
|
381
|
+
},
|
|
382
|
+
spyOn(obj, method) {
|
|
383
|
+
const original = obj[method];
|
|
384
|
+
const mock = createMockFn();
|
|
385
|
+
mock.mockImplementation((...args) => {
|
|
386
|
+
if (typeof original === "function") {
|
|
387
|
+
return original.apply(obj, args);
|
|
388
|
+
}
|
|
389
|
+
return undefined;
|
|
390
|
+
});
|
|
391
|
+
mock.mockRestore = () => {
|
|
392
|
+
obj[method] = original;
|
|
393
|
+
};
|
|
394
|
+
obj[method] = mock;
|
|
395
|
+
return mock;
|
|
396
|
+
},
|
|
397
|
+
useFakeTimers() {
|
|
398
|
+
// Fake timers not fully supported - tests using this should be skipped
|
|
399
|
+
console.warn("vi.useFakeTimers() is not fully supported in vitest-compat");
|
|
400
|
+
},
|
|
401
|
+
useRealTimers() {
|
|
402
|
+
// No-op
|
|
403
|
+
},
|
|
404
|
+
advanceTimersByTimeAsync(_ms) {
|
|
405
|
+
return Promise.resolve();
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
export async function runTests(filterOrOptions, onProgress) {
|
|
409
|
+
// Handle both old and new signatures
|
|
410
|
+
let filter;
|
|
411
|
+
let progressCallback = onProgress;
|
|
412
|
+
let concurrency = 1;
|
|
413
|
+
let keepAliveCallback;
|
|
414
|
+
let keepAliveInterval = 5000;
|
|
415
|
+
if (filterOrOptions &&
|
|
416
|
+
typeof filterOrOptions === "object" &&
|
|
417
|
+
!("test" in filterOrOptions)) {
|
|
418
|
+
// New options object
|
|
419
|
+
filter = filterOrOptions.filter;
|
|
420
|
+
progressCallback = filterOrOptions.onProgress;
|
|
421
|
+
concurrency = filterOrOptions.concurrency ?? 1;
|
|
422
|
+
keepAliveCallback = filterOrOptions.onKeepAlive;
|
|
423
|
+
keepAliveInterval = filterOrOptions.keepAliveInterval ?? 5000;
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
// Old signature: filter, onProgress
|
|
427
|
+
filter = filterOrOptions;
|
|
428
|
+
}
|
|
429
|
+
const results = [];
|
|
430
|
+
const startTime = Date.now();
|
|
431
|
+
const ranBeforeAll = new Set();
|
|
432
|
+
const ranAfterAll = new Set();
|
|
433
|
+
// Filter tests
|
|
434
|
+
const testsToRun = filter
|
|
435
|
+
? tests.filter((t) => {
|
|
436
|
+
const fullName = [...t.suite, t.name].join(" > ");
|
|
437
|
+
return typeof filter === "string"
|
|
438
|
+
? fullName.includes(filter)
|
|
439
|
+
: filter.test(fullName);
|
|
440
|
+
})
|
|
441
|
+
: tests;
|
|
442
|
+
// Collect all hooks for a test's suite hierarchy
|
|
443
|
+
const getHooks = (suitePath) => {
|
|
444
|
+
const beforeAllHooks = [];
|
|
445
|
+
const afterAllHooks = [];
|
|
446
|
+
const beforeEachHooks = [];
|
|
447
|
+
const afterEachHooks = [];
|
|
448
|
+
for (let i = 0; i <= suitePath.length; i++) {
|
|
449
|
+
const path = suitePath.slice(0, i);
|
|
450
|
+
const suite = suites.get(getSuiteKey(path));
|
|
451
|
+
if (suite) {
|
|
452
|
+
beforeAllHooks.push(...suite.beforeAll);
|
|
453
|
+
afterAllHooks.push(...suite.afterAll);
|
|
454
|
+
beforeEachHooks.push(...suite.beforeEach);
|
|
455
|
+
afterEachHooks.push(...suite.afterEach);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return { beforeAllHooks, afterAllHooks, beforeEachHooks, afterEachHooks };
|
|
459
|
+
};
|
|
460
|
+
// Suite-level shared context (populated by beforeAll, shared across tests in suite)
|
|
461
|
+
const suiteContexts = new Map();
|
|
462
|
+
// Run all beforeAll hooks upfront (sequentially to maintain order)
|
|
463
|
+
for (const testCase of testsToRun) {
|
|
464
|
+
for (let i = 0; i <= testCase.suite.length; i++) {
|
|
465
|
+
const path = testCase.suite.slice(0, i);
|
|
466
|
+
const key = getSuiteKey(path);
|
|
467
|
+
if (!ranBeforeAll.has(key)) {
|
|
468
|
+
ranBeforeAll.add(key);
|
|
469
|
+
// Create suite context
|
|
470
|
+
const suiteCtx = {};
|
|
471
|
+
suiteContexts.set(key, suiteCtx);
|
|
472
|
+
const suite = suites.get(key);
|
|
473
|
+
if (suite) {
|
|
474
|
+
for (const hook of suite.beforeAll) {
|
|
475
|
+
await hook(suiteCtx);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
// Get combined suite context for a test (merges all parent suite contexts)
|
|
482
|
+
const getSuiteContext = (suitePath) => {
|
|
483
|
+
const combined = {};
|
|
484
|
+
for (let i = 0; i <= suitePath.length; i++) {
|
|
485
|
+
const path = suitePath.slice(0, i);
|
|
486
|
+
const key = getSuiteKey(path);
|
|
487
|
+
const suiteCtx = suiteContexts.get(key);
|
|
488
|
+
if (suiteCtx) {
|
|
489
|
+
Object.assign(combined, suiteCtx);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return combined;
|
|
493
|
+
};
|
|
494
|
+
// Run a single test with isolated context
|
|
495
|
+
const runTest = async (testCase) => {
|
|
496
|
+
const testStart = Date.now();
|
|
497
|
+
const { beforeEachHooks, afterEachHooks } = getHooks(testCase.suite);
|
|
498
|
+
const fullTestName = [...testCase.suite, testCase.name].join(" > ");
|
|
499
|
+
// Set up keep-alive timer for slow tests
|
|
500
|
+
let keepAliveTimer = null;
|
|
501
|
+
if (keepAliveCallback) {
|
|
502
|
+
keepAliveTimer = setInterval(() => {
|
|
503
|
+
const elapsed = Date.now() - testStart;
|
|
504
|
+
keepAliveCallback?.(fullTestName, elapsed);
|
|
505
|
+
}, keepAliveInterval);
|
|
506
|
+
}
|
|
507
|
+
// Create isolated context for this test, seeded with suite context
|
|
508
|
+
const ctx = { ...getSuiteContext(testCase.suite) };
|
|
509
|
+
try {
|
|
510
|
+
// Run beforeEach hooks - they populate the context
|
|
511
|
+
for (const hook of beforeEachHooks) {
|
|
512
|
+
await hook(ctx);
|
|
513
|
+
}
|
|
514
|
+
// Run test with context
|
|
515
|
+
await testCase.fn(ctx);
|
|
516
|
+
// Run afterEach hooks
|
|
517
|
+
for (const hook of afterEachHooks) {
|
|
518
|
+
await hook(ctx);
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
name: testCase.name,
|
|
522
|
+
suite: testCase.suite,
|
|
523
|
+
passed: true,
|
|
524
|
+
duration: Date.now() - testStart,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
catch (err) {
|
|
528
|
+
return {
|
|
529
|
+
name: testCase.name,
|
|
530
|
+
suite: testCase.suite,
|
|
531
|
+
passed: false,
|
|
532
|
+
error: err instanceof Error ? err.message : String(err),
|
|
533
|
+
duration: Date.now() - testStart,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
finally {
|
|
537
|
+
// Clean up keep-alive timer
|
|
538
|
+
if (keepAliveTimer) {
|
|
539
|
+
clearInterval(keepAliveTimer);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
// Run tests with concurrency pool - results print immediately as tests complete
|
|
544
|
+
let nextIndex = 0;
|
|
545
|
+
const getNextTest = () => {
|
|
546
|
+
if (nextIndex < testsToRun.length) {
|
|
547
|
+
return testsToRun[nextIndex++];
|
|
548
|
+
}
|
|
549
|
+
return undefined;
|
|
550
|
+
};
|
|
551
|
+
const worker = async () => {
|
|
552
|
+
let testCase = getNextTest();
|
|
553
|
+
while (testCase !== undefined) {
|
|
554
|
+
const result = await runTest(testCase);
|
|
555
|
+
results.push(result);
|
|
556
|
+
progressCallback?.(result);
|
|
557
|
+
testCase = getNextTest();
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
// Start up to `concurrency` workers
|
|
561
|
+
const workers = [];
|
|
562
|
+
for (let i = 0; i < Math.min(concurrency, testsToRun.length); i++) {
|
|
563
|
+
workers.push(worker());
|
|
564
|
+
}
|
|
565
|
+
await Promise.all(workers);
|
|
566
|
+
// Run afterAll hooks with suite context
|
|
567
|
+
for (const [key, suite] of suites) {
|
|
568
|
+
if (!ranAfterAll.has(key)) {
|
|
569
|
+
ranAfterAll.add(key);
|
|
570
|
+
const suiteCtx = suiteContexts.get(key) ?? {};
|
|
571
|
+
for (const hook of suite.afterAll) {
|
|
572
|
+
try {
|
|
573
|
+
await hook(suiteCtx);
|
|
574
|
+
}
|
|
575
|
+
catch (err) {
|
|
576
|
+
console.error(`afterAll hook failed for ${key}:`, err);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
const passed = results.filter((r) => r.passed).length;
|
|
582
|
+
const failed = results.filter((r) => !r.passed).length;
|
|
583
|
+
return {
|
|
584
|
+
passed,
|
|
585
|
+
failed,
|
|
586
|
+
total: results.length,
|
|
587
|
+
duration: Date.now() - startTime,
|
|
588
|
+
results,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
// Reset state (useful between test file loads)
|
|
592
|
+
export function resetTestState() {
|
|
593
|
+
tests.length = 0;
|
|
594
|
+
suites.clear();
|
|
595
|
+
currentSuite = [];
|
|
596
|
+
}
|
|
597
|
+
// Export for test file registration
|
|
598
|
+
export function getRegisteredTests() {
|
|
599
|
+
return tests;
|
|
600
|
+
}
|
|
601
|
+
//# sourceMappingURL=vitest-compat.js.map
|