@donkeylabs/server 2.0.18 → 2.0.20
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/docs/caching-strategies.md +677 -0
- package/docs/dev-experience.md +656 -0
- package/docs/hot-reload-limitations.md +166 -0
- package/docs/load-testing.md +974 -0
- package/docs/plugin-registry-design.md +1064 -0
- package/docs/production.md +1229 -0
- package/docs/workflows.md +90 -3
- package/package.json +18 -2
- package/src/admin/routes.ts +153 -0
- package/src/core/cron.ts +184 -15
- package/src/core/index.ts +25 -0
- package/src/core/job-adapter-kysely.ts +176 -73
- package/src/core/job-adapter-sqlite.ts +10 -0
- package/src/core/jobs.ts +112 -17
- package/src/core/migrations/workflows/002_add_metadata_column.ts +28 -0
- package/src/core/process-adapter-kysely.ts +62 -21
- package/src/core/storage-adapter-local.test.ts +199 -0
- package/src/core/storage.test.ts +197 -0
- package/src/core/workflow-adapter-kysely.ts +66 -19
- package/src/core/workflow-executor.ts +469 -0
- package/src/core/workflow-proxy.ts +238 -0
- package/src/core/workflow-socket.ts +447 -0
- package/src/core/workflows.test.ts +415 -0
- package/src/core/workflows.ts +782 -9
- package/src/core.ts +17 -6
- package/src/index.ts +14 -0
- package/src/server.ts +40 -26
- package/src/testing/database.test.ts +263 -0
- package/src/testing/database.ts +173 -0
- package/src/testing/e2e.test.ts +189 -0
- package/src/testing/e2e.ts +272 -0
- package/src/testing/index.ts +18 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// packages/server/src/testing/e2e.test.ts
|
|
2
|
+
import { describe, it, expect } from "bun:test";
|
|
3
|
+
import { defineE2EConfig, createE2EFixtures } from "./e2e";
|
|
4
|
+
|
|
5
|
+
describe("E2E Testing Utilities", () => {
|
|
6
|
+
describe("defineE2EConfig", () => {
|
|
7
|
+
it("should return default configuration", () => {
|
|
8
|
+
const config = defineE2EConfig({
|
|
9
|
+
baseURL: "http://localhost:3000",
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
expect(config.testDir).toBe("./e2e");
|
|
13
|
+
expect(config.timeout).toBe(30000);
|
|
14
|
+
expect(config.expect?.timeout).toBe(5000);
|
|
15
|
+
expect(config.fullyParallel).toBe(true);
|
|
16
|
+
expect(config.use?.baseURL).toBe("http://localhost:3000");
|
|
17
|
+
expect(config.use?.trace).toBe("on-first-retry");
|
|
18
|
+
expect(config.use?.screenshot).toBe("only-on-failure");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should use custom port", () => {
|
|
22
|
+
const config = defineE2EConfig({
|
|
23
|
+
baseURL: "http://localhost:4000",
|
|
24
|
+
port: 4000,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(config.use?.baseURL).toBe("http://localhost:4000");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("should use custom timeout", () => {
|
|
31
|
+
const config = defineE2EConfig({
|
|
32
|
+
baseURL: "http://localhost:3000",
|
|
33
|
+
timeout: 60000,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
expect(config.timeout).toBe(60000);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should configure chromium by default", () => {
|
|
40
|
+
const config = defineE2EConfig({
|
|
41
|
+
baseURL: "http://localhost:3000",
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
expect(config.projects?.length).toBe(1);
|
|
45
|
+
expect(config.projects?.[0].name).toBe("chromium");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should add firefox when specified", () => {
|
|
49
|
+
const config = defineE2EConfig({
|
|
50
|
+
baseURL: "http://localhost:3000",
|
|
51
|
+
browsers: ["firefox"],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Chromium + Firefox
|
|
55
|
+
expect(config.projects?.length).toBe(2);
|
|
56
|
+
expect(config.projects?.some((p) => p.name === "firefox")).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should add webkit when specified", () => {
|
|
60
|
+
const config = defineE2EConfig({
|
|
61
|
+
baseURL: "http://localhost:3000",
|
|
62
|
+
browsers: ["webkit"],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(config.projects?.some((p) => p.name === "webkit")).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should add mobile viewports when testMobile is true", () => {
|
|
69
|
+
const config = defineE2EConfig({
|
|
70
|
+
baseURL: "http://localhost:3000",
|
|
71
|
+
testMobile: true,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const mobileProjects = config.projects?.filter(
|
|
75
|
+
(p) => p.name.includes("Mobile")
|
|
76
|
+
);
|
|
77
|
+
expect(mobileProjects?.length).toBe(2);
|
|
78
|
+
expect(mobileProjects?.some((p) => p.name === "Mobile Chrome")).toBe(true);
|
|
79
|
+
expect(mobileProjects?.some((p) => p.name === "Mobile Safari")).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should configure webServer for auto-start", () => {
|
|
83
|
+
const config = defineE2EConfig({
|
|
84
|
+
baseURL: "http://localhost:3000",
|
|
85
|
+
serverEntry: "./src/server.ts",
|
|
86
|
+
autoStart: true,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(config.webServer).toBeDefined();
|
|
90
|
+
expect(config.webServer?.command).toBe("bun ./src/server.ts");
|
|
91
|
+
expect(config.webServer?.url).toBe("http://localhost:3000");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should use default dev command when no serverEntry", () => {
|
|
95
|
+
const config = defineE2EConfig({
|
|
96
|
+
baseURL: "http://localhost:3000",
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(config.webServer?.command).toBe("bun run dev");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("should disable webServer when autoStart is false", () => {
|
|
103
|
+
const config = defineE2EConfig({
|
|
104
|
+
baseURL: "http://localhost:3000",
|
|
105
|
+
autoStart: false,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expect(config.webServer).toBeUndefined();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("should configure CI settings based on environment", () => {
|
|
112
|
+
const originalCI = process.env.CI;
|
|
113
|
+
|
|
114
|
+
// Test non-CI
|
|
115
|
+
delete process.env.CI;
|
|
116
|
+
const nonCIConfig = defineE2EConfig({
|
|
117
|
+
baseURL: "http://localhost:3000",
|
|
118
|
+
});
|
|
119
|
+
expect(nonCIConfig.forbidOnly).toBe(false);
|
|
120
|
+
expect(nonCIConfig.retries).toBe(0);
|
|
121
|
+
|
|
122
|
+
// Test CI
|
|
123
|
+
process.env.CI = "true";
|
|
124
|
+
const ciConfig = defineE2EConfig({
|
|
125
|
+
baseURL: "http://localhost:3000",
|
|
126
|
+
});
|
|
127
|
+
expect(ciConfig.forbidOnly).toBe(true);
|
|
128
|
+
expect(ciConfig.retries).toBe(2);
|
|
129
|
+
expect(ciConfig.workers).toBe(1);
|
|
130
|
+
|
|
131
|
+
// Restore
|
|
132
|
+
if (originalCI) {
|
|
133
|
+
process.env.CI = originalCI;
|
|
134
|
+
} else {
|
|
135
|
+
delete process.env.CI;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("should configure reporters", () => {
|
|
140
|
+
const config = defineE2EConfig({
|
|
141
|
+
baseURL: "http://localhost:3000",
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(config.reporter).toEqual([["html"], ["list"]]);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe("createE2EFixtures", () => {
|
|
149
|
+
const baseURL = "http://localhost:9999";
|
|
150
|
+
|
|
151
|
+
it("should create api fixture with all HTTP methods", () => {
|
|
152
|
+
const fixtures = createE2EFixtures(baseURL);
|
|
153
|
+
|
|
154
|
+
expect(fixtures.api).toBeDefined();
|
|
155
|
+
expect(typeof fixtures.api).toBe("function");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("should create seed fixture", () => {
|
|
159
|
+
const fixtures = createE2EFixtures(baseURL);
|
|
160
|
+
|
|
161
|
+
expect(fixtures.seed).toBeDefined();
|
|
162
|
+
expect(typeof fixtures.seed).toBe("function");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("should create cleanup fixture", () => {
|
|
166
|
+
const fixtures = createE2EFixtures(baseURL);
|
|
167
|
+
|
|
168
|
+
expect(fixtures.cleanup).toBeDefined();
|
|
169
|
+
expect(typeof fixtures.cleanup).toBe("function");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Integration tests for fixtures would require a running server
|
|
173
|
+
// These tests verify the fixture structure
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe("E2EFixtures API client", () => {
|
|
177
|
+
// These tests require a mock server
|
|
178
|
+
// For now we just verify the structure
|
|
179
|
+
|
|
180
|
+
it("should have correct fixture structure", () => {
|
|
181
|
+
const fixtures = createE2EFixtures("http://test:3000");
|
|
182
|
+
|
|
183
|
+
// Verify all expected fixtures exist
|
|
184
|
+
expect(Object.keys(fixtures)).toContain("api");
|
|
185
|
+
expect(Object.keys(fixtures)).toContain("seed");
|
|
186
|
+
expect(Object.keys(fixtures)).toContain("cleanup");
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
// packages/server/src/testing/e2e.ts
|
|
2
|
+
/**
|
|
3
|
+
* E2E Testing Utilities for DonkeyLabs Applications
|
|
4
|
+
*
|
|
5
|
+
* Simplifies Playwright integration for testing your DonkeyLabs app.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // playwright.config.ts
|
|
10
|
+
* import { defineE2EConfig } from "@donkeylabs/server";
|
|
11
|
+
*
|
|
12
|
+
* export default defineE2EConfig({
|
|
13
|
+
* baseURL: "http://localhost:3000",
|
|
14
|
+
* serverEntry: "./src/server/index.ts",
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // tests/auth.spec.ts
|
|
21
|
+
* import { test, expect } from "@donkeylabs/server";
|
|
22
|
+
*
|
|
23
|
+
* test("user can sign up", async ({ page, api }) => {
|
|
24
|
+
* await page.goto("/signup");
|
|
25
|
+
* await page.fill('[name="email"]', "test@example.com");
|
|
26
|
+
* await page.fill('[name="password"]', "password123");
|
|
27
|
+
* await page.click('button[type="submit"]');
|
|
28
|
+
*
|
|
29
|
+
* await expect(page).toHaveURL("/dashboard");
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
// Only import types - actual @playwright/test is a peer dependency
|
|
35
|
+
// Users should import { test, expect } from "@playwright/test" directly
|
|
36
|
+
type PlaywrightTestConfig = {
|
|
37
|
+
testDir?: string;
|
|
38
|
+
timeout?: number;
|
|
39
|
+
expect?: { timeout?: number };
|
|
40
|
+
fullyParallel?: boolean;
|
|
41
|
+
forbidOnly?: boolean;
|
|
42
|
+
retries?: number;
|
|
43
|
+
workers?: number | undefined;
|
|
44
|
+
reporter?: any[];
|
|
45
|
+
use?: Record<string, any>;
|
|
46
|
+
projects?: any[];
|
|
47
|
+
webServer?: {
|
|
48
|
+
command: string;
|
|
49
|
+
url: string;
|
|
50
|
+
reuseExistingServer?: boolean;
|
|
51
|
+
timeout?: number;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export interface E2EConfig {
|
|
56
|
+
/** Base URL of your application */
|
|
57
|
+
baseURL: string;
|
|
58
|
+
|
|
59
|
+
/** Server entry point file */
|
|
60
|
+
serverEntry?: string;
|
|
61
|
+
|
|
62
|
+
/** Port for test server */
|
|
63
|
+
port?: number;
|
|
64
|
+
|
|
65
|
+
/** Database to use for testing (isolated per test) */
|
|
66
|
+
database?: "sqlite" | "postgres" | "mysql";
|
|
67
|
+
|
|
68
|
+
/** Auto-start dev server */
|
|
69
|
+
autoStart?: boolean;
|
|
70
|
+
|
|
71
|
+
/** Test timeout in milliseconds */
|
|
72
|
+
timeout?: number;
|
|
73
|
+
|
|
74
|
+
/** Browsers to test */
|
|
75
|
+
browsers?: ("chromium" | "firefox" | "webkit")[];
|
|
76
|
+
|
|
77
|
+
/** Mobile viewport testing */
|
|
78
|
+
testMobile?: boolean;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface E2EFixtures {
|
|
82
|
+
/** API client for making HTTP requests */
|
|
83
|
+
api: {
|
|
84
|
+
get: (route: string) => Promise<any>;
|
|
85
|
+
post: (route: string, data: any) => Promise<any>;
|
|
86
|
+
put: (route: string, data: any) => Promise<any>;
|
|
87
|
+
delete: (route: string) => Promise<any>;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/** Database instance for direct queries */
|
|
91
|
+
db: any;
|
|
92
|
+
|
|
93
|
+
/** Helper to seed test data */
|
|
94
|
+
seed: (data: { users?: any[]; [key: string]: any[] | undefined }) => Promise<void>;
|
|
95
|
+
|
|
96
|
+
/** Helper to cleanup test data */
|
|
97
|
+
cleanup: () => Promise<void>;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Define E2E test configuration for Playwright
|
|
102
|
+
*/
|
|
103
|
+
export function defineE2EConfig(config: E2EConfig): PlaywrightTestConfig {
|
|
104
|
+
const port = config.port || 3333;
|
|
105
|
+
const baseURL = config.baseURL || `http://localhost:${port}`;
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
testDir: "./e2e",
|
|
109
|
+
timeout: config.timeout || 30000,
|
|
110
|
+
expect: {
|
|
111
|
+
timeout: 5000,
|
|
112
|
+
},
|
|
113
|
+
fullyParallel: true,
|
|
114
|
+
forbidOnly: !!process.env.CI,
|
|
115
|
+
retries: process.env.CI ? 2 : 0,
|
|
116
|
+
workers: process.env.CI ? 1 : undefined,
|
|
117
|
+
reporter: [
|
|
118
|
+
["html"],
|
|
119
|
+
["list"],
|
|
120
|
+
],
|
|
121
|
+
use: {
|
|
122
|
+
baseURL,
|
|
123
|
+
trace: "on-first-retry",
|
|
124
|
+
screenshot: "only-on-failure",
|
|
125
|
+
video: "on-first-retry",
|
|
126
|
+
},
|
|
127
|
+
projects: [
|
|
128
|
+
{
|
|
129
|
+
name: "chromium",
|
|
130
|
+
use: {
|
|
131
|
+
browserName: "chromium",
|
|
132
|
+
viewport: { width: 1280, height: 720 },
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
...(config.browsers?.includes("firefox") ? [{
|
|
136
|
+
name: "firefox",
|
|
137
|
+
use: { browserName: "firefox" as const },
|
|
138
|
+
}] : []),
|
|
139
|
+
...(config.browsers?.includes("webkit") ? [{
|
|
140
|
+
name: "webkit",
|
|
141
|
+
use: { browserName: "webkit" as const },
|
|
142
|
+
}] : []),
|
|
143
|
+
...(config.testMobile ? [
|
|
144
|
+
{
|
|
145
|
+
name: "Mobile Chrome",
|
|
146
|
+
use: {
|
|
147
|
+
browserName: "chromium" as const,
|
|
148
|
+
...devices["Pixel 5"],
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "Mobile Safari",
|
|
153
|
+
use: {
|
|
154
|
+
browserName: "webkit" as const,
|
|
155
|
+
...devices["iPhone 12"],
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
] : []),
|
|
159
|
+
],
|
|
160
|
+
webServer: config.autoStart !== false ? {
|
|
161
|
+
command: config.serverEntry
|
|
162
|
+
? `bun ${config.serverEntry}`
|
|
163
|
+
: "bun run dev",
|
|
164
|
+
url: baseURL,
|
|
165
|
+
reuseExistingServer: !process.env.CI,
|
|
166
|
+
timeout: 120 * 1000,
|
|
167
|
+
} : undefined,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Import devices from playwright
|
|
172
|
+
const devices = {
|
|
173
|
+
"Pixel 5": {
|
|
174
|
+
userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36",
|
|
175
|
+
viewport: { width: 393, height: 727 },
|
|
176
|
+
deviceScaleFactor: 2.75,
|
|
177
|
+
isMobile: true,
|
|
178
|
+
hasTouch: true,
|
|
179
|
+
},
|
|
180
|
+
"iPhone 12": {
|
|
181
|
+
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15",
|
|
182
|
+
viewport: { width: 390, height: 664 },
|
|
183
|
+
deviceScaleFactor: 3,
|
|
184
|
+
isMobile: true,
|
|
185
|
+
hasTouch: true,
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Create E2E fixtures for Playwright tests
|
|
191
|
+
* Use this in your playwright.config.ts fixtures
|
|
192
|
+
*/
|
|
193
|
+
export function createE2EFixtures(baseURL: string) {
|
|
194
|
+
return {
|
|
195
|
+
api: async ({}, use: (api: E2EFixtures["api"]) => Promise<void>) => {
|
|
196
|
+
const api: E2EFixtures["api"] = {
|
|
197
|
+
async get(route: string) {
|
|
198
|
+
const response = await fetch(`${baseURL}/${route}`);
|
|
199
|
+
if (!response.ok) {
|
|
200
|
+
throw new Error(`API Error: ${response.status}`);
|
|
201
|
+
}
|
|
202
|
+
return response.json();
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
async post(route: string, data: any) {
|
|
206
|
+
const response = await fetch(`${baseURL}/${route}`, {
|
|
207
|
+
method: "POST",
|
|
208
|
+
headers: { "Content-Type": "application/json" },
|
|
209
|
+
body: JSON.stringify(data),
|
|
210
|
+
});
|
|
211
|
+
if (!response.ok) {
|
|
212
|
+
const error = await response.text();
|
|
213
|
+
throw new Error(`API Error: ${response.status} - ${error}`);
|
|
214
|
+
}
|
|
215
|
+
return response.json();
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
async put(route: string, data: any) {
|
|
219
|
+
const response = await fetch(`${baseURL}/${route}`, {
|
|
220
|
+
method: "PUT",
|
|
221
|
+
headers: { "Content-Type": "application/json" },
|
|
222
|
+
body: JSON.stringify(data),
|
|
223
|
+
});
|
|
224
|
+
if (!response.ok) {
|
|
225
|
+
throw new Error(`API Error: ${response.status}`);
|
|
226
|
+
}
|
|
227
|
+
return response.json();
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
async delete(route: string) {
|
|
231
|
+
const response = await fetch(`${baseURL}/${route}`, {
|
|
232
|
+
method: "DELETE",
|
|
233
|
+
});
|
|
234
|
+
if (!response.ok) {
|
|
235
|
+
throw new Error(`API Error: ${response.status}`);
|
|
236
|
+
}
|
|
237
|
+
return response.json();
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
await use(api);
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
seed: async ({}, use: (fn: E2EFixtures["seed"]) => Promise<void>) => {
|
|
245
|
+
await use(async (data) => {
|
|
246
|
+
// Seed data via API
|
|
247
|
+
for (const [table, items] of Object.entries(data)) {
|
|
248
|
+
if (!items) continue;
|
|
249
|
+
for (const item of items) {
|
|
250
|
+
await fetch(`${baseURL}/${table}.create`, {
|
|
251
|
+
method: "POST",
|
|
252
|
+
headers: { "Content-Type": "application/json" },
|
|
253
|
+
body: JSON.stringify(item),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
cleanup: async ({}, use: (fn: E2EFixtures["cleanup"]) => Promise<void>) => {
|
|
261
|
+
await use(async () => {
|
|
262
|
+
// Cleanup test data
|
|
263
|
+
await fetch(`${baseURL}/test.cleanup`, {
|
|
264
|
+
method: "POST",
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Note: Import test and expect directly from @playwright/test in your test files:
|
|
272
|
+
// import { test, expect } from "@playwright/test";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Testing utilities - separate subpath export to avoid loading Playwright at runtime
|
|
2
|
+
// Usage: import { createE2EFixtures } from "@donkeylabs/server/testing";
|
|
3
|
+
|
|
4
|
+
// E2E Testing - requires @playwright/test as peer dependency
|
|
5
|
+
export {
|
|
6
|
+
createE2EFixtures,
|
|
7
|
+
defineE2EConfig,
|
|
8
|
+
type E2EFixtures,
|
|
9
|
+
type E2EConfig,
|
|
10
|
+
} from "./e2e";
|
|
11
|
+
|
|
12
|
+
// Database Testing Utilities
|
|
13
|
+
export {
|
|
14
|
+
createTestDatabase,
|
|
15
|
+
resetTestDatabase,
|
|
16
|
+
seedTestData,
|
|
17
|
+
type TestDatabaseOptions,
|
|
18
|
+
} from "./database";
|