@ereo/testing 0.1.23 → 0.1.25
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/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +351 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
* Makes testing loaders, actions, middleware, and components trivial.
|
|
6
6
|
*/
|
|
7
7
|
export { createTestContext, createContextFactory, type TestContextOptions, type TestContext, } from './context';
|
|
8
|
-
export { testLoader, createLoaderTester, type LoaderTestOptions, type LoaderTestResult, } from './loader';
|
|
9
|
-
export { testAction, createActionTester, type ActionTestOptions, type ActionTestResult, } from './action';
|
|
10
|
-
export { testMiddleware, createMiddlewareTester, type MiddlewareTestOptions, type MiddlewareTestResult, } from './middleware';
|
|
11
|
-
export { createMockRequest, createFormRequest, createMockFormData, createMockHeaders, parseJsonResponse, parseTextResponse, type MockRequestOptions, } from './request';
|
|
12
|
-
export { renderRoute, createRouteRenderer, type RenderRouteOptions, type RenderResult, } from './render';
|
|
13
|
-
export { assertRedirect, assertJson, assertStatus, assertHeaders, assertCookies, type AssertionOptions, } from './assertions';
|
|
14
|
-
export { createTestServer, type TestServer, type TestServerOptions, } from './server';
|
|
15
|
-
export { snapshotLoader, snapshotAction, type SnapshotOptions, } from './snapshot';
|
|
8
|
+
export { testLoader, createLoaderTester, testLoadersParallel, testLoaderMatrix, testLoaderError, type LoaderTestOptions, type LoaderTestResult, } from './loader';
|
|
9
|
+
export { testAction, createActionTester, testActionMatrix, testActionError, testActionWithFile, type ActionTestOptions, type ActionTestResult, } from './action';
|
|
10
|
+
export { testMiddleware, createMiddlewareTester, testMiddlewareChain, testMiddlewareMatrix, testMiddlewareError, testMiddlewareContext, type MiddlewareTestOptions, type MiddlewareTestResult, } from './middleware';
|
|
11
|
+
export { createMockRequest, createFormRequest, createMockFormData, createMockHeaders, createMockFile, parseJsonResponse, parseTextResponse, extractCookies, type MockRequestOptions, } from './request';
|
|
12
|
+
export { renderRoute, createRouteRenderer, renderComponent, renderRouteMatrix, testRouteRenders, getRouteMeta, type RenderRouteOptions, type RenderResult, } from './render';
|
|
13
|
+
export { assertRedirect, assertJson, assertStatus, assertHeaders, assertCookies, assertThrows, assertSchema, type AssertionOptions, } from './assertions';
|
|
14
|
+
export { createTestServer, createMockServer, type TestServer, type TestServerOptions, } from './server';
|
|
15
|
+
export { snapshotLoader, snapshotAction, createSnapshotMatrix, commonReplacers, applyReplacements, deterministicSnapshot, type SnapshotOptions, } from './snapshot';
|
|
16
16
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,WAAW,GACjB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,kBAAkB,GACxB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,cAAc,EACd,UAAU,EACV,YAAY,EACZ,aAAa,EACb,aAAa,EACb,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,gBAAgB,EAChB,KAAK,UAAU,EACf,KAAK,iBAAiB,GACvB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,cAAc,EACd,cAAc,EACd,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,WAAW,GACjB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,KAAK,kBAAkB,GACxB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,cAAc,EACd,UAAU,EACV,YAAY,EACZ,aAAa,EACb,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,UAAU,EACf,KAAK,iBAAiB,GACvB,MAAM,UAAU,CAAC;AAGlB,OAAO,EACL,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -179,6 +179,22 @@ async function parseJsonResponse(response) {
|
|
|
179
179
|
async function parseTextResponse(response) {
|
|
180
180
|
return response.text();
|
|
181
181
|
}
|
|
182
|
+
function createMockFile(name, content, type = "application/octet-stream") {
|
|
183
|
+
const blob = typeof content === "string" ? new Blob([content], { type }) : content;
|
|
184
|
+
return new File([blob], name, { type });
|
|
185
|
+
}
|
|
186
|
+
function extractCookies(response) {
|
|
187
|
+
const cookies = {};
|
|
188
|
+
const setCookieHeaders = response.headers.getSetCookie?.() || [];
|
|
189
|
+
for (const header of setCookieHeaders) {
|
|
190
|
+
const [nameValue] = header.split(";");
|
|
191
|
+
const [name, value] = nameValue.split("=");
|
|
192
|
+
if (name && value !== undefined) {
|
|
193
|
+
cookies[name.trim()] = value.trim();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return cookies;
|
|
197
|
+
}
|
|
182
198
|
|
|
183
199
|
// src/loader.ts
|
|
184
200
|
async function testLoader(loader, options = {}) {
|
|
@@ -211,6 +227,31 @@ function createLoaderTester(loader, baseOptions = {}) {
|
|
|
211
227
|
});
|
|
212
228
|
};
|
|
213
229
|
}
|
|
230
|
+
async function testLoadersParallel(loaders) {
|
|
231
|
+
return Promise.all(loaders.map(({ loader, params, request, context }) => testLoader(loader, { params, request, context })));
|
|
232
|
+
}
|
|
233
|
+
async function testLoaderMatrix(loader, options) {
|
|
234
|
+
return Promise.all(options.params.map((params) => testLoader(loader, {
|
|
235
|
+
params,
|
|
236
|
+
request: options.request,
|
|
237
|
+
context: options.context
|
|
238
|
+
})));
|
|
239
|
+
}
|
|
240
|
+
async function testLoaderError(loader, options = {}) {
|
|
241
|
+
const request = createMockRequest(options.request);
|
|
242
|
+
const context = createTestContext(options.context);
|
|
243
|
+
const params = options.params || {};
|
|
244
|
+
try {
|
|
245
|
+
await loader({ request, params, context });
|
|
246
|
+
return { error: null, context, request };
|
|
247
|
+
} catch (error) {
|
|
248
|
+
return {
|
|
249
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
250
|
+
context,
|
|
251
|
+
request
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
214
255
|
// src/action.ts
|
|
215
256
|
async function testAction(action, options = {}) {
|
|
216
257
|
const requestOptions = {
|
|
@@ -273,6 +314,50 @@ function createActionTester(action, baseOptions = {}) {
|
|
|
273
314
|
});
|
|
274
315
|
};
|
|
275
316
|
}
|
|
317
|
+
async function testActionMatrix(action, options) {
|
|
318
|
+
return Promise.all(options.submissions.map((submission) => testAction(action, {
|
|
319
|
+
params: options.params,
|
|
320
|
+
context: options.context,
|
|
321
|
+
formData: submission.formData,
|
|
322
|
+
body: submission.body
|
|
323
|
+
})));
|
|
324
|
+
}
|
|
325
|
+
async function testActionError(action, options = {}) {
|
|
326
|
+
const requestOptions = {
|
|
327
|
+
method: "POST",
|
|
328
|
+
...options.request
|
|
329
|
+
};
|
|
330
|
+
if (options.formData) {
|
|
331
|
+
requestOptions.formData = options.formData;
|
|
332
|
+
} else if (options.body) {
|
|
333
|
+
requestOptions.body = options.body;
|
|
334
|
+
}
|
|
335
|
+
const request = createMockRequest(requestOptions);
|
|
336
|
+
const context = createTestContext(options.context);
|
|
337
|
+
const params = options.params || {};
|
|
338
|
+
try {
|
|
339
|
+
await action({ request, params, context });
|
|
340
|
+
return { error: null, context, request };
|
|
341
|
+
} catch (error) {
|
|
342
|
+
return {
|
|
343
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
344
|
+
context,
|
|
345
|
+
request
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async function testActionWithFile(action, options) {
|
|
350
|
+
const { file, extraFields = {}, ...rest } = options;
|
|
351
|
+
const blob = typeof file.content === "string" ? new Blob([file.content], { type: file.type || "application/octet-stream" }) : file.content;
|
|
352
|
+
const formData = {
|
|
353
|
+
...extraFields,
|
|
354
|
+
[file.field]: new File([blob], file.name, { type: file.type })
|
|
355
|
+
};
|
|
356
|
+
return testAction(action, {
|
|
357
|
+
...rest,
|
|
358
|
+
formData
|
|
359
|
+
});
|
|
360
|
+
}
|
|
276
361
|
// src/middleware.ts
|
|
277
362
|
async function testMiddleware(middleware, options = {}) {
|
|
278
363
|
const request = createMockRequest(options.request);
|
|
@@ -316,6 +401,78 @@ function createMiddlewareTester(middleware, baseOptions = {}) {
|
|
|
316
401
|
});
|
|
317
402
|
};
|
|
318
403
|
}
|
|
404
|
+
async function testMiddlewareChain(middlewares, options = {}) {
|
|
405
|
+
const request = createMockRequest(options.request);
|
|
406
|
+
const context = createTestContext(options.context);
|
|
407
|
+
const middlewareResults = [];
|
|
408
|
+
let index = 0;
|
|
409
|
+
const buildNext = (currentIndex) => {
|
|
410
|
+
return async () => {
|
|
411
|
+
middlewareResults[currentIndex].nextCalled = true;
|
|
412
|
+
if (currentIndex + 1 >= middlewares.length) {
|
|
413
|
+
return options.nextResponse || new Response("OK", { status: 200 });
|
|
414
|
+
}
|
|
415
|
+
const startTime2 = performance.now();
|
|
416
|
+
middlewareResults.push({
|
|
417
|
+
index: currentIndex + 1,
|
|
418
|
+
nextCalled: false,
|
|
419
|
+
duration: 0
|
|
420
|
+
});
|
|
421
|
+
const response2 = await middlewares[currentIndex + 1](request, context, buildNext(currentIndex + 1));
|
|
422
|
+
middlewareResults[currentIndex + 1].duration = performance.now() - startTime2;
|
|
423
|
+
return response2;
|
|
424
|
+
};
|
|
425
|
+
};
|
|
426
|
+
middlewareResults.push({ index: 0, nextCalled: false, duration: 0 });
|
|
427
|
+
const startTime = performance.now();
|
|
428
|
+
const response = await middlewares[0](request, context, buildNext(0));
|
|
429
|
+
middlewareResults[0].duration = performance.now() - startTime;
|
|
430
|
+
return {
|
|
431
|
+
response,
|
|
432
|
+
context,
|
|
433
|
+
request,
|
|
434
|
+
middlewareResults
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
async function testMiddlewareMatrix(middleware, options) {
|
|
438
|
+
return Promise.all(options.requests.map((request) => testMiddleware(middleware, {
|
|
439
|
+
request,
|
|
440
|
+
context: options.context
|
|
441
|
+
})));
|
|
442
|
+
}
|
|
443
|
+
async function testMiddlewareError(middleware, options) {
|
|
444
|
+
const request = createMockRequest(options.request);
|
|
445
|
+
const context = createTestContext(options.context);
|
|
446
|
+
try {
|
|
447
|
+
const response = await middleware(request, context, options.next);
|
|
448
|
+
return { response, error: null, context };
|
|
449
|
+
} catch (error) {
|
|
450
|
+
return {
|
|
451
|
+
response: null,
|
|
452
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
453
|
+
context
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
async function testMiddlewareContext(middleware, options) {
|
|
458
|
+
const result = await testMiddleware(middleware, options);
|
|
459
|
+
const contextDiff = {};
|
|
460
|
+
let contextMatches = true;
|
|
461
|
+
for (const [key, expected] of Object.entries(options.expectContextValues)) {
|
|
462
|
+
const actual = result.context.get(key);
|
|
463
|
+
const matches = JSON.stringify(actual) === JSON.stringify(expected);
|
|
464
|
+
if (!matches) {
|
|
465
|
+
contextMatches = false;
|
|
466
|
+
contextDiff[key] = { expected, actual };
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
response: result.response,
|
|
471
|
+
context: result.context,
|
|
472
|
+
contextMatches,
|
|
473
|
+
contextDiff
|
|
474
|
+
};
|
|
475
|
+
}
|
|
319
476
|
// src/render.ts
|
|
320
477
|
async function renderRoute(module, options = {}) {
|
|
321
478
|
const request = createMockRequest(options.request);
|
|
@@ -367,6 +524,56 @@ function createRouteRenderer(module, baseOptions = {}) {
|
|
|
367
524
|
});
|
|
368
525
|
};
|
|
369
526
|
}
|
|
527
|
+
function renderComponent(Component, props) {
|
|
528
|
+
return {
|
|
529
|
+
type: Component,
|
|
530
|
+
props,
|
|
531
|
+
key: null
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
async function renderRouteMatrix(module, options) {
|
|
535
|
+
return Promise.all(options.params.map((params) => renderRoute(module, {
|
|
536
|
+
params,
|
|
537
|
+
request: options.request,
|
|
538
|
+
context: options.context
|
|
539
|
+
})));
|
|
540
|
+
}
|
|
541
|
+
async function testRouteRenders(module, options = {}) {
|
|
542
|
+
try {
|
|
543
|
+
const result = await renderRoute(module, options);
|
|
544
|
+
return { renders: true, error: null, result };
|
|
545
|
+
} catch (error) {
|
|
546
|
+
return {
|
|
547
|
+
renders: false,
|
|
548
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
549
|
+
result: null
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
async function getRouteMeta(module, options = {}) {
|
|
554
|
+
if (!module.meta) {
|
|
555
|
+
return [];
|
|
556
|
+
}
|
|
557
|
+
const request = createMockRequest(options.request);
|
|
558
|
+
const context = createTestContext(options.context);
|
|
559
|
+
const params = options.params || {};
|
|
560
|
+
const url = new URL(request.url);
|
|
561
|
+
let data;
|
|
562
|
+
if (options.loaderData !== undefined) {
|
|
563
|
+
data = options.loaderData;
|
|
564
|
+
} else if (module.loader) {
|
|
565
|
+
data = await module.loader({ request, params, context });
|
|
566
|
+
}
|
|
567
|
+
return module.meta({
|
|
568
|
+
data,
|
|
569
|
+
params,
|
|
570
|
+
location: {
|
|
571
|
+
pathname: url.pathname,
|
|
572
|
+
search: url.search,
|
|
573
|
+
hash: url.hash
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
}
|
|
370
577
|
// src/assertions.ts
|
|
371
578
|
function assertRedirect(response, expectedLocation, options = {}) {
|
|
372
579
|
if (!response) {
|
|
@@ -504,6 +711,56 @@ function assertCookies(response, expected, options = {}) {
|
|
|
504
711
|
}
|
|
505
712
|
}
|
|
506
713
|
}
|
|
714
|
+
async function assertThrows(fn, expected = {}, options = {}) {
|
|
715
|
+
let error = null;
|
|
716
|
+
try {
|
|
717
|
+
await fn();
|
|
718
|
+
} catch (e) {
|
|
719
|
+
error = e instanceof Error ? e : new Error(String(e));
|
|
720
|
+
}
|
|
721
|
+
if (!error) {
|
|
722
|
+
throw new Error(options.message || "Expected function to throw but it did not");
|
|
723
|
+
}
|
|
724
|
+
if (expected.message) {
|
|
725
|
+
if (expected.message instanceof RegExp) {
|
|
726
|
+
if (!expected.message.test(error.message)) {
|
|
727
|
+
throw new Error(options.message || `Expected error message to match ${expected.message} but got "${error.message}"`);
|
|
728
|
+
}
|
|
729
|
+
} else {
|
|
730
|
+
if (error.message !== expected.message) {
|
|
731
|
+
throw new Error(options.message || `Expected error message to be "${expected.message}" but got "${error.message}"`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
if (expected.name && error.name !== expected.name) {
|
|
736
|
+
throw new Error(options.message || `Expected error name to be "${expected.name}" but got "${error.name}"`);
|
|
737
|
+
}
|
|
738
|
+
if (expected.status && "status" in error && error.status !== expected.status) {
|
|
739
|
+
throw new Error(options.message || `Expected error status to be ${expected.status} but got ${error.status}`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
function assertSchema(data, schema, options = {}) {
|
|
743
|
+
if (typeof data !== "object" || data === null) {
|
|
744
|
+
throw new Error(options.message || "Expected data to be an object");
|
|
745
|
+
}
|
|
746
|
+
const obj = data;
|
|
747
|
+
for (const [key, expectedType] of Object.entries(schema)) {
|
|
748
|
+
const value = obj[key];
|
|
749
|
+
let actualType;
|
|
750
|
+
if (value === null) {
|
|
751
|
+
actualType = "null";
|
|
752
|
+
} else if (value === undefined) {
|
|
753
|
+
actualType = "undefined";
|
|
754
|
+
} else if (Array.isArray(value)) {
|
|
755
|
+
actualType = "array";
|
|
756
|
+
} else {
|
|
757
|
+
actualType = typeof value;
|
|
758
|
+
}
|
|
759
|
+
if (actualType !== expectedType) {
|
|
760
|
+
throw new Error(options.message || `Expected "${key}" to be ${expectedType} but got ${actualType}`);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
507
764
|
// src/server.ts
|
|
508
765
|
async function createTestServer(options = {}) {
|
|
509
766
|
const port = options.port || await getAvailablePort();
|
|
@@ -597,6 +854,45 @@ async function getAvailablePort() {
|
|
|
597
854
|
server.stop();
|
|
598
855
|
return port;
|
|
599
856
|
}
|
|
857
|
+
async function createMockServer(options) {
|
|
858
|
+
const port = options.port || await getAvailablePort();
|
|
859
|
+
const server = Bun.serve({
|
|
860
|
+
port,
|
|
861
|
+
async fetch(request) {
|
|
862
|
+
const url = new URL(request.url);
|
|
863
|
+
const method = request.method;
|
|
864
|
+
const path = url.pathname;
|
|
865
|
+
const routeKey = `${method} ${path}`;
|
|
866
|
+
const handler = options.routes[routeKey];
|
|
867
|
+
if (!handler) {
|
|
868
|
+
return new Response("Not Found", { status: 404 });
|
|
869
|
+
}
|
|
870
|
+
let responseData;
|
|
871
|
+
if (typeof handler === "function") {
|
|
872
|
+
let body;
|
|
873
|
+
try {
|
|
874
|
+
body = await request.json();
|
|
875
|
+
} catch {
|
|
876
|
+
body = undefined;
|
|
877
|
+
}
|
|
878
|
+
responseData = handler({ body, params: {} });
|
|
879
|
+
} else {
|
|
880
|
+
responseData = handler;
|
|
881
|
+
}
|
|
882
|
+
return new Response(JSON.stringify(responseData), {
|
|
883
|
+
status: 200,
|
|
884
|
+
headers: { "Content-Type": "application/json" }
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
return {
|
|
889
|
+
url: `http://localhost:${port}`,
|
|
890
|
+
port,
|
|
891
|
+
stop: async () => {
|
|
892
|
+
server.stop();
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
}
|
|
600
896
|
// src/snapshot.ts
|
|
601
897
|
function prepareForSnapshot(data, options = {}) {
|
|
602
898
|
if (data === null || data === undefined) {
|
|
@@ -633,29 +929,83 @@ async function snapshotAction(action, testOptions = {}, snapshotOptions = {}) {
|
|
|
633
929
|
const result = await testAction(action, testOptions);
|
|
634
930
|
return prepareForSnapshot(result.data, snapshotOptions);
|
|
635
931
|
}
|
|
932
|
+
async function createSnapshotMatrix(loader, options) {
|
|
933
|
+
const snapshots = {};
|
|
934
|
+
for (const [name, testOptions] of Object.entries(options.scenarios)) {
|
|
935
|
+
snapshots[name] = await snapshotLoader(loader, testOptions, options.snapshotOptions);
|
|
936
|
+
}
|
|
937
|
+
return snapshots;
|
|
938
|
+
}
|
|
939
|
+
var commonReplacers = {
|
|
940
|
+
date: /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/g,
|
|
941
|
+
uuid: /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi,
|
|
942
|
+
numericId: /\d+/g
|
|
943
|
+
};
|
|
944
|
+
function applyReplacements(data, replacements) {
|
|
945
|
+
const json = JSON.stringify(data);
|
|
946
|
+
let result = json;
|
|
947
|
+
for (const [pattern, replacement] of Object.entries(replacements)) {
|
|
948
|
+
result = result.split(pattern).join(replacement);
|
|
949
|
+
}
|
|
950
|
+
return JSON.parse(result);
|
|
951
|
+
}
|
|
952
|
+
function deterministicSnapshot(data) {
|
|
953
|
+
return JSON.stringify(data, (_, value) => {
|
|
954
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
955
|
+
return Object.keys(value).sort().reduce((sorted, key) => {
|
|
956
|
+
sorted[key] = value[key];
|
|
957
|
+
return sorted;
|
|
958
|
+
}, {});
|
|
959
|
+
}
|
|
960
|
+
return value;
|
|
961
|
+
}, 2);
|
|
962
|
+
}
|
|
636
963
|
export {
|
|
964
|
+
testRouteRenders,
|
|
965
|
+
testMiddlewareMatrix,
|
|
966
|
+
testMiddlewareError,
|
|
967
|
+
testMiddlewareContext,
|
|
968
|
+
testMiddlewareChain,
|
|
637
969
|
testMiddleware,
|
|
970
|
+
testLoadersParallel,
|
|
971
|
+
testLoaderMatrix,
|
|
972
|
+
testLoaderError,
|
|
638
973
|
testLoader,
|
|
974
|
+
testActionWithFile,
|
|
975
|
+
testActionMatrix,
|
|
976
|
+
testActionError,
|
|
639
977
|
testAction,
|
|
640
978
|
snapshotLoader,
|
|
641
979
|
snapshotAction,
|
|
980
|
+
renderRouteMatrix,
|
|
642
981
|
renderRoute,
|
|
982
|
+
renderComponent,
|
|
643
983
|
parseTextResponse,
|
|
644
984
|
parseJsonResponse,
|
|
985
|
+
getRouteMeta,
|
|
986
|
+
extractCookies,
|
|
987
|
+
deterministicSnapshot,
|
|
645
988
|
createTestServer,
|
|
646
989
|
createTestContext,
|
|
990
|
+
createSnapshotMatrix,
|
|
647
991
|
createRouteRenderer,
|
|
992
|
+
createMockServer,
|
|
648
993
|
createMockRequest,
|
|
649
994
|
createMockHeaders,
|
|
650
995
|
createMockFormData,
|
|
996
|
+
createMockFile,
|
|
651
997
|
createMiddlewareTester,
|
|
652
998
|
createLoaderTester,
|
|
653
999
|
createFormRequest,
|
|
654
1000
|
createContextFactory,
|
|
655
1001
|
createActionTester,
|
|
1002
|
+
commonReplacers,
|
|
1003
|
+
assertThrows,
|
|
656
1004
|
assertStatus,
|
|
1005
|
+
assertSchema,
|
|
657
1006
|
assertRedirect,
|
|
658
1007
|
assertJson,
|
|
659
1008
|
assertHeaders,
|
|
660
|
-
assertCookies
|
|
1009
|
+
assertCookies,
|
|
1010
|
+
applyReplacements
|
|
661
1011
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ereo/testing",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Ereo Team",
|
|
6
6
|
"homepage": "https://ereo.dev",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"typecheck": "tsc --noEmit --skipLibCheck"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@ereo/core": "^0.1.
|
|
36
|
-
"@ereo/router": "^0.1.
|
|
37
|
-
"@ereo/data": "^0.1.
|
|
35
|
+
"@ereo/core": "^0.1.25",
|
|
36
|
+
"@ereo/router": "^0.1.25",
|
|
37
|
+
"@ereo/data": "^0.1.25"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/bun": "^1.1.0",
|