@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,315 @@
|
|
|
1
|
+
# Page Object Model (POM)
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
1. [Overview](#overview)
|
|
6
|
+
2. [Basic Structure](#basic-structure)
|
|
7
|
+
3. [Component Objects](#component-objects)
|
|
8
|
+
4. [Composition Patterns](#composition-patterns)
|
|
9
|
+
5. [Factory Functions](#factory-functions)
|
|
10
|
+
6. [Best Practices](#best-practices)
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
Page Object Model encapsulates page structure and interactions, providing:
|
|
15
|
+
|
|
16
|
+
- **Maintainability**: Change selectors in one place
|
|
17
|
+
- **Reusability**: Share page interactions across tests
|
|
18
|
+
- **Readability**: Tests express intent, not implementation
|
|
19
|
+
|
|
20
|
+
## Basic Structure
|
|
21
|
+
|
|
22
|
+
### Page Class
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// pages/login.page.ts
|
|
26
|
+
import { Page, Locator, expect } from "@playwright/test";
|
|
27
|
+
|
|
28
|
+
export class LoginPage {
|
|
29
|
+
readonly page: Page;
|
|
30
|
+
readonly emailInput: Locator;
|
|
31
|
+
readonly passwordInput: Locator;
|
|
32
|
+
readonly submitButton: Locator;
|
|
33
|
+
readonly errorMessage: Locator;
|
|
34
|
+
|
|
35
|
+
constructor(page: Page) {
|
|
36
|
+
this.page = page;
|
|
37
|
+
this.emailInput = page.getByLabel("Email");
|
|
38
|
+
this.passwordInput = page.getByLabel("Password");
|
|
39
|
+
this.submitButton = page.getByRole("button", { name: "Sign in" });
|
|
40
|
+
this.errorMessage = page.getByRole("alert");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async goto() {
|
|
44
|
+
await this.page.goto("/login");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async login(email: string, password: string) {
|
|
48
|
+
await this.emailInput.fill(email);
|
|
49
|
+
await this.passwordInput.fill(password);
|
|
50
|
+
await this.submitButton.click();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async expectError(message: string) {
|
|
54
|
+
await expect(this.errorMessage).toContainText(message);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Usage in Tests
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// tests/login.spec.ts
|
|
63
|
+
import { test, expect } from "@playwright/test";
|
|
64
|
+
import { LoginPage } from "../pages/login.page";
|
|
65
|
+
|
|
66
|
+
test.describe("Login", () => {
|
|
67
|
+
test("successful login redirects to dashboard", async ({ page }) => {
|
|
68
|
+
const loginPage = new LoginPage(page);
|
|
69
|
+
await loginPage.goto();
|
|
70
|
+
await loginPage.login("user@example.com", "password123");
|
|
71
|
+
await expect(page).toHaveURL("/dashboard");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("shows error for invalid credentials", async ({ page }) => {
|
|
75
|
+
const loginPage = new LoginPage(page);
|
|
76
|
+
await loginPage.goto();
|
|
77
|
+
await loginPage.login("invalid@example.com", "wrong");
|
|
78
|
+
await loginPage.expectError("Invalid credentials");
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Component Objects
|
|
84
|
+
|
|
85
|
+
For reusable UI components:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// components/navbar.component.ts
|
|
89
|
+
import { Page, Locator } from "@playwright/test";
|
|
90
|
+
|
|
91
|
+
export class NavbarComponent {
|
|
92
|
+
readonly container: Locator;
|
|
93
|
+
readonly logo: Locator;
|
|
94
|
+
readonly searchInput: Locator;
|
|
95
|
+
readonly userMenu: Locator;
|
|
96
|
+
|
|
97
|
+
constructor(page: Page) {
|
|
98
|
+
this.container = page.getByRole("navigation");
|
|
99
|
+
this.logo = this.container.getByRole("link", { name: "Home" });
|
|
100
|
+
this.searchInput = this.container.getByRole("searchbox");
|
|
101
|
+
this.userMenu = this.container.getByRole("button", { name: /user menu/i });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async search(query: string) {
|
|
105
|
+
await this.searchInput.fill(query);
|
|
106
|
+
await this.searchInput.press("Enter");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async openUserMenu() {
|
|
110
|
+
await this.userMenu.click();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// components/modal.component.ts
|
|
117
|
+
import { Locator, expect } from "@playwright/test";
|
|
118
|
+
|
|
119
|
+
export class ModalComponent {
|
|
120
|
+
readonly container: Locator;
|
|
121
|
+
readonly title: Locator;
|
|
122
|
+
readonly closeButton: Locator;
|
|
123
|
+
readonly confirmButton: Locator;
|
|
124
|
+
|
|
125
|
+
constructor(container: Locator) {
|
|
126
|
+
this.container = container;
|
|
127
|
+
this.title = container.getByRole("heading");
|
|
128
|
+
this.closeButton = container.getByRole("button", { name: "Close" });
|
|
129
|
+
this.confirmButton = container.getByRole("button", { name: "Confirm" });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async expectTitle(title: string) {
|
|
133
|
+
await expect(this.title).toHaveText(title);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async close() {
|
|
137
|
+
await this.closeButton.click();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async confirm() {
|
|
141
|
+
await this.confirmButton.click();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Composition Patterns
|
|
147
|
+
|
|
148
|
+
### Page with Components
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// pages/dashboard.page.ts
|
|
152
|
+
import { Page, Locator } from "@playwright/test";
|
|
153
|
+
import { NavbarComponent } from "../components/navbar.component";
|
|
154
|
+
import { ModalComponent } from "../components/modal.component";
|
|
155
|
+
|
|
156
|
+
export class DashboardPage {
|
|
157
|
+
readonly page: Page;
|
|
158
|
+
readonly navbar: NavbarComponent;
|
|
159
|
+
readonly newProjectButton: Locator;
|
|
160
|
+
|
|
161
|
+
constructor(page: Page) {
|
|
162
|
+
this.page = page;
|
|
163
|
+
this.navbar = new NavbarComponent(page);
|
|
164
|
+
this.newProjectButton = page.getByRole("button", { name: "New Project" });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async goto() {
|
|
168
|
+
await this.page.goto("/dashboard");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async createProject() {
|
|
172
|
+
await this.newProjectButton.click();
|
|
173
|
+
return new ModalComponent(this.page.getByRole("dialog"));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Page Navigation
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// pages/base.page.ts
|
|
182
|
+
import { Page } from "@playwright/test";
|
|
183
|
+
|
|
184
|
+
export abstract class BasePage {
|
|
185
|
+
constructor(readonly page: Page) {}
|
|
186
|
+
|
|
187
|
+
abstract goto(): Promise<void>;
|
|
188
|
+
|
|
189
|
+
async getTitle(): Promise<string> {
|
|
190
|
+
return this.page.title();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
// Return new page object on navigation
|
|
197
|
+
export class LoginPage extends BasePage {
|
|
198
|
+
async login(email: string, password: string): Promise<DashboardPage> {
|
|
199
|
+
await this.emailInput.fill(email);
|
|
200
|
+
await this.passwordInput.fill(password);
|
|
201
|
+
await this.submitButton.click();
|
|
202
|
+
return new DashboardPage(this.page);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Usage
|
|
207
|
+
const loginPage = new LoginPage(page);
|
|
208
|
+
await loginPage.goto();
|
|
209
|
+
const dashboardPage = await loginPage.login("user@example.com", "pass");
|
|
210
|
+
await dashboardPage.expectWelcomeMessage();
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Factory Functions
|
|
214
|
+
|
|
215
|
+
Alternative to classes for simpler pages:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// pages/login.page.ts
|
|
219
|
+
import { Page } from "@playwright/test";
|
|
220
|
+
|
|
221
|
+
export function createLoginPage(page: Page) {
|
|
222
|
+
const emailInput = page.getByLabel("Email");
|
|
223
|
+
const passwordInput = page.getByLabel("Password");
|
|
224
|
+
const submitButton = page.getByRole("button", { name: "Sign in" });
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
goto: () => page.goto("/login"),
|
|
228
|
+
login: async (email: string, password: string) => {
|
|
229
|
+
await emailInput.fill(email);
|
|
230
|
+
await passwordInput.fill(password);
|
|
231
|
+
await submitButton.click();
|
|
232
|
+
},
|
|
233
|
+
emailInput,
|
|
234
|
+
passwordInput,
|
|
235
|
+
submitButton,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Usage
|
|
240
|
+
const loginPage = createLoginPage(page);
|
|
241
|
+
await loginPage.goto();
|
|
242
|
+
await loginPage.login("user@example.com", "password");
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Best Practices
|
|
246
|
+
|
|
247
|
+
### Do
|
|
248
|
+
|
|
249
|
+
- **Keep locators in page objects** - Single source of truth
|
|
250
|
+
- **Return new page objects** when navigation occurs
|
|
251
|
+
- **Expose elements** for custom assertions in tests
|
|
252
|
+
- **Use descriptive method names** - `submitOrder()` not `clickButton()`
|
|
253
|
+
- **Keep methods focused** - One action per method
|
|
254
|
+
|
|
255
|
+
### Don't
|
|
256
|
+
|
|
257
|
+
- **Don't include assertions in page methods** (usually) - Keep in tests
|
|
258
|
+
- **Don't expose implementation details** - Hide complex interactions
|
|
259
|
+
- **Don't make page objects too large** - Split into components
|
|
260
|
+
- **Don't share state** between page object instances
|
|
261
|
+
|
|
262
|
+
### Directory Structure
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
tests/
|
|
266
|
+
├── pages/
|
|
267
|
+
│ ├── base.page.ts
|
|
268
|
+
│ ├── login.page.ts
|
|
269
|
+
│ ├── dashboard.page.ts
|
|
270
|
+
│ └── settings.page.ts
|
|
271
|
+
├── components/
|
|
272
|
+
│ ├── navbar.component.ts
|
|
273
|
+
│ ├── modal.component.ts
|
|
274
|
+
│ └── table.component.ts
|
|
275
|
+
├── fixtures/
|
|
276
|
+
│ └── pages.fixture.ts
|
|
277
|
+
└── specs/
|
|
278
|
+
├── login.spec.ts
|
|
279
|
+
└── dashboard.spec.ts
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Using with Fixtures
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
// fixtures/pages.fixture.ts
|
|
286
|
+
import { test as base } from "@playwright/test";
|
|
287
|
+
import { LoginPage } from "../pages/login.page";
|
|
288
|
+
import { DashboardPage } from "../pages/dashboard.page";
|
|
289
|
+
|
|
290
|
+
type Pages = {
|
|
291
|
+
loginPage: LoginPage;
|
|
292
|
+
dashboardPage: DashboardPage;
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
export const test = base.extend<Pages>({
|
|
296
|
+
loginPage: async ({ page }, use) => {
|
|
297
|
+
await use(new LoginPage(page));
|
|
298
|
+
},
|
|
299
|
+
dashboardPage: async ({ page }, use) => {
|
|
300
|
+
await use(new DashboardPage(page));
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// Usage in tests
|
|
305
|
+
test("can login", async ({ loginPage }) => {
|
|
306
|
+
await loginPage.goto();
|
|
307
|
+
await loginPage.login("user@example.com", "password");
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Related References
|
|
312
|
+
|
|
313
|
+
- **Locator strategies**: See [locators.md](locators.md) for selecting elements
|
|
314
|
+
- **Fixtures**: See [fixtures-hooks.md](fixtures-hooks.md) for advanced fixture patterns
|
|
315
|
+
- **Test organization**: See [test-suite-structure.md](test-suite-structure.md) for structuring test suites
|