@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,403 @@
1
+ # iFrame Testing
2
+
3
+ ## Table of Contents
4
+
5
+ 1. [Basic iFrame Access](#basic-iframe-access)
6
+ 2. [Cross-Origin iFrames](#cross-origin-iframes)
7
+ 3. [Nested iFrames](#nested-iframes)
8
+ 4. [Dynamic iFrames](#dynamic-iframes)
9
+ 5. [iFrame Navigation](#iframe-navigation)
10
+ 6. [Common Patterns](#common-patterns)
11
+
12
+ ## Basic iFrame Access
13
+
14
+ ### Using frameLocator
15
+
16
+ ```typescript
17
+ // Access iframe by selector
18
+ const frame = page.frameLocator("iframe#payment");
19
+ await frame.getByRole("button", { name: "Pay" }).click();
20
+
21
+ // Access by name attribute
22
+ const namedFrame = page.frameLocator('iframe[name="checkout"]');
23
+ await namedFrame.getByLabel("Card number").fill("4242424242424242");
24
+
25
+ // Access by title
26
+ const titledFrame = page.frameLocator('iframe[title="Payment Form"]');
27
+
28
+ // Access by src (partial match)
29
+ const srcFrame = page.frameLocator('iframe[src*="stripe.com"]');
30
+ ```
31
+
32
+ ### Frame vs FrameLocator
33
+
34
+ ```typescript
35
+ // frameLocator - for locator-based operations (recommended)
36
+ const frameLocator = page.frameLocator("#my-iframe");
37
+ await frameLocator.getByRole("button").click();
38
+
39
+ // frame() - for Frame object operations (navigation, evaluation)
40
+ const frame = page.frame({ name: "my-frame" });
41
+ if (frame) {
42
+ await frame.goto("https://example.com");
43
+ const title = await frame.title();
44
+ }
45
+
46
+ // Get all frames
47
+ const frames = page.frames();
48
+ for (const f of frames) {
49
+ console.log("Frame URL:", f.url());
50
+ }
51
+ ```
52
+
53
+ ### Waiting for iFrame Content
54
+
55
+ ```typescript
56
+ // Wait for iframe to load
57
+ const frame = page.frameLocator("#dynamic-iframe");
58
+
59
+ // Wait for element inside iframe
60
+ await expect(frame.getByRole("heading")).toBeVisible({ timeout: 10000 });
61
+
62
+ // Wait for iframe src to change
63
+ await page.waitForFunction(() => {
64
+ const iframe = document.querySelector("iframe#my-frame") as HTMLIFrameElement;
65
+ return iframe?.src.includes("loaded");
66
+ });
67
+ ```
68
+
69
+ ## Cross-Origin iFrames
70
+
71
+ ### Accessing Cross-Origin Content
72
+
73
+ ```typescript
74
+ // Cross-origin iframes work seamlessly with frameLocator
75
+ const thirdPartyFrame = page.frameLocator('iframe[src*="third-party.com"]');
76
+
77
+ // Interact with elements inside cross-origin iframe
78
+ await thirdPartyFrame.getByRole("textbox").fill("test@example.com");
79
+ await thirdPartyFrame.getByRole("button", { name: "Submit" }).click();
80
+
81
+ // Wait for cross-origin iframe to be ready
82
+ await expect(thirdPartyFrame.locator("body")).toBeVisible();
83
+ ```
84
+
85
+ ### Payment Provider iFrames (Stripe, PayPal)
86
+
87
+ ```typescript
88
+ test("Stripe payment iframe", async ({ page }) => {
89
+ await page.goto("/checkout");
90
+
91
+ // Stripe uses multiple iframes for each field
92
+ const cardFrame = page
93
+ .frameLocator('iframe[name*="__privateStripeFrame"]')
94
+ .first();
95
+
96
+ // Wait for Stripe to initialize
97
+ await expect(cardFrame.locator('[placeholder="Card number"]')).toBeVisible({
98
+ timeout: 15000,
99
+ });
100
+
101
+ // Fill card details
102
+ await cardFrame
103
+ .locator('[placeholder="Card number"]')
104
+ .fill("4242424242424242");
105
+ await cardFrame.locator('[placeholder="MM / YY"]').fill("12/30");
106
+ await cardFrame.locator('[placeholder="CVC"]').fill("123");
107
+ });
108
+ ```
109
+
110
+ ### Handling OAuth in iFrames
111
+
112
+ ```typescript
113
+ test("OAuth iframe flow", async ({ page }) => {
114
+ await page.goto("/login");
115
+ await page.getByRole("button", { name: "Sign in with Google" }).click();
116
+
117
+ // If OAuth opens in iframe instead of popup
118
+ const oauthFrame = page.frameLocator('iframe[src*="accounts.google.com"]');
119
+
120
+ // Wait for OAuth form
121
+ await expect(oauthFrame.getByLabel("Email")).toBeVisible({ timeout: 10000 });
122
+ await oauthFrame.getByLabel("Email").fill("test@gmail.com");
123
+ });
124
+ ```
125
+
126
+ ## Nested iFrames
127
+
128
+ ### Accessing Nested Frames
129
+
130
+ ```typescript
131
+ // Parent iframe contains child iframe
132
+ const parentFrame = page.frameLocator("#outer-frame");
133
+ const childFrame = parentFrame.frameLocator("#inner-frame");
134
+
135
+ // Interact with deeply nested content
136
+ await childFrame.getByRole("button", { name: "Submit" }).click();
137
+
138
+ // Multiple levels of nesting
139
+ const level1 = page.frameLocator("#level1");
140
+ const level2 = level1.frameLocator("#level2");
141
+ const level3 = level2.frameLocator("#level3");
142
+ await level3.getByText("Deep content").click();
143
+ ```
144
+
145
+ ### Finding Elements Across Frame Hierarchy
146
+
147
+ ```typescript
148
+ // Helper to search all frames for an element
149
+ async function findInAnyFrame(
150
+ page: Page,
151
+ selector: string,
152
+ ): Promise<Locator | null> {
153
+ // Check main page first
154
+ const mainCount = await page.locator(selector).count();
155
+ if (mainCount > 0) return page.locator(selector);
156
+
157
+ // Check all frames
158
+ for (const frame of page.frames()) {
159
+ const count = await frame.locator(selector).count();
160
+ if (count > 0) {
161
+ return frame.locator(selector);
162
+ }
163
+ }
164
+ return null;
165
+ }
166
+
167
+ test("find element in any frame", async ({ page }) => {
168
+ await page.goto("/complex-page");
169
+ const element = await findInAnyFrame(page, '[data-testid="submit-btn"]');
170
+ if (element) await element.click();
171
+ });
172
+ ```
173
+
174
+ ## Dynamic iFrames
175
+
176
+ ### iFrames Created at Runtime
177
+
178
+ ```typescript
179
+ test("handle dynamically created iframe", async ({ page }) => {
180
+ await page.goto("/dashboard");
181
+
182
+ // Click button that creates iframe
183
+ await page.getByRole("button", { name: "Open Widget" }).click();
184
+
185
+ // Wait for iframe to appear in DOM
186
+ await page.waitForSelector("iframe#widget-frame");
187
+
188
+ // Now access the frame
189
+ const widgetFrame = page.frameLocator("#widget-frame");
190
+ await expect(widgetFrame.getByText("Widget Loaded")).toBeVisible();
191
+ });
192
+ ```
193
+
194
+ ### iFrames with Changing src
195
+
196
+ ```typescript
197
+ test("iframe src changes", async ({ page }) => {
198
+ await page.goto("/multi-step");
199
+
200
+ const frame = page.frameLocator("#step-frame");
201
+
202
+ // Step 1
203
+ await expect(frame.getByText("Step 1")).toBeVisible();
204
+ await frame.getByRole("button", { name: "Next" }).click();
205
+
206
+ // Wait for iframe to reload with new content
207
+ await expect(frame.getByText("Step 2")).toBeVisible({ timeout: 10000 });
208
+ await frame.getByRole("button", { name: "Next" }).click();
209
+
210
+ // Step 3
211
+ await expect(frame.getByText("Step 3")).toBeVisible({ timeout: 10000 });
212
+ });
213
+ ```
214
+
215
+ ### Lazy-Loaded iFrames
216
+
217
+ ```typescript
218
+ test("lazy loaded iframe", async ({ page }) => {
219
+ await page.goto("/page-with-lazy-iframe");
220
+
221
+ // Scroll to trigger lazy load
222
+ await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
223
+
224
+ // Wait for iframe to load
225
+ const lazyFrame = page.frameLocator("#lazy-iframe");
226
+ await expect(lazyFrame.locator("body")).not.toBeEmpty({ timeout: 15000 });
227
+
228
+ // Interact with content
229
+ await lazyFrame.getByRole("button").click();
230
+ });
231
+ ```
232
+
233
+ ## iFrame Navigation
234
+
235
+ ### Navigating Within iFrame
236
+
237
+ ```typescript
238
+ test("iframe internal navigation", async ({ page }) => {
239
+ await page.goto("/app");
240
+
241
+ // Get frame object for navigation control
242
+ const frame = page.frame({ name: "content-frame" });
243
+ if (!frame) throw new Error("Frame not found");
244
+
245
+ // Navigate within iframe
246
+ await frame.goto("https://embedded-app.com/page2");
247
+
248
+ // Wait for navigation
249
+ await frame.waitForURL("**/page2");
250
+
251
+ // Verify content
252
+ await expect(frame.getByRole("heading")).toHaveText("Page 2");
253
+ });
254
+ ```
255
+
256
+ ### Handling Frame Navigation Events
257
+
258
+ ```typescript
259
+ test("track iframe navigation", async ({ page }) => {
260
+ const navigations: string[] = [];
261
+
262
+ // Listen to frame navigation
263
+ page.on("framenavigated", (frame) => {
264
+ if (frame.parentFrame()) {
265
+ // This is an iframe navigation
266
+ navigations.push(frame.url());
267
+ }
268
+ });
269
+
270
+ await page.goto("/with-iframe");
271
+ await page
272
+ .frameLocator("#nav-frame")
273
+ .getByRole("link", { name: "Page 2" })
274
+ .click();
275
+
276
+ // Verify navigation occurred
277
+ expect(navigations.some((url) => url.includes("page2"))).toBe(true);
278
+ });
279
+ ```
280
+
281
+ ## Common Patterns
282
+
283
+ ### iFrame Fixture
284
+
285
+ ```typescript
286
+ // fixtures.ts
287
+ import { test as base, FrameLocator } from "@playwright/test";
288
+
289
+ export const test = base.extend<{ paymentFrame: FrameLocator }>({
290
+ paymentFrame: async ({ page }, use) => {
291
+ await page.goto("/checkout");
292
+
293
+ // Wait for payment iframe to be ready
294
+ const frame = page.frameLocator('iframe[src*="payment"]');
295
+ await expect(frame.locator("body")).toBeVisible({ timeout: 15000 });
296
+
297
+ await use(frame);
298
+ },
299
+ });
300
+
301
+ // test file
302
+ test("complete payment", async ({ paymentFrame }) => {
303
+ await paymentFrame.getByLabel("Card").fill("4242424242424242");
304
+ await paymentFrame.getByRole("button", { name: "Pay" }).click();
305
+ });
306
+ ```
307
+
308
+ ### Debugging iFrame Issues
309
+
310
+ ```typescript
311
+ test("debug iframe content", async ({ page }) => {
312
+ await page.goto("/page-with-iframes");
313
+
314
+ // List all frames
315
+ console.log("All frames:");
316
+ for (const frame of page.frames()) {
317
+ console.log(` - ${frame.name() || "(unnamed)"}: ${frame.url()}`);
318
+ }
319
+
320
+ // Screenshot specific iframe content
321
+ const frame = page.frame({ name: "target-frame" });
322
+ if (frame) {
323
+ const body = frame.locator("body");
324
+ await body.screenshot({ path: "iframe-content.png" });
325
+ }
326
+
327
+ // Get iframe HTML for debugging
328
+ const frameContent = page.frameLocator("#my-frame");
329
+ const html = await frameContent.locator("body").innerHTML();
330
+ console.log("iFrame HTML:", html.substring(0, 500));
331
+ });
332
+ ```
333
+
334
+ ### Handling iFrame Load Failures
335
+
336
+ ```typescript
337
+ test("handle iframe load failure", async ({ page }) => {
338
+ await page.goto("/page-with-unreliable-iframe");
339
+
340
+ const frame = page.frameLocator("#unreliable-frame");
341
+
342
+ try {
343
+ // Try to interact with iframe content
344
+ await expect(frame.getByRole("button")).toBeVisible({ timeout: 5000 });
345
+ await frame.getByRole("button").click();
346
+ } catch (error) {
347
+ // Fallback: refresh iframe
348
+ await page.evaluate(() => {
349
+ const iframe = document.querySelector(
350
+ "#unreliable-frame",
351
+ ) as HTMLIFrameElement;
352
+ if (iframe) iframe.src = iframe.src;
353
+ });
354
+
355
+ // Retry
356
+ await expect(frame.getByRole("button")).toBeVisible({ timeout: 10000 });
357
+ await frame.getByRole("button").click();
358
+ }
359
+ });
360
+ ```
361
+
362
+ ### Mocking iFrame Content
363
+
364
+ ```typescript
365
+ test("mock iframe response", async ({ page }) => {
366
+ // Intercept iframe src request
367
+ await page.route("**/embedded-widget**", (route) => {
368
+ route.fulfill({
369
+ contentType: "text/html",
370
+ body: `
371
+ <!DOCTYPE html>
372
+ <html>
373
+ <body>
374
+ <h1>Mocked Widget</h1>
375
+ <button>Mocked Button</button>
376
+ </body>
377
+ </html>
378
+ `,
379
+ });
380
+ });
381
+
382
+ await page.goto("/page-with-widget");
383
+
384
+ const frame = page.frameLocator("#widget-frame");
385
+ await expect(frame.getByRole("heading")).toHaveText("Mocked Widget");
386
+ });
387
+ ```
388
+
389
+ ## Anti-Patterns to Avoid
390
+
391
+ | Anti-Pattern | Problem | Solution |
392
+ | ------------------------------------- | --------------------------------- | -------------------------------------------------- |
393
+ | Using `page.frame()` for interactions | Less reliable than frameLocator | Use `page.frameLocator()` for element interactions |
394
+ | Hardcoding iframe index | Fragile if DOM order changes | Use name, id, or src attribute selectors |
395
+ | Not waiting for iframe load | Race conditions | Wait for element inside iframe to be visible |
396
+ | Assuming same-origin | Cross-origin has different timing | Always wait for iframe content explicitly |
397
+ | Ignoring nested iframes | Element not found | Chain frameLocator calls for nested frames |
398
+
399
+ ## Related References
400
+
401
+ - **Locators**: See [locators.md](../core/locators.md) for selector strategies
402
+ - **Third-party services**: See [third-party.md](../advanced/third-party.md) for payment iframe patterns
403
+ - **Debugging**: See [debugging.md](../debugging/debugging.md) for troubleshooting iframe issues