@torus-engineering/tas-kit 1.6.0 → 1.8.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/.claude/commands/tas-adr.md +33 -29
- package/.claude/commands/tas-api-test.md +95 -0
- package/.claude/commands/tas-bug.md +113 -109
- package/.claude/commands/tas-design.md +37 -33
- package/.claude/commands/tas-dev.md +128 -115
- package/.claude/commands/tas-e2e-mobile.md +155 -0
- package/.claude/commands/tas-e2e-web.md +163 -0
- package/.claude/commands/tas-e2e.md +102 -0
- package/.claude/commands/tas-epic.md +35 -31
- package/.claude/commands/tas-feature.md +47 -43
- package/.claude/commands/tas-fix.md +51 -47
- package/.claude/commands/tas-functest-mobile.md +144 -0
- package/.claude/commands/tas-functest-web.md +192 -0
- package/.claude/commands/tas-functest.md +76 -0
- package/.claude/commands/tas-plan.md +200 -184
- package/.claude/commands/tas-prd.md +37 -33
- package/.claude/commands/tas-review.md +111 -104
- package/.claude/commands/tas-sad.md +43 -39
- package/.claude/commands/tas-security.md +81 -80
- package/.claude/commands/tas-story.md +91 -87
- package/.claude/commands/tas-verify.md +51 -41
- package/.claude/rules/common/post-review-agent.md +49 -39
- package/.claude/rules/common/testing.md +24 -0
- package/.claude/rules/common/token-logging.md +27 -0
- package/.claude/rules/csharp/api-testing.md +171 -0
- package/.claude/rules/csharp/patterns.md +10 -0
- package/.claude/rules/python/patterns.md +10 -0
- package/.claude/rules/typescript/patterns.md +10 -0
- package/.claude/rules/web/performance.md +9 -0
- package/.claude/skills/api-design/SKILL.md +3 -1
- package/.claude/skills/{backend-patterns → js-backend-patterns}/SKILL.md +2 -1
- package/.claude/skills/tas-implementation-complete/SKILL.md +99 -97
- package/.claude/skills/tas-tdd/SKILL.md +123 -82
- package/.claude/skills/token-logger/SKILL.md +19 -0
- package/.tas/templates/E2E-Execution-Report.md +198 -0
- package/.tas/templates/E2E-Mobile-Spec.md +130 -0
- package/.tas/templates/E2E-Report.md +174 -0
- package/.tas/templates/E2E-Scenario.md +180 -0
- package/.tas/templates/E2E-Web-Spec.md +164 -0
- package/.tas/templates/Feature.md +55 -55
- package/.tas/templates/Func-Test-Script.md +254 -0
- package/.tas/templates/Func-Test-Spec.md +187 -0
- package/.tas/templates/SAD.md +274 -64
- package/.tas/templates/Story.md +90 -88
- package/bin/cli.js +56 -49
- package/lib/deleted-files.json +33 -0
- package/lib/install.js +213 -114
- package/package.json +34 -34
- package/.claude/agents/README.md +0 -83
- package/.claude/agents/ado-agent.md +0 -39
- package/.claude/agents/code-architect.md +0 -62
- package/.claude/agents/code-simplifier.md +0 -53
- package/.claude/agents/comment-analyzer.md +0 -59
- package/.claude/agents/conversation-analyzer.md +0 -57
- package/.claude/agents/docs-lookup.md +0 -55
- package/.claude/agents/harness-optimizer.md +0 -62
- package/.claude/agents/loop-operator.md +0 -56
- package/.claude/agents/performance-optimizer.md +0 -78
- package/.claude/agents/pr-test-analyzer.md +0 -68
- package/.claude/agents/pytorch-build-resolver.md +0 -76
- package/.claude/agents/refactor-cleaner.md +0 -70
- package/.claude/agents/seo-specialist.md +0 -75
- package/.claude/agents/silent-failure-hunter.md +0 -69
- package/.claude/agents/type-design-analyzer.md +0 -75
- package/.claude/rules/common/agents.md +0 -65
- package/.claude/rules/common/coding-style.md +0 -90
- package/.claude/rules/common/development-workflow.md +0 -44
- package/.claude/rules/common/git-workflow.md +0 -24
- package/.claude/rules/common/performance.md +0 -55
- package/.claude/skills/agent-harness-construction/SKILL.md +0 -77
- package/.claude/skills/agent-introspection-debugging/SKILL.md +0 -157
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
created_date:
|
|
3
|
+
updated_date:
|
|
4
|
+
executor:
|
|
5
|
+
status: Draft
|
|
6
|
+
epic_id:
|
|
7
|
+
scenario_id:
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# E2E Web Test Specification: {Scenario Name}
|
|
11
|
+
|
|
12
|
+
**Scenario**: [{Scenario_ID}]({SCENARIO_LINK})
|
|
13
|
+
**Epic(s)**: [{Epic_ID}]({Epic_LINK})
|
|
14
|
+
**Author**: @[executor]
|
|
15
|
+
**Created**: [created_date]
|
|
16
|
+
**Status**: [status] (Draft | Ready | Implemented | Verified)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Test Framework
|
|
21
|
+
|
|
22
|
+
- **Framework**: Playwright
|
|
23
|
+
- **Config**: `apps/web/playwright.config.ts`
|
|
24
|
+
- **Script Location**: `apps/web/e2e/flows/{scenario-slug}.spec.ts`
|
|
25
|
+
- **Run Command**: `yarn e2e:flow:{scenario-slug}`
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Browser Matrix
|
|
30
|
+
|
|
31
|
+
| Browser | Engine | Version | Required |
|
|
32
|
+
|---------|--------|---------|----------|
|
|
33
|
+
| Chrome | Chromium | Latest | Yes |
|
|
34
|
+
| Firefox | Gecko | Latest | Yes |
|
|
35
|
+
| Safari | WebKit | Latest | Yes (via Playwright) |
|
|
36
|
+
| Edge | Chromium | Latest | No (covered by Chromium) |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Viewport Testing
|
|
41
|
+
|
|
42
|
+
| Viewport | Width | Height | Test Required |
|
|
43
|
+
|----------|-------|--------|---------------|
|
|
44
|
+
| Mobile | 375px | 812px | Yes |
|
|
45
|
+
| Tablet | 768px | 1024px | Yes |
|
|
46
|
+
| Desktop | 1280px | 720px | Yes |
|
|
47
|
+
| Large Desktop | 1920px | 1080px | Optional |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Test Cases
|
|
52
|
+
|
|
53
|
+
### Main Flow
|
|
54
|
+
|
|
55
|
+
| Test ID | Step | Page | Action | Expected | Browser | Viewport |
|
|
56
|
+
|---------|------|------|--------|----------|---------|----------|
|
|
57
|
+
| {PROJECT}_E{EPIC}_E2E_001_H | 1 | {Page} | {Action} | {Expected} | All | All |
|
|
58
|
+
| {PROJECT}_E{EPIC}_E2E_001_H | 2 | {Page} | {Action} | {Expected} | All | All |
|
|
59
|
+
|
|
60
|
+
### Cross-Browser Specific
|
|
61
|
+
|
|
62
|
+
| Test ID | Browser | Issue | Test Action | Expected |
|
|
63
|
+
|---------|---------|-------|-------------|----------|
|
|
64
|
+
| {ID}_BROWSER_001 | Firefox | {Known issue} | {Action} | {Expected} |
|
|
65
|
+
| {ID}_BROWSER_002 | WebKit | {Known issue} | {Action} | {Expected} |
|
|
66
|
+
|
|
67
|
+
### Responsive Specific
|
|
68
|
+
|
|
69
|
+
| Test ID | Viewport | Component | Test Action | Expected |
|
|
70
|
+
|---------|----------|-----------|-------------|----------|
|
|
71
|
+
| {ID}_RESP_001 | Mobile | {Nav menu} | {Hamburger menu test} | {Expected} |
|
|
72
|
+
| {ID}_RESP_002 | Tablet | {Layout} | {Grid layout test} | {Expected} |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Playwright Configuration
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// playwright.config.ts additions for this scenario
|
|
80
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
81
|
+
|
|
82
|
+
export default defineConfig({
|
|
83
|
+
testDir: './e2e',
|
|
84
|
+
timeout: 30000,
|
|
85
|
+
retries: 1,
|
|
86
|
+
projects: [
|
|
87
|
+
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
88
|
+
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
|
|
89
|
+
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
|
|
90
|
+
{ name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
|
|
91
|
+
{ name: 'mobile-safari', use: { ...devices['iPhone 13'] } },
|
|
92
|
+
],
|
|
93
|
+
reporter: [
|
|
94
|
+
['html', { outputFolder: 'reports/e2e' }],
|
|
95
|
+
['junit', { outputFile: 'reports/e2e-results.xml' }],
|
|
96
|
+
],
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Selectors Strategy
|
|
103
|
+
|
|
104
|
+
| Element Type | Selector Pattern | Example |
|
|
105
|
+
|-------------|-----------------|---------|
|
|
106
|
+
| Interactive | `data-testid` | `page.getByTestId('login-submit')` |
|
|
107
|
+
| Text | `getByText` | `page.getByText('Welcome')` |
|
|
108
|
+
| Role | `getByRole` | `page.getByRole('button', { name: 'Submit' })` |
|
|
109
|
+
| Label | `getByLabel` | `page.getByLabel('Email')` |
|
|
110
|
+
|
|
111
|
+
> Prefer `data-testid` for stability. Use semantic selectors (role, label) for accessibility validation.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Network Handling
|
|
116
|
+
|
|
117
|
+
| Scenario | Mock/Real | Handler |
|
|
118
|
+
|----------|-----------|---------|
|
|
119
|
+
| API calls | Mock (route intercept) | `page.route('/api/**', handler)` |
|
|
120
|
+
| Auth token | From .env | `process.env.TEST_AUTH_TOKEN` |
|
|
121
|
+
| Slow network | Simulated | `page.route('**', route => setTimeout(() => route.continue(), 3000))` |
|
|
122
|
+
| Offline | Simulated | `context.setOffline(true)` |
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Accessibility Checks
|
|
127
|
+
|
|
128
|
+
- [ ] All interactive elements are keyboard accessible
|
|
129
|
+
- [ ] ARIA labels present on custom components
|
|
130
|
+
- [ ] Color contrast meets WCAG AA
|
|
131
|
+
- [ ] Screen reader navigation works for main flow
|
|
132
|
+
- [ ] Focus management correct during navigation
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Performance Budgets
|
|
137
|
+
|
|
138
|
+
| Metric | Target | Measurement |
|
|
139
|
+
|--------|--------|-------------|
|
|
140
|
+
| Page Load (LCP) | < 2.5s | Playwright metrics |
|
|
141
|
+
| Interaction (INP) | < 200ms | Playwright metrics |
|
|
142
|
+
| Layout Shift (CLS) | < 0.1 | Playwright metrics |
|
|
143
|
+
| JS Bundle | < 300KB | Build output |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Success Criteria
|
|
148
|
+
|
|
149
|
+
- [ ] All test cases pass on Chromium
|
|
150
|
+
- [ ] All test cases pass on Firefox
|
|
151
|
+
- [ ] All test cases pass on WebKit
|
|
152
|
+
- [ ] All viewport tests pass (mobile, tablet, desktop)
|
|
153
|
+
- [ ] No flaky tests (100% pass on 3 runs)
|
|
154
|
+
- [ ] Total execution time < {X} minutes
|
|
155
|
+
- [ ] Accessibility checks pass
|
|
156
|
+
- [ ] Performance budgets met
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Changelog
|
|
161
|
+
|
|
162
|
+
| Date | Changes | Author |
|
|
163
|
+
|------|---------|--------|
|
|
164
|
+
| [created_date] | Initial web E2E spec created | @[executor] |
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
---
|
|
2
|
-
ado_id:
|
|
3
|
-
ado_type: Feature
|
|
4
|
-
ado_title: "{Title}"
|
|
5
|
-
ado_state: New
|
|
6
|
-
ado_assigned_to:
|
|
7
|
-
ado_created:
|
|
8
|
-
last_ado_sync:
|
|
9
|
-
parent_ado_id:
|
|
10
|
-
---
|
|
11
|
-
# Feature-{NNN}: {Title}
|
|
12
|
-
|
|
13
|
-
> **Status:** New | Pending | In Design | In Progress | Ready for Dev | In Development | Ready To Verify | Verified | Done | Removed
|
|
14
|
-
> **Epic:** Epic-{NNN}
|
|
15
|
-
> **Owner:** {PE name}
|
|
16
|
-
> **Created:** {Date}
|
|
17
|
-
> **Verified Date:** {Date khi status = Verified}
|
|
18
|
-
|
|
19
|
-
## Description
|
|
20
|
-
{Mô tả chức năng}
|
|
21
|
-
|
|
22
|
-
## User Stories
|
|
23
|
-
| ID | Story | Priority | Estimate | Status |
|
|
24
|
-
|----|-------|----------|----------|--------|
|
|
25
|
-
| Story-001 | {name} | Must | S | New |
|
|
26
|
-
|
|
27
|
-
## Acceptance Criteria
|
|
28
|
-
- [ ] AC-1: {criteria}
|
|
29
|
-
- [ ] AC-2: {criteria}
|
|
30
|
-
|
|
31
|
-
## UI/UX Notes
|
|
32
|
-
{Nếu có, reference đến design-spec.md}
|
|
33
|
-
|
|
34
|
-
## Technical Notes
|
|
35
|
-
{Nếu có, reference đến SAD/ADR}
|
|
36
|
-
|
|
37
|
-
## Integration Test Cases
|
|
38
|
-
PE thiết kế khi tạo Feature. SE implement trong code.
|
|
39
|
-
|
|
40
|
-
| ID | Scenario | Expected Result | Status |
|
|
41
|
-
|
|
42
|
-
| IT-1 | {Mô tả flow liên kết} | {Kết quả mong đợi} | - |
|
|
43
|
-
| IT-2 | {Mô tả flow liên kết} | {Kết quả mong đợi} | - |
|
|
44
|
-
|
|
45
|
-
## E2E / Acceptance Test Cases
|
|
46
|
-
PE thiết kế khi tạo Feature. PE verify trên Staging ở Phase 2 bằng /tas-verify.
|
|
47
|
-
|
|
48
|
-
| ID | User Scenario | Steps | Expected Result | Status | Verified Date |
|
|
49
|
-
|
|
50
|
-
| E2E-1 | {Scenario} | {Bước thực hiện} | {Kết quả mong đợi} | - | - |
|
|
51
|
-
| E2E-2 | {Scenario} | {Bước thực hiện} | {Kết quả mong đợi} | - | - |
|
|
52
|
-
|
|
53
|
-
## Changelog
|
|
54
|
-
| Date | Changes | Author |
|
|
55
|
-
|------|---------|--------|
|
|
1
|
+
---
|
|
2
|
+
ado_id:
|
|
3
|
+
ado_type: Feature
|
|
4
|
+
ado_title: "{Title}"
|
|
5
|
+
ado_state: New
|
|
6
|
+
ado_assigned_to:
|
|
7
|
+
ado_created:
|
|
8
|
+
last_ado_sync:
|
|
9
|
+
parent_ado_id:
|
|
10
|
+
---
|
|
11
|
+
# Feature-{NNN}: {Title}
|
|
12
|
+
|
|
13
|
+
> **Status:** New | Pending | In Design | In Progress | Ready for Dev | In Development | Ready To Verify | Verified | Done | Removed
|
|
14
|
+
> **Epic:** Epic-{NNN}
|
|
15
|
+
> **Owner:** {PE name}
|
|
16
|
+
> **Created:** {Date}
|
|
17
|
+
> **Verified Date:** {Date khi status = Verified}
|
|
18
|
+
|
|
19
|
+
## Description
|
|
20
|
+
{Mô tả chức năng}
|
|
21
|
+
|
|
22
|
+
## User Stories
|
|
23
|
+
| ID | Story | Priority | Estimate | Status |
|
|
24
|
+
|----|-------|----------|----------|--------|
|
|
25
|
+
| Story-001 | {name} | Must | S | New |
|
|
26
|
+
|
|
27
|
+
## Acceptance Criteria
|
|
28
|
+
- [ ] AC-1: {criteria}
|
|
29
|
+
- [ ] AC-2: {criteria}
|
|
30
|
+
|
|
31
|
+
## UI/UX Notes
|
|
32
|
+
{Nếu có, reference đến design-spec.md}
|
|
33
|
+
|
|
34
|
+
## Technical Notes
|
|
35
|
+
{Nếu có, reference đến SAD/ADR}
|
|
36
|
+
|
|
37
|
+
## Integration Test Cases
|
|
38
|
+
PE thiết kế khi tạo Feature. SE implement trong code.
|
|
39
|
+
|
|
40
|
+
| ID | AC Ref | Scenario | Expected Result | Status |
|
|
41
|
+
|----|--------|----------|-----------------|--------|
|
|
42
|
+
| IT-1 | AC-1 | {Mô tả flow liên kết} | {Kết quả mong đợi} | - |
|
|
43
|
+
| IT-2 | AC-2 | {Mô tả flow liên kết} | {Kết quả mong đợi} | - |
|
|
44
|
+
|
|
45
|
+
## E2E / Acceptance Test Cases
|
|
46
|
+
PE thiết kế khi tạo Feature. PE verify trên Staging ở Phase 2 bằng /tas-verify.
|
|
47
|
+
|
|
48
|
+
| ID | AC Ref | User Scenario | Steps | Expected Result | Status | Verified Date |
|
|
49
|
+
|----|--------|---------------|-------|-----------------|--------|---------------|
|
|
50
|
+
| E2E-1 | AC-1 | {Scenario} | {Bước thực hiện} | {Kết quả mong đợi} | - | - |
|
|
51
|
+
| E2E-2 | AC-2 | {Scenario} | {Bước thực hiện} | {Kết quả mong đợi} | - | - |
|
|
52
|
+
|
|
53
|
+
## Changelog
|
|
54
|
+
| Date | Changes | Author |
|
|
55
|
+
|------|---------|--------|
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Functional Test Script Guide
|
|
2
|
+
|
|
3
|
+
> This document defines the conventions for generated functional test scripts.
|
|
4
|
+
> Used by `/tas-functest-mobile` and `/tas-functest-web` commands.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## File Structure Convention
|
|
9
|
+
|
|
10
|
+
### Mobile (Detox)
|
|
11
|
+
```
|
|
12
|
+
apps/mobile/e2e/
|
|
13
|
+
├── features/ # Layer 2: Functional test scripts
|
|
14
|
+
│ ├── {epic-slug}/
|
|
15
|
+
│ │ ├── {feature-slug}/
|
|
16
|
+
│ │ │ ├── {story-slug}.func.e2e.ts # One file per story
|
|
17
|
+
│ │ │ ├── helpers.ts # Feature-specific helpers (reusable by E2E)
|
|
18
|
+
│ │ │ └── index.ts # Barrel export
|
|
19
|
+
│ │ └── ...
|
|
20
|
+
│ └── ...
|
|
21
|
+
├── flows/ # Layer 3: E2E scripts (separate)
|
|
22
|
+
├── data/ # Test data per environment
|
|
23
|
+
│ ├── test-data.dev.json
|
|
24
|
+
│ ├── test-data.staging.json
|
|
25
|
+
│ └── test-data.prod.json
|
|
26
|
+
├── helpers/ # Shared helpers
|
|
27
|
+
│ ├── data-loader.ts
|
|
28
|
+
│ └── test-utils.ts
|
|
29
|
+
└── test-ids.ts # Centralized test ID registry
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Web (Playwright)
|
|
33
|
+
```
|
|
34
|
+
apps/web/e2e/
|
|
35
|
+
├── features/ # Layer 2: Functional test scripts
|
|
36
|
+
│ ├── {epic-slug}/
|
|
37
|
+
│ │ ├── {feature-slug}/
|
|
38
|
+
│ │ │ ├── {story-slug}.func.spec.ts # One file per story
|
|
39
|
+
│ │ │ ├── helpers.ts
|
|
40
|
+
│ │ │ └── index.ts
|
|
41
|
+
│ │ └── ...
|
|
42
|
+
│ └── ...
|
|
43
|
+
├── flows/ # Layer 3: E2E scripts
|
|
44
|
+
├── data/
|
|
45
|
+
│ ├── test-data.dev.json
|
|
46
|
+
│ ├── test-data.staging.json
|
|
47
|
+
│ └── test-data.prod.json
|
|
48
|
+
├── helpers/
|
|
49
|
+
│ ├── data-loader.ts
|
|
50
|
+
│ └── test-utils.ts
|
|
51
|
+
└── page-objects/ # Playwright page objects
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## File Naming Convention
|
|
57
|
+
|
|
58
|
+
| Layer | Platform | Suffix | Example |
|
|
59
|
+
|-------|----------|--------|---------|
|
|
60
|
+
| Functional | Mobile | `.func.e2e.ts` | `login-form.func.e2e.ts` |
|
|
61
|
+
| Functional | Web | `.func.spec.ts` | `login-form.func.spec.ts` |
|
|
62
|
+
| E2E Flow | Mobile | `.e2e.ts` | `auth-complete-flow.e2e.ts` |
|
|
63
|
+
| E2E Flow | Web | `.spec.ts` | `auth-complete-flow.spec.ts` |
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Script Template: Mobile (Detox)
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
/**
|
|
71
|
+
* Functional Tests: {Story Name}
|
|
72
|
+
* Story: {Story_ID}
|
|
73
|
+
* Feature: {Feature_ID}
|
|
74
|
+
* Epic: {Epic_ID}
|
|
75
|
+
*
|
|
76
|
+
* Generated by /tas-functest-mobile
|
|
77
|
+
* Spec: docs/epics/{epic-dir}/{feature-dir}/Func-Test-{story-slug}.md
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
import { device, element, expect, by, waitFor } from 'detox';
|
|
81
|
+
import { TEST_IDS } from '../../test-ids';
|
|
82
|
+
import { loadTestData, getCredentials } from '../../helpers/data-loader';
|
|
83
|
+
import { login, navigateTo } from '../../helpers/test-utils';
|
|
84
|
+
|
|
85
|
+
const testData = loadTestData();
|
|
86
|
+
|
|
87
|
+
describe('{Feature Name} - {Story Name}', () => {
|
|
88
|
+
beforeAll(async () => {
|
|
89
|
+
await device.launchApp({ newInstance: true });
|
|
90
|
+
// Setup: login, navigate to target screen, etc.
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
beforeEach(async () => {
|
|
94
|
+
await device.reloadReactNative();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// AC Reference: AC-1
|
|
98
|
+
describe('{PROJECT}_E{EPIC}_F{FEATURE}_S{STORY}_FT_001_H', () => {
|
|
99
|
+
it('should {happy path description}', async () => {
|
|
100
|
+
// Given: {precondition}
|
|
101
|
+
// When: {action}
|
|
102
|
+
await element(by.id(TEST_IDS.FEATURE.ELEMENT)).tap();
|
|
103
|
+
// Then: {expected}
|
|
104
|
+
await expect(element(by.id(TEST_IDS.FEATURE.RESULT))).toBeVisible();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// AC Reference: AC-1
|
|
109
|
+
describe('{PROJECT}_E{EPIC}_F{FEATURE}_S{STORY}_FT_002_N', () => {
|
|
110
|
+
it('should {negative scenario description}', async () => {
|
|
111
|
+
// Given: {precondition}
|
|
112
|
+
// When: {invalid action}
|
|
113
|
+
await element(by.id(TEST_IDS.FEATURE.INPUT)).typeText('invalid');
|
|
114
|
+
await element(by.id(TEST_IDS.FEATURE.SUBMIT)).tap();
|
|
115
|
+
// Then: {error handling}
|
|
116
|
+
await expect(element(by.id(TEST_IDS.FEATURE.ERROR))).toBeVisible();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Script Template: Web (Playwright)
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
/**
|
|
128
|
+
* Functional Tests: {Story Name}
|
|
129
|
+
* Story: {Story_ID}
|
|
130
|
+
* Feature: {Feature_ID}
|
|
131
|
+
* Epic: {Epic_ID}
|
|
132
|
+
*
|
|
133
|
+
* Generated by /tas-functest-web
|
|
134
|
+
* Spec: docs/epics/{epic-dir}/{feature-dir}/Func-Test-{story-slug}.md
|
|
135
|
+
*/
|
|
136
|
+
|
|
137
|
+
import { test, expect } from '@playwright/test';
|
|
138
|
+
import { loadTestData, getCredentials } from '../../helpers/data-loader';
|
|
139
|
+
|
|
140
|
+
const testData = loadTestData();
|
|
141
|
+
|
|
142
|
+
test.describe('{Feature Name} - {Story Name}', () => {
|
|
143
|
+
test.beforeEach(async ({ page }) => {
|
|
144
|
+
// Setup: login, navigate to target page, etc.
|
|
145
|
+
await page.goto('/target-page');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// AC Reference: AC-1
|
|
149
|
+
test.describe('{PROJECT}_E{EPIC}_F{FEATURE}_S{STORY}_FT_001_H', () => {
|
|
150
|
+
test('should {happy path description}', async ({ page }) => {
|
|
151
|
+
// Given: {precondition}
|
|
152
|
+
// When: {action}
|
|
153
|
+
await page.getByTestId('feature-element').click();
|
|
154
|
+
// Then: {expected}
|
|
155
|
+
await expect(page.getByTestId('feature-result')).toBeVisible();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Cross-viewport test
|
|
159
|
+
for (const viewport of [
|
|
160
|
+
{ width: 375, height: 812, name: 'mobile' },
|
|
161
|
+
{ width: 768, height: 1024, name: 'tablet' },
|
|
162
|
+
{ width: 1280, height: 720, name: 'desktop' },
|
|
163
|
+
]) {
|
|
164
|
+
test(`should work on ${viewport.name}`, async ({ page }) => {
|
|
165
|
+
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
|
166
|
+
// Test responsive behavior
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// AC Reference: AC-1
|
|
172
|
+
test.describe('{PROJECT}_E{EPIC}_F{FEATURE}_S{STORY}_FT_002_N', () => {
|
|
173
|
+
test('should {negative scenario description}', async ({ page }) => {
|
|
174
|
+
// Given: {precondition}
|
|
175
|
+
// When: {invalid action}
|
|
176
|
+
await page.getByTestId('feature-input').fill('invalid');
|
|
177
|
+
await page.getByTestId('feature-submit').click();
|
|
178
|
+
// Then: {error handling}
|
|
179
|
+
await expect(page.getByTestId('feature-error')).toBeVisible();
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Data Loading Pattern
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// Import data loader
|
|
191
|
+
import { loadTestData, getCredentials } from '../../helpers/data-loader';
|
|
192
|
+
|
|
193
|
+
// Load environment-specific data (reads TEST_ENV env var)
|
|
194
|
+
const testData = loadTestData();
|
|
195
|
+
// testData.users.default.email -> "e2e-user@yopmail.com" (from JSON)
|
|
196
|
+
|
|
197
|
+
// Load credentials (passwords from .env, never from JSON)
|
|
198
|
+
const creds = getCredentials();
|
|
199
|
+
// creds.email -> from JSON
|
|
200
|
+
// creds.password -> from process.env.TEST_USER_PASSWORD
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## describe/it Block Naming Convention
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// Top-level: Feature + Story name
|
|
209
|
+
describe('{Feature Name} - {Story Name}', () => {
|
|
210
|
+
// Mid-level: Full FT ID (enables grep by test ID)
|
|
211
|
+
describe('{PROJECT}_E{EPIC}_F{FEATURE}_S{STORY}_FT_001_H', () => {
|
|
212
|
+
// Leaf: Human-readable description
|
|
213
|
+
it('should {action} when {condition}', async () => { ... });
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Reusable Helpers
|
|
221
|
+
|
|
222
|
+
Feature-specific helpers should be exported for Layer 3 (E2E) reuse:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// features/{epic}/{feature}/helpers.ts
|
|
226
|
+
export async function fillLoginForm(email: string, password: string) {
|
|
227
|
+
await element(by.id(TEST_IDS.AUTH.LOGIN.EMAIL_INPUT)).typeText(email);
|
|
228
|
+
await element(by.id(TEST_IDS.AUTH.LOGIN.PASSWORD_INPUT)).typeText(password);
|
|
229
|
+
await element(by.id(TEST_IDS.AUTH.LOGIN.SUBMIT_BUTTON)).tap();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export async function verifyLoginSuccess() {
|
|
233
|
+
await waitFor(element(by.id(TEST_IDS.HOME.SCREEN)))
|
|
234
|
+
.toBeVisible()
|
|
235
|
+
.withTimeout(5000);
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
These helpers are imported by E2E flow scripts in `flows/` to avoid duplication.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## package.json Script Convention
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"scripts": {
|
|
248
|
+
"functest:mobile:{feature-slug}": "detox test --configuration ios.sim.debug --testPathPattern='features/{epic}/{feature}'",
|
|
249
|
+
"functest:web:{feature-slug}": "npx playwright test --grep '@{feature-slug}' e2e/features/{epic}/{feature}",
|
|
250
|
+
"functest:mobile:all": "detox test --configuration ios.sim.debug --testPathPattern='features/'",
|
|
251
|
+
"functest:web:all": "npx playwright test e2e/features/"
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|