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.
- package/README.md +489 -0
- package/bin/cli.mjs +35 -0
- package/lib/installer.mjs +495 -0
- package/lib/questions.mjs +332 -0
- package/lib/ui.mjs +76 -0
- package/lib/utils.mjs +231 -0
- package/package.json +26 -0
- package/templates/base/CLAUDE.md +34 -0
- package/templates/base/_ai/_guidelines_header.md +70 -0
- package/templates/base/_ai/context/README.md +20 -0
- package/templates/base/_ai/prompts/codereview.prompt.md +324 -0
- package/templates/base/_ai/prompts/duplicate-code-analysis.prompt.md +128 -0
- package/templates/base/_ai/prompts/figma-analysis.prompt.md +155 -0
- package/templates/base/_ai/prompts/security-review.prompt.md +46 -0
- package/templates/base/_ai/skills/README.md +80 -0
- package/templates/base/_ai/skills/TEMPLATE.md +106 -0
- package/templates/base/_ai/skills/babysit-prs/SKILL.md +105 -0
- package/templates/base/_ai/skills/debug/SKILL.md +93 -0
- package/templates/base/_ai/skills/fill-worklogs/SKILL.md +158 -0
- package/templates/base/_ai/skills/hotfix/SKILL.md +52 -0
- package/templates/base/_ai/skills/jira-task/SKILL.md +170 -0
- package/templates/base/_ai/skills/my-prs/SKILL.md +78 -0
- package/templates/base/_ai/skills/pr-dashboard/SKILL.md +43 -0
- package/templates/base/_ai/skills/pr-prepare/SKILL.md +106 -0
- package/templates/base/_ai/skills/refactor/SKILL.md +87 -0
- package/templates/base/_ai/skills/write-tests/SKILL.md +109 -0
- package/templates/base/_claude/settings.local.json +37 -0
- package/templates/base/_cursor/rules/global.mdc +7 -0
- package/templates/base/_editorconfig +18 -0
- package/templates/base/_gemini/settings.json +3 -0
- package/templates/base/_github/copilot-instructions.md +1 -0
- package/templates/base/_github/pull_request_template.md +23 -0
- package/templates/base/_gitignore +22 -0
- package/templates/base/_junie/guidelines.md +1 -0
- package/templates/base/commit-instructions.md +92 -0
- package/templates/packs/docker/_ai/instructions/docker.instructions.md +193 -0
- package/templates/packs/docker/_guidelines.md +10 -0
- package/templates/packs/docker/pack.json +8 -0
- package/templates/packs/laravel/_ai/instructions/api-resource.instructions.md +251 -0
- package/templates/packs/laravel/_ai/instructions/module.instructions.md +133 -0
- package/templates/packs/laravel/_ai/instructions/service-repository.instructions.md +215 -0
- package/templates/packs/laravel/_ai/instructions/testing.instructions.md +278 -0
- package/templates/packs/laravel/_ai/skills/migration/SKILL.md +172 -0
- package/templates/packs/laravel/_ai/skills/new-endpoint/SKILL.md +165 -0
- package/templates/packs/laravel/_ai/skills/new-module/SKILL.md +208 -0
- package/templates/packs/laravel/_ai/skills/queued-job/SKILL.md +248 -0
- package/templates/packs/laravel/_ai/skills/testing-feature/SKILL.md +196 -0
- package/templates/packs/laravel/_ai/skills/testing-manual/SKILL.md +186 -0
- package/templates/packs/laravel/_ai/skills/testing-unit/SKILL.md +200 -0
- package/templates/packs/laravel/_guidelines.md +25 -0
- package/templates/packs/laravel/pack.json +6 -0
- package/templates/packs/playwright/_ai/instructions/playwright.instructions.md +219 -0
- package/templates/packs/playwright/_ai/skills/playwright/README.md +194 -0
- package/templates/packs/playwright/_ai/skills/playwright/SKILL.md +1245 -0
- package/templates/packs/playwright/_ai/skills/playwright-codereview/SKILL.md +642 -0
- package/templates/packs/playwright/_ai/skills/playwright-record/README.md +87 -0
- package/templates/packs/playwright/_ai/skills/playwright-record/SKILL.md +564 -0
- package/templates/packs/playwright/_guidelines.md +12 -0
- package/templates/packs/playwright/pack.json +9 -0
- package/templates/packs/storybook/_ai/instructions/storybook.instructions.md +181 -0
- package/templates/packs/storybook/pack.json +6 -0
- package/templates/packs/vitest/_ai/instructions/vitest.instructions.md +688 -0
- package/templates/packs/vitest/pack.json +6 -0
- package/templates/packs/vue3/_ai/instructions/api.instructions.md +163 -0
- package/templates/packs/vue3/_ai/instructions/coding-conventions.instructions.md +160 -0
- package/templates/packs/vue3/_ai/instructions/composables.instructions.md +218 -0
- package/templates/packs/vue3/_ai/instructions/forms.instructions.md +227 -0
- package/templates/packs/vue3/_ai/instructions/store.instructions.md +504 -0
- package/templates/packs/vue3/_ai/instructions/vue.instructions.md +339 -0
- package/templates/packs/vue3/_ai/skills/api-integration/SKILL.md +195 -0
- package/templates/packs/vue3/_ai/skills/new-component/SKILL.md +133 -0
- package/templates/packs/vue3/_ai/skills/new-module/SKILL.md +177 -0
- package/templates/packs/vue3/_guidelines.md +45 -0
- package/templates/packs/vue3/pack.json +11 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Skill: Laravel Unit Tests
|
|
2
|
+
|
|
3
|
+
## Trigger
|
|
4
|
+
Use when writing unit tests — isolated class tests without database, HTTP, or Laravel container.
|
|
5
|
+
|
|
6
|
+
## File Location & Namespace
|
|
7
|
+
|
|
8
|
+
- `Modules/{Module}/Tests/Unit/{Subdirectory}/{ClassName}Test.php`
|
|
9
|
+
- Namespace mirrors path: `Modules\{Module}\Tests\Unit\{Subdirectory}`
|
|
10
|
+
- Subdirectory should reflect what is being tested: `Services/`, `Listeners/`, `Export/`, `DTO/`, etc.
|
|
11
|
+
|
|
12
|
+
## Base Class
|
|
13
|
+
|
|
14
|
+
Unit tests extend `PHPUnit\Framework\TestCase` — **not** `Tests\TestCase`:
|
|
15
|
+
```php
|
|
16
|
+
use PHPUnit\Framework\TestCase;
|
|
17
|
+
|
|
18
|
+
class MyServiceTest extends TestCase
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Unit tests must **never** touch the database, make HTTP requests, or boot the Laravel application. If you need any of these, write a Feature test instead.
|
|
22
|
+
|
|
23
|
+
## When to Use Unit vs Feature Tests
|
|
24
|
+
|
|
25
|
+
| Use Unit Test when... | Use Feature Test when... |
|
|
26
|
+
|---|---|
|
|
27
|
+
| Testing pure logic (calculations, transformations) | Testing with real database (Eloquent, repositories) |
|
|
28
|
+
| Testing a class in isolation with mocked dependencies | Testing HTTP endpoints (controllers) |
|
|
29
|
+
| Testing event/listener logic without dispatching | Testing service integration with real DB |
|
|
30
|
+
| Testing DTOs, value objects, enums | Testing permissions, auth, middleware |
|
|
31
|
+
| No Laravel container needed | Laravel container / `app()` needed |
|
|
32
|
+
|
|
33
|
+
## Test Structure
|
|
34
|
+
|
|
35
|
+
```php
|
|
36
|
+
<?php
|
|
37
|
+
|
|
38
|
+
declare(strict_types=1);
|
|
39
|
+
|
|
40
|
+
namespace Modules\{Module}\Tests\Unit\{Subdirectory};
|
|
41
|
+
|
|
42
|
+
use PHPUnit\Framework\TestCase;
|
|
43
|
+
|
|
44
|
+
class {ClassName}Test extends TestCase
|
|
45
|
+
{
|
|
46
|
+
private SomeDependency $dependency;
|
|
47
|
+
private MyService $service;
|
|
48
|
+
|
|
49
|
+
protected function setUp(): void
|
|
50
|
+
{
|
|
51
|
+
parent::setUp();
|
|
52
|
+
|
|
53
|
+
$this->dependency = $this->createMock(SomeDependency::class);
|
|
54
|
+
$this->service = new MyService($this->dependency);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// --- public test methods ---
|
|
58
|
+
|
|
59
|
+
public function test_method_returns_expected_result(): void
|
|
60
|
+
{
|
|
61
|
+
$this->dependency->expects($this->once())
|
|
62
|
+
->method('getData')
|
|
63
|
+
->with(42)
|
|
64
|
+
->willReturn('result');
|
|
65
|
+
|
|
66
|
+
$result = $this->service->process(42);
|
|
67
|
+
|
|
68
|
+
$this->assertSame('expected', $result);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// --- private helpers at BOTTOM ---
|
|
72
|
+
|
|
73
|
+
private function createSomeFixture(): SomeClass
|
|
74
|
+
{
|
|
75
|
+
// ...
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Required Conventions
|
|
81
|
+
|
|
82
|
+
### Method naming
|
|
83
|
+
- Prefix: `test_` + `snake_case` description
|
|
84
|
+
- No `@test` annotation
|
|
85
|
+
- All methods must have explicit return type (`: void` for tests)
|
|
86
|
+
|
|
87
|
+
### Class structure order
|
|
88
|
+
1. Properties
|
|
89
|
+
2. `setUp()` / `tearDown()`
|
|
90
|
+
3. All `public` test methods
|
|
91
|
+
4. All `private` helper methods — **always at the bottom**
|
|
92
|
+
|
|
93
|
+
### PHP standards
|
|
94
|
+
- `declare(strict_types=1)` at top
|
|
95
|
+
- All namespaces as FQN/import statements
|
|
96
|
+
- All closure parameters must be typed
|
|
97
|
+
|
|
98
|
+
## Mocking Approaches
|
|
99
|
+
|
|
100
|
+
### PHPUnit `createMock` (preferred)
|
|
101
|
+
|
|
102
|
+
```php
|
|
103
|
+
private SomeRepository $repository;
|
|
104
|
+
|
|
105
|
+
protected function setUp(): void
|
|
106
|
+
{
|
|
107
|
+
parent::setUp();
|
|
108
|
+
$this->repository = $this->createMock(SomeRepository::class);
|
|
109
|
+
$this->service = new MyService($this->repository);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public function test_calls_repository_with_correct_args(): void
|
|
113
|
+
{
|
|
114
|
+
$this->repository->expects($this->once())
|
|
115
|
+
->method('findById')
|
|
116
|
+
->with(42)
|
|
117
|
+
->willReturn($entity);
|
|
118
|
+
|
|
119
|
+
$this->service->process(42);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Mockery (when PHPUnit mocks are insufficient)
|
|
124
|
+
|
|
125
|
+
```php
|
|
126
|
+
use Mockery;
|
|
127
|
+
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
|
|
128
|
+
use PHPUnit\Framework\TestCase;
|
|
129
|
+
|
|
130
|
+
class MyTest extends TestCase
|
|
131
|
+
{
|
|
132
|
+
use MockeryPHPUnitIntegration;
|
|
133
|
+
|
|
134
|
+
public function test_something(): void
|
|
135
|
+
{
|
|
136
|
+
$mock = Mockery::mock(SomeClass::class);
|
|
137
|
+
$mock->shouldReceive('method')->once()->andReturn('value');
|
|
138
|
+
// ...
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Mocking Eloquent Models
|
|
144
|
+
|
|
145
|
+
```php
|
|
146
|
+
private function createUserMock(int $id, string $name): User
|
|
147
|
+
{
|
|
148
|
+
$user = $this->getMockBuilder(User::class)
|
|
149
|
+
->onlyMethods(['notify'])
|
|
150
|
+
->getMock();
|
|
151
|
+
|
|
152
|
+
$user->setAttribute(User::COLUMN_ID, $id);
|
|
153
|
+
$user->setAttribute(User::COLUMN_NAME, $name);
|
|
154
|
+
|
|
155
|
+
return $user;
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Data Providers
|
|
160
|
+
|
|
161
|
+
```php
|
|
162
|
+
/**
|
|
163
|
+
* @dataProvider invalidInputProvider
|
|
164
|
+
*/
|
|
165
|
+
public function test_throws_on_invalid_input(mixed $input): void
|
|
166
|
+
{
|
|
167
|
+
$this->expectException(InvalidArgumentException::class);
|
|
168
|
+
$this->service->process($input);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public static function invalidInputProvider(): array
|
|
172
|
+
{
|
|
173
|
+
return [
|
|
174
|
+
'null' => [null],
|
|
175
|
+
'empty string' => [''],
|
|
176
|
+
'negative number' => [-1],
|
|
177
|
+
];
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Data provider methods **must** be `public static`.
|
|
182
|
+
|
|
183
|
+
## Things to Avoid
|
|
184
|
+
|
|
185
|
+
- **No database access** — if you need DB, write a Feature test
|
|
186
|
+
- **No `app()` or service container** — instantiate classes manually with mocked deps
|
|
187
|
+
- **No HTTP requests** — that's Feature test territory
|
|
188
|
+
- **No `Event::fake()` or `Mail::fake()`** — those are Laravel facade fakes for Feature tests
|
|
189
|
+
- **No `Tests\TestCase`** — only use `PHPUnit\Framework\TestCase`
|
|
190
|
+
- **No meaningless test data** — avoid "test", "foo", "bar"; use realistic values
|
|
191
|
+
|
|
192
|
+
## Running Tests
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# Single test class
|
|
196
|
+
docker exec {{DOCKER_CONTAINER}} php artisan test --filter='{TestClassName}'
|
|
197
|
+
|
|
198
|
+
# Unit tests only for a module
|
|
199
|
+
docker exec {{DOCKER_CONTAINER}} php artisan test --filter='Modules\\{Module}\\Tests\\Unit'
|
|
200
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Laravel + PHP
|
|
6
|
+
|
|
7
|
+
### Core Principle
|
|
8
|
+
Follow Laravel conventions first. Use [official docs](https://laravel.com/docs).
|
|
9
|
+
|
|
10
|
+
### PHP Standards
|
|
11
|
+
- PER 3.0 coding style, `declare(strict_types=1)` everywhere
|
|
12
|
+
- FQN imports, `?string` not `string|null`, `void` return types
|
|
13
|
+
- Typed properties over docblocks, constructor property promotion
|
|
14
|
+
|
|
15
|
+
### Architecture
|
|
16
|
+
- SOLID, especially Single Responsibility
|
|
17
|
+
- No business logic in controllers
|
|
18
|
+
- Repository pattern for data access (CQRS: Command/Query repos)
|
|
19
|
+
- Events + Listeners for decoupled communication
|
|
20
|
+
- Dependency injection, no static calls
|
|
21
|
+
|
|
22
|
+
### Testing
|
|
23
|
+
- PHPUnit with data providers and factories
|
|
24
|
+
- Laravel fakes for Mail, Queue, Notification, Storage
|
|
25
|
+
- See `.ai/instructions/testing.instructions.md`
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*.spec.ts"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Playwright E2E Testing Instructions
|
|
6
|
+
|
|
7
|
+
## File and Directory Naming
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Directories: NN_Name_Module/ (e.g., tests/20_Tasks/)
|
|
11
|
+
Sections: NN_NN_Name_Section/ (e.g., tests/20_Tasks/20_01_Other/)
|
|
12
|
+
Test files: NN_NN_NN_Name.spec.ts (e.g., 20_01_06_Draft_version.spec.ts)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## data-testid Convention (kebab-case)
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Structure: "{module}-{action}-{element-type}"
|
|
19
|
+
|
|
20
|
+
Correct:
|
|
21
|
+
data-testid="task-add-save-button"
|
|
22
|
+
data-testid="task-title-input"
|
|
23
|
+
data-testid="task-acceptance-checkbox"
|
|
24
|
+
|
|
25
|
+
Wrong:
|
|
26
|
+
data-testid="addTaskButton" (camelCase)
|
|
27
|
+
data-testid="task_add_button" (snake_case)
|
|
28
|
+
data-testid="save-button" (missing module)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Dynamic data-testid
|
|
32
|
+
```vue
|
|
33
|
+
:data-testid="`task-row-${index}`"
|
|
34
|
+
:data-testid="`task-user-${user.id}-checkbox`"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Test Structure
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { test, expect } from '@playwright/test';
|
|
41
|
+
import { PageObject } from '../../pages/PageObject';
|
|
42
|
+
|
|
43
|
+
test.describe('Module: Feature name', { tag: ['@module', '@verified'] }, () => {
|
|
44
|
+
let page: PageObject;
|
|
45
|
+
|
|
46
|
+
test.beforeEach(async ({ page: browserPage }) => {
|
|
47
|
+
page = new PageObject(browserPage);
|
|
48
|
+
// Login and setup
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('Feature: Specific scenario', async () => {
|
|
52
|
+
await test.step('Fill form with data', async () => {
|
|
53
|
+
// Arrange + Act
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
await test.step('Navigate and verify', async () => {
|
|
57
|
+
// Act
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
await test.step('Validate results', async () => {
|
|
61
|
+
// Assert
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Selector Priorities
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// 1. data-testid (PREFERRED)
|
|
71
|
+
this.page.locator('[data-testid="task-add-button"]')
|
|
72
|
+
|
|
73
|
+
// 2. ID (if unique)
|
|
74
|
+
this.page.locator('#taskTitle')
|
|
75
|
+
|
|
76
|
+
// 3. Semantic attributes
|
|
77
|
+
this.page.locator('input[type="email"]')
|
|
78
|
+
|
|
79
|
+
// 4. CSS classes (LAST RESORT)
|
|
80
|
+
this.page.locator('.btn-primary') // Avoid
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Page Object Model (POM)
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { Page, Locator, expect } from '@playwright/test';
|
|
87
|
+
|
|
88
|
+
export class TasksPage {
|
|
89
|
+
protected page: Page;
|
|
90
|
+
|
|
91
|
+
private readonly addButton: Locator;
|
|
92
|
+
private readonly listTable: Locator;
|
|
93
|
+
private readonly searchInput: Locator;
|
|
94
|
+
|
|
95
|
+
constructor(page: Page) {
|
|
96
|
+
this.page = page;
|
|
97
|
+
this.addButton = this.page.locator('[data-testid="task-add-button"]');
|
|
98
|
+
this.listTable = this.page.locator('[data-testid="task-list-table"]');
|
|
99
|
+
this.searchInput = this.page.locator('[data-testid="task-search-input"]');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async visit(): Promise<void> {
|
|
103
|
+
await this.page.goto('/tasks');
|
|
104
|
+
await this.page.waitForLoadState('networkidle');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async addClick(): Promise<void> {
|
|
108
|
+
await this.addButton.waitFor({ state: 'visible', timeout: 10000 });
|
|
109
|
+
await this.addButton.click();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async search(name: string): Promise<void> {
|
|
113
|
+
await this.searchInput.fill(name);
|
|
114
|
+
await this.searchInput.press('Enter');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async shouldBeOnList(name: string): Promise<void> {
|
|
118
|
+
const row = this.listTable.locator('[data-testid*="task-row"]', { hasText: name });
|
|
119
|
+
await expect(row).toBeVisible({ timeout: 10000 });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### POM Rules
|
|
125
|
+
- Locators **PRIVATE** - don't export directly
|
|
126
|
+
- Methods **PUBLIC** - only actions and validations
|
|
127
|
+
- Use getters for dynamic locators
|
|
128
|
+
- Always add timeout to `waitFor()`
|
|
129
|
+
- Use `expect()` inside validation methods
|
|
130
|
+
|
|
131
|
+
## Timeouts and Waiting
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// PREFERRED: waitFor with specific state
|
|
135
|
+
await element.waitFor({ state: 'visible', timeout: 10000 });
|
|
136
|
+
|
|
137
|
+
// Wait for URL
|
|
138
|
+
await page.waitForURL('**/tasks/**', { timeout: 15000 });
|
|
139
|
+
|
|
140
|
+
// Wait for network
|
|
141
|
+
await page.waitForLoadState('networkidle');
|
|
142
|
+
|
|
143
|
+
// Wait for response
|
|
144
|
+
const responsePromise = page.waitForResponse('**/api/tasks/**');
|
|
145
|
+
await button.click();
|
|
146
|
+
const response = await responsePromise;
|
|
147
|
+
|
|
148
|
+
// AVOID: Only when no alternative
|
|
149
|
+
await page.waitForTimeout(2000);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Assertions
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
await expect(page).toHaveURL(/.*tasks/);
|
|
156
|
+
await expect(element).toBeVisible();
|
|
157
|
+
await expect(element).toBeDisabled();
|
|
158
|
+
await expect(element).toHaveText('Done');
|
|
159
|
+
await expect(element).toHaveCount(5);
|
|
160
|
+
|
|
161
|
+
// Soft assertions (don't stop test)
|
|
162
|
+
await expect.soft(element).toBeVisible();
|
|
163
|
+
|
|
164
|
+
// Custom messages
|
|
165
|
+
await expect(element, 'Task should be visible').toBeVisible();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Test Tags
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
{ tag: ['@module', '@verified'] }
|
|
172
|
+
{ tag: ['@module', '@smoke'] }
|
|
173
|
+
{ tag: ['@module', '@regression'] }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## What to AVOID
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// Hardcoded URLs
|
|
180
|
+
await page.goto('https://example.com/tasks'); // Use baseURL
|
|
181
|
+
|
|
182
|
+
// Hardcoded data
|
|
183
|
+
const username = 'user@example.com'; // Use fixtures
|
|
184
|
+
|
|
185
|
+
// CSS selectors first
|
|
186
|
+
await page.locator('.btn-primary').click(); // Use data-testid
|
|
187
|
+
|
|
188
|
+
// test.only() in commits
|
|
189
|
+
test.only('should work', async () => {}); // Remove before commit
|
|
190
|
+
|
|
191
|
+
// Code duplication
|
|
192
|
+
await page.locator('[data-testid="btn"]').click(); // Use POM
|
|
193
|
+
|
|
194
|
+
// Long timeouts without reason
|
|
195
|
+
await page.waitForTimeout(10000); // Use deterministic waits
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Pre-Commit Checklist
|
|
199
|
+
|
|
200
|
+
- [ ] All tests pass locally
|
|
201
|
+
- [ ] No `console.log()` or `debugger`
|
|
202
|
+
- [ ] No `test.only()`
|
|
203
|
+
- [ ] All `data-testid` follow kebab-case convention
|
|
204
|
+
- [ ] POMs follow structure
|
|
205
|
+
- [ ] Tags added correctly
|
|
206
|
+
- [ ] TypeScript compiles without errors
|
|
207
|
+
- [ ] Imports organized
|
|
208
|
+
|
|
209
|
+
## Running Tests
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
cd {{PLAYWRIGHT_DIR}} && \
|
|
213
|
+
BASE_URL="{{BASE_URL}}" \
|
|
214
|
+
npx playwright test tests/path/to/test.spec.ts \
|
|
215
|
+
--project=chromium --workers=1 --retries=0
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
- Use `--workers=1 --retries=0` for debugging
|
|
219
|
+
- Use `--workers=2 --retries=1` for confirmed tests
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# Playwright Testing Skill
|
|
2
|
+
|
|
3
|
+
**Expert guidance for running, debugging, and refactoring EXISTING Playwright E2E tests**
|
|
4
|
+
|
|
5
|
+
## What This Skill Does
|
|
6
|
+
|
|
7
|
+
This skill helps with **EXISTING tests** - running, debugging, analyzing, and refactoring already written Playwright tests.
|
|
8
|
+
|
|
9
|
+
## Quick Facts
|
|
10
|
+
|
|
11
|
+
| Aspect | Details |
|
|
12
|
+
|--------|---------|
|
|
13
|
+
| **Purpose** | Work with EXISTING tests (already written) |
|
|
14
|
+
| **Input** | Existing test file (TypeScript) |
|
|
15
|
+
| **Output** | Test results, debugging info, refactored code |
|
|
16
|
+
| **Use cases** | Run tests, debug failures, refactor code, analyze coverage |
|
|
17
|
+
|
|
18
|
+
## When to Use This Skill
|
|
19
|
+
|
|
20
|
+
**Use `playwright` when**:
|
|
21
|
+
- **Converting recorded test from `tmp/`** (after `playwright-record`)
|
|
22
|
+
- Running existing tests
|
|
23
|
+
- Debugging test failures
|
|
24
|
+
- Refactoring test code
|
|
25
|
+
- Analyzing test results
|
|
26
|
+
- Improving test quality
|
|
27
|
+
- Test already exists in `{{PLAYWRIGHT_DIR}}/tests/` or `{{PLAYWRIGHT_DIR}}/tmp/`
|
|
28
|
+
|
|
29
|
+
**Use `playwright-record` skill instead when**:
|
|
30
|
+
- Need to RECORD a NEW test by clicking in browser
|
|
31
|
+
- No test exists yet
|
|
32
|
+
- Want to capture user flow
|
|
33
|
+
|
|
34
|
+
## Converting Tests from tmp/
|
|
35
|
+
|
|
36
|
+
**CRITICAL**: When converting recorded tests, this skill:
|
|
37
|
+
|
|
38
|
+
1. **Checks existing Page Objects FIRST** (no duplication!)
|
|
39
|
+
2. Reuses existing POMs (LoginPage, DashboardPage, etc.)
|
|
40
|
+
3. Creates NEW POMs only when needed
|
|
41
|
+
4. Converts JavaScript to TypeScript
|
|
42
|
+
5. Adds proper assertions
|
|
43
|
+
6. Moves to production `tests/` directory
|
|
44
|
+
7. **RUNS test to verify it works** (using Makefile or npx)
|
|
45
|
+
8. **DEBUGS failures** if test doesn't pass
|
|
46
|
+
9. **FIXES issues** in Page Objects or test code
|
|
47
|
+
10. **RE-RUNS** until test passes
|
|
48
|
+
|
|
49
|
+
**Example**:
|
|
50
|
+
```
|
|
51
|
+
User: "Convert tmp/playwright-test-mytest.js"
|
|
52
|
+
|
|
53
|
+
AI Process:
|
|
54
|
+
1. Check existing POMs -> LoginPage exists
|
|
55
|
+
2. Reuse LoginPage + Create MyFeaturePage
|
|
56
|
+
3. Convert to TypeScript test
|
|
57
|
+
4. Move to tests/NN_Module/NN_NN_NN_Name.spec.ts
|
|
58
|
+
5. RUN: {{RUN_TEST_COMMAND}} playwright_spec_file="tests/.../test.spec.ts"
|
|
59
|
+
6. IF FAILS: Check screenshot, analyze error
|
|
60
|
+
7. FIX: Update selectors/waits in Page Object
|
|
61
|
+
8. RE-RUN: Until test passes
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Test Verification Methods
|
|
65
|
+
|
|
66
|
+
**After converting test, ALWAYS verify using one of these methods:**
|
|
67
|
+
|
|
68
|
+
### Method 1: Makefile + Docker (Recommended)
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Run specific test
|
|
72
|
+
{{RUN_TEST_COMMAND}} playwright_spec_file="{{PLAYWRIGHT_DIR}}/tests/{{MODULE_PATH}}/{{TEST_FILE}}.spec.ts"
|
|
73
|
+
|
|
74
|
+
# Run with tags
|
|
75
|
+
{{RUN_TEST_COMMAND}} playwright_grep_tags="@{{MODULE_TAG}}"
|
|
76
|
+
|
|
77
|
+
# Run with retries disabled (for debugging)
|
|
78
|
+
{{RUN_TEST_COMMAND}} \
|
|
79
|
+
playwright_spec_file="{{PLAYWRIGHT_DIR}}/tests/{{MODULE_PATH}}/{{TEST_FILE}}.spec.ts" \
|
|
80
|
+
playwright_retries="0"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Method 2: Local npx (Development)
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
cd {{PLAYWRIGHT_DIR}}
|
|
87
|
+
|
|
88
|
+
# Run test
|
|
89
|
+
npx playwright test tests/{{MODULE_PATH}}/{{TEST_FILE}}.spec.ts
|
|
90
|
+
|
|
91
|
+
# Debug mode (step through)
|
|
92
|
+
npx playwright test tests/{{MODULE_PATH}}/{{TEST_FILE}}.spec.ts --debug
|
|
93
|
+
|
|
94
|
+
# UI mode (interactive)
|
|
95
|
+
npx playwright test tests/{{MODULE_PATH}}/{{TEST_FILE}}.spec.ts --ui
|
|
96
|
+
|
|
97
|
+
# Headed mode (see browser)
|
|
98
|
+
npx playwright test tests/{{MODULE_PATH}}/{{TEST_FILE}}.spec.ts --headed
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Debugging Failed Tests
|
|
102
|
+
|
|
103
|
+
**When test fails:**
|
|
104
|
+
|
|
105
|
+
1. **Check artifacts:**
|
|
106
|
+
- Screenshots: `{{PLAYWRIGHT_DIR}}/test-results/[test-name]/test-failed-1.png`
|
|
107
|
+
- HTML Report: `npx playwright show-report` (in {{PLAYWRIGHT_DIR}}/)
|
|
108
|
+
|
|
109
|
+
2. **Analyze error:**
|
|
110
|
+
- Timeout -> Add `waitFor({ state: 'visible' })`
|
|
111
|
+
- Selector not found -> Check data-testId in screenshot
|
|
112
|
+
- Navigation issue -> Add waits between steps
|
|
113
|
+
|
|
114
|
+
3. **Fix code:**
|
|
115
|
+
- Update Page Object methods
|
|
116
|
+
- Add proper waits
|
|
117
|
+
- Fix selectors
|
|
118
|
+
|
|
119
|
+
4. **Re-run until passes:**
|
|
120
|
+
```bash
|
|
121
|
+
{{RUN_TEST_COMMAND}} playwright_spec_file="tests/..."
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Key Capabilities
|
|
125
|
+
|
|
126
|
+
### Test Execution
|
|
127
|
+
- Run tests with specific tags
|
|
128
|
+
- Run tests in specific browsers
|
|
129
|
+
- Run tests in headed/headless mode
|
|
130
|
+
- Generate reports (HTML, JSON, JUnit)
|
|
131
|
+
|
|
132
|
+
### Debugging
|
|
133
|
+
- Analyze test failures
|
|
134
|
+
- Identify flaky tests
|
|
135
|
+
- Fix selector issues
|
|
136
|
+
- Adjust timeouts and waits
|
|
137
|
+
|
|
138
|
+
### Refactoring
|
|
139
|
+
- Extract Page Objects
|
|
140
|
+
- Create reusable helpers
|
|
141
|
+
- Optimize selectors
|
|
142
|
+
- Add missing assertions
|
|
143
|
+
- Improve test structure
|
|
144
|
+
|
|
145
|
+
### Analysis
|
|
146
|
+
- Coverage reports
|
|
147
|
+
- Performance metrics
|
|
148
|
+
- Test reliability analysis
|
|
149
|
+
|
|
150
|
+
## Difference from `playwright-record`
|
|
151
|
+
|
|
152
|
+
| Aspect | `playwright` (THIS) | `playwright-record` |
|
|
153
|
+
|--------|---------------------|---------------------|
|
|
154
|
+
| **Purpose** | Work with EXISTING tests | CREATE NEW tests by recording |
|
|
155
|
+
| **Input** | Test file (TypeScript) | User clicks in browser |
|
|
156
|
+
| **Process** | Execute -> Debug -> Refactor | Record -> Convert -> Deploy |
|
|
157
|
+
| **Output** | Test results, fixes | Production-ready test from clicks |
|
|
158
|
+
| **User involvement** | None (or specify changes) | Clicking during recording |
|
|
159
|
+
|
|
160
|
+
## Documentation
|
|
161
|
+
|
|
162
|
+
**[SKILL.md](./SKILL.md)** - Complete documentation with:
|
|
163
|
+
- Page Object Model patterns
|
|
164
|
+
- Test organization structure
|
|
165
|
+
- Selector strategies (data-testId)
|
|
166
|
+
- Fixture management
|
|
167
|
+
- Tagging strategies
|
|
168
|
+
- Best practices
|
|
169
|
+
|
|
170
|
+
## Quick Examples
|
|
171
|
+
|
|
172
|
+
### Run specific test
|
|
173
|
+
```bash
|
|
174
|
+
npx playwright test tests/{{MODULE_PATH}}/
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Debug failing test
|
|
178
|
+
```bash
|
|
179
|
+
npx playwright test tests/{{MODULE_PATH}}/ --debug
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Run with specific tag
|
|
183
|
+
```bash
|
|
184
|
+
npx playwright test --grep @smoke
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Generate HTML report
|
|
188
|
+
```bash
|
|
189
|
+
npx playwright test --reporter=html
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
**Remember**: Use this skill for EXISTING tests. For NEW tests, use `playwright-record` skill!
|