@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.
Files changed (97) hide show
  1. package/.agents/skills/e2e-testing-expert/SKILL.md +28 -0
  2. package/.agents/skills/frontend-design/LICENSE.txt +177 -0
  3. package/.agents/skills/frontend-design/SKILL.md +42 -0
  4. package/.agents/skills/nodejs-backend-patterns/SKILL.md +639 -0
  5. package/.agents/skills/nodejs-backend-patterns/references/advanced-patterns.md +430 -0
  6. package/.agents/skills/playwright-best-practices/LICENSE.md +7 -0
  7. package/.agents/skills/playwright-best-practices/README.md +147 -0
  8. package/.agents/skills/playwright-best-practices/SKILL.md +303 -0
  9. package/.agents/skills/playwright-best-practices/advanced/authentication-flows.md +360 -0
  10. package/.agents/skills/playwright-best-practices/advanced/authentication.md +871 -0
  11. package/.agents/skills/playwright-best-practices/advanced/clock-mocking.md +364 -0
  12. package/.agents/skills/playwright-best-practices/advanced/mobile-testing.md +409 -0
  13. package/.agents/skills/playwright-best-practices/advanced/multi-context.md +288 -0
  14. package/.agents/skills/playwright-best-practices/advanced/multi-user.md +393 -0
  15. package/.agents/skills/playwright-best-practices/advanced/network-advanced.md +452 -0
  16. package/.agents/skills/playwright-best-practices/advanced/third-party.md +464 -0
  17. package/.agents/skills/playwright-best-practices/architecture/pom-vs-fixtures.md +363 -0
  18. package/.agents/skills/playwright-best-practices/architecture/test-architecture.md +369 -0
  19. package/.agents/skills/playwright-best-practices/architecture/when-to-mock.md +383 -0
  20. package/.agents/skills/playwright-best-practices/browser-apis/browser-apis.md +391 -0
  21. package/.agents/skills/playwright-best-practices/browser-apis/iframes.md +403 -0
  22. package/.agents/skills/playwright-best-practices/browser-apis/service-workers.md +504 -0
  23. package/.agents/skills/playwright-best-practices/browser-apis/websockets.md +403 -0
  24. package/.agents/skills/playwright-best-practices/core/annotations.md +424 -0
  25. package/.agents/skills/playwright-best-practices/core/assertions-waiting.md +361 -0
  26. package/.agents/skills/playwright-best-practices/core/configuration.md +452 -0
  27. package/.agents/skills/playwright-best-practices/core/fixtures-hooks.md +417 -0
  28. package/.agents/skills/playwright-best-practices/core/global-setup.md +434 -0
  29. package/.agents/skills/playwright-best-practices/core/locators.md +242 -0
  30. package/.agents/skills/playwright-best-practices/core/page-object-model.md +315 -0
  31. package/.agents/skills/playwright-best-practices/core/projects-dependencies.md +453 -0
  32. package/.agents/skills/playwright-best-practices/core/test-data.md +492 -0
  33. package/.agents/skills/playwright-best-practices/core/test-suite-structure.md +361 -0
  34. package/.agents/skills/playwright-best-practices/core/test-tags.md +298 -0
  35. package/.agents/skills/playwright-best-practices/debugging/console-errors.md +420 -0
  36. package/.agents/skills/playwright-best-practices/debugging/debugging.md +504 -0
  37. package/.agents/skills/playwright-best-practices/debugging/error-testing.md +360 -0
  38. package/.agents/skills/playwright-best-practices/debugging/flaky-tests.md +496 -0
  39. package/.agents/skills/playwright-best-practices/frameworks/angular.md +530 -0
  40. package/.agents/skills/playwright-best-practices/frameworks/nextjs.md +469 -0
  41. package/.agents/skills/playwright-best-practices/frameworks/react.md +531 -0
  42. package/.agents/skills/playwright-best-practices/frameworks/vue.md +574 -0
  43. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/ci-cd.md +468 -0
  44. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/docker.md +283 -0
  45. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/github-actions.md +546 -0
  46. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/gitlab.md +397 -0
  47. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/other-providers.md +521 -0
  48. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/parallel-sharding.md +371 -0
  49. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/performance.md +453 -0
  50. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/reporting.md +424 -0
  51. package/.agents/skills/playwright-best-practices/infrastructure-ci-cd/test-coverage.md +497 -0
  52. package/.agents/skills/playwright-best-practices/testing-patterns/accessibility.md +359 -0
  53. package/.agents/skills/playwright-best-practices/testing-patterns/api-testing.md +719 -0
  54. package/.agents/skills/playwright-best-practices/testing-patterns/browser-extensions.md +506 -0
  55. package/.agents/skills/playwright-best-practices/testing-patterns/canvas-webgl.md +493 -0
  56. package/.agents/skills/playwright-best-practices/testing-patterns/component-testing.md +500 -0
  57. package/.agents/skills/playwright-best-practices/testing-patterns/drag-drop.md +576 -0
  58. package/.agents/skills/playwright-best-practices/testing-patterns/electron.md +509 -0
  59. package/.agents/skills/playwright-best-practices/testing-patterns/file-operations.md +377 -0
  60. package/.agents/skills/playwright-best-practices/testing-patterns/file-upload-download.md +562 -0
  61. package/.agents/skills/playwright-best-practices/testing-patterns/forms-validation.md +561 -0
  62. package/.agents/skills/playwright-best-practices/testing-patterns/graphql-testing.md +331 -0
  63. package/.agents/skills/playwright-best-practices/testing-patterns/i18n.md +508 -0
  64. package/.agents/skills/playwright-best-practices/testing-patterns/performance-testing.md +476 -0
  65. package/.agents/skills/playwright-best-practices/testing-patterns/security-testing.md +430 -0
  66. package/.agents/skills/playwright-best-practices/testing-patterns/visual-regression.md +634 -0
  67. package/.env.example +21 -0
  68. package/README.md +30 -0
  69. package/bin/arcality.mjs +86 -0
  70. package/package.json +66 -0
  71. package/playwright.config.ts +12 -0
  72. package/scripts/cleanup-qmsdev.mjs +63 -0
  73. package/scripts/discover-view.mjs +52 -0
  74. package/scripts/extract-view.mjs +64 -0
  75. package/scripts/gen-and-run.mjs +838 -0
  76. package/scripts/init.mjs +290 -0
  77. package/scripts/migrate-to-central-out.mjs +157 -0
  78. package/scripts/postinstall.mjs +63 -0
  79. package/scripts/rebrand-report.mjs +241 -0
  80. package/scripts/setup.mjs +166 -0
  81. package/src/KnowledgeService.ts +239 -0
  82. package/src/arcalityClient.mjs +266 -0
  83. package/src/configLoader.mjs +179 -0
  84. package/src/configManager.mjs +172 -0
  85. package/src/consoleBanner.ts +32 -0
  86. package/src/envSetup.ts +205 -0
  87. package/src/index.ts +25 -0
  88. package/src/projectInspector.ts +42 -0
  89. package/src/services/collectiveMemoryService.ts +178 -0
  90. package/src/testRunner.ts +201 -0
  91. package/tests/_helpers/ArcalityReporter.ts +490 -0
  92. package/tests/_helpers/agentic-runner.spec.ts +741 -0
  93. package/tests/_helpers/ai-agent-helper.ts +1573 -0
  94. package/tests/_helpers/discover-view.spec.ts +238 -0
  95. package/tests/_helpers/extract-view.spec.ts +118 -0
  96. package/tests/_helpers/qa-tools.ts +333 -0
  97. package/tests/_helpers/smart-action.spec.ts +1458 -0
