symfonia-ai-tools 1.0.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 (74) hide show
  1. package/README.md +489 -0
  2. package/bin/cli.mjs +35 -0
  3. package/lib/installer.mjs +495 -0
  4. package/lib/questions.mjs +332 -0
  5. package/lib/ui.mjs +76 -0
  6. package/lib/utils.mjs +231 -0
  7. package/package.json +26 -0
  8. package/templates/base/CLAUDE.md +34 -0
  9. package/templates/base/_ai/_guidelines_header.md +70 -0
  10. package/templates/base/_ai/context/README.md +20 -0
  11. package/templates/base/_ai/prompts/codereview.prompt.md +324 -0
  12. package/templates/base/_ai/prompts/duplicate-code-analysis.prompt.md +128 -0
  13. package/templates/base/_ai/prompts/figma-analysis.prompt.md +155 -0
  14. package/templates/base/_ai/prompts/security-review.prompt.md +46 -0
  15. package/templates/base/_ai/skills/README.md +80 -0
  16. package/templates/base/_ai/skills/TEMPLATE.md +106 -0
  17. package/templates/base/_ai/skills/babysit-prs/SKILL.md +105 -0
  18. package/templates/base/_ai/skills/debug/SKILL.md +93 -0
  19. package/templates/base/_ai/skills/fill-worklogs/SKILL.md +158 -0
  20. package/templates/base/_ai/skills/hotfix/SKILL.md +52 -0
  21. package/templates/base/_ai/skills/jira-task/SKILL.md +170 -0
  22. package/templates/base/_ai/skills/my-prs/SKILL.md +78 -0
  23. package/templates/base/_ai/skills/pr-dashboard/SKILL.md +43 -0
  24. package/templates/base/_ai/skills/pr-prepare/SKILL.md +106 -0
  25. package/templates/base/_ai/skills/refactor/SKILL.md +87 -0
  26. package/templates/base/_ai/skills/write-tests/SKILL.md +109 -0
  27. package/templates/base/_claude/settings.local.json +37 -0
  28. package/templates/base/_cursor/rules/global.mdc +7 -0
  29. package/templates/base/_editorconfig +18 -0
  30. package/templates/base/_gemini/settings.json +3 -0
  31. package/templates/base/_github/copilot-instructions.md +1 -0
  32. package/templates/base/_github/pull_request_template.md +23 -0
  33. package/templates/base/_gitignore +22 -0
  34. package/templates/base/_junie/guidelines.md +1 -0
  35. package/templates/base/commit-instructions.md +92 -0
  36. package/templates/packs/docker/_ai/instructions/docker.instructions.md +193 -0
  37. package/templates/packs/docker/_guidelines.md +10 -0
  38. package/templates/packs/docker/pack.json +8 -0
  39. package/templates/packs/laravel/_ai/instructions/api-resource.instructions.md +251 -0
  40. package/templates/packs/laravel/_ai/instructions/module.instructions.md +133 -0
  41. package/templates/packs/laravel/_ai/instructions/service-repository.instructions.md +215 -0
  42. package/templates/packs/laravel/_ai/instructions/testing.instructions.md +278 -0
  43. package/templates/packs/laravel/_ai/skills/migration/SKILL.md +172 -0
  44. package/templates/packs/laravel/_ai/skills/new-endpoint/SKILL.md +165 -0
  45. package/templates/packs/laravel/_ai/skills/new-module/SKILL.md +208 -0
  46. package/templates/packs/laravel/_ai/skills/queued-job/SKILL.md +248 -0
  47. package/templates/packs/laravel/_ai/skills/testing-feature/SKILL.md +196 -0
  48. package/templates/packs/laravel/_ai/skills/testing-manual/SKILL.md +186 -0
  49. package/templates/packs/laravel/_ai/skills/testing-unit/SKILL.md +200 -0
  50. package/templates/packs/laravel/_guidelines.md +25 -0
  51. package/templates/packs/laravel/pack.json +6 -0
  52. package/templates/packs/playwright/_ai/instructions/playwright.instructions.md +219 -0
  53. package/templates/packs/playwright/_ai/skills/playwright/README.md +194 -0
  54. package/templates/packs/playwright/_ai/skills/playwright/SKILL.md +1245 -0
  55. package/templates/packs/playwright/_ai/skills/playwright-codereview/SKILL.md +642 -0
  56. package/templates/packs/playwright/_ai/skills/playwright-record/README.md +87 -0
  57. package/templates/packs/playwright/_ai/skills/playwright-record/SKILL.md +564 -0
  58. package/templates/packs/playwright/_guidelines.md +12 -0
  59. package/templates/packs/playwright/pack.json +9 -0
  60. package/templates/packs/storybook/_ai/instructions/storybook.instructions.md +181 -0
  61. package/templates/packs/storybook/pack.json +6 -0
  62. package/templates/packs/vitest/_ai/instructions/vitest.instructions.md +688 -0
  63. package/templates/packs/vitest/pack.json +6 -0
  64. package/templates/packs/vue3/_ai/instructions/api.instructions.md +163 -0
  65. package/templates/packs/vue3/_ai/instructions/coding-conventions.instructions.md +160 -0
  66. package/templates/packs/vue3/_ai/instructions/composables.instructions.md +218 -0
  67. package/templates/packs/vue3/_ai/instructions/forms.instructions.md +227 -0
  68. package/templates/packs/vue3/_ai/instructions/store.instructions.md +504 -0
  69. package/templates/packs/vue3/_ai/instructions/vue.instructions.md +339 -0
  70. package/templates/packs/vue3/_ai/skills/api-integration/SKILL.md +195 -0
  71. package/templates/packs/vue3/_ai/skills/new-component/SKILL.md +133 -0
  72. package/templates/packs/vue3/_ai/skills/new-module/SKILL.md +177 -0
  73. package/templates/packs/vue3/_guidelines.md +45 -0
  74. package/templates/packs/vue3/pack.json +11 -0
