@arcadialdev/arcality 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/e2e-testing-expert/SKILL.md +28 -0
- package/.agents/skills/frontend-design/LICENSE.txt +177 -0
- package/.agents/skills/frontend-design/SKILL.md +42 -0
- package/.agents/skills/nodejs-backend-patterns/SKILL.md +639 -0
- package/.agents/skills/nodejs-backend-patterns/references/advanced-patterns.md +430 -0
- package/.agents/skills/playwright-best-practices/LICENSE.md +7 -0
- package/.agents/skills/playwright-best-practices/README.md +147 -0
- package/.agents/skills/playwright-best-practices/SKILL.md +303 -0
- package/.agents/skills/playwright-best-practices/advanced/authentication-flows.md +360 -0
- package/.agents/skills/playwright-best-practices/advanced/authentication.md +871 -0
- package/.agents/skills/playwright-best-practices/advanced/clock-mocking.md +364 -0
- package/.agents/skills/playwright-best-practices/advanced/mobile-testing.md +409 -0
- package/.agents/skills/playwright-best-practices/advanced/multi-context.md +288 -0
- package/.agents/skills/playwright-best-practices/advanced/multi-user.md +393 -0
- package/.agents/skills/playwright-best-practices/advanced/network-advanced.md +452 -0
- package/.agents/skills/playwright-best-practices/advanced/third-party.md +464 -0
- package/.agents/skills/playwright-best-practices/architecture/pom-vs-fixtures.md +363 -0
- package/.agents/skills/playwright-best-practices/architecture/test-architecture.md +369 -0
- package/.agents/skills/playwright-best-practices/architecture/when-to-mock.md +383 -0
- package/.agents/skills/playwright-best-practices/browser-apis/browser-apis.md +391 -0
- package/.agents/skills/playwright-best-practices/browser-apis/iframes.md +403 -0
- package/.agents/skills/playwright-best-practices/browser-apis/service-workers.md +504 -0
- package/.agents/skills/playwright-best-practices/browser-apis/websockets.md +403 -0
- package/.agents/skills/playwright-best-practices/core/annotations.md +424 -0
- package/.agents/skills/playwright-best-practices/core/assertions-waiting.md +361 -0
- package/.agents/skills/playwright-best-practices/core/configuration.md +452 -0
- package/.agents/skills/playwright-best-practices/core/fixtures-hooks.md +417 -0
- package/.agents/skills/playwright-best-practices/core/global-setup.md +434 -0
- package/.agents/skills/playwright-best-practices/core/locators.md +242 -0
- package/.agents/skills/playwright-best-practices/core/page-object-model.md +315 -0
- package/.agents/skills/playwright-best-practices/core/projects-dependencies.md +453 -0
- package/.agents/skills/playwright-best-practices/core/test-data.md +492 -0
- package/.agents/skills/playwright-best-practices/core/test-suite-structure.md +361 -0
- package/.agents/skills/playwright-best-practices/core/test-tags.md +298 -0
- package/.agents/skills/playwright-best-practices/debugging/console-errors.md +420 -0
- package/.agents/skills/playwright-best-practices/debugging/debugging.md +504 -0
- package/.agents/skills/playwright-best-practices/debugging/error-testing.md +360 -0
- package/.agents/skills/playwright-best-practices/debugging/flaky-tests.md +496 -0
- package/.agents/skills/playwright-best-practices/frameworks/angular.md +530 -0
- package/.agents/skills/playwright-best-practices/frameworks/nextjs.md +469 -0
- package/.agents/skills/playwright-best-practices/frameworks/react.md +531 -0
- package/.agents/skills/playwright-best-practices/frameworks/vue.md +574 -0
- package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/ci-cd.md +468 -0
- package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/docker.md +283 -0
- package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/github-actions.md +546 -0
- package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/gitlab.md +397 -0
- package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/other-providers.md +521 -0
- package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/parallel-sharding.md +371 -0
- package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/performance.md +453 -0
- package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/reporting.md +424 -0
- package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/test-coverage.md +497 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/accessibility.md +359 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/api-testing.md +719 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/browser-extensions.md +506 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/canvas-webgl.md +493 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/component-testing.md +500 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/drag-drop.md +576 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/electron.md +509 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/file-operations.md +377 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/file-upload-download.md +562 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/forms-validation.md +561 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/graphql-testing.md +331 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/i18n.md +508 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/performance-testing.md +476 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/security-testing.md +430 -0
- package/.agents/skills/playwright-best-practices/testing-patterns/visual-regression.md +634 -0
- package/.env.example +21 -0
- package/README.md +30 -0
- package/bin/arcality.mjs +86 -0
- package/package.json +66 -0
- package/playwright.config.ts +12 -0
- package/scripts/cleanup-qmsdev.mjs +63 -0
- package/scripts/discover-view.mjs +52 -0
- package/scripts/extract-view.mjs +64 -0
- package/scripts/gen-and-run.mjs +838 -0
- package/scripts/init.mjs +290 -0
- package/scripts/migrate-to-central-out.mjs +157 -0
- package/scripts/postinstall.mjs +63 -0
- package/scripts/rebrand-report.mjs +241 -0
- package/scripts/setup.mjs +166 -0
- package/src/KnowledgeService.ts +239 -0
- package/src/arcalityClient.mjs +266 -0
- package/src/configLoader.mjs +179 -0
- package/src/configManager.mjs +172 -0
- package/src/consoleBanner.ts +32 -0
- package/src/envSetup.ts +205 -0
- package/src/index.ts +25 -0
- package/src/projectInspector.ts +42 -0
- package/src/services/collectiveMemoryService.ts +178 -0
- package/src/testRunner.ts +201 -0
- package/tests/_helpers/ArcalityReporter.ts +490 -0
- package/tests/_helpers/agentic-runner.spec.ts +741 -0
- package/tests/_helpers/ai-agent-helper.ts +1573 -0
- package/tests/_helpers/discover-view.spec.ts +238 -0
- package/tests/_helpers/extract-view.spec.ts +118 -0
- package/tests/_helpers/qa-tools.ts +333 -0
- package/tests/_helpers/smart-action.spec.ts +1458 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
# Fixtures & Hooks
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
1. [Built-in Fixtures](#built-in-fixtures)
|
|
6
|
+
2. [Custom Fixtures](#custom-fixtures)
|
|
7
|
+
3. [Fixture Scopes](#fixture-scopes)
|
|
8
|
+
4. [Hooks](#hooks)
|
|
9
|
+
5. [Authentication Patterns](#authentication-patterns)
|
|
10
|
+
6. [Database Fixtures](#database-fixtures)
|
|
11
|
+
|
|
12
|
+
## Built-in Fixtures
|
|
13
|
+
|
|
14
|
+
### Core Fixtures
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
test("example", async ({
|
|
18
|
+
page, // Isolated page instance
|
|
19
|
+
context, // Browser context (cookies, localStorage)
|
|
20
|
+
browser, // Browser instance
|
|
21
|
+
browserName, // 'chromium', 'firefox', or 'webkit'
|
|
22
|
+
request, // API request context
|
|
23
|
+
}) => {
|
|
24
|
+
// Each test gets fresh instances
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Request Fixture
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
test("API call", async ({ request }) => {
|
|
32
|
+
const response = await request.get("/api/users");
|
|
33
|
+
await expect(response).toBeOK();
|
|
34
|
+
|
|
35
|
+
const users = await response.json();
|
|
36
|
+
expect(users).toHaveLength(5);
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Custom Fixtures
|
|
41
|
+
|
|
42
|
+
### Basic Custom Fixture
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// fixtures.ts
|
|
46
|
+
import { test as base } from "@playwright/test";
|
|
47
|
+
|
|
48
|
+
// Declare fixture types
|
|
49
|
+
type MyFixtures = {
|
|
50
|
+
todoPage: TodoPage;
|
|
51
|
+
apiClient: ApiClient;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const test = base.extend<MyFixtures>({
|
|
55
|
+
// Fixture with setup and teardown
|
|
56
|
+
todoPage: async ({ page }, use) => {
|
|
57
|
+
const todoPage = new TodoPage(page);
|
|
58
|
+
await todoPage.goto();
|
|
59
|
+
|
|
60
|
+
await use(todoPage); // Test runs here
|
|
61
|
+
|
|
62
|
+
// Teardown (optional)
|
|
63
|
+
await todoPage.clearTodos();
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// Simple fixture
|
|
67
|
+
apiClient: async ({ request }, use) => {
|
|
68
|
+
await use(new ApiClient(request));
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export { expect } from "@playwright/test";
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Fixture with Options
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
type Options = {
|
|
79
|
+
defaultUser: { email: string; password: string };
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
type Fixtures = {
|
|
83
|
+
authenticatedPage: Page;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const test = base.extend<Options & Fixtures>({
|
|
87
|
+
// Define option with default
|
|
88
|
+
defaultUser: [
|
|
89
|
+
{ email: "test@example.com", password: "pass123" },
|
|
90
|
+
{ option: true },
|
|
91
|
+
],
|
|
92
|
+
|
|
93
|
+
// Use option in fixture
|
|
94
|
+
authenticatedPage: async ({ page, defaultUser }, use) => {
|
|
95
|
+
await page.goto("/login");
|
|
96
|
+
await page.getByLabel("Email").fill(defaultUser.email);
|
|
97
|
+
await page.getByLabel("Password").fill(defaultUser.password);
|
|
98
|
+
await page.getByRole("button", { name: "Sign in" }).click();
|
|
99
|
+
await use(page);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Override in config
|
|
104
|
+
export default defineConfig({
|
|
105
|
+
use: {
|
|
106
|
+
defaultUser: { email: "admin@example.com", password: "admin123" },
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Automatic Fixtures
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
export const test = base.extend<{}, { setupDb: void }>({
|
|
115
|
+
// Auto-fixture runs for every test without explicit usage
|
|
116
|
+
setupDb: [
|
|
117
|
+
async ({}, use) => {
|
|
118
|
+
await seedDatabase();
|
|
119
|
+
await use();
|
|
120
|
+
await cleanDatabase();
|
|
121
|
+
},
|
|
122
|
+
{ auto: true },
|
|
123
|
+
],
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Fixture Scopes
|
|
128
|
+
|
|
129
|
+
### Test Scope (Default)
|
|
130
|
+
|
|
131
|
+
Created fresh for each test:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
test.extend({
|
|
135
|
+
page: async ({ browser }, use) => {
|
|
136
|
+
const page = await browser.newPage();
|
|
137
|
+
await use(page);
|
|
138
|
+
await page.close();
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Worker Scope
|
|
144
|
+
|
|
145
|
+
Shared across tests in the same worker (each worker gets its own instance; tests in different workers do not share it):
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
type WorkerFixtures = {
|
|
149
|
+
sharedAccount: Account;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const test = base.extend<{}, WorkerFixtures>({
|
|
153
|
+
sharedAccount: [
|
|
154
|
+
async ({ browser }, use) => {
|
|
155
|
+
// Expensive setup - runs once per worker
|
|
156
|
+
const account = await createTestAccount();
|
|
157
|
+
await use(account);
|
|
158
|
+
await deleteTestAccount(account);
|
|
159
|
+
},
|
|
160
|
+
{ scope: "worker" },
|
|
161
|
+
],
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Isolate test data between parallel workers
|
|
166
|
+
|
|
167
|
+
When tests in different workers touch the same backend or DB (e.g. same user, same tenant), they can collide and cause flaky failures. Use `testInfo.workerIndex` (or `process.env.TEST_WORKER_INDEX`) in a worker-scoped fixture to create unique data per worker:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { test as baseTest } from "@playwright/test";
|
|
171
|
+
|
|
172
|
+
type WorkerFixtures = {
|
|
173
|
+
dbUserName: string;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const test = baseTest.extend<{}, WorkerFixtures>({
|
|
177
|
+
dbUserName: [
|
|
178
|
+
async ({}, use, testInfo) => {
|
|
179
|
+
const userName = `user-${testInfo.workerIndex}`;
|
|
180
|
+
await createUserInTestDatabase(userName);
|
|
181
|
+
await use(userName);
|
|
182
|
+
await deleteUserFromTestDatabase(userName);
|
|
183
|
+
},
|
|
184
|
+
{ scope: "worker" },
|
|
185
|
+
],
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Then each worker uses a distinct user (e.g. `user-1`, `user-2`), so parallel workers do not overwrite each other’s data.
|
|
190
|
+
|
|
191
|
+
## Hooks
|
|
192
|
+
|
|
193
|
+
### beforeEach / afterEach
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
test.beforeEach(async ({ page }) => {
|
|
197
|
+
// Runs before each test in file
|
|
198
|
+
await page.goto("/");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test.afterEach(async ({ page }, testInfo) => {
|
|
202
|
+
// Runs after each test
|
|
203
|
+
if (testInfo.status !== "passed") {
|
|
204
|
+
await page.screenshot({ path: `failed-${testInfo.title}.png` });
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### beforeAll / afterAll
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
test.beforeAll(async ({ browser }) => {
|
|
213
|
+
// Runs once before all tests in file
|
|
214
|
+
// Note: Cannot use page fixture here
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test.afterAll(async () => {
|
|
218
|
+
// Runs once after all tests in file
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Describe-Level Hooks
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
test.describe("User Management", () => {
|
|
226
|
+
test.beforeEach(async ({ page }) => {
|
|
227
|
+
await page.goto("/users");
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("can list users", async ({ page }) => {
|
|
231
|
+
// Starts at /users
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("can add user", async ({ page }) => {
|
|
235
|
+
// Starts at /users
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Authentication Patterns
|
|
241
|
+
|
|
242
|
+
### Global Setup with Storage State
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
// auth.setup.ts
|
|
246
|
+
import { test as setup, expect } from "@playwright/test";
|
|
247
|
+
|
|
248
|
+
const authFile = ".auth/user.json";
|
|
249
|
+
|
|
250
|
+
setup("authenticate", async ({ page }) => {
|
|
251
|
+
await page.goto("/login");
|
|
252
|
+
await page.getByLabel("Email").fill(process.env.TEST_EMAIL!);
|
|
253
|
+
await page.getByLabel("Password").fill(process.env.TEST_PASSWORD!);
|
|
254
|
+
await page.getByRole("button", { name: "Sign in" }).click();
|
|
255
|
+
|
|
256
|
+
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
|
|
257
|
+
await page.context().storageState({ path: authFile });
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// playwright.config.ts
|
|
263
|
+
export default defineConfig({
|
|
264
|
+
projects: [
|
|
265
|
+
{ name: "setup", testMatch: /.*\.setup\.ts/ },
|
|
266
|
+
{
|
|
267
|
+
name: "chromium",
|
|
268
|
+
use: {
|
|
269
|
+
...devices["Desktop Chrome"],
|
|
270
|
+
storageState: ".auth/user.json",
|
|
271
|
+
},
|
|
272
|
+
dependencies: ["setup"],
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
});
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Multiple Auth States
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
// auth.setup.ts
|
|
282
|
+
setup("admin auth", async ({ page }) => {
|
|
283
|
+
await login(page, "admin@example.com", "adminpass");
|
|
284
|
+
await page.context().storageState({ path: ".auth/admin.json" });
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
setup("user auth", async ({ page }) => {
|
|
288
|
+
await login(page, "user@example.com", "userpass");
|
|
289
|
+
await page.context().storageState({ path: ".auth/user.json" });
|
|
290
|
+
});
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// playwright.config.ts
|
|
295
|
+
projects: [
|
|
296
|
+
{
|
|
297
|
+
name: "admin tests",
|
|
298
|
+
testMatch: /.*admin.*\.spec\.ts/,
|
|
299
|
+
use: { storageState: ".auth/admin.json" },
|
|
300
|
+
dependencies: ["setup"],
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: "user tests",
|
|
304
|
+
testMatch: /.*user.*\.spec\.ts/,
|
|
305
|
+
use: { storageState: ".auth/user.json" },
|
|
306
|
+
dependencies: ["setup"],
|
|
307
|
+
},
|
|
308
|
+
];
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Auth Fixture
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
// fixtures/auth.fixture.ts
|
|
315
|
+
export const test = base.extend<{ adminPage: Page; userPage: Page }>({
|
|
316
|
+
adminPage: async ({ browser }, use) => {
|
|
317
|
+
const context = await browser.newContext({
|
|
318
|
+
storageState: ".auth/admin.json",
|
|
319
|
+
});
|
|
320
|
+
const page = await context.newPage();
|
|
321
|
+
await use(page);
|
|
322
|
+
await context.close();
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
userPage: async ({ browser }, use) => {
|
|
326
|
+
const context = await browser.newContext({
|
|
327
|
+
storageState: ".auth/user.json",
|
|
328
|
+
});
|
|
329
|
+
const page = await context.newPage();
|
|
330
|
+
await use(page);
|
|
331
|
+
await context.close();
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Database Fixtures
|
|
337
|
+
|
|
338
|
+
This section covers **per-test database fixtures** (isolation, transaction rollback). For related topics:
|
|
339
|
+
|
|
340
|
+
- **Test data factories** (builders, Faker): See [test-data.md](test-data.md)
|
|
341
|
+
- **One-time database setup** (migrations, snapshots): See [global-setup.md](global-setup.md#database-patterns)
|
|
342
|
+
|
|
343
|
+
### Transaction Rollback Pattern
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
import { test as base } from "@playwright/test";
|
|
347
|
+
import { db } from "../db";
|
|
348
|
+
|
|
349
|
+
export const test = base.extend<{ dbTransaction: Transaction }>({
|
|
350
|
+
dbTransaction: async ({}, use) => {
|
|
351
|
+
const transaction = await db.beginTransaction();
|
|
352
|
+
|
|
353
|
+
await use(transaction);
|
|
354
|
+
|
|
355
|
+
await transaction.rollback(); // Clean slate for next test
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Seed Data Fixture
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
type TestData = {
|
|
364
|
+
testUser: User;
|
|
365
|
+
testProducts: Product[];
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
export const test = base.extend<TestData>({
|
|
369
|
+
testUser: async ({}, use) => {
|
|
370
|
+
const user = await db.users.create({
|
|
371
|
+
email: `test-${Date.now()}@example.com`,
|
|
372
|
+
name: "Test User",
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
await use(user);
|
|
376
|
+
|
|
377
|
+
await db.users.delete(user.id);
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
testProducts: async ({ testUser }, use) => {
|
|
381
|
+
const products = await db.products.createMany([
|
|
382
|
+
{ name: "Product A", ownerId: testUser.id },
|
|
383
|
+
{ name: "Product B", ownerId: testUser.id },
|
|
384
|
+
]);
|
|
385
|
+
|
|
386
|
+
await use(products);
|
|
387
|
+
|
|
388
|
+
await db.products.deleteMany(products.map((p) => p.id));
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
## Fixture Tips
|
|
394
|
+
|
|
395
|
+
| Tip | Explanation |
|
|
396
|
+
| ------------------ | ------------------------------------------- |
|
|
397
|
+
| Fixtures are lazy | Only created when used |
|
|
398
|
+
| Compose fixtures | Use other fixtures as dependencies |
|
|
399
|
+
| Keep setup minimal | Do heavy lifting in worker-scoped fixtures |
|
|
400
|
+
| Clean up resources | Use teardown in fixtures, not afterEach |
|
|
401
|
+
| Avoid shared state | Each fixture instance should be independent |
|
|
402
|
+
|
|
403
|
+
## Anti-Patterns to Avoid
|
|
404
|
+
|
|
405
|
+
| Anti-Pattern | Problem | Solution |
|
|
406
|
+
| ----------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
407
|
+
| Shared mutable state between tests | Race conditions, order dependencies | Use fixtures for isolation |
|
|
408
|
+
| Global variables in tests | Tests depend on execution order | Use fixtures or beforeEach for setup |
|
|
409
|
+
| Not cleaning up test data | Tests interfere with each other | Use fixtures with teardown or database transactions |
|
|
410
|
+
| Shared `page` or `context` in `beforeAll` | State leak between tests; flaky when tests run in parallel | Use default one-context-per-test, or `beforeEach` + fresh page; if serial is required, prefer `test.describe.configure({ mode: 'serial' })` and document that isolation is sacrificed |
|
|
411
|
+
| Backend/DB state shared across workers | Tests in different workers collide on same data | Use worker-scoped fixture with `testInfo.workerIndex` to create unique data per worker |
|
|
412
|
+
|
|
413
|
+
## Related References
|
|
414
|
+
|
|
415
|
+
- **Page Objects with fixtures**: See [page-object-model.md](page-object-model.md) for POM patterns
|
|
416
|
+
- **Test organization**: See [test-suite-structure.md](test-suite-structure.md) for test structure
|
|
417
|
+
- **Debugging fixture issues**: See [debugging.md](../debugging/debugging.md) for troubleshooting
|