@@ -0,0 +1,393 @@
1
+ # Multi-User & Collaboration Testing
2
+
3
+ ## Table of Contents
4
+
5
+ 1. [Multiple Browser Contexts](#multiple-browser-contexts)
6
+ 2. [Real-Time Collaboration](#real-time-collaboration)
7
+ 3. [Role-Based Testing](#role-based-testing)
8
+ 4. [Concurrent Actions](#concurrent-actions)
9
+ 5. [Chat & Messaging](#chat--messaging)
10
+
11
+ ## Multiple Browser Contexts
12
+
13
+ ### Two Users in Same Test
14
+
15
+ ```typescript
16
+ test("two users see each other's changes", async ({ browser }) => {
17
+ // Create two isolated contexts (like two browsers)
18
+ const userAContext = await browser.newContext();
19
+ const userBContext = await browser.newContext();
20
+
21
+ const userAPage = await userAContext.newPage();
22
+ const userBPage = await userBContext.newPage();
23
+
24
+ // Both users go to the same document
25
+ await userAPage.goto("/doc/shared-123");
26
+ await userBPage.goto("/doc/shared-123");
27
+
28
+ // User A types
29
+ await userAPage.getByLabel("Content").fill("Hello from User A");
30
+
31
+ // User B should see the change
32
+ await expect(userBPage.getByText("Hello from User A")).toBeVisible();
33
+
34
+ // Cleanup
35
+ await userAContext.close();
36
+ await userBContext.close();
37
+ });
38
+ ```
39
+
40
+ ### Multiple Users with Auth States
41
+
42
+ ```typescript
43
+ test("admin and user interaction", async ({ browser }) => {
44
+ // Load different auth states
45
+ const adminContext = await browser.newContext({
46
+ storageState: ".auth/admin.json",
47
+ });
48
+ const userContext = await browser.newContext({
49
+ storageState: ".auth/user.json",
50
+ });
51
+
52
+ const adminPage = await adminContext.newPage();
53
+ const userPage = await userContext.newPage();
54
+
55
+ // User submits request
56
+ await userPage.goto("/support");
57
+ await userPage.getByLabel("Message").fill("Need help!");
58
+ await userPage.getByRole("button", { name: "Submit" }).click();
59
+
60
+ // Admin sees and responds
61
+ await adminPage.goto("/admin/tickets");
62
+ await expect(adminPage.getByText("Need help!")).toBeVisible();
63
+ await adminPage.getByRole("button", { name: "Reply" }).click();
64
+ await adminPage.getByLabel("Response").fill("How can I help?");
65
+ await adminPage.getByRole("button", { name: "Send" }).click();
66
+
67
+ // User sees response
68
+ await expect(userPage.getByText("How can I help?")).toBeVisible();
69
+
70
+ await adminContext.close();
71
+ await userContext.close();
72
+ });
73
+ ```
74
+
75
+ ### Multi-User Fixture
76
+
77
+ ```typescript
78
+ // fixtures/multi-user.fixture.ts
79
+ import { test as base, Browser, BrowserContext, Page } from "@playwright/test";
80
+
81
+ type UserSession = {
82
+ context: BrowserContext;
83
+ page: Page;
84
+ };
85
+
86
+ type MultiUserFixtures = {
87
+ createUser: (authState?: string) => Promise<UserSession>;
88
+ };
89
+
90
+ export const test = base.extend<MultiUserFixtures>({
91
+ createUser: async ({ browser }, use) => {
92
+ const sessions: UserSession[] = [];
93
+
94
+ await use(async (authState) => {
95
+ const context = await browser.newContext({
96
+ storageState: authState,
97
+ });
98
+ const page = await context.newPage();
99
+ sessions.push({ context, page });
100
+ return { context, page };
101
+ });
102
+
103
+ // Cleanup all sessions
104
+ for (const session of sessions) {
105
+ await session.context.close();
106
+ }
107
+ },
108
+ });
109
+
110
+ // Usage
111
+ test("3 users collaborate", async ({ createUser }) => {
112
+ const alice = await createUser(".auth/alice.json");
113
+ const bob = await createUser(".auth/bob.json");
114
+ const charlie = await createUser(".auth/charlie.json");
115
+
116
+ // All navigate to same room
117
+ await alice.page.goto("/room/123");
118
+ await bob.page.goto("/room/123");
119
+ await charlie.page.goto("/room/123");
120
+
121
+ // Test interactions...
122
+ });
123
+ ```
124
+
125
+ ## Real-Time Collaboration
126
+
127
+ ### Collaborative Document
128
+
129
+ ```typescript
130
+ test("real-time collaborative editing", async ({ browser }) => {
131
+ const user1 = await browser.newContext();
132
+ const user2 = await browser.newContext();
133
+
134
+ const page1 = await user1.newPage();
135
+ const page2 = await user2.newPage();
136
+
137
+ await page1.goto("/docs/shared");
138
+ await page2.goto("/docs/shared");
139
+
140
+ // User 1 types at the beginning
141
+ const editor1 = page1.getByRole("textbox");
142
+ await editor1.click();
143
+ await editor1.press("Home");
144
+ await editor1.type("User 1: ");
145
+
146
+ // User 2 types at the end
147
+ const editor2 = page2.getByRole("textbox");
148
+ await editor2.click();
149
+ await editor2.press("End");
150
+ await editor2.type(" - User 2");
151
+
152
+ // Both should see combined result
153
+ await expect(page1.getByRole("textbox")).toContainText("User 1:");
154
+ await expect(page1.getByRole("textbox")).toContainText("- User 2");
155
+ await expect(page2.getByRole("textbox")).toContainText("User 1:");
156
+ await expect(page2.getByRole("textbox")).toContainText("- User 2");
157
+
158
+ await user1.close();
159
+ await user2.close();
160
+ });
161
+ ```
162
+
163
+ ### Cursor Presence
164
+
165
+ ```typescript
166
+ test("shows other user cursors", async ({ browser }) => {
167
+ const ctx1 = await browser.newContext();
168
+ const ctx2 = await browser.newContext();
169
+
170
+ const page1 = await ctx1.newPage();
171
+ const page2 = await ctx2.newPage();
172
+
173
+ // Mock to identify users
174
+ await page1.route("**/api/me", (route) =>
175
+ route.fulfill({ json: { id: "user-1", name: "Alice" } }),
176
+ );
177
+ await page2.route("**/api/me", (route) =>
178
+ route.fulfill({ json: { id: "user-2", name: "Bob" } }),
179
+ );
180
+
181
+ await page1.goto("/whiteboard/123");
182
+ await page2.goto("/whiteboard/123");
183
+
184
+ // Move cursor on page1
185
+ await page1.mouse.move(200, 200);
186
+
187
+ // Page2 should see Alice's cursor
188
+ await expect(page2.getByTestId("cursor-user-1")).toBeVisible();
189
+ await expect(page2.getByText("Alice")).toBeVisible();
190
+
191
+ await ctx1.close();
192
+ await ctx2.close();
193
+ });
194
+ ```
195
+
196
+ ## Role-Based Testing
197
+
198
+ ### Test RBAC
199
+
200
+ ```typescript
201
+ const roles = [
202
+ { role: "admin", canDelete: true, canEdit: true, canView: true },
203
+ { role: "editor", canDelete: false, canEdit: true, canView: true },
204
+ { role: "viewer", canDelete: false, canEdit: false, canView: true },
205
+ ];
206
+
207
+ for (const { role, canDelete, canEdit, canView } of roles) {
208
+ test(`${role} permissions`, async ({ browser }) => {
209
+ const context = await browser.newContext({
210
+ storageState: `.auth/${role}.json`,
211
+ });
212
+ const page = await context.newPage();
213
+
214
+ await page.goto("/document/123");
215
+
216
+ // Check view permission
217
+ if (canView) {
218
+ await expect(page.getByTestId("content")).toBeVisible();
219
+ } else {
220
+ await expect(page.getByText("Access denied")).toBeVisible();
221
+ }
222
+
223
+ // Check edit permission
224
+ const editButton = page.getByRole("button", { name: "Edit" });
225
+ if (canEdit) {
226
+ await expect(editButton).toBeEnabled();
227
+ } else {
228
+ await expect(editButton).toBeDisabled();
229
+ }
230
+
231
+ // Check delete permission
232
+ const deleteButton = page.getByRole("button", { name: "Delete" });
233
+ if (canDelete) {
234
+ await expect(deleteButton).toBeVisible();
235
+ } else {
236
+ await expect(deleteButton).toBeHidden();
237
+ }
238
+
239
+ await context.close();
240
+ });
241
+ }
242
+ ```
243
+
244
+ ### Permission Escalation Test
245
+
246
+ ```typescript
247
+ test("cannot access admin routes as user", async ({ browser }) => {
248
+ const userContext = await browser.newContext({
249
+ storageState: ".auth/user.json",
250
+ });
251
+ const page = await userContext.newPage();
252
+
253
+ // Try to access admin page directly
254
+ await page.goto("/admin/users");
255
+
256
+ // Should redirect or show error
257
+ await expect(page).not.toHaveURL("/admin/users");
258
+ await expect(page.getByText("Access denied")).toBeVisible();
259
+
260
+ await userContext.close();
261
+ });
262
+ ```
263
+
264
+ ## Concurrent Actions
265
+
266
+ ### Race Condition Testing
267
+
268
+ ```typescript
269
+ test("handles concurrent edits", async ({ browser }) => {
270
+ const ctx1 = await browser.newContext();
271
+ const ctx2 = await browser.newContext();
272
+
273
+ const page1 = await ctx1.newPage();
274
+ const page2 = await ctx2.newPage();
275
+
276
+ await page1.goto("/item/123");
277
+ await page2.goto("/item/123");
278
+
279
+ // Both click edit at the same time
280
+ await Promise.all([
281
+ page1.getByRole("button", { name: "Edit" }).click(),
282
+ page2.getByRole("button", { name: "Edit" }).click(),
283
+ ]);
284
+
285
+ // Both try to save different values
286
+ await page1.getByLabel("Name").fill("Value from User 1");
287
+ await page2.getByLabel("Name").fill("Value from User 2");
288
+
289
+ await Promise.all([
290
+ page1.getByRole("button", { name: "Save" }).click(),
291
+ page2.getByRole("button", { name: "Save" }).click(),
292
+ ]);
293
+
294
+ // One should succeed, one should get conflict error
295
+ const page1HasConflict = await page1.getByText("Conflict").isVisible();
296
+ const page2HasConflict = await page2.getByText("Conflict").isVisible();
297
+
298
+ // Exactly one should have conflict
299
+ expect(page1HasConflict || page2HasConflict).toBe(true);
300
+ expect(page1HasConflict && page2HasConflict).toBe(false);
301
+
302
+ await ctx1.close();
303
+ await ctx2.close();
304
+ });
305
+ ```
306
+
307
+ ### Optimistic Locking Test
308
+
309
+ ```typescript
310
+ test("optimistic locking prevents overwrites", async ({ browser }) => {
311
+ const ctx1 = await browser.newContext();
312
+ const ctx2 = await browser.newContext();
313
+
314
+ const page1 = await ctx1.newPage();
315
+ const page2 = await ctx2.newPage();
316
+
317
+ // Both load the same version
318
+ await page1.goto("/record/123");
319
+ await page2.goto("/record/123");
320
+
321
+ // User 1 edits and saves first
322
+ await page1.getByRole("button", { name: "Edit" }).click();
323
+ await page1.getByLabel("Value").fill("Updated by User 1");
324
+ await page1.getByRole("button", { name: "Save" }).click();
325
+ await expect(page1.getByText("Saved")).toBeVisible();
326
+
327
+ // User 2 tries to save with stale version
328
+ await page2.getByRole("button", { name: "Edit" }).click();
329
+ await page2.getByLabel("Value").fill("Updated by User 2");
330
+ await page2.getByRole("button", { name: "Save" }).click();
331
+
332
+ // Should fail with version conflict
333
+ await expect(page2.getByText("Someone else modified this")).toBeVisible();
334
+ await expect(page2.getByRole("button", { name: "Reload" })).toBeVisible();
335
+
336
+ await ctx1.close();
337
+ await ctx2.close();
338
+ });
339
+ ```
340
+
341
+ ## Chat & Messaging
342
+
343
+ ### Real-Time Chat
344
+
345
+ ```typescript
346
+ test("chat messages sync between users", async ({ browser }) => {
347
+ const aliceCtx = await browser.newContext();
348
+ const bobCtx = await browser.newContext();
349
+
350
+ const alicePage = await aliceCtx.newPage();
351
+ const bobPage = await bobCtx.newPage();
352
+
353
+ // Setup user identities
354
+ await alicePage.route("**/api/me", (r) =>
355
+ r.fulfill({ json: { name: "Alice" } }),
356
+ );
357
+ await bobPage.route("**/api/me", (r) => r.fulfill({ json: { name: "Bob" } }));
358
+
359
+ await alicePage.goto("/chat/room-1");
360
+ await bobPage.goto("/chat/room-1");
361
+
362
+ // Alice sends message
363
+ await alicePage.getByLabel("Message").fill("Hi Bob!");
364
+ await alicePage.getByRole("button", { name: "Send" }).click();
365
+
366
+ // Bob sees it
367
+ await expect(bobPage.getByText("Alice: Hi Bob!")).toBeVisible();
368
+
369
+ // Bob replies
370
+ await bobPage.getByLabel("Message").fill("Hey Alice!");
371
+ await bobPage.getByRole("button", { name: "Send" }).click();
372
+
373
+ // Alice sees it
374
+ await expect(alicePage.getByText("Bob: Hey Alice!")).toBeVisible();
375
+
376
+ await aliceCtx.close();
377
+ await bobCtx.close();
378
+ });
379
+ ```
380
+
381
+ ## Anti-Patterns to Avoid
382
+
383
+ | Anti-Pattern | Problem | Solution |
384
+ | ----------------------------- | ----------------------------- | ---------------------------- |
385
+ | Sharing context between users | State leaks, not isolated | Create separate contexts |
386
+ | Not closing contexts | Memory leak, browser overload | Always close in cleanup |
387
+ | Hardcoded timing for sync | Flaky tests | Use `expect().toBeVisible()` |
388
+ | Testing only single user | Misses collaboration bugs | Test multi-user scenarios |
389
+
390
+ ## Related References
391
+
392
+ - **Authentication**: See [fixtures-hooks.md](../core/fixtures-hooks.md) for auth setup
393
+ - **WebSockets**: See [websockets.md](../browser-apis/websockets.md) for real-time mocking