@@ -0,0 +1,1245 @@
1
+ ---
2
+ name: playwright
3
+ description: Expert guidance for writing, maintaining, debugging, and reviewing Playwright E2E tests. Provides comprehensive knowledge about Page Object Model patterns, test organization, fixture management, data-testid selectors, tagging strategies, and best practices for a structured testing framework.
4
+ ---
5
+
6
+ # Playwright E2E Testing - {{PROJECT_NAME}}
7
+
8
+ Expert skill for writing, maintaining, and reviewing end-to-end tests using Playwright in the {{PROJECT_NAME}} frontend project. This skill provides specialized knowledge about the project's testing architecture, conventions, and best practices.
9
+
10
+ ## When to Use This Skill
11
+
12
+ Use this skill when the user:
13
+
14
+ - **Asks to CONVERT recorded test from `tmp/` to production** (from `playwright-record` skill)
15
+ - **AI Agent MUST**: Convert test AND run it to verify it works
16
+ - **AI Agent MUST**: Debug failures and fix issues
17
+ - **AI Agent MUST**: Re-run until test passes
18
+ - **AI Agent MUST NOT**: Mark conversion as done without running test
19
+ - Asks to write new Playwright E2E tests
20
+ - Needs help debugging or fixing existing Playwright tests
21
+ - Wants to understand test structure or Page Object Models
22
+ - Asks about test organization, naming conventions, or file structure
23
+ - Needs guidance on selectors (data-testid patterns)
24
+ - Wants to configure test execution (tags, browsers, workers)
25
+ - Requests test fixtures or test data management
26
+ - Asks about running tests locally or in Docker
27
+ - Needs help with authentication flows or session management
28
+ - Wants to review test code quality or refactor tests
29
+
30
+ **CRITICAL for AI Agents:**
31
+ - **NEVER** consider conversion complete without running the test
32
+ - **ALWAYS** use `make playwright-test` or `npx playwright test` to verify
33
+ - **ALWAYS** check exit code and output for failures
34
+ - **ALWAYS** debug and fix issues if test fails
35
+ - **ALWAYS** re-run until test passes (exit code 0)
36
+
37
+ ## Converting Recorded Tests from tmp/
38
+
39
+ **CRITICAL WORKFLOW**: When converting tests recorded by `playwright-record` skill:
40
+
41
+ ### Step 1: Check Existing Page Objects FIRST
42
+
43
+ **BEFORE creating ANY new Page Objects**, you MUST:
44
+
45
+ 1. **Search** `{{PLAYWRIGHT_DIR}}/pages/` for existing POMs
46
+ 2. **Check** `{{PLAYWRIGHT_DIR}}/pages/index.ts` for available exports
47
+ 3. **Analyze** if existing POM can be reused or extended
48
+ 4. **Only create new POM** if NO suitable one exists
49
+
50
+ **Example check**:
51
+ ```bash
52
+ # Check if LoginPage exists
53
+ ls {{PLAYWRIGHT_DIR}}/pages/login/LoginPage.ts
54
+
55
+ # Check if a specific page object exists
56
+ ls {{PLAYWRIGHT_DIR}}/pages/{{MODULE_PATH}}/SomePage.ts
57
+
58
+ # Check index.ts exports
59
+ grep "export.*Page" {{PLAYWRIGHT_DIR}}/pages/index.ts
60
+ ```
61
+
62
+ ### Step 2: Reuse Existing POMs When Possible
63
+
64
+ **If Page Object EXISTS** -> **REUSE IT**:
65
+ ```typescript
66
+ // CORRECT: Reuse existing LoginPage
67
+ import { LoginPage } from '../../pages/login/LoginPage';
68
+
69
+ test('My test', async ({ page }) => {
70
+ const loginPage = new LoginPage(page);
71
+ await loginPage.login(email, password);
72
+ });
73
+ ```
74
+
75
+ **If Page Object DOES NOT EXIST** -> **CREATE NEW ONE**:
76
+ ```typescript
77
+ // CORRECT: Create new POM only if nothing exists
78
+ export class SomeModulePage {
79
+ // ... new implementation
80
+ }
81
+ ```
82
+
83
+ ### Step 3: Full Conversion Workflow (3 Sub-Steps)
84
+
85
+ #### Step 3A: Automatic JavaScript to TypeScript Conversion
86
+
87
+ **AI Agent MUST automatically transform raw Codegen output:**
88
+
89
+ **Before (Raw JavaScript from tmp/)**:
90
+ ```javascript
91
+ test('test', async ({ page }) => {
92
+ await page.goto('{{BASE_URL}}/login');
93
+ await page.getByPlaceholder('Email').fill('test@example.com');
94
+ await page.getByRole('button', { name: 'Login' }).click();
95
+ // ... more raw interactions
96
+ });
97
+ ```
98
+
99
+ **After (Converted TypeScript with POMs)**:
100
+ ```typescript
101
+ import { expect, test } from '@playwright/test';
102
+ import { LoginPage, DashboardPage } from '../../pages';
103
+ import { CredentialsLoader } from '../../fixtures/loader';
104
+ import { clearBrowserStorage } from '../../utils/test-helpers';
105
+
106
+ test.describe('05_ModuleName - Tests', { tag: ['@module-tag', '@verified'] }, () => {
107
+ let loginPage: LoginPage;
108
+ let dashboardPage: DashboardPage;
109
+
110
+ test.beforeEach(async ({ page }) => {
111
+ loginPage = new LoginPage(page);
112
+ dashboardPage = new DashboardPage(page);
113
+
114
+ await page.goto('/login');
115
+ await clearBrowserStorage(page);
116
+ });
117
+
118
+ test('05_01_01: Test scenario description', async ({ page }) => {
119
+ const credentials = await CredentialsLoader.getCredentials();
120
+
121
+ await test.step('Login', async () => {
122
+ await loginPage.login(credentials.email.right, credentials.password.right);
123
+ });
124
+
125
+ await test.step('Navigate to target page', async () => {
126
+ await dashboardPage.navigateToModule();
127
+ });
128
+
129
+ await test.step('Verify success', async () => {
130
+ await expect(page.getByText('Success')).toBeVisible();
131
+ });
132
+ });
133
+ });
134
+ ```
135
+
136
+ **Conversion checklist**:
137
+ - Import proper types and POMs
138
+ - Add test.describe() with tags
139
+ - Initialize POMs in beforeEach
140
+ - Add clearBrowserStorage()
141
+ - Use CredentialsLoader for credentials
142
+ - Add test.step() for each logical action
143
+ - Use Page Object methods instead of raw interactions
144
+ - Add proper assertions (expect)
145
+
146
+ #### Step 3B: Update pages/index.ts for New POMs
147
+
148
+ **When creating a NEW Page Object**, update exports file:
149
+
150
+ **File**: `{{PLAYWRIGHT_DIR}}/pages/index.ts`
151
+
152
+ **Template**:
153
+ ```typescript
154
+ // Add this line for new POM
155
+ export { NewPageName } from './module/NewPageName.ts';
156
+ ```
157
+
158
+ **Example**:
159
+ ```typescript
160
+ // existing
161
+ export { LoginPage } from './login/LoginPage';
162
+ export { DashboardPage } from './dashboard/DashboardPage';
163
+
164
+ // ADD NEW LIKE THIS:
165
+ export { SomeModulePage } from './some-module/SomeModulePage'; // <- NEW
166
+ ```
167
+
168
+ **Why?** Allows clean imports in tests:
169
+ ```typescript
170
+ // GOOD
171
+ import { LoginPage, SomeModulePage } from '../../pages';
172
+
173
+ // BAD - Don't do this
174
+ import { LoginPage } from '../../pages/login/LoginPage';
175
+ import { SomeModulePage } from '../../pages/some-module/SomeModulePage';
176
+ ```
177
+
178
+ #### Step 3C: Create Test Directory Structure If Needed
179
+
180
+ **If test module directory doesn't exist, create it:**
181
+
182
+ ```bash
183
+ # From project root
184
+ mkdir -p {{PLAYWRIGHT_DIR}}/tests/NN_Module/
185
+
186
+ # Example: Create a module directory
187
+ mkdir -p {{PLAYWRIGHT_DIR}}/tests/05_ModuleName/
188
+ ```
189
+
190
+ **File placement**:
191
+ ```
192
+ {{PLAYWRIGHT_DIR}}/tests/
193
+ ├── 05_ModuleName/ # <- Module
194
+ │ ├── 05_01_01_Scenario_one.spec.ts # <- Test file
195
+ │ ├── 05_01_02_Scenario_two.spec.ts # <- Another test
196
+ │ └── 05_01_03_Scenario_three.spec.ts # <- Another test
197
+ ```
198
+
199
+ **Naming Rules**:
200
+ - **First NN** (01-99): Main module number
201
+ - **Second NN** (01-99): Sub-module/category number
202
+ - **Third NN** (01-99): Specific test scenario number
203
+ - **Suffix**: Always `.spec.ts`
204
+
205
+ ---
206
+
207
+ ## Project Context
208
+
209
+ **Framework**: Playwright for E2E testing
210
+ **Location**: `{{PLAYWRIGHT_DIR}}/`
211
+ **Architecture**: Page Object Model (POM) with modular organization
212
+ **Target**: {{PROJECT_NAME}} frontend application
213
+ **Environments**: Local development + Docker + CI/CD
214
+
215
+ ### Key Technologies
216
+
217
+ - **@playwright/test** - Main testing framework
218
+ - **TypeScript** - Type-safe test code
219
+ - **Docker** - Containerized test execution
220
+ - **Multiple browsers** - Chromium (default), Firefox, Webkit, Mobile
221
+
222
+ ### Configuration Files
223
+
224
+ The project uses several configuration files for environment settings and test data:
225
+
226
+ **1. `.env` file** (`{{PLAYWRIGHT_DIR}}/.env`):
227
+ - Environment configuration for test execution
228
+ - Key variables:
229
+ - `BASE_URL` - Application base URL (default: `{{BASE_URL}}`)
230
+ - `COMPANY_NAME` - Company/tenant to test
231
+ - `BROWSER` - Browser to use (chromium, firefox, webkit)
232
+ - `HEADLESS` - Run tests in headless mode (true/false)
233
+ - `WORKERS` - Number of parallel workers
234
+ - `RETRIES` - Number of test retries on failure
235
+ - `TIMEOUT` - Test timeout in milliseconds
236
+
237
+ **2. Credentials file** (`{{PLAYWRIGHT_DIR}}/credentials.env.json`):
238
+ - Secure credentials storage (**NOT in git**)
239
+ - Contains login credentials for different tenants/environments
240
+ - Structure:
241
+ ```json
242
+ {
243
+ "tenantName": {
244
+ "email": { "right": "valid@email.com", "wrong": "invalid@email.com" },
245
+ "password": { "right": "correctPassword", "wrong": "wrongPassword" },
246
+ "username": { "right": "validUser", "wrong": "invalidUser" }
247
+ }
248
+ }
249
+ ```
250
+ - Loaded dynamically via `CredentialsLoader.getCredentials()`
251
+
252
+ **3. `playwright.config.ts` file** (`{{PLAYWRIGHT_DIR}}/playwright.config.ts`):
253
+ - Main Playwright configuration
254
+ - Loads environment variables from `.env` using `dotenv`
255
+ - Configures:
256
+ - Test directory (`testDir: './tests'`)
257
+ - Browser projects (Chromium, Firefox, Webkit, Mobile)
258
+ - Reporters (HTML, JSON, JUnit, Allure)
259
+ - Timeouts and retries
260
+ - Base URL and navigation settings
261
+ - Locale and timezone
262
+ - Screenshots and video recording
263
+ - Dynamic configuration based on environment variables
264
+
265
+ **Usage in tests**:
266
+ ```typescript
267
+ // Load credentials from credentials file
268
+ const credentials = await CredentialsLoader.getCredentials();
269
+ // Uses COMPANY_NAME from .env to select tenant data
270
+
271
+ // Base URL is automatically used from playwright.config.ts
272
+ await page.goto('/login'); // Uses BASE_URL from .env
273
+ ```
274
+
275
+ ## Test Organization & Structure
276
+
277
+ ### Directory Structure
278
+
279
+ ```
280
+ {{PLAYWRIGHT_DIR}}/
281
+ ├── tests/ # Test files (organized by module)
282
+ │ ├── 00_Setup/ # Setup and initialization tests
283
+ │ ├── 01_Login/ # Login module tests
284
+ │ │ ├── 01_01_01_Login_scenarios.spec.ts
285
+ │ │ └── 01_01_02_Login_verification.spec.ts
286
+ │ ├── 02_Settings/ # Settings module tests
287
+ │ ├── 03_ModuleA/ # Module A tests
288
+ │ ├── 10_ModuleB/ # Module B tests
289
+ │ └── 11_ModuleC/ # Module C tests
290
+ │ └── 01_SubModule/ # Sub-module
291
+ ├── tmp/ # Temporary/recorded tests (gitignored)
292
+ │ └── playwright-test-*.js # Raw Codegen outputs
293
+ ├── pages/ # Page Object Models
294
+ │ ├── login/LoginPage.ts
295
+ │ ├── dashboard/DashboardPage.ts
296
+ │ ├── components/ # Reusable component POMs
297
+ │ └── index.ts # Centralized exports
298
+ ├── fixtures/ # Test data and fixtures
299
+ │ ├── loader.ts # CredentialsLoader for dynamic data
300
+ │ ├── users.ts # User fixtures
301
+ │ ├── tenants/ # Tenant-specific data
302
+ │ └── types/ # TypeScript type definitions
303
+ ├── utils/ # Utility functions
304
+ │ ├── locators.ts # Selector helpers
305
+ │ ├── test-helpers.ts # Common test utilities
306
+ │ └── commands/ # Command utilities
307
+ │ └── auth/ # Authentication helpers
308
+ ├── helpers/ # Global setup/teardown
309
+ │ ├── global-setup.ts
310
+ │ ├── global-teardown.ts
311
+ │ └── archive-reports.ts
312
+ ├── playwright.config.ts # Main config (Chromium only - fast)
313
+ ├── playwright.all-browsers.config.ts # Full browser coverage
314
+ ├── playwright.chromium-only.config.ts # Explicit Chromium config
315
+ ├── .env # Local environment variables
316
+ ├── .gitignore # Git ignore rules (includes tmp/)
317
+ └── credentials.env.json # Secure credentials (not in git)
318
+ ```
319
+
320
+ ### Test File Naming Convention
321
+
322
+ **Format**: `NN_NN_NN_Test_name.spec.ts`
323
+
324
+ - **NN** - Module/category/test number (hierarchical)
325
+ - **Test_name** - Descriptive name (using underscores)
326
+ - **Extension**: Always `.spec.ts`
327
+
328
+ **Examples**:
329
+ ```
330
+ 01_01_01_Login_scenarios.spec.ts
331
+ 01_01_02_Login_verification.spec.ts
332
+ 11_01_01_Add_item.spec.ts
333
+ 11_01_02_Edit_item.spec.ts
334
+ ```
335
+
336
+ **Hierarchy**:
337
+ - **First NN**: Main module (01 = Login, 02 = Settings, 11 = ModuleC)
338
+ - **Second NN**: Sub-module or category
339
+ - **Third NN**: Specific test scenario within category
340
+
341
+ ## Test Writing Guidelines
342
+
343
+ ### Basic Test Structure
344
+
345
+ ```typescript
346
+ import { expect, test } from '@playwright/test';
347
+ import { LoginPage } from '../../pages/login/LoginPage';
348
+ import { DashboardPage } from '../../pages/dashboard/DashboardPage';
349
+ import { CredentialsLoader } from '../../fixtures/loader';
350
+ import { clearBrowserStorage } from '../../utils/test-helpers';
351
+
352
+ test.describe('01_Login - tests', { tag: ['@login', '@verified', '@smoke'] }, () => {
353
+ let loginPage: LoginPage;
354
+ let dashboardPage: DashboardPage;
355
+
356
+ test.beforeEach(async ({ page }) => {
357
+ loginPage = new LoginPage(page);
358
+ dashboardPage = new DashboardPage(page);
359
+
360
+ // Navigate to login page
361
+ await page.goto('/login');
362
+
363
+ // Clear browser storage (localStorage, sessionStorage, cookies)
364
+ await clearBrowserStorage(page);
365
+
366
+ // Wait for page to be ready
367
+ await loginPage.waitForPageReady();
368
+ });
369
+
370
+ test('01_01_01: Login - positive result', async ({ page }) => {
371
+ // Load test data from fixtures
372
+ const credentials = await CredentialsLoader.getCredentials();
373
+
374
+ // Use test.step for better reporting
375
+ await test.step('Enter email and password', async () => {
376
+ await loginPage.login(
377
+ credentials.email.right,
378
+ credentials.password.right
379
+ );
380
+ });
381
+
382
+ await test.step('Verify successful login', async () => {
383
+ await loginPage.verifySuccessfulLogin();
384
+ await expect(page).toHaveURL(/\/dashboard/);
385
+ });
386
+ });
387
+
388
+ test('01_01_02: Login - wrong password', async ({ page }) => {
389
+ const credentials = await CredentialsLoader.getCredentials();
390
+
391
+ await test.step('Enter valid email', async () => {
392
+ await loginPage.fillEmail(credentials.email.right);
393
+ await loginPage.clickContinue();
394
+ });
395
+
396
+ await test.step('Enter wrong password', async () => {
397
+ await loginPage.passwordInputLocator.waitFor({ state: 'visible' });
398
+ await loginPage.fillPassword(credentials.password.wrong);
399
+ await loginPage.clickLoginButton();
400
+ });
401
+
402
+ await test.step('Verify error message', async () => {
403
+ await loginPage.verifyErrorAlert();
404
+ await loginPage.verifyStillOnLoginPage();
405
+ });
406
+ });
407
+ });
408
+ ```
409
+
410
+ ### Key Patterns
411
+
412
+ 1. **Use `test.describe()` with tags** - Group related tests with descriptive tags
413
+ 2. **Initialize Page Objects in `beforeEach`** - Create fresh instances for each test
414
+ 3. **Use `test.step()` for clarity** - Break tests into logical steps for better reporting
415
+ 4. **Load data dynamically** - Use `CredentialsLoader` for credentials and test data
416
+ 5. **Clear browser state** - Always use `clearBrowserStorage()` in `beforeEach`
417
+ 6. **Wait for visibility** - Use `.waitFor({ state: 'visible' })` before interactions
418
+
419
+ ## Page Object Model (POM)
420
+
421
+ ### Creating a Page Object
422
+
423
+ **Location**: `{{PLAYWRIGHT_DIR}}/pages/[module]/[PageName]Page.ts`
424
+
425
+ ```typescript
426
+ import type { Locator, Page } from '@playwright/test';
427
+ import { getByDataTestId } from '../../utils/locators';
428
+
429
+ /**
430
+ * Page Object for Login page
431
+ * Handles email/password authentication flow
432
+ */
433
+ export class LoginPage {
434
+ protected page: Page;
435
+
436
+ // Locators - private and readonly
437
+ private readonly emailInput: Locator;
438
+ private readonly passwordInput: Locator;
439
+ private readonly submitButton: Locator;
440
+ private readonly errorAlert: Locator;
441
+
442
+ constructor(page: Page) {
443
+ this.page = page;
444
+
445
+ // Initialize locators with fallbacks for legacy selectors
446
+ this.emailInput = page
447
+ .locator('[data-testid="loginViewEmailStepInput"], [data-testid="loginRightPanelEmailInput"]')
448
+ .first();
449
+
450
+ this.passwordInput = page
451
+ .locator('[data-testid="loginViewPasswordStepInput"]')
452
+ .first();
453
+
454
+ this.submitButton = page
455
+ .locator('[data-testid="loginViewSubmitButton"]')
456
+ .first();
457
+
458
+ this.errorAlert = page
459
+ .locator('[data-testid="loginViewErrorAlert"]')
460
+ .first();
461
+ }
462
+
463
+ /**
464
+ * Navigate to login page and wait for form to be visible
465
+ */
466
+ async navigate(): Promise<void> {
467
+ await this.page.goto('/login');
468
+ await this.emailInput.waitFor({ state: 'visible', timeout: 10000 });
469
+ }
470
+
471
+ /**
472
+ * Fill email input field
473
+ */
474
+ async fillEmail(email: string): Promise<void> {
475
+ await this.emailInput.waitFor({ state: 'visible' });
476
+ await this.emailInput.clear();
477
+ await this.emailInput.fill(email);
478
+ }
479
+
480
+ /**
481
+ * Fill password input field
482
+ */
483
+ async fillPassword(password: string): Promise<void> {
484
+ await this.passwordInput.waitFor({ state: 'visible' });
485
+ await this.passwordInput.clear();
486
+ await this.passwordInput.fill(password);
487
+ }
488
+
489
+ /**
490
+ * Complete login flow with email and password
491
+ */
492
+ async login(email: string, password: string): Promise<void> {
493
+ await this.fillEmail(email);
494
+ await this.submitButton.click();
495
+ await this.passwordInput.waitFor({ state: 'visible' });
496
+ await this.fillPassword(password);
497
+ await this.submitButton.click();
498
+ }
499
+
500
+ /**
501
+ * Verify successful login by checking URL redirect
502
+ */
503
+ async verifySuccessfulLogin(): Promise<void> {
504
+ await this.page.waitForURL(/\/dashboard/, { timeout: 15000 });
505
+ await this.page.waitForLoadState('networkidle');
506
+ }
507
+
508
+ /**
509
+ * Verify error alert is visible
510
+ */
511
+ async verifyErrorAlert(): Promise<void> {
512
+ await this.errorAlert.waitFor({ state: 'visible', timeout: 5000 });
513
+ }
514
+ }
515
+ ```
516
+
517
+ ### POM Best Practices
518
+
519
+ 1. **One class per page/component** - Keep POMs focused and single-responsibility
520
+ 2. **Locators as private readonly** - Encapsulate selectors, expose methods
521
+ 3. **Descriptive method names** - `fillEmail()`, `clickSubmitButton()`, `verifyErrorAlert()`
522
+ 4. **Return void for actions** - Methods that perform actions return `Promise<void>`
523
+ 5. **JSDoc comments** - Document public methods for better IDE support
524
+ 6. **Export via index.ts** - Centralize exports in `pages/index.ts`
525
+ 7. **Use `getByDataTestId()` helper** - For consistent data-testid selection
526
+
527
+ ### Project Structure Example
528
+
529
+ **Page Objects** in the project:
530
+
531
+ ```
532
+ {{PLAYWRIGHT_DIR}}/pages/
533
+ ├── index.ts <- Centralized exports
534
+ ├── login/
535
+ │ └── LoginPage.ts <- Login module POM
536
+ ├── dashboard/
537
+ │ └── DashboardPage.ts <- Dashboard module POM
538
+ ├── profile/
539
+ │ └── ProfilePage.ts <- Profile module POM (User profile interactions)
540
+ ├── settings/
541
+ │ ├── GlobalSettingsPage.ts
542
+ │ ├── ModuleSettingsPage.ts
543
+ │ └── BrowserSettingsPage.ts
544
+ ├── module-a/
545
+ │ ├── ModuleAPage.ts
546
+ │ ├── ModuleAFormPage.ts
547
+ │ ├── ModuleADetailsPage.ts
548
+ │ └── ModuleASettingsPage.ts
549
+ └── components/
550
+ ├── SidebarComponent.ts
551
+ ├── DataTableComponent.ts
552
+ └── ModalComponent.ts
553
+ ```
554
+
555
+ ### pages/index.ts - Centralized Exports
556
+
557
+ ```typescript
558
+ /**
559
+ * Page Object Models index file
560
+ * Central export point for all page objects
561
+ */
562
+
563
+ // Login pages
564
+ export { LoginPage } from './login/LoginPage';
565
+ // Dashboard pages
566
+ export { DashboardPage } from './dashboard/DashboardPage';
567
+ // Profile pages
568
+ export { ProfilePage } from './profile/ProfilePage';
569
+ // Settings pages
570
+ export { GlobalSettingsPage } from './settings/GlobalSettingsPage';
571
+ // ... more exports
572
+ ```
573
+
574
+ ### Profile Module Example
575
+
576
+ **File**: `{{PLAYWRIGHT_DIR}}/pages/profile/ProfilePage.ts`
577
+
578
+ ```typescript
579
+ import type { Locator, Page } from '@playwright/test';
580
+ import { expect } from '@playwright/test';
581
+ import { getByDataTestId } from '../../utils/locators';
582
+
583
+ /**
584
+ * Page Object for User Profile page
585
+ * Handles user profile navigation and interactions
586
+ */
587
+ export class ProfilePage {
588
+ protected page: Page;
589
+
590
+ // Avatar selector with fallbacks for different UI versions
591
+ private readonly avatarSelectors = [
592
+ '[data-testid="mainLayoutTopMenuMobileAvatar"]',
593
+ '[data-testid="desktopMenuAvatar"]',
594
+ '[data-testid="mobileMenuAvatar"]',
595
+ '[data-testid="userMenuDropdownIcon"]',
596
+ '[data-testid="mainLayoutTopMenuDesktopAvatar"]',
597
+ ];
598
+
599
+ constructor(page: Page) {
600
+ this.page = page;
601
+ }
602
+
603
+ /**
604
+ * Navigate to user profile via avatar menu
605
+ * Tries multiple selectors for compatibility with different UI versions
606
+ */
607
+ async navigateToUserProfile(): Promise<void> {
608
+ let avatarFound = false;
609
+
610
+ for (const selector of this.avatarSelectors) {
611
+ const avatarButton = this.page.locator(selector).first();
612
+ const isVisible = await avatarButton.isVisible({ timeout: 2000 }).catch(() => false);
613
+ if (isVisible) {
614
+ await avatarButton.click();
615
+ avatarFound = true;
616
+ break;
617
+ }
618
+ }
619
+
620
+ if (!avatarFound) {
621
+ throw new Error(`Could not find avatar button. Tried selectors: ${this.avatarSelectors.join(', ')}`);
622
+ }
623
+
624
+ const profileOption = getByDataTestId(this.page, 'profilePopoverItemProfile');
625
+ await profileOption.waitFor({ state: 'visible', timeout: 5000 });
626
+ await profileOption.click();
627
+
628
+ await this.page.waitForLoadState('networkidle');
629
+ }
630
+
631
+ /**
632
+ * Find and open a specific item by content
633
+ */
634
+ async findAndOpenItem(itemContent: string): Promise<void> {
635
+ const itemElement = this.page.getByRole('paragraph').filter({ hasText: itemContent });
636
+ await itemElement.waitFor({ state: 'visible', timeout: 5000 });
637
+ await itemElement.click();
638
+ }
639
+
640
+ /**
641
+ * Add a comment to an item
642
+ */
643
+ async addComment(commentText: string): Promise<void> {
644
+ const commentInput = this.page.getByRole('textbox', { name: /Write a message/i });
645
+ await commentInput.waitFor({ state: 'visible', timeout: 5000 });
646
+ await commentInput.click();
647
+ await commentInput.fill(commentText);
648
+ await commentInput.press('Enter');
649
+ await this.page.waitForTimeout(500);
650
+ }
651
+
652
+ /**
653
+ * Verify comment is visible
654
+ */
655
+ async verifyCommentVisible(commentText: string): Promise<void> {
656
+ const commentElement = this.page.getByText(commentText, { exact: true });
657
+ await expect(commentElement).toBeVisible({ timeout: 5000 });
658
+ }
659
+ }
660
+ ```
661
+
662
+ ### Test File Using Profile POM
663
+
664
+ **File**: `tests/02_Profile/02_01_01_Profile_Comments.spec.ts`
665
+
666
+ ```typescript
667
+ import { LoginPage, DashboardPage, ProfilePage } from '../../pages';
668
+ import { CredentialsLoader } from '../../fixtures/loader';
669
+ import { clearBrowserStorage } from '../../utils/test-helpers';
670
+
671
+ describe('02_Profile - Comments Workflow',
672
+ { tag: ['@profile', '@messaging', '@verified'] },
673
+ () => {
674
+ let loginPage: LoginPage;
675
+ let dashboardPage: DashboardPage;
676
+ let profilePage: ProfilePage;
677
+
678
+ beforeEach(async ({ page }) => {
679
+ loginPage = new LoginPage(page);
680
+ dashboardPage = new DashboardPage(page);
681
+ profilePage = new ProfilePage(page);
682
+
683
+ // Load credentials from CredentialsLoader
684
+ const credentials = await CredentialsLoader.getCredentials();
685
+
686
+ // Login workflow
687
+ await loginPage.navigate();
688
+ await loginPage.fillEmail(credentials.email.right);
689
+ await loginPage.clickContinue();
690
+ await loginPage.fillPassword(credentials.password.right);
691
+ await loginPage.clickLoginButton();
692
+ await dashboardPage.navigate();
693
+ });
694
+
695
+ it('02_01_01: should login and add comment in user profile',
696
+ async ({ page }) => {
697
+ // Profile navigation and comment addition
698
+ await profilePage.navigateToUserProfile();
699
+ await profilePage.findAndOpenItem('TestItem');
700
+ await profilePage.addComment('test comment');
701
+ await profilePage.verifyCommentVisible('test comment');
702
+ });
703
+ });
704
+ ```
705
+
706
+ ### Key Points for POM
707
+
708
+ 1. **Location**: `pages/[module]/[PageName]Page.ts` (matches pattern `pages/[module]/[PageName]Page.ts`)
709
+ 2. **Export**: Added to `pages/index.ts` for centralized import
710
+ 3. **Fallback Selectors**: Handles multiple UI versions (desktop, mobile, different layouts)
711
+ 4. **Semantic Methods**: `navigateToUserProfile()`, `addComment()`, `verifyCommentVisible()`
712
+ 5. **Type Safety**: Full TypeScript with proper return types
713
+ 6. **Documentation**: JSDoc for all public methods
714
+
715
+ ## Selectors & data-testid
716
+
717
+ ### Selector Priority
718
+
719
+ 1. **`[data-testid="value"]`** - **PREFERRED** for all new elements
720
+ 2. **`[test-id="value"]`** - Alternative (legacy support)
721
+ 3. **`#id`** - For elements with unique IDs
722
+ 4. **`input[type="email"]`** - For form elements
723
+ 5. **`.class-name`** - Last resort only
724
+
725
+ ### data-testid Naming Convention
726
+
727
+ **Format**: `moduleName-element-type`
728
+
729
+ **Rules**:
730
+ - Write in **English**
731
+ - Separate with **hyphens** (`-`)
732
+ - First part: **module name**
733
+ - Middle parts: **description**
734
+ - Last part: **element type** (input, button, checkbox, etc.)
735
+
736
+ **Examples**:
737
+ ```html
738
+ <!-- Login module -->
739
+ <input data-testid="login-email-input" />
740
+ <input data-testid="login-password-input" />
741
+ <input data-testid="login-remember-me-checkbox" />
742
+ <button data-testid="login-submit-button" />
743
+
744
+ <!-- Tasks module -->
745
+ <button data-testid="tasks-add-new-button" />
746
+ <input data-testid="tasks-search-input" />
747
+ <select data-testid="tasks-status-filter-select" />
748
+ ```
749
+
750
+ ### Using the Locator Helper
751
+
752
+ ```typescript
753
+ import { getByDataTestId } from '../../utils/locators';
754
+
755
+ // In Page Object
756
+ constructor(page: Page) {
757
+ this.page = page;
758
+
759
+ // Simple usage
760
+ this.submitButton = getByDataTestId(page, 'login-submit-button');
761
+
762
+ // With fallbacks for legacy selectors
763
+ this.emailInput = page
764
+ .locator('[data-testid="loginViewEmailStepInput"], [data-testid="loginRightPanelEmailInput"]')
765
+ .first();
766
+ }
767
+ ```
768
+
769
+ **Helper function** (`utils/locators.ts`):
770
+ ```typescript
771
+ export function getByDataTestId(page: Page, value: string) {
772
+ return page.locator(
773
+ `[data-testid="${value}"], [data-testid="${value}"], [data-testId="${value}"]`
774
+ ).first();
775
+ }
776
+ ```
777
+
778
+ ## Test Data & Fixtures
779
+
780
+ ### Using CredentialsLoader
781
+
782
+ **Location**: `fixtures/loader.ts`
783
+
784
+ The `CredentialsLoader` provides dynamic test data loading based on environment variables:
785
+
786
+ ```typescript
787
+ import { CredentialsLoader } from '../../fixtures/loader';
788
+
789
+ // Get credentials for current tenant (from COMPANY_NAME env var)
790
+ const credentials = await CredentialsLoader.getCredentials();
791
+
792
+ // Available credential fields:
793
+ credentials.email.right // Valid email
794
+ credentials.email.wrong // Invalid email
795
+ credentials.password.right // Valid password
796
+ credentials.password.wrong // Invalid password
797
+ credentials.username.right // Valid username (login)
798
+ credentials.username.wrong // Invalid username
799
+
800
+ // Get all tenant data (users, departments, etc.)
801
+ const tenantData = await CredentialsLoader.getAllData();
802
+
803
+ // Get specific users
804
+ const users = await CredentialsLoader.getUsers();
805
+
806
+ // Get departments
807
+ const departments = await CredentialsLoader.getDepartments();
808
+ ```
809
+
810
+ ### Environment Variables
811
+
812
+ **File**: `.env` (local) or set via Docker/Makefile
813
+
814
+ ```bash
815
+ # Tenant selection
816
+ COMPANY_NAME=default
817
+
818
+ # Base URL
819
+ BASE_URL={{BASE_URL}}
820
+
821
+ # Test execution
822
+ PLAYWRIGHT_WORKERS=4
823
+ PLAYWRIGHT_RETRIES=1
824
+ PLAYWRIGHT_GREP_TAGS=@verified
825
+
826
+ # Browser
827
+ PLAYWRIGHT_BROWSERS=chromium # chromium, firefox, webkit
828
+ ```
829
+
830
+ ### Tenant Data Structure
831
+
832
+ **File**: `fixtures/tenants/default.ts`
833
+
834
+ ```typescript
835
+ const tenantData = {
836
+ name: 'Default Tenant',
837
+ code: 'default',
838
+
839
+ // Credentials loaded from credentials.env.json (secure)
840
+ // Not stored in repository!
841
+
842
+ users: {
843
+ admin: {
844
+ fullName: 'Admin User',
845
+ id: 1,
846
+ position: 'Administrator',
847
+ department: 'IT',
848
+ },
849
+ manager1: {
850
+ fullName: 'Manager One',
851
+ id: 2,
852
+ position: 'Department Manager',
853
+ department: 'Sales',
854
+ },
855
+ // ... more users
856
+ },
857
+
858
+ departments: {
859
+ // Department data
860
+ }
861
+ };
862
+
863
+ module.exports = tenantData;
864
+ ```
865
+
866
+ ### Secure Credentials
867
+
868
+ **File**: `credentials.env.json` (NOT in git, copy from `.example`)
869
+
870
+ ```json
871
+ {
872
+ "default": {
873
+ "username": {
874
+ "right": "valid.user@example.com",
875
+ "wrong": "invalid@example.com"
876
+ },
877
+ "password": {
878
+ "right": "ValidPassword123!",
879
+ "wrong": "WrongPassword"
880
+ },
881
+ "email": {
882
+ "right": "test@example.com",
883
+ "wrong": "invalid@test.com"
884
+ }
885
+ }
886
+ }
887
+ ```
888
+
889
+ ## Test Tags & Filtering
890
+
891
+ ### Available Tags
892
+ Tags: English language, prefixed with `@` for filtering
893
+ - **`@verified`** - Fully tested and verified tests (default for CI/CD)
894
+ - **`@smoke`** - Critical smoke tests (subset of @verified)
895
+ - **`@login`** - Login module tests
896
+ - **`@settings`** - Settings module tests
897
+ - **`@module-a`** - Module A tests
898
+ - **`@module-b`** - Module B tests
899
+
900
+ ### Using Tags in Tests
901
+
902
+ ```typescript
903
+ // Single tag
904
+ test.describe('Login tests', { tag: '@login' }, () => {
905
+ // tests
906
+ });
907
+
908
+ // Multiple tags
909
+ test.describe('Login tests', { tag: ['@login', '@verified', '@smoke'] }, () => {
910
+ // tests
911
+ });
912
+
913
+ // Individual test tags (overrides describe tags)
914
+ test('Critical login flow', { tag: '@smoke' }, async ({ page }) => {
915
+ // test
916
+ });
917
+ ```
918
+
919
+ ### Running Tests by Tags
920
+
921
+ ```bash
922
+ # Run all verified tests
923
+ npx playwright test --grep @verified
924
+
925
+ # Run smoke tests only
926
+ npx playwright test --grep @smoke
927
+
928
+ # Run specific module
929
+ npx playwright test --grep @login
930
+
931
+ # Multiple tags (OR logic)
932
+ npx playwright test --grep "@login|@smoke"
933
+
934
+ # Via Makefile
935
+ make playwright-test playwright_grep_tags="@login"
936
+ make playwright-test playwright_grep_tags="@smoke"
937
+ ```
938
+
939
+ ## Running Tests
940
+
941
+ ### Local Execution
942
+
943
+ ```bash
944
+ cd {{PLAYWRIGHT_DIR}}
945
+
946
+ # Quick tests - Chromium only
947
+ npx playwright test
948
+
949
+ # With UI (like Cypress)
950
+ npx playwright test --ui
951
+
952
+ # Debug mode
953
+ npx playwright test --debug
954
+
955
+ # Specific file
956
+ npx playwright test tests/01_Login/01_01_01_Login_scenarios.spec.ts
957
+
958
+ # All browsers
959
+ npx playwright test --config=playwright.all-browsers.config.ts
960
+
961
+ # Specific browser
962
+ npx playwright test --project=firefox
963
+
964
+ # Parallel execution (4 workers)
965
+ npx playwright test --workers=4
966
+ ```
967
+
968
+ ### Docker Execution (via Makefile)
969
+
970
+ **From project root**:
971
+
972
+ ```bash
973
+ # Default - verified tests on Chromium
974
+ make playwright-test
975
+
976
+ # Custom URL
977
+ make playwright-test playwright_base_url="{{BASE_URL}}"
978
+
979
+ # Specific tags
980
+ make playwright-test playwright_grep_tags="@login"
981
+
982
+ # Specific file
983
+ make playwright-test playwright_spec_file="tests/01_Login/01_01_01_Login_scenarios.spec.ts"
984
+
985
+ # Different browser
986
+ make playwright-test playwright_browsers="firefox"
987
+
988
+ # Parallel execution (5 workers)
989
+ make playwright-test playwright_parallel_machines="5"
990
+
991
+ # Different tenant
992
+ make playwright-test playwright_client_name="tenant-name"
993
+ ```
994
+
995
+ ### Configuration Files
996
+
997
+ - **`playwright.config.ts`** - Default (Chromium only, fast)
998
+ - **`playwright.all-browsers.config.ts`** - All browsers (Chromium, Firefox, Webkit, Mobile)
999
+ - **`playwright.chromium-only.config.ts`** - Explicit Chromium only
1000
+
1001
+ ## Authentication & Session Management
1002
+
1003
+ ### Session Manager
1004
+
1005
+ **Location**: `utils/commands/auth/session.manager.ts`
1006
+
1007
+ Provides session caching for efficient test execution:
1008
+
1009
+ ```typescript
1010
+ import { SessionManager } from '../../utils/commands/auth';
1011
+
1012
+ // In test
1013
+ test.beforeEach(async ({ page }) => {
1014
+ // Restore session if exists
1015
+ await SessionManager.restoreSession(page, 'user-session');
1016
+
1017
+ // If no session, login manually
1018
+ if (!await SessionManager.hasSession('user-session')) {
1019
+ const loginPage = new LoginPage(page);
1020
+ const credentials = await CredentialsLoader.getCredentials();
1021
+ await loginPage.login(credentials.email.right, credentials.password.right);
1022
+
1023
+ // Save session
1024
+ await SessionManager.saveSession(page, 'user-session');
1025
+ }
1026
+ });
1027
+ ```
1028
+
1029
+ ### Clear Browser Storage
1030
+
1031
+ ```typescript
1032
+ import { clearBrowserStorage } from '../../utils/test-helpers';
1033
+
1034
+ test.beforeEach(async ({ page }) => {
1035
+ await page.goto('/login');
1036
+
1037
+ // Clear localStorage, sessionStorage, cookies
1038
+ await clearBrowserStorage(page);
1039
+ });
1040
+ ```
1041
+
1042
+ ## Reporting & Debugging
1043
+
1044
+ ### Reports Location
1045
+
1046
+ - **HTML Report**: `playwright-report/index.html`
1047
+ - **JSON Report**: `reports/summary.json`
1048
+ - **Screenshots**: `reports/screenshots/`
1049
+ - **Videos**: `test-results/` (on failure)
1050
+ - **Archives**: `reports/archives/run-TIMESTAMP/`
1051
+
1052
+ ### View Reports
1053
+
1054
+ ```bash
1055
+ # Open HTML report
1056
+ npx playwright show-report
1057
+
1058
+ # Via Makefile
1059
+ make playwright-show-report
1060
+
1061
+ # Custom port
1062
+ npx playwright show-report --port=9324
1063
+ ```
1064
+
1065
+ ### Screenshots & Videos
1066
+
1067
+ **Automatic**:
1068
+ - Screenshots: Taken for ALL tests (pass/fail)
1069
+ - Videos: Only on failure (local) or off (CI)
1070
+ - Traces: On first retry
1071
+
1072
+ **Manual screenshots**:
1073
+ ```typescript
1074
+ await page.screenshot({ path: 'screenshot.png', fullPage: true });
1075
+ ```
1076
+
1077
+ ## Common Utilities
1078
+
1079
+ ### Test Helpers
1080
+
1081
+ **File**: `utils/test-helpers.ts`
1082
+
1083
+ ```typescript
1084
+ // Clear browser storage
1085
+ await clearBrowserStorage(page);
1086
+
1087
+ // Wait for stable state (no animations)
1088
+ await waitForStable(page, '[data-testid="element"]');
1089
+
1090
+ // Timestamped screenshot
1091
+ await takeTimestampedScreenshot(page, 'login-error');
1092
+ ```
1093
+
1094
+ ### Locator Helpers
1095
+
1096
+ ```typescript
1097
+ import { getByDataTestId } from '../../utils/locators';
1098
+
1099
+ // Get element by data-testid (with fallbacks)
1100
+ const button = getByDataTestId(page, 'submit-button');
1101
+ ```
1102
+
1103
+ ## Best Practices
1104
+
1105
+ ### 1. Test Organization
1106
+ - Use hierarchical numbering (NN_NN_NN)
1107
+ - Group related tests in `test.describe()`
1108
+ - Use descriptive test names
1109
+ - Tag tests appropriately
1110
+
1111
+ ### 2. Page Objects
1112
+ - One POM per page/component
1113
+ - Private readonly locators
1114
+ - Public methods for actions
1115
+ - Export via `pages/index.ts`
1116
+
1117
+ ### 3. Selectors
1118
+ - Prefer `data-testid`
1119
+ - Follow naming convention (module-element-type)
1120
+ - Use `getByDataTestId()` helper
1121
+ - Provide fallbacks for legacy selectors
1122
+
1123
+ ### 4. Test Data
1124
+ - Use `CredentialsLoader` for dynamic data
1125
+ - Never hardcode credentials
1126
+ - Store sensitive data in `credentials.env.json`
1127
+ - Use environment variables
1128
+
1129
+ ### 5. Assertions
1130
+ - Use `test.step()` for clarity
1131
+ - Wait for visibility before assertions
1132
+ - Use specific expectations (`toHaveURL`, `toBeVisible`)
1133
+ - Verify state changes
1134
+
1135
+ ### 6. Performance
1136
+ - Use Chromium-only config for fast feedback
1137
+ - Leverage parallel execution (workers)
1138
+ - Clear browser state in `beforeEach`
1139
+ - Use all-browsers config before release
1140
+
1141
+ ### 7. Debugging
1142
+ - Use `--ui` mode for interactive debugging
1143
+ - Use `--debug` for step-by-step execution
1144
+ - Check screenshots in reports
1145
+ - Review trace files on failures
1146
+
1147
+ ## Anti-Patterns (Avoid)
1148
+
1149
+ - Hardcoding credentials in tests
1150
+ - Using CSS class selectors instead of data-testid
1151
+ - Not using Page Objects (direct page interactions in tests)
1152
+ - Missing test tags
1153
+ - Not clearing browser storage in beforeEach
1154
+ - Not waiting for visibility before interactions
1155
+ - Mixing business logic with test code
1156
+ - Not using `test.step()` for multi-step tests
1157
+ - Inconsistent naming conventions
1158
+
1159
+ ## Troubleshooting
1160
+
1161
+ ### Common Issues
1162
+
1163
+ **1. Element not found**
1164
+ ```typescript
1165
+ // Wrong
1166
+ await page.locator('.button').click();
1167
+
1168
+ // Correct
1169
+ await page.locator('[data-testid="submit-button"]').waitFor({ state: 'visible' });
1170
+ await page.locator('[data-testid="submit-button"]').click();
1171
+ ```
1172
+
1173
+ **2. Flaky tests**
1174
+ ```typescript
1175
+ // Wait for network idle
1176
+ await page.waitForLoadState('networkidle');
1177
+
1178
+ // Wait for specific element
1179
+ await element.waitFor({ state: 'visible' });
1180
+
1181
+ // Use test.step() to identify failure point
1182
+ await test.step('Login', async () => {
1183
+ await loginPage.login(email, password);
1184
+ });
1185
+ ```
1186
+
1187
+ **3. Authentication issues**
1188
+ ```typescript
1189
+ // Clear storage before each test
1190
+ test.beforeEach(async ({ page }) => {
1191
+ await clearBrowserStorage(page);
1192
+ });
1193
+
1194
+ // Verify credentials are loaded
1195
+ const credentials = await CredentialsLoader.getCredentials();
1196
+ console.log('Using email:', credentials.email.right);
1197
+ ```
1198
+
1199
+ ## Quick Reference
1200
+
1201
+ ### Essential Commands
1202
+
1203
+ ```bash
1204
+ # Local testing
1205
+ npx playwright test # Quick (Chromium only)
1206
+ npx playwright test --ui # GUI mode
1207
+ npx playwright test --debug # Debug mode
1208
+
1209
+ # Docker testing
1210
+ make playwright-test # Default (@verified)
1211
+ make playwright-test playwright_grep_tags="@smoke" # Smoke tests
1212
+
1213
+ # Reports
1214
+ npx playwright show-report # View HTML report
1215
+ make playwright-show-report # Via Makefile
1216
+
1217
+ # Cleanup
1218
+ make playwright-clean # Clean reports
1219
+ ```
1220
+
1221
+ ### File Templates
1222
+
1223
+ **Test file**: `tests/NN_Module/NN_NN_NN_Test_name.spec.ts`
1224
+ **Page Object**: `pages/module/ModulePage.ts`
1225
+ **Fixtures**: `fixtures/tenants/tenant.ts`
1226
+
1227
+ ### Key Imports
1228
+
1229
+ ```typescript
1230
+ import { expect, test } from '@playwright/test';
1231
+ import { LoginPage } from '../../pages/login/LoginPage';
1232
+ import { CredentialsLoader } from '../../fixtures/loader';
1233
+ import { clearBrowserStorage } from '../../utils/test-helpers';
1234
+ import { getByDataTestId } from '../../utils/locators';
1235
+ ```
1236
+
1237
+ ## Resources
1238
+
1239
+ - **Project README**: `{{PLAYWRIGHT_DIR}}/README.md`
1240
+ - **Docker Guide**: `{{PLAYWRIGHT_DIR}}/DEVOPS-DOCKER.md`
1241
+ - **Playwright Docs**: https://playwright.dev/docs/intro
1242
+
1243
+ ---
1244
+
1245
+ **Remember**: Adapt this skill template to your specific project by replacing all `{{PLACEHOLDER}}` values with your project's actual paths, URLs, and naming conventions. Always follow the established patterns, naming conventions, and best practices documented here.