@tyravel/testing 0.1.0
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/application-helpers.d.ts +8 -0
- package/dist/application-helpers.d.ts.map +1 -0
- package/dist/application-helpers.js +20 -0
- package/dist/application-helpers.js.map +1 -0
- package/dist/cookie-jar.d.ts +12 -0
- package/dist/cookie-jar.d.ts.map +1 -0
- package/dist/cookie-jar.js +45 -0
- package/dist/cookie-jar.js.map +1 -0
- package/dist/fakes.d.ts +16 -0
- package/dist/fakes.d.ts.map +1 -0
- package/dist/fakes.js +28 -0
- package/dist/fakes.js.map +1 -0
- package/dist/http-test-client.d.ts +24 -0
- package/dist/http-test-client.d.ts.map +1 -0
- package/dist/http-test-client.js +64 -0
- package/dist/http-test-client.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/json-match.d.ts +8 -0
- package/dist/json-match.d.ts.map +1 -0
- package/dist/json-match.js +26 -0
- package/dist/json-match.js.map +1 -0
- package/dist/json-match.test.d.ts +2 -0
- package/dist/json-match.test.d.ts.map +1 -0
- package/dist/json-match.test.js +9 -0
- package/dist/json-match.test.js.map +1 -0
- package/dist/test-case.d.ts +29 -0
- package/dist/test-case.d.ts.map +1 -0
- package/dist/test-case.js +40 -0
- package/dist/test-case.js.map +1 -0
- package/dist/test-response.d.ts +18 -0
- package/dist/test-response.d.ts.map +1 -0
- package/dist/test-response.js +67 -0
- package/dist/test-response.js.map +1 -0
- package/dist/testing.integration.test.d.ts +2 -0
- package/dist/testing.integration.test.d.ts.map +1 -0
- package/dist/testing.integration.test.js +32 -0
- package/dist/testing.integration.test.js.map +1 -0
- package/dist/vitest.d.ts +15 -0
- package/dist/vitest.d.ts.map +1 -0
- package/dist/vitest.js +24 -0
- package/dist/vitest.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Application } from '@tyravel/core';
|
|
2
|
+
import { HttpKernel } from '@tyravel/core';
|
|
3
|
+
/**
|
|
4
|
+
* Wire Tyravel facades to the application under test (call after boot).
|
|
5
|
+
*/
|
|
6
|
+
export declare function wireFacades(app: Application): void;
|
|
7
|
+
export declare function createHttpKernel(app: Application): HttpKernel;
|
|
8
|
+
//# sourceMappingURL=application-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application-helpers.d.ts","sourceRoot":"","sources":["../src/application-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EACL,UAAU,EAWX,MAAM,eAAe,CAAC;AAEvB;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAWlD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,WAAW,GAAG,UAAU,CAE7D"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { HttpKernel, setAuthApplication, setCacheApplication, setEventApplication, setGateApplication, setMailApplication, setNotificationApplication, setPasswordApplication, setQueueApplication, setRouteApplication, setViewApplication, } from '@tyravel/core';
|
|
2
|
+
/**
|
|
3
|
+
* Wire Tyravel facades to the application under test (call after boot).
|
|
4
|
+
*/
|
|
5
|
+
export function wireFacades(app) {
|
|
6
|
+
setRouteApplication(app);
|
|
7
|
+
setViewApplication(app);
|
|
8
|
+
setQueueApplication(app);
|
|
9
|
+
setEventApplication(app);
|
|
10
|
+
setAuthApplication(app);
|
|
11
|
+
setGateApplication(app);
|
|
12
|
+
setPasswordApplication(app);
|
|
13
|
+
setCacheApplication(app);
|
|
14
|
+
setMailApplication(app);
|
|
15
|
+
setNotificationApplication(app);
|
|
16
|
+
}
|
|
17
|
+
export function createHttpKernel(app) {
|
|
18
|
+
return new HttpKernel(app);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=application-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application-helpers.js","sourceRoot":"","sources":["../src/application-helpers.ts"],"names":[],"mappings":"AACA,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,0BAA0B,EAC1B,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAEvB;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,GAAgB;IAC1C,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACzB,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACxB,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACzB,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACzB,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACxB,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACxB,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAC5B,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACzB,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACxB,0BAA0B,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAgB;IAC/C,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse Set-Cookie headers into a map for subsequent requests.
|
|
3
|
+
*/
|
|
4
|
+
export declare class CookieJar {
|
|
5
|
+
private cookies;
|
|
6
|
+
absorb(response: Response): void;
|
|
7
|
+
headerValue(): string | undefined;
|
|
8
|
+
set(name: string, value: string): void;
|
|
9
|
+
clear(): void;
|
|
10
|
+
private storeLine;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=cookie-jar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookie-jar.d.ts","sourceRoot":"","sources":["../src/cookie-jar.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAA6B;IAE5C,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAehC,WAAW,IAAI,MAAM,GAAG,SAAS;IAOjC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAItC,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,SAAS;CAalB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse Set-Cookie headers into a map for subsequent requests.
|
|
3
|
+
*/
|
|
4
|
+
export class CookieJar {
|
|
5
|
+
cookies = new Map();
|
|
6
|
+
absorb(response) {
|
|
7
|
+
const raw = response.headers.getSetCookie?.() ?? [];
|
|
8
|
+
if (raw.length > 0) {
|
|
9
|
+
for (const line of raw) {
|
|
10
|
+
this.storeLine(line);
|
|
11
|
+
}
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const single = response.headers.get('set-cookie');
|
|
15
|
+
if (single) {
|
|
16
|
+
this.storeLine(single);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
headerValue() {
|
|
20
|
+
if (this.cookies.size === 0) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
return [...this.cookies.entries()].map(([k, v]) => `${k}=${v}`).join('; ');
|
|
24
|
+
}
|
|
25
|
+
set(name, value) {
|
|
26
|
+
this.cookies.set(name, value);
|
|
27
|
+
}
|
|
28
|
+
clear() {
|
|
29
|
+
this.cookies.clear();
|
|
30
|
+
}
|
|
31
|
+
storeLine(line) {
|
|
32
|
+
const part = line.split(';')[0]?.trim();
|
|
33
|
+
if (!part) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const eq = part.indexOf('=');
|
|
37
|
+
if (eq <= 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const name = part.slice(0, eq).trim();
|
|
41
|
+
const value = part.slice(eq + 1).trim();
|
|
42
|
+
this.cookies.set(name, value);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=cookie-jar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookie-jar.js","sourceRoot":"","sources":["../src/cookie-jar.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,SAAS;IACZ,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE5C,MAAM,CAAC,QAAkB;QACvB,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,CAAC;QACpD,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnB,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;gBACvB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACvB,CAAC;YACD,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAClD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,WAAW;QACT,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7E,CAAC;IAED,GAAG,CAAC,IAAY,EAAE,KAAa;QAC7B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAEO,SAAS,CAAC,IAAY;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACZ,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;CACF"}
|
package/dist/fakes.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Abstract } from '@tyravel/container';
|
|
2
|
+
import type { Application } from '@tyravel/core';
|
|
3
|
+
import { vi } from 'vitest';
|
|
4
|
+
/**
|
|
5
|
+
* Replace a container binding with a test double for the duration of a test.
|
|
6
|
+
*/
|
|
7
|
+
export declare function fake<T>(app: Application, abstract: Abstract<T>, instance: T): T;
|
|
8
|
+
/**
|
|
9
|
+
* Build a plain object with vitest mock functions for the given method names.
|
|
10
|
+
*/
|
|
11
|
+
export declare function mockInstance<T extends object>(methods: (keyof T)[]): T;
|
|
12
|
+
/**
|
|
13
|
+
* Spy on an existing container binding (re-binds as singleton mock wrapper).
|
|
14
|
+
*/
|
|
15
|
+
export declare function spyOnBinding<T extends object>(app: Application, abstract: Abstract<T>, method: keyof T): ReturnType<typeof vi.fn>;
|
|
16
|
+
//# sourceMappingURL=fakes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fakes.d.ts","sourceRoot":"","sources":["../src/fakes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE5B;;GAEG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACpB,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EACrB,QAAQ,EAAE,CAAC,GACV,CAAC,CAGH;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAMtE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EACrB,MAAM,EAAE,MAAM,CAAC,GACd,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAK1B"}
|
package/dist/fakes.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
/**
|
|
3
|
+
* Replace a container binding with a test double for the duration of a test.
|
|
4
|
+
*/
|
|
5
|
+
export function fake(app, abstract, instance) {
|
|
6
|
+
app.instance(abstract, instance);
|
|
7
|
+
return instance;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Build a plain object with vitest mock functions for the given method names.
|
|
11
|
+
*/
|
|
12
|
+
export function mockInstance(methods) {
|
|
13
|
+
const obj = {};
|
|
14
|
+
for (const method of methods) {
|
|
15
|
+
obj[method] = vi.fn();
|
|
16
|
+
}
|
|
17
|
+
return obj;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Spy on an existing container binding (re-binds as singleton mock wrapper).
|
|
21
|
+
*/
|
|
22
|
+
export function spyOnBinding(app, abstract, method) {
|
|
23
|
+
const original = app.make(abstract);
|
|
24
|
+
const spy = vi.spyOn(original, method);
|
|
25
|
+
app.instance(abstract, original);
|
|
26
|
+
return spy;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=fakes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fakes.js","sourceRoot":"","sources":["../src/fakes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE5B;;GAEG;AACH,MAAM,UAAU,IAAI,CAClB,GAAgB,EAChB,QAAqB,EACrB,QAAW;IAEX,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACjC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAmB,OAAoB;IACjE,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,GAAG,CAAC,MAAgB,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;IAClC,CAAC;IACD,OAAO,GAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,GAAgB,EAChB,QAAqB,EACrB,MAAe;IAEf,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAe,CAAC,CAAC;IAChD,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACjC,OAAO,GAA+B,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { HttpKernel } from '@tyravel/core';
|
|
2
|
+
import { TestResponse } from './test-response.js';
|
|
3
|
+
export interface HttpTestOptions {
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
json?: unknown;
|
|
6
|
+
body?: RequestInit['body'];
|
|
7
|
+
}
|
|
8
|
+
export declare class HttpTestClient {
|
|
9
|
+
private readonly kernel;
|
|
10
|
+
private readonly jar;
|
|
11
|
+
private defaultHeaders;
|
|
12
|
+
constructor(kernel: HttpKernel);
|
|
13
|
+
withHeaders(headers: Record<string, string>): this;
|
|
14
|
+
withToken(plainTextToken: string): this;
|
|
15
|
+
withSessionCookie(name: string, value: string): this;
|
|
16
|
+
get(url: string, options?: HttpTestOptions): Promise<TestResponse>;
|
|
17
|
+
post(url: string, options?: HttpTestOptions): Promise<TestResponse>;
|
|
18
|
+
put(url: string, options?: HttpTestOptions): Promise<TestResponse>;
|
|
19
|
+
patch(url: string, options?: HttpTestOptions): Promise<TestResponse>;
|
|
20
|
+
delete(url: string, options?: HttpTestOptions): Promise<TestResponse>;
|
|
21
|
+
request(method: string, url: string, options?: HttpTestOptions): Promise<TestResponse>;
|
|
22
|
+
resetCookies(): void;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=http-test-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-test-client.d.ts","sourceRoot":"","sources":["../src/http-test-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED,qBAAa,cAAc;IAMb,OAAO,CAAC,QAAQ,CAAC,MAAM;IALnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAmB;IACvC,OAAO,CAAC,cAAc,CAEpB;gBAE2B,MAAM,EAAE,UAAU;IAE/C,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAKlD,SAAS,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAMvC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAK9C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,YAAY,CAAC;IAItE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,YAAY,CAAC;IAIvE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,YAAY,CAAC;IAItE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,YAAY,CAAC;IAIxE,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,YAAY,CAAC;IAIzE,OAAO,CACX,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,YAAY,CAAC;IA0BxB,YAAY,IAAI,IAAI;CAGrB"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { CookieJar } from './cookie-jar.js';
|
|
2
|
+
import { TestResponse } from './test-response.js';
|
|
3
|
+
export class HttpTestClient {
|
|
4
|
+
kernel;
|
|
5
|
+
jar = new CookieJar();
|
|
6
|
+
defaultHeaders = {
|
|
7
|
+
accept: 'application/json',
|
|
8
|
+
};
|
|
9
|
+
constructor(kernel) {
|
|
10
|
+
this.kernel = kernel;
|
|
11
|
+
}
|
|
12
|
+
withHeaders(headers) {
|
|
13
|
+
this.defaultHeaders = { ...this.defaultHeaders, ...headers };
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
withToken(plainTextToken) {
|
|
17
|
+
return this.withHeaders({
|
|
18
|
+
authorization: `Bearer ${plainTextToken}`,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
withSessionCookie(name, value) {
|
|
22
|
+
this.jar.set(name, value);
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
async get(url, options = {}) {
|
|
26
|
+
return this.request('GET', url, options);
|
|
27
|
+
}
|
|
28
|
+
async post(url, options = {}) {
|
|
29
|
+
return this.request('POST', url, options);
|
|
30
|
+
}
|
|
31
|
+
async put(url, options = {}) {
|
|
32
|
+
return this.request('PUT', url, options);
|
|
33
|
+
}
|
|
34
|
+
async patch(url, options = {}) {
|
|
35
|
+
return this.request('PATCH', url, options);
|
|
36
|
+
}
|
|
37
|
+
async delete(url, options = {}) {
|
|
38
|
+
return this.request('DELETE', url, options);
|
|
39
|
+
}
|
|
40
|
+
async request(method, url, options = {}) {
|
|
41
|
+
const headers = new Headers({ ...this.defaultHeaders, ...options.headers });
|
|
42
|
+
if (options.json !== undefined) {
|
|
43
|
+
headers.set('content-type', 'application/json');
|
|
44
|
+
}
|
|
45
|
+
const cookie = this.jar.headerValue();
|
|
46
|
+
if (cookie) {
|
|
47
|
+
headers.set('cookie', cookie);
|
|
48
|
+
}
|
|
49
|
+
const init = {
|
|
50
|
+
method,
|
|
51
|
+
headers,
|
|
52
|
+
body: options.json !== undefined
|
|
53
|
+
? JSON.stringify(options.json)
|
|
54
|
+
: options.body,
|
|
55
|
+
};
|
|
56
|
+
const response = await this.kernel.handle(new Request(url, init));
|
|
57
|
+
this.jar.absorb(response);
|
|
58
|
+
return new TestResponse(response);
|
|
59
|
+
}
|
|
60
|
+
resetCookies() {
|
|
61
|
+
this.jar.clear();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=http-test-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-test-client.js","sourceRoot":"","sources":["../src/http-test-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQlD,MAAM,OAAO,cAAc;IAMI;IALZ,GAAG,GAAG,IAAI,SAAS,EAAE,CAAC;IAC/B,cAAc,GAA2B;QAC/C,MAAM,EAAE,kBAAkB;KAC3B,CAAC;IAEF,YAA6B,MAAkB;QAAlB,WAAM,GAAN,MAAM,CAAY;IAAG,CAAC;IAEnD,WAAW,CAAC,OAA+B;QACzC,IAAI,CAAC,cAAc,GAAG,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE,GAAG,OAAO,EAAE,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,SAAS,CAAC,cAAsB;QAC9B,OAAO,IAAI,CAAC,WAAW,CAAC;YACtB,aAAa,EAAE,UAAU,cAAc,EAAE;SAC1C,CAAC,CAAC;IACL,CAAC;IAED,iBAAiB,CAAC,IAAY,EAAE,KAAa;QAC3C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,UAA2B,EAAE;QAClD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,UAA2B,EAAE;QACnD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,UAA2B,EAAE;QAClD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,UAA2B,EAAE;QACpD,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,UAA2B,EAAE;QACrD,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,OAAO,CACX,MAAc,EACd,GAAW,EACX,UAA2B,EAAE;QAE7B,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QAE5E,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;QAClD,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QACtC,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAChC,CAAC;QAED,MAAM,IAAI,GAAgB;YACxB,MAAM;YACN,OAAO;YACP,IAAI,EACF,OAAO,CAAC,IAAI,KAAK,SAAS;gBACxB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC9B,CAAC,CAAC,OAAO,CAAC,IAAI;SACnB,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1B,OAAO,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,YAAY;QACV,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { TestCase } from './test-case.js';
|
|
2
|
+
export { HttpTestClient } from './http-test-client.js';
|
|
3
|
+
export type { HttpTestOptions } from './http-test-client.js';
|
|
4
|
+
export { TestResponse } from './test-response.js';
|
|
5
|
+
export { CookieJar } from './cookie-jar.js';
|
|
6
|
+
export { jsonContains } from './json-match.js';
|
|
7
|
+
export type { JsonValue } from './json-match.js';
|
|
8
|
+
export { fake, mockInstance, spyOnBinding } from './fakes.js';
|
|
9
|
+
export { wireFacades, createHttpKernel } from './application-helpers.js';
|
|
10
|
+
export { withTyravelTest } from './vitest.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { TestCase } from './test-case.js';
|
|
2
|
+
export { HttpTestClient } from './http-test-client.js';
|
|
3
|
+
export { TestResponse } from './test-response.js';
|
|
4
|
+
export { CookieJar } from './cookie-jar.js';
|
|
5
|
+
export { jsonContains } from './json-match.js';
|
|
6
|
+
export { fake, mockInstance, spyOnBinding } from './fakes.js';
|
|
7
|
+
export { wireFacades, createHttpKernel } from './application-helpers.js';
|
|
8
|
+
export { withTyravelTest } from './vitest.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
2
|
+
[key: string]: JsonValue;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* Laravel-style partial JSON match (expected must be contained in actual).
|
|
6
|
+
*/
|
|
7
|
+
export declare function jsonContains(actual: JsonValue, expected: JsonValue): boolean;
|
|
8
|
+
//# sourceMappingURL=json-match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-match.d.ts","sourceRoot":"","sources":["../src/json-match.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjC;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,GAAG,OAAO,CAyB5E"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Laravel-style partial JSON match (expected must be contained in actual).
|
|
3
|
+
*/
|
|
4
|
+
export function jsonContains(actual, expected) {
|
|
5
|
+
if (expected === null || typeof expected !== 'object' || Array.isArray(expected)) {
|
|
6
|
+
if (Array.isArray(expected) && Array.isArray(actual)) {
|
|
7
|
+
return expected.every((item, index) => jsonContains(actual[index], item));
|
|
8
|
+
}
|
|
9
|
+
return actual === expected;
|
|
10
|
+
}
|
|
11
|
+
if (typeof actual !== 'object' || actual === null || Array.isArray(actual)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
const expectedObj = expected;
|
|
15
|
+
const actualObj = actual;
|
|
16
|
+
for (const key of Object.keys(expectedObj)) {
|
|
17
|
+
if (!(key in actualObj)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (!jsonContains(actualObj[key], expectedObj[key])) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=json-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-match.js","sourceRoot":"","sources":["../src/json-match.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAAiB,EAAE,QAAmB;IACjE,IAAI,QAAQ,KAAK,IAAI,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjF,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACrD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAc,EAAE,IAAI,CAAC,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,MAAM,KAAK,QAAQ,CAAC;IAC7B,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3E,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,WAAW,GAAG,QAAqC,CAAC;IAC1D,MAAM,SAAS,GAAG,MAAmC,CAAC;IAEtD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QAC3C,IAAI,CAAC,CAAC,GAAG,IAAI,SAAS,CAAC,EAAE,CAAC;YACxB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,GAAG,CAAc,EAAE,WAAW,CAAC,GAAG,CAAc,CAAC,EAAE,CAAC;YAC9E,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-match.test.d.ts","sourceRoot":"","sources":["../src/json-match.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { jsonContains } from './json-match.js';
|
|
3
|
+
describe('jsonContains', () => {
|
|
4
|
+
it('matches partial objects', () => {
|
|
5
|
+
expect(jsonContains({ a: 1, b: { c: 2 } }, { b: { c: 2 } })).toBe(true);
|
|
6
|
+
expect(jsonContains({ a: 1 }, { a: 2 })).toBe(false);
|
|
7
|
+
});
|
|
8
|
+
});
|
|
9
|
+
//# sourceMappingURL=json-match.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-match.test.js","sourceRoot":"","sources":["../src/json-match.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxE,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Application } from '@tyravel/core';
|
|
2
|
+
import type { ServiceProvider } from '@tyravel/core';
|
|
3
|
+
import { HttpTestClient } from './http-test-client.js';
|
|
4
|
+
type ProviderConstructor = new (app: Application) => ServiceProvider;
|
|
5
|
+
export declare abstract class TestCase {
|
|
6
|
+
app: Application;
|
|
7
|
+
kernel: import('@tyravel/core').HttpKernel;
|
|
8
|
+
http: HttpTestClient;
|
|
9
|
+
/**
|
|
10
|
+
* Build a fresh application instance (not booted).
|
|
11
|
+
*/
|
|
12
|
+
protected abstract createApplication(): Application | Promise<Application>;
|
|
13
|
+
/**
|
|
14
|
+
* Optional service providers to register before boot.
|
|
15
|
+
*/
|
|
16
|
+
protected providers(): ProviderConstructor[];
|
|
17
|
+
/**
|
|
18
|
+
* Override to load routes, config, etc. after providers register, before boot.
|
|
19
|
+
*/
|
|
20
|
+
protected configureApplication(_app: Application): Promise<void>;
|
|
21
|
+
setUp(): Promise<void>;
|
|
22
|
+
tearDown(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Reboot the application (fresh container, same class config).
|
|
25
|
+
*/
|
|
26
|
+
refreshApplication(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=test-case.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-case.d.ts","sourceRoot":"","sources":["../src/test-case.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,KAAK,mBAAmB,GAAG,KAAK,GAAG,EAAE,WAAW,KAAK,eAAe,CAAC;AAErE,8BAAsB,QAAQ;IACrB,GAAG,EAAG,WAAW,CAAC;IAClB,MAAM,EAAG,OAAO,eAAe,EAAE,UAAU,CAAC;IAC5C,IAAI,EAAG,cAAc,CAAC;IAE7B;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,iBAAiB,IAAI,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAE1E;;OAEG;IACH,SAAS,CAAC,SAAS,IAAI,mBAAmB,EAAE;IAI5C;;OAEG;cACa,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAEhE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAYtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAK/B;;OAEG;IACG,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;CAI1C"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createHttpKernel, wireFacades } from './application-helpers.js';
|
|
2
|
+
import { HttpTestClient } from './http-test-client.js';
|
|
3
|
+
export class TestCase {
|
|
4
|
+
app;
|
|
5
|
+
kernel;
|
|
6
|
+
http;
|
|
7
|
+
/**
|
|
8
|
+
* Optional service providers to register before boot.
|
|
9
|
+
*/
|
|
10
|
+
providers() {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Override to load routes, config, etc. after providers register, before boot.
|
|
15
|
+
*/
|
|
16
|
+
async configureApplication(_app) { }
|
|
17
|
+
async setUp() {
|
|
18
|
+
this.app = await this.createApplication();
|
|
19
|
+
for (const Provider of this.providers()) {
|
|
20
|
+
this.app.register(Provider);
|
|
21
|
+
}
|
|
22
|
+
await this.configureApplication(this.app);
|
|
23
|
+
await this.app.boot();
|
|
24
|
+
wireFacades(this.app);
|
|
25
|
+
this.kernel = createHttpKernel(this.app);
|
|
26
|
+
this.http = new HttpTestClient(this.kernel);
|
|
27
|
+
}
|
|
28
|
+
async tearDown() {
|
|
29
|
+
this.app?.flush();
|
|
30
|
+
this.http?.resetCookies();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Reboot the application (fresh container, same class config).
|
|
34
|
+
*/
|
|
35
|
+
async refreshApplication() {
|
|
36
|
+
await this.tearDown();
|
|
37
|
+
await this.setUp();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=test-case.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-case.js","sourceRoot":"","sources":["../src/test-case.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAIvD,MAAM,OAAgB,QAAQ;IACrB,GAAG,CAAe;IAClB,MAAM,CAAsC;IAC5C,IAAI,CAAkB;IAO7B;;OAEG;IACO,SAAS;QACjB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED;;OAEG;IACO,KAAK,CAAC,oBAAoB,CAAC,IAAiB,IAAkB,CAAC;IAEzE,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,GAAG,GAAG,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC1C,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YACxC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC;QACD,MAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACtB,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC;QAClB,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,kBAAkB;QACtB,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { JsonValue } from './json-match.js';
|
|
2
|
+
export declare class TestResponse {
|
|
3
|
+
readonly response: Response;
|
|
4
|
+
constructor(response: Response);
|
|
5
|
+
status(): number;
|
|
6
|
+
assertStatus(expected: number): this;
|
|
7
|
+
assertOk(): this;
|
|
8
|
+
assertNotFound(): this;
|
|
9
|
+
assertUnauthorized(): this;
|
|
10
|
+
assertForbidden(): this;
|
|
11
|
+
assertUnprocessable(): this;
|
|
12
|
+
json<T = unknown>(): Promise<T>;
|
|
13
|
+
text(): Promise<string>;
|
|
14
|
+
assertJson(expected: JsonValue): Promise<this>;
|
|
15
|
+
assertJsonPath(path: string, expected: unknown): Promise<this>;
|
|
16
|
+
header(name: string): string | null;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=test-response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-response.d.ts","sourceRoot":"","sources":["../src/test-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAGjD,qBAAa,YAAY;aACK,QAAQ,EAAE,QAAQ;gBAAlB,QAAQ,EAAE,QAAQ;IAE9C,MAAM,IAAI,MAAM;IAIhB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IASpC,QAAQ,IAAI,IAAI;IAIhB,cAAc,IAAI,IAAI;IAItB,kBAAkB,IAAI,IAAI;IAI1B,eAAe,IAAI,IAAI;IAIvB,mBAAmB,IAAI,IAAI;IAIrB,IAAI,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC;IAI/B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAIvB,UAAU,CAAC,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAU9C,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAWpE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;CAGpC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsonContains } from './json-match.js';
|
|
2
|
+
export class TestResponse {
|
|
3
|
+
response;
|
|
4
|
+
constructor(response) {
|
|
5
|
+
this.response = response;
|
|
6
|
+
}
|
|
7
|
+
status() {
|
|
8
|
+
return this.response.status;
|
|
9
|
+
}
|
|
10
|
+
assertStatus(expected) {
|
|
11
|
+
if (this.response.status !== expected) {
|
|
12
|
+
throw new Error(`Expected HTTP status ${expected}, received ${this.response.status}.`);
|
|
13
|
+
}
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
assertOk() {
|
|
17
|
+
return this.assertStatus(200);
|
|
18
|
+
}
|
|
19
|
+
assertNotFound() {
|
|
20
|
+
return this.assertStatus(404);
|
|
21
|
+
}
|
|
22
|
+
assertUnauthorized() {
|
|
23
|
+
return this.assertStatus(401);
|
|
24
|
+
}
|
|
25
|
+
assertForbidden() {
|
|
26
|
+
return this.assertStatus(403);
|
|
27
|
+
}
|
|
28
|
+
assertUnprocessable() {
|
|
29
|
+
return this.assertStatus(422);
|
|
30
|
+
}
|
|
31
|
+
async json() {
|
|
32
|
+
return this.response.json();
|
|
33
|
+
}
|
|
34
|
+
async text() {
|
|
35
|
+
return this.response.text();
|
|
36
|
+
}
|
|
37
|
+
async assertJson(expected) {
|
|
38
|
+
const body = await this.json();
|
|
39
|
+
if (!jsonContains(body, expected)) {
|
|
40
|
+
throw new Error(`JSON response does not contain expected fragment.\nExpected: ${JSON.stringify(expected)}\nActual: ${JSON.stringify(body)}`);
|
|
41
|
+
}
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
async assertJsonPath(path, expected) {
|
|
45
|
+
const body = await this.json();
|
|
46
|
+
const value = readPath(body, path);
|
|
47
|
+
if (value !== expected) {
|
|
48
|
+
throw new Error(`JSON path "${path}" expected ${JSON.stringify(expected)}, got ${JSON.stringify(value)}`);
|
|
49
|
+
}
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
header(name) {
|
|
53
|
+
return this.response.headers.get(name);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function readPath(value, path) {
|
|
57
|
+
const segments = path.split('.').filter(Boolean);
|
|
58
|
+
let current = value;
|
|
59
|
+
for (const segment of segments) {
|
|
60
|
+
if (current === null || typeof current !== 'object') {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
current = current[segment];
|
|
64
|
+
}
|
|
65
|
+
return current;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=test-response.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-response.js","sourceRoot":"","sources":["../src/test-response.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,OAAO,YAAY;IACK;IAA5B,YAA4B,QAAkB;QAAlB,aAAQ,GAAR,QAAQ,CAAU;IAAG,CAAC;IAElD,MAAM;QACJ,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,YAAY,CAAC,QAAgB;QAC3B,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CACb,wBAAwB,QAAQ,cAAc,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CACtE,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,kBAAkB;QAChB,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,mBAAmB;QACjB,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAgB,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,QAAmB;QAClC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,YAAY,CAAC,IAAiB,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CACb,gEAAgE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,aAAa,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAC5H,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,IAAY,EAAE,QAAiB;QAClD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAA2B,CAAC;QACxD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,cAAc,IAAI,cAAc,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CACzF,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,CAAC,IAAY;QACjB,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;CACF;AAED,SAAS,QAAQ,CAAC,KAAc,EAAE,IAAY;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjD,IAAI,OAAO,GAAY,KAAK,CAAC;IAC7B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YACpD,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,GAAI,OAAmC,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.integration.test.d.ts","sourceRoot":"","sources":["../src/testing.integration.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, it } from 'vitest';
|
|
2
|
+
import { Application, HttpKernel, Route, setRouteApplication } from '@tyravel/core';
|
|
3
|
+
import { Response } from '@tyravel/http';
|
|
4
|
+
import { TestCase, withTyravelTest } from './index.js';
|
|
5
|
+
class HomeTest extends TestCase {
|
|
6
|
+
createApplication() {
|
|
7
|
+
return new Application('/tmp/tyravel-testing');
|
|
8
|
+
}
|
|
9
|
+
async configureApplication(app) {
|
|
10
|
+
setRouteApplication(app);
|
|
11
|
+
Route.get('/health', () => Response.json({ ok: true }));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
describe('HttpTestClient', () => {
|
|
15
|
+
it('dispatches through kernel with assertions', async () => {
|
|
16
|
+
const app = new Application();
|
|
17
|
+
setRouteApplication(app);
|
|
18
|
+
Route.get('/ping', () => Response.json({ pong: true }));
|
|
19
|
+
const kernel = new HttpKernel(app);
|
|
20
|
+
const client = new (await import('./http-test-client.js')).HttpTestClient(kernel);
|
|
21
|
+
const response = await client.get('http://localhost/ping');
|
|
22
|
+
await response.assertOk().assertJson({ pong: true });
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('withTyravelTest', () => {
|
|
26
|
+
const t = withTyravelTest(HomeTest);
|
|
27
|
+
it('boots TestCase per example', async () => {
|
|
28
|
+
const response = await t.http.get('http://localhost/health');
|
|
29
|
+
await response.assertJson({ ok: true });
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=testing.integration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing.integration.test.js","sourceRoot":"","sources":["../src/testing.integration.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAU,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAe,MAAM,YAAY,CAAC;AAEpE,MAAM,QAAS,SAAQ,QAAQ;IACnB,iBAAiB;QACzB,OAAO,IAAI,WAAW,CAAC,sBAAsB,CAAC,CAAC;IACjD,CAAC;IAEkB,KAAK,CAAC,oBAAoB,CAAC,GAAgB;QAC5D,mBAAmB,CAAC,GAAG,CAAC,CAAC;QACzB,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1D,CAAC;CACF;AAED,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,mBAAmB,CAAC,GAAG,CAAC,CAAC;QACzB,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAExD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAElF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QAC3D,MAAM,QAAQ,CAAC,QAAQ,EAAE,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,MAAM,CAAC,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAEpC,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QAC7D,MAAM,QAAQ,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/vitest.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TestCase } from './test-case.js';
|
|
2
|
+
/**
|
|
3
|
+
* Bind Tyravel TestCase lifecycle to Vitest hooks and return the shared instance.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* class AppTest extends TestCase { ... }
|
|
7
|
+
* const t = withTyravelTest(AppTest);
|
|
8
|
+
*
|
|
9
|
+
* it('hits home', async () => {
|
|
10
|
+
* await t.http.get('http://localhost/').assertOk();
|
|
11
|
+
* });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare function withTyravelTest<T extends TestCase>(CaseClass: new () => T): T;
|
|
15
|
+
//# sourceMappingURL=vitest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,QAAQ,EAChD,SAAS,EAAE,UAAU,CAAC,GACrB,CAAC,CAYH"}
|
package/dist/vitest.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { afterEach, beforeEach } from 'vitest';
|
|
2
|
+
/**
|
|
3
|
+
* Bind Tyravel TestCase lifecycle to Vitest hooks and return the shared instance.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* class AppTest extends TestCase { ... }
|
|
7
|
+
* const t = withTyravelTest(AppTest);
|
|
8
|
+
*
|
|
9
|
+
* it('hits home', async () => {
|
|
10
|
+
* await t.http.get('http://localhost/').assertOk();
|
|
11
|
+
* });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function withTyravelTest(CaseClass) {
|
|
15
|
+
const instance = new CaseClass();
|
|
16
|
+
beforeEach(async () => {
|
|
17
|
+
await instance.setUp();
|
|
18
|
+
});
|
|
19
|
+
afterEach(async () => {
|
|
20
|
+
await instance.tearDown();
|
|
21
|
+
});
|
|
22
|
+
return instance;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=vitest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG/C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAC7B,SAAsB;IAEtB,MAAM,QAAQ,GAAG,IAAI,SAAS,EAAE,CAAC;IAEjC,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tyravel/testing",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc -p tsconfig.json",
|
|
15
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@tyravel/auth": "0.1.0",
|
|
19
|
+
"@tyravel/container": "0.1.0",
|
|
20
|
+
"@tyravel/core": "0.1.0",
|
|
21
|
+
"@tyravel/http": "0.1.0"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"vitest": ">=2.0.0"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^22.15.30",
|
|
34
|
+
"vitest": "^3.2.4"
|
|
35
|
+
},
|
|
36
|
+
"description": "Testing utilities for Tyravel applications",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/thesimonharms/tyravel.git",
|
|
41
|
+
"directory": "packages/testing"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
}
|
|
46
|
+
}
|