@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,403 @@
|
|
|
1
|
+
# WebSocket & Real-Time Testing
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
1. [WebSocket Basics](#websocket-basics)
|
|
6
|
+
2. [Mocking WebSocket Messages](#mocking-websocket-messages)
|
|
7
|
+
3. [Testing Real-Time Features](#testing-real-time-features)
|
|
8
|
+
4. [Server-Sent Events](#server-sent-events)
|
|
9
|
+
5. [Reconnection Testing](#reconnection-testing)
|
|
10
|
+
|
|
11
|
+
## WebSocket Basics
|
|
12
|
+
|
|
13
|
+
### Wait for WebSocket Connection
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
test("chat connects via websocket", async ({ page }) => {
|
|
17
|
+
// Listen for WebSocket connection
|
|
18
|
+
const wsPromise = page.waitForEvent("websocket");
|
|
19
|
+
|
|
20
|
+
await page.goto("/chat");
|
|
21
|
+
|
|
22
|
+
const ws = await wsPromise;
|
|
23
|
+
expect(ws.url()).toContain("/ws/chat");
|
|
24
|
+
|
|
25
|
+
// Wait for connection to be established
|
|
26
|
+
await ws.waitForEvent("framesent");
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Monitor WebSocket Messages
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
test("receives real-time updates", async ({ page }) => {
|
|
34
|
+
const messages: string[] = [];
|
|
35
|
+
|
|
36
|
+
// Set up listener before navigation
|
|
37
|
+
page.on("websocket", (ws) => {
|
|
38
|
+
ws.on("framereceived", (frame) => {
|
|
39
|
+
messages.push(frame.payload as string);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
await page.goto("/dashboard");
|
|
44
|
+
|
|
45
|
+
// Wait for some messages
|
|
46
|
+
await expect.poll(() => messages.length).toBeGreaterThan(0);
|
|
47
|
+
|
|
48
|
+
// Verify message format
|
|
49
|
+
const data = JSON.parse(messages[0]);
|
|
50
|
+
expect(data).toHaveProperty("type");
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Capture Sent Messages
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
test("sends correct message format", async ({ page }) => {
|
|
58
|
+
const sentMessages: string[] = [];
|
|
59
|
+
|
|
60
|
+
page.on("websocket", (ws) => {
|
|
61
|
+
ws.on("framesent", (frame) => {
|
|
62
|
+
sentMessages.push(frame.payload as string);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
await page.goto("/chat");
|
|
67
|
+
await page.getByLabel("Message").fill("Hello!");
|
|
68
|
+
await page.getByRole("button", { name: "Send" }).click();
|
|
69
|
+
|
|
70
|
+
// Verify sent message
|
|
71
|
+
await expect.poll(() => sentMessages.length).toBeGreaterThan(0);
|
|
72
|
+
|
|
73
|
+
const sent = JSON.parse(sentMessages[sentMessages.length - 1]);
|
|
74
|
+
expect(sent).toEqual({
|
|
75
|
+
type: "message",
|
|
76
|
+
content: "Hello!",
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Mocking WebSocket Messages
|
|
82
|
+
|
|
83
|
+
### Inject Messages via Page Evaluate
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
test("displays incoming chat message", async ({ page }) => {
|
|
87
|
+
await page.goto("/chat");
|
|
88
|
+
|
|
89
|
+
// Wait for WebSocket to be ready
|
|
90
|
+
await page.waitForFunction(
|
|
91
|
+
() => (window as any).chatSocket?.readyState === 1,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// Simulate incoming message
|
|
95
|
+
await page.evaluate(() => {
|
|
96
|
+
const event = new MessageEvent("message", {
|
|
97
|
+
data: JSON.stringify({
|
|
98
|
+
type: "message",
|
|
99
|
+
from: "Alice",
|
|
100
|
+
content: "Hello there!",
|
|
101
|
+
}),
|
|
102
|
+
});
|
|
103
|
+
(window as any).chatSocket.dispatchEvent(event);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
await expect(page.getByText("Alice: Hello there!")).toBeVisible();
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Mock WebSocket with Route Handler
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
test("mock websocket entirely", async ({ page, context }) => {
|
|
114
|
+
// Intercept the WebSocket upgrade
|
|
115
|
+
await context.route("**/ws/**", async (route) => {
|
|
116
|
+
// For WebSocket routes, we can't fulfill directly
|
|
117
|
+
// Instead, use page.evaluate to mock the client-side
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Alternative: Mock at application level
|
|
121
|
+
await page.addInitScript(() => {
|
|
122
|
+
const OriginalWebSocket = window.WebSocket;
|
|
123
|
+
(window as any).WebSocket = function (url: string) {
|
|
124
|
+
const ws = {
|
|
125
|
+
readyState: 1,
|
|
126
|
+
send: (data: string) => {
|
|
127
|
+
console.log("WS Send:", data);
|
|
128
|
+
},
|
|
129
|
+
close: () => {},
|
|
130
|
+
addEventListener: () => {},
|
|
131
|
+
removeEventListener: () => {},
|
|
132
|
+
};
|
|
133
|
+
setTimeout(() => ws.onopen?.(), 100);
|
|
134
|
+
return ws;
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
await page.goto("/chat");
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### WebSocket Mock Fixture
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// fixtures/websocket.fixture.ts
|
|
146
|
+
import { test as base, Page } from "@playwright/test";
|
|
147
|
+
|
|
148
|
+
type WsMessage = { type: string; [key: string]: any };
|
|
149
|
+
|
|
150
|
+
type WebSocketFixtures = {
|
|
151
|
+
mockWebSocket: {
|
|
152
|
+
injectMessage: (message: WsMessage) => Promise<void>;
|
|
153
|
+
getSentMessages: () => Promise<WsMessage[]>;
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export const test = base.extend<WebSocketFixtures>({
|
|
158
|
+
mockWebSocket: async ({ page }, use) => {
|
|
159
|
+
const sentMessages: WsMessage[] = [];
|
|
160
|
+
|
|
161
|
+
// Capture sent messages
|
|
162
|
+
await page.addInitScript(() => {
|
|
163
|
+
(window as any).__wsSent = [];
|
|
164
|
+
const OriginalWebSocket = window.WebSocket;
|
|
165
|
+
window.WebSocket = function (url: string) {
|
|
166
|
+
const ws = new OriginalWebSocket(url);
|
|
167
|
+
const originalSend = ws.send.bind(ws);
|
|
168
|
+
ws.send = (data: string) => {
|
|
169
|
+
(window as any).__wsSent.push(JSON.parse(data));
|
|
170
|
+
originalSend(data);
|
|
171
|
+
};
|
|
172
|
+
(window as any).__ws = ws;
|
|
173
|
+
return ws;
|
|
174
|
+
} as any;
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
await use({
|
|
178
|
+
injectMessage: async (message) => {
|
|
179
|
+
await page.evaluate((msg) => {
|
|
180
|
+
const event = new MessageEvent("message", {
|
|
181
|
+
data: JSON.stringify(msg),
|
|
182
|
+
});
|
|
183
|
+
(window as any).__ws?.dispatchEvent(event);
|
|
184
|
+
}, message);
|
|
185
|
+
},
|
|
186
|
+
getSentMessages: async () => {
|
|
187
|
+
return page.evaluate(() => (window as any).__wsSent || []);
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Usage
|
|
194
|
+
test("chat with mocked websocket", async ({ page, mockWebSocket }) => {
|
|
195
|
+
await page.goto("/chat");
|
|
196
|
+
|
|
197
|
+
// Inject incoming message
|
|
198
|
+
await mockWebSocket.injectMessage({
|
|
199
|
+
type: "message",
|
|
200
|
+
from: "Bob",
|
|
201
|
+
content: "Hi!",
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
await expect(page.getByText("Bob: Hi!")).toBeVisible();
|
|
205
|
+
|
|
206
|
+
// Send a reply
|
|
207
|
+
await page.getByLabel("Message").fill("Hello Bob!");
|
|
208
|
+
await page.getByRole("button", { name: "Send" }).click();
|
|
209
|
+
|
|
210
|
+
// Verify sent message
|
|
211
|
+
const sent = await mockWebSocket.getSentMessages();
|
|
212
|
+
expect(sent).toContainEqual(
|
|
213
|
+
expect.objectContaining({ content: "Hello Bob!" }),
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Testing Real-Time Features
|
|
219
|
+
|
|
220
|
+
### Live Notifications
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
test("displays live notification", async ({ page }) => {
|
|
224
|
+
await page.goto("/dashboard");
|
|
225
|
+
|
|
226
|
+
// Simulate notification via WebSocket
|
|
227
|
+
await page.evaluate(() => {
|
|
228
|
+
const event = new MessageEvent("message", {
|
|
229
|
+
data: JSON.stringify({
|
|
230
|
+
type: "notification",
|
|
231
|
+
title: "New Order",
|
|
232
|
+
message: "Order #123 received",
|
|
233
|
+
}),
|
|
234
|
+
});
|
|
235
|
+
(window as any).notificationSocket.dispatchEvent(event);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
await expect(page.getByRole("alert")).toContainText("Order #123 received");
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Live Data Updates
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
test("updates stock price in real-time", async ({ page }) => {
|
|
246
|
+
await page.goto("/stocks/AAPL");
|
|
247
|
+
|
|
248
|
+
const priceElement = page.getByTestId("stock-price");
|
|
249
|
+
const initialPrice = await priceElement.textContent();
|
|
250
|
+
|
|
251
|
+
// Simulate price update
|
|
252
|
+
await page.evaluate(() => {
|
|
253
|
+
const event = new MessageEvent("message", {
|
|
254
|
+
data: JSON.stringify({
|
|
255
|
+
type: "price_update",
|
|
256
|
+
symbol: "AAPL",
|
|
257
|
+
price: 150.25,
|
|
258
|
+
}),
|
|
259
|
+
});
|
|
260
|
+
(window as any).stockSocket.dispatchEvent(event);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
await expect(priceElement).not.toHaveText(initialPrice!);
|
|
264
|
+
await expect(priceElement).toContainText("150.25");
|
|
265
|
+
});
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Collaborative Editing
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
test("shows collaborator cursor", async ({ page }) => {
|
|
272
|
+
await page.goto("/document/123");
|
|
273
|
+
|
|
274
|
+
// Simulate another user's cursor position
|
|
275
|
+
await page.evaluate(() => {
|
|
276
|
+
const event = new MessageEvent("message", {
|
|
277
|
+
data: JSON.stringify({
|
|
278
|
+
type: "cursor",
|
|
279
|
+
userId: "user-456",
|
|
280
|
+
userName: "Alice",
|
|
281
|
+
position: { x: 100, y: 200 },
|
|
282
|
+
}),
|
|
283
|
+
});
|
|
284
|
+
(window as any).docSocket.dispatchEvent(event);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
await expect(page.getByTestId("cursor-user-456")).toBeVisible();
|
|
288
|
+
await expect(page.getByText("Alice")).toBeVisible();
|
|
289
|
+
});
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Server-Sent Events
|
|
293
|
+
|
|
294
|
+
### Test SSE Updates
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
test("receives SSE updates", async ({ page }) => {
|
|
298
|
+
// Mock SSE endpoint
|
|
299
|
+
await page.route("**/api/events", (route) => {
|
|
300
|
+
route.fulfill({
|
|
301
|
+
status: 200,
|
|
302
|
+
headers: {
|
|
303
|
+
"Content-Type": "text/event-stream",
|
|
304
|
+
"Cache-Control": "no-cache",
|
|
305
|
+
Connection: "keep-alive",
|
|
306
|
+
},
|
|
307
|
+
body: `data: {"type":"update","value":42}\n\n`,
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
await page.goto("/live-data");
|
|
312
|
+
|
|
313
|
+
await expect(page.getByTestId("value")).toHaveText("42");
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Simulate Multiple SSE Events
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
test("handles multiple SSE events", async ({ page }) => {
|
|
321
|
+
await page.route("**/api/events", async (route) => {
|
|
322
|
+
const encoder = new TextEncoder();
|
|
323
|
+
const events = [
|
|
324
|
+
`data: {"count":1}\n\n`,
|
|
325
|
+
`data: {"count":2}\n\n`,
|
|
326
|
+
`data: {"count":3}\n\n`,
|
|
327
|
+
];
|
|
328
|
+
|
|
329
|
+
route.fulfill({
|
|
330
|
+
status: 200,
|
|
331
|
+
headers: { "Content-Type": "text/event-stream" },
|
|
332
|
+
body: events.join(""),
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
await page.goto("/counter");
|
|
337
|
+
|
|
338
|
+
// Should receive all events
|
|
339
|
+
await expect(page.getByTestId("count")).toHaveText("3");
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Reconnection Testing
|
|
344
|
+
|
|
345
|
+
### Test Connection Loss
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
test("handles connection loss gracefully", async ({ page }) => {
|
|
349
|
+
await page.goto("/chat");
|
|
350
|
+
|
|
351
|
+
// Simulate connection close
|
|
352
|
+
await page.evaluate(() => {
|
|
353
|
+
(window as any).chatSocket.close();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Should show disconnected state
|
|
357
|
+
await expect(page.getByText("Reconnecting...")).toBeVisible();
|
|
358
|
+
});
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Test Reconnection
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
test("reconnects after connection loss", async ({ page }) => {
|
|
365
|
+
await page.goto("/chat");
|
|
366
|
+
|
|
367
|
+
// Simulate disconnect
|
|
368
|
+
await page.evaluate(() => {
|
|
369
|
+
(window as any).chatSocket.close();
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
await expect(page.getByText("Reconnecting...")).toBeVisible();
|
|
373
|
+
|
|
374
|
+
// Simulate reconnection
|
|
375
|
+
await page.evaluate(() => {
|
|
376
|
+
const event = new Event("open");
|
|
377
|
+
(window as any).chatSocket = { readyState: 1 };
|
|
378
|
+
(window as any).chatSocket.dispatchEvent?.(event);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// Force component to re-check connection
|
|
382
|
+
await page.evaluate(() => {
|
|
383
|
+
window.dispatchEvent(new Event("online"));
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
await expect(page.getByText("Connected")).toBeVisible();
|
|
387
|
+
});
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
## Anti-Patterns to Avoid
|
|
391
|
+
|
|
392
|
+
| Anti-Pattern | Problem | Solution |
|
|
393
|
+
| ------------------------------------- | ----------------------------- | ---------------------------------- |
|
|
394
|
+
| Not waiting for WebSocket ready | Messages sent too early | Wait for `readyState === 1` |
|
|
395
|
+
| Testing against real WebSocket server | Flaky, timing-dependent | Mock WebSocket messages |
|
|
396
|
+
| Ignoring connection state | Tests pass but feature broken | Test connected/disconnected states |
|
|
397
|
+
| No cleanup of listeners | Memory leaks in tests | Clean up event listeners |
|
|
398
|
+
|
|
399
|
+
## Related References
|
|
400
|
+
|
|
401
|
+
- **Network**: See [network-advanced.md](../advanced/network-advanced.md) for HTTP mocking patterns
|
|
402
|
+
- **Assertions**: See [assertions-waiting.md](../core/assertions-waiting.md) for polling patterns
|
|
403
|
+
- **Multi-User**: See [multi-user.md](../advanced/multi-user.md) for real-time collaboration testing with multiple users
|