@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,464 @@
|
|
|
1
|
+
# Third-Party Service Mocking
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
1. [OAuth/SSO Mocking](#oauthsso-mocking)
|
|
6
|
+
2. [Payment Gateway Mocking](#payment-gateway-mocking)
|
|
7
|
+
3. [Email Verification](#email-verification)
|
|
8
|
+
4. [SMS Verification](#sms-verification)
|
|
9
|
+
5. [Analytics & Tracking](#analytics--tracking)
|
|
10
|
+
|
|
11
|
+
## OAuth/SSO Mocking
|
|
12
|
+
|
|
13
|
+
### Mock Google OAuth
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
test("Google OAuth login", async ({ page }) => {
|
|
17
|
+
// Mock the OAuth callback
|
|
18
|
+
await page.route("**/auth/google/callback**", (route) => {
|
|
19
|
+
const url = new URL(route.request().url());
|
|
20
|
+
// Simulate successful OAuth by redirecting with token
|
|
21
|
+
route.fulfill({
|
|
22
|
+
status: 302,
|
|
23
|
+
headers: {
|
|
24
|
+
Location: "/dashboard?token=mock-jwt-token",
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Mock the token verification endpoint
|
|
30
|
+
await page.route("**/api/auth/verify", (route) =>
|
|
31
|
+
route.fulfill({
|
|
32
|
+
json: {
|
|
33
|
+
valid: true,
|
|
34
|
+
user: {
|
|
35
|
+
id: "123",
|
|
36
|
+
email: "test@gmail.com",
|
|
37
|
+
name: "Test User",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
await page.goto("/login");
|
|
44
|
+
await page.getByRole("button", { name: "Sign in with Google" }).click();
|
|
45
|
+
|
|
46
|
+
await expect(page.getByText("Welcome, Test User")).toBeVisible();
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### OAuth Fixture
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// fixtures/oauth.fixture.ts
|
|
54
|
+
type OAuthProvider = "google" | "github" | "microsoft";
|
|
55
|
+
|
|
56
|
+
type OAuthUser = {
|
|
57
|
+
id: string;
|
|
58
|
+
email: string;
|
|
59
|
+
name: string;
|
|
60
|
+
avatar?: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
type OAuthFixtures = {
|
|
64
|
+
mockOAuth: (provider: OAuthProvider, user: OAuthUser) => Promise<void>;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const test = base.extend<OAuthFixtures>({
|
|
68
|
+
mockOAuth: async ({ page }, use) => {
|
|
69
|
+
await use(async (provider, user) => {
|
|
70
|
+
// Mock callback redirect
|
|
71
|
+
await page.route(`**/auth/${provider}/callback**`, (route) =>
|
|
72
|
+
route.fulfill({
|
|
73
|
+
status: 302,
|
|
74
|
+
headers: { Location: `/auth/success?provider=${provider}` },
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Mock session/user endpoint
|
|
79
|
+
await page.route("**/api/auth/session", (route) =>
|
|
80
|
+
route.fulfill({
|
|
81
|
+
json: { user, provider, authenticated: true },
|
|
82
|
+
}),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Mock user info endpoint
|
|
86
|
+
await page.route("**/api/me", (route) => route.fulfill({ json: user }));
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Usage
|
|
92
|
+
test("login with GitHub", async ({ page, mockOAuth }) => {
|
|
93
|
+
await mockOAuth("github", {
|
|
94
|
+
id: "gh-123",
|
|
95
|
+
email: "dev@github.com",
|
|
96
|
+
name: "GitHub User",
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await page.goto("/login");
|
|
100
|
+
await page.getByRole("button", { name: "Sign in with GitHub" }).click();
|
|
101
|
+
|
|
102
|
+
await expect(page.getByText("Welcome, GitHub User")).toBeVisible();
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Mock SAML SSO
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
test("SAML SSO login", async ({ page }) => {
|
|
110
|
+
// Mock SAML assertion consumer service
|
|
111
|
+
await page.route("**/saml/acs", async (route) => {
|
|
112
|
+
route.fulfill({
|
|
113
|
+
status: 302,
|
|
114
|
+
headers: {
|
|
115
|
+
Location: "/dashboard",
|
|
116
|
+
"Set-Cookie": "session=mock-saml-session; Path=/; HttpOnly",
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Mock session validation
|
|
122
|
+
await page.route("**/api/session", (route) =>
|
|
123
|
+
route.fulfill({
|
|
124
|
+
json: {
|
|
125
|
+
user: { email: "user@company.com", name: "SSO User" },
|
|
126
|
+
provider: "saml",
|
|
127
|
+
},
|
|
128
|
+
}),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
await page.goto("/login");
|
|
132
|
+
await page.getByRole("button", { name: "SSO Login" }).click();
|
|
133
|
+
|
|
134
|
+
await expect(page).toHaveURL("/dashboard");
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Payment Gateway Mocking
|
|
139
|
+
|
|
140
|
+
### Mock Stripe
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
test("Stripe checkout", async ({ page }) => {
|
|
144
|
+
// Mock Stripe.js
|
|
145
|
+
await page.addInitScript(() => {
|
|
146
|
+
(window as any).Stripe = () => ({
|
|
147
|
+
elements: () => ({
|
|
148
|
+
create: () => ({
|
|
149
|
+
mount: () => {},
|
|
150
|
+
on: () => {},
|
|
151
|
+
destroy: () => {},
|
|
152
|
+
}),
|
|
153
|
+
}),
|
|
154
|
+
confirmCardPayment: async () => ({
|
|
155
|
+
paymentIntent: { status: "succeeded", id: "pi_mock_123" },
|
|
156
|
+
}),
|
|
157
|
+
createPaymentMethod: async () => ({
|
|
158
|
+
paymentMethod: { id: "pm_mock_123" },
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Mock backend payment endpoint
|
|
164
|
+
await page.route("**/api/create-payment-intent", (route) =>
|
|
165
|
+
route.fulfill({
|
|
166
|
+
json: { clientSecret: "pi_mock_123_secret_mock" },
|
|
167
|
+
}),
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
await page.route("**/api/confirm-payment", (route) =>
|
|
171
|
+
route.fulfill({
|
|
172
|
+
json: { success: true, orderId: "order-123" },
|
|
173
|
+
}),
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
await page.goto("/checkout");
|
|
177
|
+
await page.getByRole("button", { name: "Pay $99.99" }).click();
|
|
178
|
+
|
|
179
|
+
await expect(page.getByText("Payment successful")).toBeVisible();
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Mock PayPal
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
test("PayPal checkout", async ({ page }) => {
|
|
187
|
+
// Mock PayPal SDK
|
|
188
|
+
await page.addInitScript(() => {
|
|
189
|
+
(window as any).paypal = {
|
|
190
|
+
Buttons: () => ({
|
|
191
|
+
render: () => Promise.resolve(),
|
|
192
|
+
isEligible: () => true,
|
|
193
|
+
}),
|
|
194
|
+
FUNDING: { PAYPAL: "paypal", CARD: "card" },
|
|
195
|
+
};
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Mock PayPal order creation
|
|
199
|
+
await page.route("**/api/paypal/create-order", (route) =>
|
|
200
|
+
route.fulfill({
|
|
201
|
+
json: { orderId: "PAYPAL-ORDER-123" },
|
|
202
|
+
}),
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// Mock PayPal capture
|
|
206
|
+
await page.route("**/api/paypal/capture", (route) =>
|
|
207
|
+
route.fulfill({
|
|
208
|
+
json: { success: true, transactionId: "TXN-123" },
|
|
209
|
+
}),
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
await page.goto("/checkout");
|
|
213
|
+
|
|
214
|
+
// Simulate PayPal approval callback
|
|
215
|
+
await page.evaluate(() => {
|
|
216
|
+
(window as any).onPayPalApprove?.({ orderID: "PAYPAL-ORDER-123" });
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
await expect(page.getByText("Order confirmed")).toBeVisible();
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Payment Fixture
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
// fixtures/payment.fixture.ts
|
|
227
|
+
type PaymentFixtures = {
|
|
228
|
+
mockStripe: (options?: { failPayment?: boolean }) => Promise<void>;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export const test = base.extend<PaymentFixtures>({
|
|
232
|
+
mockStripe: async ({ page }, use) => {
|
|
233
|
+
await use(async (options = {}) => {
|
|
234
|
+
await page.addInitScript(
|
|
235
|
+
([shouldFail]) => {
|
|
236
|
+
(window as any).Stripe = () => ({
|
|
237
|
+
elements: () => ({
|
|
238
|
+
create: () => ({
|
|
239
|
+
mount: () => {},
|
|
240
|
+
on: (event: string, handler: Function) => {
|
|
241
|
+
if (event === "ready") setTimeout(handler, 100);
|
|
242
|
+
},
|
|
243
|
+
destroy: () => {},
|
|
244
|
+
}),
|
|
245
|
+
}),
|
|
246
|
+
confirmCardPayment: async () => {
|
|
247
|
+
if (shouldFail) {
|
|
248
|
+
return { error: { message: "Card declined" } };
|
|
249
|
+
}
|
|
250
|
+
return { paymentIntent: { status: "succeeded" } };
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
},
|
|
254
|
+
[options.failPayment],
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Usage
|
|
261
|
+
test("handles declined card", async ({ page, mockStripe }) => {
|
|
262
|
+
await mockStripe({ failPayment: true });
|
|
263
|
+
|
|
264
|
+
await page.goto("/checkout");
|
|
265
|
+
await page.getByRole("button", { name: "Pay" }).click();
|
|
266
|
+
|
|
267
|
+
await expect(page.getByText("Card declined")).toBeVisible();
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Email Verification
|
|
272
|
+
|
|
273
|
+
### Mock Email API
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
test("email verification flow", async ({ page, request }) => {
|
|
277
|
+
let verificationToken: string;
|
|
278
|
+
|
|
279
|
+
// Capture the verification email
|
|
280
|
+
await page.route("**/api/send-verification", async (route) => {
|
|
281
|
+
const body = route.request().postDataJSON();
|
|
282
|
+
verificationToken = `mock-token-${Date.now()}`;
|
|
283
|
+
|
|
284
|
+
// Don't actually send email, just store token
|
|
285
|
+
route.fulfill({
|
|
286
|
+
json: { sent: true, messageId: "msg-123" },
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Mock token verification
|
|
291
|
+
await page.route("**/api/verify-email**", (route) => {
|
|
292
|
+
const url = new URL(route.request().url());
|
|
293
|
+
const token = url.searchParams.get("token");
|
|
294
|
+
|
|
295
|
+
if (token === verificationToken) {
|
|
296
|
+
route.fulfill({ json: { verified: true } });
|
|
297
|
+
} else {
|
|
298
|
+
route.fulfill({ status: 400, json: { error: "Invalid token" } });
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
await page.goto("/signup");
|
|
303
|
+
await page.getByLabel("Email").fill("test@example.com");
|
|
304
|
+
await page.getByRole("button", { name: "Sign Up" }).click();
|
|
305
|
+
|
|
306
|
+
await expect(page.getByText("Check your email")).toBeVisible();
|
|
307
|
+
|
|
308
|
+
// Simulate clicking email link
|
|
309
|
+
await page.goto(`/verify?token=${verificationToken}`);
|
|
310
|
+
|
|
311
|
+
await expect(page.getByText("Email verified")).toBeVisible();
|
|
312
|
+
});
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Use Mailinator/Temp Mail
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
// fixtures/email.fixture.ts
|
|
319
|
+
type EmailFixtures = {
|
|
320
|
+
getVerificationEmail: (inbox: string) => Promise<{ link: string }>;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
export const test = base.extend<EmailFixtures>({
|
|
324
|
+
getVerificationEmail: async ({ request }, use) => {
|
|
325
|
+
await use(async (inbox) => {
|
|
326
|
+
// Poll Mailinator API for new email
|
|
327
|
+
const response = await request.get(
|
|
328
|
+
`https://api.mailinator.com/v2/domains/public/inboxes/${inbox}`,
|
|
329
|
+
{
|
|
330
|
+
headers: {
|
|
331
|
+
Authorization: `Bearer ${process.env.MAILINATOR_API_KEY}`,
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
const messages = await response.json();
|
|
337
|
+
const latest = messages.msgs[0];
|
|
338
|
+
|
|
339
|
+
// Get full message
|
|
340
|
+
const msgResponse = await request.get(
|
|
341
|
+
`https://api.mailinator.com/v2/domains/public/inboxes/${inbox}/messages/${latest.id}`,
|
|
342
|
+
{
|
|
343
|
+
headers: {
|
|
344
|
+
Authorization: `Bearer ${process.env.MAILINATOR_API_KEY}`,
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
const message = await msgResponse.json();
|
|
350
|
+
|
|
351
|
+
// Extract verification link from HTML
|
|
352
|
+
const linkMatch = message.parts[0].body.match(
|
|
353
|
+
/href="([^"]*verify[^"]*)"/,
|
|
354
|
+
);
|
|
355
|
+
return { link: linkMatch?.[1] || "" };
|
|
356
|
+
});
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## SMS Verification
|
|
362
|
+
|
|
363
|
+
### Mock SMS API
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
test("SMS verification", async ({ page }) => {
|
|
367
|
+
let smsCode: string;
|
|
368
|
+
|
|
369
|
+
// Capture SMS send
|
|
370
|
+
await page.route("**/api/send-sms", (route) => {
|
|
371
|
+
smsCode = Math.random().toString().slice(2, 8); // 6-digit code
|
|
372
|
+
|
|
373
|
+
route.fulfill({
|
|
374
|
+
json: { sent: true, messageId: "sms-123" },
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// Mock code verification
|
|
379
|
+
await page.route("**/api/verify-sms", (route) => {
|
|
380
|
+
const body = route.request().postDataJSON();
|
|
381
|
+
|
|
382
|
+
if (body.code === smsCode) {
|
|
383
|
+
route.fulfill({ json: { verified: true } });
|
|
384
|
+
} else {
|
|
385
|
+
route.fulfill({ status: 400, json: { error: "Invalid code" } });
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
await page.goto("/verify-phone");
|
|
390
|
+
await page.getByLabel("Phone").fill("+1234567890");
|
|
391
|
+
await page.getByRole("button", { name: "Send Code" }).click();
|
|
392
|
+
|
|
393
|
+
// Enter the code
|
|
394
|
+
await page.getByLabel("Verification Code").fill(smsCode);
|
|
395
|
+
await page.getByRole("button", { name: "Verify" }).click();
|
|
396
|
+
|
|
397
|
+
await expect(page.getByText("Phone verified")).toBeVisible();
|
|
398
|
+
});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
## Analytics & Tracking
|
|
402
|
+
|
|
403
|
+
### Block Analytics in Tests
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
test.beforeEach(async ({ page }) => {
|
|
407
|
+
// Block all analytics/tracking
|
|
408
|
+
await page.route(
|
|
409
|
+
/google-analytics|googletagmanager|facebook|hotjar|segment|mixpanel|amplitude/,
|
|
410
|
+
(route) => route.abort(),
|
|
411
|
+
);
|
|
412
|
+
});
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Mock Analytics for Verification
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
test("tracks purchase event", async ({ page }) => {
|
|
419
|
+
const analyticsEvents: any[] = [];
|
|
420
|
+
|
|
421
|
+
// Capture analytics calls
|
|
422
|
+
await page.route("**/api/analytics/**", (route) => {
|
|
423
|
+
analyticsEvents.push(route.request().postDataJSON());
|
|
424
|
+
route.fulfill({ status: 200 });
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// Mock analytics SDK
|
|
428
|
+
await page.addInitScript(() => {
|
|
429
|
+
(window as any).analytics = {
|
|
430
|
+
track: (event: string, props: any) => {
|
|
431
|
+
fetch("/api/analytics/track", {
|
|
432
|
+
method: "POST",
|
|
433
|
+
body: JSON.stringify({ event, props }),
|
|
434
|
+
});
|
|
435
|
+
},
|
|
436
|
+
};
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
await page.goto("/checkout");
|
|
440
|
+
await page.getByRole("button", { name: "Complete Purchase" }).click();
|
|
441
|
+
|
|
442
|
+
// Verify analytics event was sent
|
|
443
|
+
expect(analyticsEvents).toContainEqual(
|
|
444
|
+
expect.objectContaining({
|
|
445
|
+
event: "Purchase Completed",
|
|
446
|
+
props: expect.objectContaining({ amount: expect.any(Number) }),
|
|
447
|
+
}),
|
|
448
|
+
);
|
|
449
|
+
});
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## Anti-Patterns to Avoid
|
|
453
|
+
|
|
454
|
+
| Anti-Pattern | Problem | Solution |
|
|
455
|
+
| ------------------------- | ------------------------------ | ----------------------- |
|
|
456
|
+
| Using real OAuth in tests | Slow, needs credentials, flaky | Mock OAuth endpoints |
|
|
457
|
+
| Real payment processing | Charges real money, slow | Use test mode or mock |
|
|
458
|
+
| Waiting for real emails | Very slow, unreliable | Mock email API |
|
|
459
|
+
| Not mocking analytics | Pollutes analytics data | Block or mock analytics |
|
|
460
|
+
|
|
461
|
+
## Related References
|
|
462
|
+
|
|
463
|
+
- **Network Mocking**: See [network-advanced.md](network-advanced.md) for route patterns
|
|
464
|
+
- **Authentication**: See [fixtures-hooks.md](../core/fixtures-hooks.md) for auth patterns
|