@flightdev/testing 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Flight Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,203 @@
1
+ # @flight-framework/testing
2
+
3
+ Testing utilities for Flight Framework. Test components, routes, and server actions with ease.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Quick Start](#quick-start)
9
+ - [Component Testing](#component-testing)
10
+ - [Route Testing](#route-testing)
11
+ - [Server Action Testing](#server-action-testing)
12
+ - [API Reference](#api-reference)
13
+ - [License](#license)
14
+
15
+ ---
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install -D @flight-framework/testing
21
+ ```
22
+
23
+ ---
24
+
25
+ ## Quick Start
26
+
27
+ ```typescript
28
+ // vitest.config.ts
29
+ import { defineConfig } from 'vitest/config';
30
+ import { flightTestPlugin } from '@flight-framework/testing/vitest';
31
+
32
+ export default defineConfig({
33
+ plugins: [flightTestPlugin()],
34
+ test: {
35
+ environment: 'jsdom',
36
+ },
37
+ });
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Component Testing
43
+
44
+ ### React Components
45
+
46
+ ```tsx
47
+ import { render, screen } from '@flight-framework/testing/react';
48
+ import { Button } from './Button';
49
+
50
+ test('renders button with text', () => {
51
+ render(<Button>Click me</Button>);
52
+ expect(screen.getByRole('button')).toHaveTextContent('Click me');
53
+ });
54
+
55
+ test('calls onClick when clicked', async () => {
56
+ const onClick = vi.fn();
57
+ const { user } = render(<Button onClick={onClick}>Click</Button>);
58
+
59
+ await user.click(screen.getByRole('button'));
60
+ expect(onClick).toHaveBeenCalled();
61
+ });
62
+ ```
63
+
64
+ ### Vue Components
65
+
66
+ ```typescript
67
+ import { mount } from '@flight-framework/testing/vue';
68
+ import Button from './Button.vue';
69
+
70
+ test('renders button', async () => {
71
+ const wrapper = mount(Button, {
72
+ props: { label: 'Click me' },
73
+ });
74
+
75
+ expect(wrapper.text()).toContain('Click me');
76
+ await wrapper.trigger('click');
77
+ });
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Route Testing
83
+
84
+ ### Test Loaders
85
+
86
+ ```typescript
87
+ import { createTestContext } from '@flight-framework/testing';
88
+ import { loader } from './blog/[slug].page';
89
+
90
+ test('loader returns post data', async () => {
91
+ const ctx = createTestContext({
92
+ params: { slug: 'hello-world' },
93
+ });
94
+
95
+ const data = await loader(ctx);
96
+ expect(data.post.title).toBe('Hello World');
97
+ });
98
+
99
+ test('loader throws 404 for missing post', async () => {
100
+ const ctx = createTestContext({
101
+ params: { slug: 'does-not-exist' },
102
+ });
103
+
104
+ await expect(loader(ctx)).rejects.toThrow('Not Found');
105
+ });
106
+ ```
107
+
108
+ ### Test Actions
109
+
110
+ ```typescript
111
+ import { createTestContext } from '@flight-framework/testing';
112
+ import { action } from './contact.page';
113
+
114
+ test('action creates contact submission', async () => {
115
+ const ctx = createTestContext({
116
+ method: 'POST',
117
+ body: new FormData([
118
+ ['email', 'test@example.com'],
119
+ ['message', 'Hello'],
120
+ ]),
121
+ });
122
+
123
+ const response = await action(ctx);
124
+ expect(response.status).toBe(200);
125
+ });
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Server Action Testing
131
+
132
+ ```typescript
133
+ import { createActionTest } from '@flight-framework/testing';
134
+ import { submitForm } from './actions';
135
+
136
+ test('submitForm validates input', async () => {
137
+ const testAction = createActionTest(submitForm);
138
+
139
+ // Test with invalid data
140
+ const result = await testAction({ email: 'invalid' });
141
+ expect(result.error).toBe('Invalid email');
142
+
143
+ // Test with valid data
144
+ const success = await testAction({ email: 'test@example.com' });
145
+ expect(success.data).toBeDefined();
146
+ });
147
+ ```
148
+
149
+ ---
150
+
151
+ ## API Reference
152
+
153
+ ### createTestContext
154
+
155
+ Create a mock request context for testing loaders and actions.
156
+
157
+ ```typescript
158
+ const ctx = createTestContext({
159
+ url: '/blog/hello',
160
+ method: 'GET',
161
+ params: { slug: 'hello' },
162
+ searchParams: { page: '1' },
163
+ headers: { 'Authorization': 'Bearer token' },
164
+ cookies: { session: 'abc123' },
165
+ body: { data: 'value' },
166
+ });
167
+ ```
168
+
169
+ ### render (React)
170
+
171
+ Render a component with Flight context.
172
+
173
+ ```typescript
174
+ const { user, rerender, unmount } = render(
175
+ <Component prop="value" />,
176
+ {
177
+ route: '/current-path',
178
+ loader: { data: 'from loader' },
179
+ }
180
+ );
181
+ ```
182
+
183
+ ### Mock Utilities
184
+
185
+ ```typescript
186
+ import { mockFetch, mockLoader, mockAction } from '@flight-framework/testing';
187
+
188
+ // Mock fetch globally
189
+ mockFetch({
190
+ '/api/users': { users: [] },
191
+ });
192
+
193
+ // Mock a loader
194
+ mockLoader('./blog/[slug].page', async () => ({
195
+ post: { title: 'Mocked' },
196
+ }));
197
+ ```
198
+
199
+ ---
200
+
201
+ ## License
202
+
203
+ MIT
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Browser Testing Utilities for Flight Framework
3
+ *
4
+ * Playwright integration for E2E and browser testing.
5
+ * Optional, non-imposing - use only if you need browser testing.
6
+ *
7
+ * REQUIRES: npm install -D playwright @playwright/test
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { createFlightBrowser } from '@flightdev/testing/browser';
12
+ *
13
+ * test('homepage loads', async () => {
14
+ * const { page, goto, close } = await createFlightBrowser();
15
+ * await goto('/');
16
+ * expect(await page.title()).toBe('My App');
17
+ * await close();
18
+ * });
19
+ * ```
20
+ */
21
+ /**
22
+ * Options for creating a Flight browser test context
23
+ */
24
+ export interface FlightBrowserOptions {
25
+ /** Base URL for navigation (default: http://localhost:3000) */
26
+ baseUrl?: string;
27
+ /** Browser type to use */
28
+ browser?: 'chromium' | 'firefox' | 'webkit';
29
+ /** Run in headless mode (default: true) */
30
+ headless?: boolean;
31
+ /** Timeout for operations in ms */
32
+ timeout?: number;
33
+ /** Viewport size */
34
+ viewport?: {
35
+ width: number;
36
+ height: number;
37
+ };
38
+ }
39
+ /**
40
+ * Flight browser test context
41
+ *
42
+ * Page, browser, and context are Playwright types when Playwright is installed.
43
+ */
44
+ export interface FlightBrowserContext {
45
+ /** Playwright page instance */
46
+ page: unknown;
47
+ /** Playwright browser instance */
48
+ browser: unknown;
49
+ /** Browser context */
50
+ context: unknown;
51
+ /** Navigate to a path */
52
+ goto: (path: string) => Promise<void>;
53
+ /** Wait for navigation to complete */
54
+ waitForNavigation: () => Promise<void>;
55
+ /** Wait for Flight hydration to complete */
56
+ waitForHydration: () => Promise<void>;
57
+ /** Close browser */
58
+ close: () => Promise<void>;
59
+ }
60
+ /**
61
+ * Create a Flight browser test context
62
+ *
63
+ * Requires Playwright to be installed:
64
+ * ```bash
65
+ * npm install -D playwright
66
+ * npx playwright install
67
+ * ```
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * import { createFlightBrowser } from '@flightdev/testing/browser';
72
+ *
73
+ * describe('E2E tests', () => {
74
+ * let browser: FlightBrowserContext;
75
+ *
76
+ * beforeAll(async () => {
77
+ * browser = await createFlightBrowser({ baseUrl: 'http://localhost:3000' });
78
+ * });
79
+ *
80
+ * afterAll(async () => {
81
+ * await browser.close();
82
+ * });
83
+ *
84
+ * test('navigation works', async () => {
85
+ * await browser.goto('/about');
86
+ * await browser.waitForHydration();
87
+ * const page = browser.page as import('playwright').Page;
88
+ * expect(await page.textContent('h1')).toBe('About');
89
+ * });
90
+ * });
91
+ * ```
92
+ */
93
+ export declare function createFlightBrowser(options?: FlightBrowserOptions): Promise<FlightBrowserContext>;
94
+ /**
95
+ * Page object base class for structured E2E testing
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * import type { Page } from 'playwright';
100
+ *
101
+ * class LoginPage extends PageObject {
102
+ * async login(email: string, password: string) {
103
+ * await this.page.fill('[name="email"]', email);
104
+ * await this.page.fill('[name="password"]', password);
105
+ * await this.page.click('button[type="submit"]');
106
+ * }
107
+ * }
108
+ *
109
+ * test('login flow', async () => {
110
+ * const { page } = await createFlightBrowser();
111
+ * const loginPage = new LoginPage(page);
112
+ * await loginPage.goto('/login');
113
+ * await loginPage.login('user@example.com', 'password');
114
+ * });
115
+ * ```
116
+ */
117
+ export declare class PageObject {
118
+ protected page: unknown;
119
+ protected baseUrl: string;
120
+ constructor(page: unknown, baseUrl?: string);
121
+ goto(path: string): Promise<void>;
122
+ waitForNavigation(): Promise<void>;
123
+ getText(selector: string): Promise<string | null>;
124
+ click(selector: string): Promise<void>;
125
+ fill(selector: string, value: string): Promise<void>;
126
+ }
127
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;IAE5C,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,oBAAoB;IACpB,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAChD;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACjC,+BAA+B;IAC/B,IAAI,EAAE,OAAO,CAAC;IAEd,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IAEjB,sBAAsB;IACtB,OAAO,EAAE,OAAO,CAAC;IAEjB,yBAAyB;IACzB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtC,sCAAsC;IACtC,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvC,4CAA4C;IAC5C,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtC,oBAAoB;IACpB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,mBAAmB,CACrC,OAAO,GAAE,oBAAyB,GACnC,OAAO,CAAC,oBAAoB,CAAC,CA4D/B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,UAAU;IACnB,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEd,IAAI,EAAE,OAAO,EAAE,OAAO,SAA0B;IAKtD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjC,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAKlC,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAKjD,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAI7D"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Browser Testing Utilities for Flight Framework
3
+ *
4
+ * Playwright integration for E2E and browser testing.
5
+ * Optional, non-imposing - use only if you need browser testing.
6
+ *
7
+ * REQUIRES: npm install -D playwright @playwright/test
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { createFlightBrowser } from '@flightdev/testing/browser';
12
+ *
13
+ * test('homepage loads', async () => {
14
+ * const { page, goto, close } = await createFlightBrowser();
15
+ * await goto('/');
16
+ * expect(await page.title()).toBe('My App');
17
+ * await close();
18
+ * });
19
+ * ```
20
+ */
21
+ /**
22
+ * Create a Flight browser test context
23
+ *
24
+ * Requires Playwright to be installed:
25
+ * ```bash
26
+ * npm install -D playwright
27
+ * npx playwright install
28
+ * ```
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * import { createFlightBrowser } from '@flightdev/testing/browser';
33
+ *
34
+ * describe('E2E tests', () => {
35
+ * let browser: FlightBrowserContext;
36
+ *
37
+ * beforeAll(async () => {
38
+ * browser = await createFlightBrowser({ baseUrl: 'http://localhost:3000' });
39
+ * });
40
+ *
41
+ * afterAll(async () => {
42
+ * await browser.close();
43
+ * });
44
+ *
45
+ * test('navigation works', async () => {
46
+ * await browser.goto('/about');
47
+ * await browser.waitForHydration();
48
+ * const page = browser.page as import('playwright').Page;
49
+ * expect(await page.textContent('h1')).toBe('About');
50
+ * });
51
+ * });
52
+ * ```
53
+ */
54
+ export async function createFlightBrowser(options = {}) {
55
+ const { baseUrl = 'http://localhost:3000', browser: browserType = 'chromium', headless = true, timeout = 30000, viewport = { width: 1280, height: 720 }, } = options;
56
+ // Dynamic import to make playwright optional
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ let playwright;
59
+ try {
60
+ // @ts-expect-error - playwright is an optional peer dependency
61
+ playwright = await import('playwright');
62
+ }
63
+ catch {
64
+ throw new Error('Playwright is not installed. Install it with:\n' +
65
+ ' npm install -D playwright\n' +
66
+ ' npx playwright install');
67
+ }
68
+ const browser = await playwright[browserType].launch({ headless });
69
+ const context = await browser.newContext({ viewport });
70
+ const page = await context.newPage();
71
+ page.setDefaultTimeout(timeout);
72
+ const goto = async (path) => {
73
+ const url = path.startsWith('http') ? path : `${baseUrl}${path}`;
74
+ await page.goto(url);
75
+ };
76
+ const waitForNavigation = async () => {
77
+ await page.waitForLoadState('networkidle');
78
+ };
79
+ const waitForHydration = async () => {
80
+ // Wait for Flight's hydration marker
81
+ await page.waitForFunction(() => {
82
+ return window.__FLIGHT_HYDRATED__ === true ||
83
+ document.documentElement.hasAttribute('data-flight-hydrated');
84
+ }, { timeout });
85
+ };
86
+ const close = async () => {
87
+ await context.close();
88
+ await browser.close();
89
+ };
90
+ return {
91
+ page,
92
+ browser,
93
+ context,
94
+ goto,
95
+ waitForNavigation,
96
+ waitForHydration,
97
+ close,
98
+ };
99
+ }
100
+ /**
101
+ * Page object base class for structured E2E testing
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * import type { Page } from 'playwright';
106
+ *
107
+ * class LoginPage extends PageObject {
108
+ * async login(email: string, password: string) {
109
+ * await this.page.fill('[name="email"]', email);
110
+ * await this.page.fill('[name="password"]', password);
111
+ * await this.page.click('button[type="submit"]');
112
+ * }
113
+ * }
114
+ *
115
+ * test('login flow', async () => {
116
+ * const { page } = await createFlightBrowser();
117
+ * const loginPage = new LoginPage(page);
118
+ * await loginPage.goto('/login');
119
+ * await loginPage.login('user@example.com', 'password');
120
+ * });
121
+ * ```
122
+ */
123
+ export class PageObject {
124
+ page;
125
+ baseUrl;
126
+ constructor(page, baseUrl = 'http://localhost:3000') {
127
+ this.page = page;
128
+ this.baseUrl = baseUrl;
129
+ }
130
+ async goto(path) {
131
+ const p = this.page;
132
+ await p.goto(`${this.baseUrl}${path}`);
133
+ }
134
+ async waitForNavigation() {
135
+ const p = this.page;
136
+ await p.waitForLoadState('networkidle');
137
+ }
138
+ async getText(selector) {
139
+ const p = this.page;
140
+ return p.textContent(selector);
141
+ }
142
+ async click(selector) {
143
+ const p = this.page;
144
+ await p.click(selector);
145
+ }
146
+ async fill(selector, value) {
147
+ const p = this.page;
148
+ await p.fill(selector, value);
149
+ }
150
+ }
151
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @flightdev/testing - Testing Utilities
3
+ *
4
+ * Provides helpers for testing Flight applications with Vitest.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { createMockRequest, renderPage } from '@flightdev/testing';
9
+ *
10
+ * test('homepage renders', async () => {
11
+ * const { html, status } = await renderPage('/');
12
+ * expect(status).toBe(200);
13
+ * expect(html).toContain('Welcome');
14
+ * });
15
+ *
16
+ * test('API endpoint works', async () => {
17
+ * const request = createMockRequest({
18
+ * method: 'POST',
19
+ * path: '/api/users',
20
+ * body: { name: 'Test User' },
21
+ * });
22
+ *
23
+ * const response = await handler(request);
24
+ * expect(response.status).toBe(201);
25
+ * });
26
+ * ```
27
+ */
28
+ export interface MockRequestOptions {
29
+ /** HTTP method */
30
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
31
+ /** Request path */
32
+ path: string;
33
+ /** Request body (will be JSON stringified) */
34
+ body?: unknown;
35
+ /** Request headers */
36
+ headers?: Record<string, string>;
37
+ /** Query parameters */
38
+ query?: Record<string, string>;
39
+ /** Cookies */
40
+ cookies?: Record<string, string>;
41
+ }
42
+ export interface RenderResult {
43
+ /** Rendered HTML string */
44
+ html: string;
45
+ /** HTTP status code */
46
+ status: number;
47
+ /** Response headers */
48
+ headers: Headers;
49
+ }
50
+ export interface MockAdapterOptions<T = unknown> {
51
+ /** Adapter name */
52
+ name: string;
53
+ /** Mock data to return */
54
+ data?: T;
55
+ /** Whether to simulate errors */
56
+ shouldError?: boolean;
57
+ /** Delay in ms to simulate latency */
58
+ delay?: number;
59
+ }
60
+ /**
61
+ * Create a mock Request object for testing API routes
62
+ */
63
+ export declare function createMockRequest(options: MockRequestOptions): Request;
64
+ /**
65
+ * Create a mock Response object for testing
66
+ */
67
+ export declare function createMockResponse(options: {
68
+ status?: number;
69
+ body?: unknown;
70
+ headers?: Record<string, string>;
71
+ }): Response;
72
+ /**
73
+ * Create a mock adapter for testing
74
+ * Works with any Flight adapter interface (db, auth, storage, etc.)
75
+ */
76
+ export declare function createMockAdapter<T extends Record<string, (...args: unknown[]) => unknown>>(options: MockAdapterOptions & {
77
+ methods: Partial<T>;
78
+ }): T;
79
+ /**
80
+ * Render a page and return the result
81
+ * Note: This requires the Flight app to be set up with the test server
82
+ */
83
+ export declare function renderPage(path: string, options?: {
84
+ headers?: Record<string, string>;
85
+ cookies?: Record<string, string>;
86
+ }): Promise<RenderResult>;
87
+ /**
88
+ * Assert that a response is a redirect
89
+ */
90
+ export declare function expectRedirect(response: Response, expectedLocation?: string): void;
91
+ /**
92
+ * Assert that a response is JSON and return the parsed body
93
+ */
94
+ export declare function expectJSON<T = unknown>(response: Response): Promise<T>;
95
+ /**
96
+ * Assert that a response is an error with specific status
97
+ */
98
+ export declare function expectError(response: Response, expectedStatus?: number): void;
99
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAMH,MAAM,WAAW,kBAAkB;IAC/B,kBAAkB;IAClB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;IACrD,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,uBAAuB;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,cAAc;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IACzB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,OAAO,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,OAAO;IAC3C,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,iCAAiC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAqCtE;AAMD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC,GAAG,QAAQ,CAaX;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,EACvF,OAAO,EAAE,kBAAkB,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;CAAE,GACtD,CAAC,CAkCH;AAMD;;;GAGG;AACH,wBAAsB,UAAU,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IACL,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B,GACP,OAAO,CAAC,YAAY,CAAC,CAgBvB;AAMD;;GAEG;AACH,wBAAgB,cAAc,CAC1B,QAAQ,EAAE,QAAQ,EAClB,gBAAgB,CAAC,EAAE,MAAM,GAC1B,IAAI,CAaN;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAU5E;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAU7E"}
package/dist/index.js ADDED
@@ -0,0 +1,166 @@
1
+ /**
2
+ * @flightdev/testing - Testing Utilities
3
+ *
4
+ * Provides helpers for testing Flight applications with Vitest.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { createMockRequest, renderPage } from '@flightdev/testing';
9
+ *
10
+ * test('homepage renders', async () => {
11
+ * const { html, status } = await renderPage('/');
12
+ * expect(status).toBe(200);
13
+ * expect(html).toContain('Welcome');
14
+ * });
15
+ *
16
+ * test('API endpoint works', async () => {
17
+ * const request = createMockRequest({
18
+ * method: 'POST',
19
+ * path: '/api/users',
20
+ * body: { name: 'Test User' },
21
+ * });
22
+ *
23
+ * const response = await handler(request);
24
+ * expect(response.status).toBe(201);
25
+ * });
26
+ * ```
27
+ */
28
+ // ============================================================================
29
+ // Mock Request
30
+ // ============================================================================
31
+ /**
32
+ * Create a mock Request object for testing API routes
33
+ */
34
+ export function createMockRequest(options) {
35
+ const { method = 'GET', path, body, headers = {}, query = {}, cookies = {}, } = options;
36
+ // Build URL with query params
37
+ const url = new URL(path, 'http://localhost:3000');
38
+ for (const [key, value] of Object.entries(query)) {
39
+ url.searchParams.set(key, value);
40
+ }
41
+ // Build headers
42
+ const requestHeaders = new Headers(headers);
43
+ // Add cookies
44
+ if (Object.keys(cookies).length > 0) {
45
+ const cookieString = Object.entries(cookies)
46
+ .map(([key, value]) => `${key}=${value}`)
47
+ .join('; ');
48
+ requestHeaders.set('Cookie', cookieString);
49
+ }
50
+ // Add content-type for body
51
+ if (body && !requestHeaders.has('Content-Type')) {
52
+ requestHeaders.set('Content-Type', 'application/json');
53
+ }
54
+ return new Request(url.toString(), {
55
+ method,
56
+ headers: requestHeaders,
57
+ body: body ? JSON.stringify(body) : undefined,
58
+ });
59
+ }
60
+ // ============================================================================
61
+ // Mock Response
62
+ // ============================================================================
63
+ /**
64
+ * Create a mock Response object for testing
65
+ */
66
+ export function createMockResponse(options) {
67
+ const { status = 200, body, headers = {} } = options;
68
+ const responseHeaders = new Headers(headers);
69
+ if (body && !responseHeaders.has('Content-Type')) {
70
+ responseHeaders.set('Content-Type', 'application/json');
71
+ }
72
+ return new Response(body ? JSON.stringify(body) : null, { status, headers: responseHeaders });
73
+ }
74
+ // ============================================================================
75
+ // Mock Adapter
76
+ // ============================================================================
77
+ /**
78
+ * Create a mock adapter for testing
79
+ * Works with any Flight adapter interface (db, auth, storage, etc.)
80
+ */
81
+ export function createMockAdapter(options) {
82
+ const { name, data, shouldError, delay, methods } = options;
83
+ const mockAdapter = {
84
+ name,
85
+ };
86
+ // Create mock methods
87
+ for (const [key, originalMethod] of Object.entries(methods)) {
88
+ mockAdapter[key] = async (...args) => {
89
+ // Simulate delay
90
+ if (delay) {
91
+ await new Promise(resolve => setTimeout(resolve, delay));
92
+ }
93
+ // Simulate error
94
+ if (shouldError) {
95
+ throw new Error(`Mock error in ${name}.${key}`);
96
+ }
97
+ // Return mock data or call original
98
+ if (data !== undefined) {
99
+ return data;
100
+ }
101
+ if (typeof originalMethod === 'function') {
102
+ return originalMethod(...args);
103
+ }
104
+ return undefined;
105
+ };
106
+ }
107
+ return mockAdapter;
108
+ }
109
+ // ============================================================================
110
+ // Render Utilities
111
+ // ============================================================================
112
+ /**
113
+ * Render a page and return the result
114
+ * Note: This requires the Flight app to be set up with the test server
115
+ */
116
+ export async function renderPage(path, options = {}) {
117
+ const request = createMockRequest({
118
+ method: 'GET',
119
+ path,
120
+ headers: options.headers,
121
+ cookies: options.cookies,
122
+ });
123
+ // This is a placeholder - actual implementation depends on Flight's server
124
+ // In practice, you'd import the app and call it:
125
+ // const response = await app.fetch(request);
126
+ throw new Error('renderPage requires a Flight app instance. ' +
127
+ 'Import createTestServer from @flightdev/testing/vitest');
128
+ }
129
+ // ============================================================================
130
+ // Assertions Helpers
131
+ // ============================================================================
132
+ /**
133
+ * Assert that a response is a redirect
134
+ */
135
+ export function expectRedirect(response, expectedLocation) {
136
+ const status = response.status;
137
+ const location = response.headers.get('Location');
138
+ if (status < 300 || status >= 400) {
139
+ throw new Error(`Expected redirect status (3xx), got ${status}`);
140
+ }
141
+ if (expectedLocation && location !== expectedLocation) {
142
+ throw new Error(`Expected redirect to "${expectedLocation}", got "${location}"`);
143
+ }
144
+ }
145
+ /**
146
+ * Assert that a response is JSON and return the parsed body
147
+ */
148
+ export async function expectJSON(response) {
149
+ const contentType = response.headers.get('Content-Type');
150
+ if (!contentType?.includes('application/json')) {
151
+ throw new Error(`Expected JSON response, got ${contentType}`);
152
+ }
153
+ return response.json();
154
+ }
155
+ /**
156
+ * Assert that a response is an error with specific status
157
+ */
158
+ export function expectError(response, expectedStatus) {
159
+ if (response.ok) {
160
+ throw new Error(`Expected error response, got ${response.status}`);
161
+ }
162
+ if (expectedStatus && response.status !== expectedStatus) {
163
+ throw new Error(`Expected status ${expectedStatus}, got ${response.status}`);
164
+ }
165
+ }
166
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Vitest Monorepo Configuration Utilities
3
+ *
4
+ * Helpers for configuring Vitest in Flight monorepo setups.
5
+ * Optional - use only if you have a monorepo with multiple packages.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // vitest.config.ts (root)
10
+ * import { defineConfig } from 'vitest/config';
11
+ * import { createFlightProjects, flightCoverageConfig } from '@flightdev/testing/projects';
12
+ *
13
+ * export default defineConfig({
14
+ * test: {
15
+ * projects: createFlightProjects(['web', 'admin'], ['ui', 'utils']),
16
+ * coverage: flightCoverageConfig({
17
+ * threshold: { statements: 80 },
18
+ * }),
19
+ * },
20
+ * });
21
+ * ```
22
+ */
23
+ /**
24
+ * Project configuration for Vitest
25
+ */
26
+ export interface FlightProjectConfig {
27
+ /** Project name for display */
28
+ name: string;
29
+ /** Root directory relative to monorepo root */
30
+ root: string;
31
+ /** Test environment */
32
+ environment?: 'node' | 'jsdom' | 'happy-dom';
33
+ /** Include patterns */
34
+ include?: string[];
35
+ /** Exclude patterns */
36
+ exclude?: string[];
37
+ /** Setup files */
38
+ setupFiles?: string[];
39
+ }
40
+ /**
41
+ * Create project configurations for Flight monorepo
42
+ *
43
+ * @param apps - Array of app names in apps/ directory
44
+ * @param packages - Array of package names in packages/ directory
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * // vitest.config.ts
49
+ * import { createFlightProjects } from '@flightdev/testing/projects';
50
+ *
51
+ * export default defineConfig({
52
+ * test: {
53
+ * projects: createFlightProjects(['web', 'admin'], ['ui', 'utils']),
54
+ * },
55
+ * });
56
+ * ```
57
+ */
58
+ export declare function createFlightProjects(apps?: string[], packages?: string[]): FlightProjectConfig[];
59
+ /**
60
+ * Coverage configuration options
61
+ */
62
+ export interface FlightCoverageOptions {
63
+ /** Coverage provider */
64
+ provider?: 'v8' | 'istanbul';
65
+ /** Coverage thresholds */
66
+ threshold?: {
67
+ statements?: number;
68
+ branches?: number;
69
+ functions?: number;
70
+ lines?: number;
71
+ };
72
+ /** Patterns to exclude from coverage */
73
+ exclude?: string[];
74
+ /** Enable HTML reporter */
75
+ htmlReporter?: boolean;
76
+ }
77
+ /**
78
+ * Create coverage configuration for Flight projects
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * import { flightCoverageConfig } from '@flightdev/testing/projects';
83
+ *
84
+ * export default defineConfig({
85
+ * test: {
86
+ * coverage: flightCoverageConfig({
87
+ * threshold: { statements: 80, branches: 70 },
88
+ * }),
89
+ * },
90
+ * });
91
+ * ```
92
+ */
93
+ export declare function flightCoverageConfig(options?: FlightCoverageOptions): Record<string, unknown>;
94
+ /**
95
+ * Create a workspace-aware Vitest configuration
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * import { createFlightVitestConfig } from '@flightdev/testing/projects';
100
+ *
101
+ * export default createFlightVitestConfig({
102
+ * apps: ['web', 'admin'],
103
+ * packages: ['ui', 'utils'],
104
+ * coverage: { threshold: { statements: 80 } },
105
+ * });
106
+ * ```
107
+ */
108
+ export declare function createFlightVitestConfig(options: {
109
+ apps?: string[];
110
+ packages?: string[];
111
+ coverage?: FlightCoverageOptions;
112
+ }): Record<string, unknown>;
113
+ //# sourceMappingURL=projects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../src/projects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IAEb,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IAEb,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;IAE7C,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,kBAAkB;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAChC,IAAI,GAAE,MAAM,EAAO,EACnB,QAAQ,GAAE,MAAM,EAAO,GACxB,mBAAmB,EAAE,CAwBvB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC,wBAAwB;IACxB,QAAQ,CAAC,EAAE,IAAI,GAAG,UAAU,CAAC;IAE7B,0BAA0B;IAC1B,SAAS,CAAC,EAAE;QACR,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IAEF,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,2BAA2B;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,qBAA0B,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsBjG;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE;IAC9C,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CACpC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAU1B"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Vitest Monorepo Configuration Utilities
3
+ *
4
+ * Helpers for configuring Vitest in Flight monorepo setups.
5
+ * Optional - use only if you have a monorepo with multiple packages.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // vitest.config.ts (root)
10
+ * import { defineConfig } from 'vitest/config';
11
+ * import { createFlightProjects, flightCoverageConfig } from '@flightdev/testing/projects';
12
+ *
13
+ * export default defineConfig({
14
+ * test: {
15
+ * projects: createFlightProjects(['web', 'admin'], ['ui', 'utils']),
16
+ * coverage: flightCoverageConfig({
17
+ * threshold: { statements: 80 },
18
+ * }),
19
+ * },
20
+ * });
21
+ * ```
22
+ */
23
+ /**
24
+ * Create project configurations for Flight monorepo
25
+ *
26
+ * @param apps - Array of app names in apps/ directory
27
+ * @param packages - Array of package names in packages/ directory
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * // vitest.config.ts
32
+ * import { createFlightProjects } from '@flightdev/testing/projects';
33
+ *
34
+ * export default defineConfig({
35
+ * test: {
36
+ * projects: createFlightProjects(['web', 'admin'], ['ui', 'utils']),
37
+ * },
38
+ * });
39
+ * ```
40
+ */
41
+ export function createFlightProjects(apps = [], packages = []) {
42
+ const projects = [];
43
+ for (const app of apps) {
44
+ projects.push({
45
+ name: `app:${app}`,
46
+ root: `./apps/${app}`,
47
+ environment: 'jsdom',
48
+ include: ['**/*.{test,spec}.{ts,tsx}'],
49
+ exclude: ['**/node_modules/**', '**/dist/**'],
50
+ });
51
+ }
52
+ for (const pkg of packages) {
53
+ projects.push({
54
+ name: `pkg:${pkg}`,
55
+ root: `./packages/${pkg}`,
56
+ environment: 'node',
57
+ include: ['**/*.{test,spec}.{ts,tsx}'],
58
+ exclude: ['**/node_modules/**', '**/dist/**'],
59
+ });
60
+ }
61
+ return projects;
62
+ }
63
+ /**
64
+ * Create coverage configuration for Flight projects
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * import { flightCoverageConfig } from '@flightdev/testing/projects';
69
+ *
70
+ * export default defineConfig({
71
+ * test: {
72
+ * coverage: flightCoverageConfig({
73
+ * threshold: { statements: 80, branches: 70 },
74
+ * }),
75
+ * },
76
+ * });
77
+ * ```
78
+ */
79
+ export function flightCoverageConfig(options = {}) {
80
+ const { provider = 'v8', threshold, exclude = [], htmlReporter = true, } = options;
81
+ return {
82
+ provider,
83
+ reporter: htmlReporter ? ['text', 'html', 'lcov'] : ['text', 'lcov'],
84
+ exclude: [
85
+ '**/node_modules/**',
86
+ '**/dist/**',
87
+ '**/*.d.ts',
88
+ '**/tests/**',
89
+ '**/*.test.{ts,tsx}',
90
+ '**/*.spec.{ts,tsx}',
91
+ ...exclude,
92
+ ],
93
+ thresholds: threshold,
94
+ };
95
+ }
96
+ /**
97
+ * Create a workspace-aware Vitest configuration
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * import { createFlightVitestConfig } from '@flightdev/testing/projects';
102
+ *
103
+ * export default createFlightVitestConfig({
104
+ * apps: ['web', 'admin'],
105
+ * packages: ['ui', 'utils'],
106
+ * coverage: { threshold: { statements: 80 } },
107
+ * });
108
+ * ```
109
+ */
110
+ export function createFlightVitestConfig(options) {
111
+ const { apps = [], packages = [], coverage } = options;
112
+ return {
113
+ test: {
114
+ globals: true,
115
+ projects: createFlightProjects(apps, packages),
116
+ coverage: coverage ? flightCoverageConfig(coverage) : undefined,
117
+ },
118
+ };
119
+ }
120
+ //# sourceMappingURL=projects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects.js","sourceRoot":"","sources":["../src/projects.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAyBH;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,oBAAoB,CAChC,OAAiB,EAAE,EACnB,WAAqB,EAAE;IAEvB,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAE3C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,OAAO,GAAG,EAAE;YAClB,IAAI,EAAE,UAAU,GAAG,EAAE;YACrB,WAAW,EAAE,OAAO;YACpB,OAAO,EAAE,CAAC,2BAA2B,CAAC;YACtC,OAAO,EAAE,CAAC,oBAAoB,EAAE,YAAY,CAAC;SAChD,CAAC,CAAC;IACP,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QACzB,QAAQ,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,OAAO,GAAG,EAAE;YAClB,IAAI,EAAE,cAAc,GAAG,EAAE;YACzB,WAAW,EAAE,MAAM;YACnB,OAAO,EAAE,CAAC,2BAA2B,CAAC;YACtC,OAAO,EAAE,CAAC,oBAAoB,EAAE,YAAY,CAAC;SAChD,CAAC,CAAC;IACP,CAAC;IAED,OAAO,QAAQ,CAAC;AACpB,CAAC;AAwBD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAiC,EAAE;IACpE,MAAM,EACF,QAAQ,GAAG,IAAI,EACf,SAAS,EACT,OAAO,GAAG,EAAE,EACZ,YAAY,GAAG,IAAI,GACtB,GAAG,OAAO,CAAC;IAEZ,OAAO;QACH,QAAQ;QACR,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC;QACpE,OAAO,EAAE;YACL,oBAAoB;YACpB,YAAY;YACZ,WAAW;YACX,aAAa;YACb,oBAAoB;YACpB,oBAAoB;YACpB,GAAG,OAAO;SACb;QACD,UAAU,EAAE,SAAS;KACxB,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAIxC;IACG,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,QAAQ,GAAG,EAAE,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAEvD,OAAO;QACH,IAAI,EAAE;YACF,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,oBAAoB,CAAC,IAAI,EAAE,QAAQ,CAAC;YAC9C,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;SAClE;KACJ,CAAC;AACN,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @flightdev/testing/vitest - Vitest Integration
3
+ *
4
+ * Provides Vitest-specific utilities for testing Flight applications.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { createTestServer, withTestServer } from '@flightdev/testing/vitest';
9
+ *
10
+ * describe('API Routes', () => {
11
+ * const server = createTestServer({
12
+ * routesDir: './src/routes',
13
+ * });
14
+ *
15
+ * beforeAll(() => server.start());
16
+ * afterAll(() => server.stop());
17
+ *
18
+ * test('GET /api/users', async () => {
19
+ * const response = await server.fetch('/api/users');
20
+ * expect(response.status).toBe(200);
21
+ * });
22
+ * });
23
+ * ```
24
+ */
25
+ import { type RenderResult } from './index.js';
26
+ export interface TestServerOptions {
27
+ /** Routes directory to load */
28
+ routesDir?: string;
29
+ /** Port to run on (default: random available) */
30
+ port?: number;
31
+ /** Environment variables to set */
32
+ env?: Record<string, string>;
33
+ }
34
+ export interface TestServer {
35
+ /** Server URL */
36
+ url: string;
37
+ /** Port number */
38
+ port: number;
39
+ /** Fetch a path from the test server */
40
+ fetch: (path: string, init?: RequestInit) => Promise<Response>;
41
+ /** Render a page and return HTML */
42
+ renderPage: (path: string) => Promise<RenderResult>;
43
+ /** Start the server */
44
+ start: () => Promise<void>;
45
+ /** Stop the server */
46
+ stop: () => Promise<void>;
47
+ }
48
+ /**
49
+ * Create a test server instance for integration testing
50
+ */
51
+ export declare function createTestServer(options?: TestServerOptions): TestServer;
52
+ /**
53
+ * HOF to wrap tests with a test server
54
+ */
55
+ export declare function withTestServer(options: TestServerOptions, testFn: (server: TestServer) => Promise<void> | void): () => Promise<void>;
56
+ /**
57
+ * Custom Vitest matchers for Flight testing
58
+ * Add to vitest.setup.ts: expect.extend(flightMatchers)
59
+ */
60
+ export declare const flightMatchers: {
61
+ toBeSuccessResponse(received: Response): {
62
+ pass: boolean;
63
+ message: () => string;
64
+ };
65
+ toBeRedirect(received: Response, expectedLocation?: string): {
66
+ pass: boolean;
67
+ message: () => string;
68
+ };
69
+ toHaveStatus(received: Response, expected: number): {
70
+ pass: boolean;
71
+ message: () => string;
72
+ };
73
+ };
74
+ //# sourceMappingURL=vitest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAMlE,MAAM,WAAW,iBAAiB;IAC9B,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACvB,iBAAiB;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/D,oCAAoC;IACpC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACpD,uBAAuB;IACvB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,sBAAsB;IACtB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAMD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,iBAAsB,GAAG,UAAU,CAiE5E;AAMD;;GAEG;AACH,wBAAgB,cAAc,CAC1B,OAAO,EAAE,iBAAiB,EAC1B,MAAM,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GACrD,MAAM,OAAO,CAAC,IAAI,CAAC,CAWrB;AAMD;;;GAGG;AACH,eAAO,MAAM,cAAc;kCACO,QAAQ;;;;2BAWf,QAAQ,qBAAqB,MAAM;;;;2BAoBnC,QAAQ,YAAY,MAAM;;;;CAUpD,CAAC"}
package/dist/vitest.js ADDED
@@ -0,0 +1,157 @@
1
+ /**
2
+ * @flightdev/testing/vitest - Vitest Integration
3
+ *
4
+ * Provides Vitest-specific utilities for testing Flight applications.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { createTestServer, withTestServer } from '@flightdev/testing/vitest';
9
+ *
10
+ * describe('API Routes', () => {
11
+ * const server = createTestServer({
12
+ * routesDir: './src/routes',
13
+ * });
14
+ *
15
+ * beforeAll(() => server.start());
16
+ * afterAll(() => server.stop());
17
+ *
18
+ * test('GET /api/users', async () => {
19
+ * const response = await server.fetch('/api/users');
20
+ * expect(response.status).toBe(200);
21
+ * });
22
+ * });
23
+ * ```
24
+ */
25
+ import { createMockRequest } from './index.js';
26
+ // ============================================================================
27
+ // Test Server
28
+ // ============================================================================
29
+ /**
30
+ * Create a test server instance for integration testing
31
+ */
32
+ export function createTestServer(options = {}) {
33
+ const { port = 0, env = {} } = options;
34
+ let serverUrl = '';
35
+ let serverPort = port;
36
+ let isRunning = false;
37
+ // Set environment variables
38
+ for (const [key, value] of Object.entries(env)) {
39
+ process.env[key] = value;
40
+ }
41
+ return {
42
+ get url() {
43
+ return serverUrl;
44
+ },
45
+ get port() {
46
+ return serverPort;
47
+ },
48
+ async fetch(path, init) {
49
+ if (!isRunning) {
50
+ throw new Error('Test server is not running. Call start() first.');
51
+ }
52
+ const url = new URL(path, serverUrl);
53
+ return fetch(url.toString(), init);
54
+ },
55
+ async renderPage(path) {
56
+ const response = await this.fetch(path);
57
+ const html = await response.text();
58
+ return {
59
+ html,
60
+ status: response.status,
61
+ headers: response.headers,
62
+ };
63
+ },
64
+ async start() {
65
+ if (isRunning)
66
+ return;
67
+ // In a real implementation, this would:
68
+ // 1. Import the Flight app
69
+ // 2. Start it on an available port
70
+ // 3. Store the URL
71
+ // For now, use a placeholder port
72
+ serverPort = port || 3999;
73
+ serverUrl = `http://localhost:${serverPort}`;
74
+ isRunning = true;
75
+ console.log(`[Flight Test] Server started at ${serverUrl}`);
76
+ },
77
+ async stop() {
78
+ if (!isRunning)
79
+ return;
80
+ // Clean up server
81
+ isRunning = false;
82
+ console.log('[Flight Test] Server stopped');
83
+ },
84
+ };
85
+ }
86
+ // ============================================================================
87
+ // Test Wrapper
88
+ // ============================================================================
89
+ /**
90
+ * HOF to wrap tests with a test server
91
+ */
92
+ export function withTestServer(options, testFn) {
93
+ return async () => {
94
+ const server = createTestServer(options);
95
+ await server.start();
96
+ try {
97
+ await testFn(server);
98
+ }
99
+ finally {
100
+ await server.stop();
101
+ }
102
+ };
103
+ }
104
+ // ============================================================================
105
+ // Vitest Matchers
106
+ // ============================================================================
107
+ /**
108
+ * Custom Vitest matchers for Flight testing
109
+ * Add to vitest.setup.ts: expect.extend(flightMatchers)
110
+ */
111
+ export const flightMatchers = {
112
+ toBeSuccessResponse(received) {
113
+ const pass = received.ok;
114
+ return {
115
+ pass,
116
+ message: () => pass
117
+ ? `Expected response not to be successful, got ${received.status}`
118
+ : `Expected successful response (2xx), got ${received.status}`,
119
+ };
120
+ },
121
+ toBeRedirect(received, expectedLocation) {
122
+ const isRedirect = received.status >= 300 && received.status < 400;
123
+ const location = received.headers.get('Location');
124
+ const locationMatches = !expectedLocation || location === expectedLocation;
125
+ const pass = isRedirect && locationMatches;
126
+ return {
127
+ pass,
128
+ message: () => {
129
+ if (!isRedirect) {
130
+ return `Expected redirect (3xx), got ${received.status}`;
131
+ }
132
+ if (!locationMatches) {
133
+ return `Expected redirect to "${expectedLocation}", got "${location}"`;
134
+ }
135
+ return `Expected no redirect, got ${received.status} to ${location}`;
136
+ },
137
+ };
138
+ },
139
+ toHaveStatus(received, expected) {
140
+ const pass = received.status === expected;
141
+ return {
142
+ pass,
143
+ message: () => pass
144
+ ? `Expected status not to be ${expected}`
145
+ : `Expected status ${expected}, got ${received.status}`,
146
+ };
147
+ },
148
+ };
149
+ // Type augmentation for Vitest - users should add this to their vitest.setup.ts:
150
+ // declare module 'vitest' {
151
+ // interface Assertion<T> {
152
+ // toBeSuccessResponse(): void;
153
+ // toBeRedirect(expectedLocation?: string): void;
154
+ // toHaveStatus(expected: number): void;
155
+ // }
156
+ // }
157
+ //# sourceMappingURL=vitest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@flightdev/testing",
3
+ "version": "0.0.5",
4
+ "description": "Testing utilities for Flight Framework applications",
5
+ "type": "module",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./vitest": {
17
+ "types": "./dist/vitest.d.ts",
18
+ "import": "./dist/vitest.js"
19
+ },
20
+ "./browser": {
21
+ "types": "./dist/browser.d.ts",
22
+ "import": "./dist/browser.js"
23
+ },
24
+ "./projects": {
25
+ "types": "./dist/projects.d.ts",
26
+ "import": "./dist/projects.js"
27
+ }
28
+ },
29
+ "peerDependencies": {
30
+ "vitest": ">=2.0.0"
31
+ },
32
+ "peerDependenciesMeta": {
33
+ "vitest": {
34
+ "optional": true
35
+ }
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^22.0.0",
39
+ "typescript": "^5.7.0"
40
+ },
41
+ "keywords": [
42
+ "flight",
43
+ "testing",
44
+ "vitest",
45
+ "unit-test"
46
+ ],
47
+ "license": "MIT",
48
+ "scripts": {
49
+ "build": "tsc",
50
+ "dev": "tsc --watch"
51
+ }
52
+ }