@donkeylabs/server 2.0.19 → 2.0.21

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.
@@ -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";