@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 +21 -0
- package/README.md +203 -0
- package/dist/browser.d.ts +127 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +151 -0
- package/dist/browser.js.map +1 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +166 -0
- package/dist/index.js.map +1 -0
- package/dist/projects.d.ts +113 -0
- package/dist/projects.d.ts.map +1 -0
- package/dist/projects.js +120 -0
- package/dist/projects.js.map +1 -0
- package/dist/vitest.d.ts +74 -0
- package/dist/vitest.d.ts.map +1 -0
- package/dist/vitest.js +157 -0
- package/dist/vitest.js.map +1 -0
- package/package.json +52 -0
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"}
|
package/dist/browser.js
ADDED
|
@@ -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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|
package/dist/projects.js
ADDED
|
@@ -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"}
|
package/dist/vitest.d.ts
ADDED
|
@@ -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
|
+
}
|