@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.
Files changed (71) hide show
  1. package/.claude/commands/tas-adr.md +33 -29
  2. package/.claude/commands/tas-api-test.md +95 -0
  3. package/.claude/commands/tas-bug.md +113 -109
  4. package/.claude/commands/tas-design.md +37 -33
  5. package/.claude/commands/tas-dev.md +128 -115
  6. package/.claude/commands/tas-e2e-mobile.md +155 -0
  7. package/.claude/commands/tas-e2e-web.md +163 -0
  8. package/.claude/commands/tas-e2e.md +102 -0
  9. package/.claude/commands/tas-epic.md +35 -31
  10. package/.claude/commands/tas-feature.md +47 -43
  11. package/.claude/commands/tas-fix.md +51 -47
  12. package/.claude/commands/tas-functest-mobile.md +144 -0
  13. package/.claude/commands/tas-functest-web.md +192 -0
  14. package/.claude/commands/tas-functest.md +76 -0
  15. package/.claude/commands/tas-plan.md +200 -184
  16. package/.claude/commands/tas-prd.md +37 -33
  17. package/.claude/commands/tas-review.md +111 -104
  18. package/.claude/commands/tas-sad.md +43 -39
  19. package/.claude/commands/tas-security.md +81 -80
  20. package/.claude/commands/tas-story.md +91 -87
  21. package/.claude/commands/tas-verify.md +51 -41
  22. package/.claude/rules/common/post-review-agent.md +49 -39
  23. package/.claude/rules/common/testing.md +24 -0
  24. package/.claude/rules/common/token-logging.md +27 -0
  25. package/.claude/rules/csharp/api-testing.md +171 -0
  26. package/.claude/rules/csharp/patterns.md +10 -0
  27. package/.claude/rules/python/patterns.md +10 -0
  28. package/.claude/rules/typescript/patterns.md +10 -0
  29. package/.claude/rules/web/performance.md +9 -0
  30. package/.claude/skills/api-design/SKILL.md +3 -1
  31. package/.claude/skills/{backend-patterns → js-backend-patterns}/SKILL.md +2 -1
  32. package/.claude/skills/tas-implementation-complete/SKILL.md +99 -97
  33. package/.claude/skills/tas-tdd/SKILL.md +123 -82
  34. package/.claude/skills/token-logger/SKILL.md +19 -0
  35. package/.tas/templates/E2E-Execution-Report.md +198 -0
  36. package/.tas/templates/E2E-Mobile-Spec.md +130 -0
  37. package/.tas/templates/E2E-Report.md +174 -0
  38. package/.tas/templates/E2E-Scenario.md +180 -0
  39. package/.tas/templates/E2E-Web-Spec.md +164 -0
  40. package/.tas/templates/Feature.md +55 -55
  41. package/.tas/templates/Func-Test-Script.md +254 -0
  42. package/.tas/templates/Func-Test-Spec.md +187 -0
  43. package/.tas/templates/SAD.md +274 -64
  44. package/.tas/templates/Story.md +90 -88
  45. package/bin/cli.js +56 -49
  46. package/lib/deleted-files.json +33 -0
  47. package/lib/install.js +213 -114
  48. package/package.json +34 -34
  49. package/.claude/agents/README.md +0 -83
  50. package/.claude/agents/ado-agent.md +0 -39
  51. package/.claude/agents/code-architect.md +0 -62
  52. package/.claude/agents/code-simplifier.md +0 -53
  53. package/.claude/agents/comment-analyzer.md +0 -59
  54. package/.claude/agents/conversation-analyzer.md +0 -57
  55. package/.claude/agents/docs-lookup.md +0 -55
  56. package/.claude/agents/harness-optimizer.md +0 -62
  57. package/.claude/agents/loop-operator.md +0 -56
  58. package/.claude/agents/performance-optimizer.md +0 -78
  59. package/.claude/agents/pr-test-analyzer.md +0 -68
  60. package/.claude/agents/pytorch-build-resolver.md +0 -76
  61. package/.claude/agents/refactor-cleaner.md +0 -70
  62. package/.claude/agents/seo-specialist.md +0 -75
  63. package/.claude/agents/silent-failure-hunter.md +0 -69
  64. package/.claude/agents/type-design-analyzer.md +0 -75
  65. package/.claude/rules/common/agents.md +0 -65
  66. package/.claude/rules/common/coding-style.md +0 -90
  67. package/.claude/rules/common/development-workflow.md +0 -44
  68. package/.claude/rules/common/git-workflow.md +0 -24
  69. package/.claude/rules/common/performance.md +0 -55
  70. package/.claude/skills/agent-harness-construction/SKILL.md +0 -77
  71. 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
+ ```