@tuan_son.dinh/gsd 2.6.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/LICENSE +21 -0
- package/README.md +453 -0
- package/dist/app-paths.d.ts +4 -0
- package/dist/app-paths.js +6 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +269 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +70 -0
- package/dist/logo.d.ts +16 -0
- package/dist/logo.js +25 -0
- package/dist/onboarding.d.ts +43 -0
- package/dist/onboarding.js +418 -0
- package/dist/pi-migration.d.ts +14 -0
- package/dist/pi-migration.js +57 -0
- package/dist/resource-loader.d.ts +22 -0
- package/dist/resource-loader.js +60 -0
- package/dist/tool-bootstrap.d.ts +4 -0
- package/dist/tool-bootstrap.js +74 -0
- package/dist/wizard.d.ts +7 -0
- package/dist/wizard.js +25 -0
- package/package.json +60 -0
- package/patches/@mariozechner+pi-coding-agent+0.57.1.patch +108 -0
- package/patches/@mariozechner+pi-tui+0.57.1.patch +47 -0
- package/pkg/dist/modes/interactive/theme/dark.json +85 -0
- package/pkg/dist/modes/interactive/theme/light.json +84 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme.js +949 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
- package/pkg/package.json +8 -0
- package/scripts/postinstall.js +127 -0
- package/src/resources/GSD-WORKFLOW.md +661 -0
- package/src/resources/agents/researcher.md +29 -0
- package/src/resources/agents/scout.md +56 -0
- package/src/resources/agents/worker.md +31 -0
- package/src/resources/extensions/ask-user-questions.ts +249 -0
- package/src/resources/extensions/bg-shell/index.ts +2808 -0
- package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
- package/src/resources/extensions/browser-tools/core.js +1057 -0
- package/src/resources/extensions/browser-tools/index.ts +4989 -0
- package/src/resources/extensions/browser-tools/package.json +20 -0
- package/src/resources/extensions/context7/index.ts +428 -0
- package/src/resources/extensions/context7/package.json +11 -0
- package/src/resources/extensions/get-secrets-from-user.ts +352 -0
- package/src/resources/extensions/google-search/index.ts +323 -0
- package/src/resources/extensions/google-search/package.json +9 -0
- package/src/resources/extensions/gsd/activity-log.ts +69 -0
- package/src/resources/extensions/gsd/auto.ts +2744 -0
- package/src/resources/extensions/gsd/commands.ts +313 -0
- package/src/resources/extensions/gsd/crash-recovery.ts +85 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +521 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +176 -0
- package/src/resources/extensions/gsd/doctor.ts +690 -0
- package/src/resources/extensions/gsd/files.ts +732 -0
- package/src/resources/extensions/gsd/git-service.ts +597 -0
- package/src/resources/extensions/gsd/gitignore.ts +168 -0
- package/src/resources/extensions/gsd/guided-flow.ts +817 -0
- package/src/resources/extensions/gsd/index.ts +558 -0
- package/src/resources/extensions/gsd/metrics.ts +374 -0
- package/src/resources/extensions/gsd/migrate/command.ts +218 -0
- package/src/resources/extensions/gsd/migrate/index.ts +42 -0
- package/src/resources/extensions/gsd/migrate/parser.ts +323 -0
- package/src/resources/extensions/gsd/migrate/parsers.ts +624 -0
- package/src/resources/extensions/gsd/migrate/preview.ts +48 -0
- package/src/resources/extensions/gsd/migrate/transformer.ts +346 -0
- package/src/resources/extensions/gsd/migrate/types.ts +370 -0
- package/src/resources/extensions/gsd/migrate/validator.ts +55 -0
- package/src/resources/extensions/gsd/migrate/writer.ts +539 -0
- package/src/resources/extensions/gsd/observability-validator.ts +408 -0
- package/src/resources/extensions/gsd/package.json +11 -0
- package/src/resources/extensions/gsd/paths.ts +308 -0
- package/src/resources/extensions/gsd/preferences.ts +757 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +50 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +25 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +29 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +189 -0
- package/src/resources/extensions/gsd/prompts/doctor-heal.md +29 -0
- package/src/resources/extensions/gsd/prompts/execute-task.md +61 -0
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +59 -0
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +23 -0
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +11 -0
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -0
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +65 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +51 -0
- package/src/resources/extensions/gsd/prompts/queue.md +85 -0
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +48 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +39 -0
- package/src/resources/extensions/gsd/prompts/research-milestone.md +37 -0
- package/src/resources/extensions/gsd/prompts/research-slice.md +28 -0
- package/src/resources/extensions/gsd/prompts/review-migration.md +66 -0
- package/src/resources/extensions/gsd/prompts/run-uat.md +109 -0
- package/src/resources/extensions/gsd/prompts/system.md +187 -0
- package/src/resources/extensions/gsd/prompts/worktree-merge.md +123 -0
- package/src/resources/extensions/gsd/session-forensics.ts +487 -0
- package/src/resources/extensions/gsd/skill-discovery.ts +137 -0
- package/src/resources/extensions/gsd/state.ts +460 -0
- package/src/resources/extensions/gsd/templates/context.md +76 -0
- package/src/resources/extensions/gsd/templates/decisions.md +8 -0
- package/src/resources/extensions/gsd/templates/milestone-summary.md +73 -0
- package/src/resources/extensions/gsd/templates/plan.md +131 -0
- package/src/resources/extensions/gsd/templates/preferences.md +24 -0
- package/src/resources/extensions/gsd/templates/project.md +31 -0
- package/src/resources/extensions/gsd/templates/reassessment.md +28 -0
- package/src/resources/extensions/gsd/templates/requirements.md +81 -0
- package/src/resources/extensions/gsd/templates/research.md +46 -0
- package/src/resources/extensions/gsd/templates/roadmap.md +118 -0
- package/src/resources/extensions/gsd/templates/slice-context.md +58 -0
- package/src/resources/extensions/gsd/templates/slice-summary.md +99 -0
- package/src/resources/extensions/gsd/templates/state.md +19 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +52 -0
- package/src/resources/extensions/gsd/templates/task-summary.md +57 -0
- package/src/resources/extensions/gsd/templates/uat.md +54 -0
- package/src/resources/extensions/gsd/tests/activity-log-prune.test.ts +327 -0
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +225 -0
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +160 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +341 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +689 -0
- package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/doctor.test.ts +505 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +1313 -0
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +308 -0
- package/src/resources/extensions/gsd/tests/metrics-io.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +217 -0
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +390 -0
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +786 -0
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +657 -0
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +443 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +318 -0
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +420 -0
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +309 -0
- package/src/resources/extensions/gsd/tests/parsers.test.ts +1351 -0
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +163 -0
- package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +386 -0
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/remote-status.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +521 -0
- package/src/resources/extensions/gsd/tests/requirements.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +34 -0
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +11 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +348 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +247 -0
- package/src/resources/extensions/gsd/tests/workflow-config.test.mjs +53 -0
- package/src/resources/extensions/gsd/tests/workspace-index.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +253 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +160 -0
- package/src/resources/extensions/gsd/tests/worktree.test.ts +264 -0
- package/src/resources/extensions/gsd/types.ts +159 -0
- package/src/resources/extensions/gsd/unit-runtime.ts +184 -0
- package/src/resources/extensions/gsd/workspace-index.ts +203 -0
- package/src/resources/extensions/gsd/worktree-command.ts +845 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +392 -0
- package/src/resources/extensions/gsd/worktree.ts +183 -0
- package/src/resources/extensions/mac-tools/index.ts +852 -0
- package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
- package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
- package/src/resources/extensions/mcporter/index.ts +429 -0
- package/src/resources/extensions/remote-questions/config.ts +81 -0
- package/src/resources/extensions/remote-questions/discord-adapter.ts +128 -0
- package/src/resources/extensions/remote-questions/format.ts +163 -0
- package/src/resources/extensions/remote-questions/manager.ts +192 -0
- package/src/resources/extensions/remote-questions/remote-command.ts +307 -0
- package/src/resources/extensions/remote-questions/slack-adapter.ts +92 -0
- package/src/resources/extensions/remote-questions/status.ts +31 -0
- package/src/resources/extensions/remote-questions/store.ts +77 -0
- package/src/resources/extensions/remote-questions/types.ts +75 -0
- package/src/resources/extensions/search-the-web/cache.ts +78 -0
- package/src/resources/extensions/search-the-web/command-search-provider.ts +95 -0
- package/src/resources/extensions/search-the-web/format.ts +258 -0
- package/src/resources/extensions/search-the-web/http.ts +238 -0
- package/src/resources/extensions/search-the-web/index.ts +65 -0
- package/src/resources/extensions/search-the-web/native-search.ts +157 -0
- package/src/resources/extensions/search-the-web/provider.ts +118 -0
- package/src/resources/extensions/search-the-web/tavily.ts +116 -0
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +561 -0
- package/src/resources/extensions/search-the-web/tool-search.ts +576 -0
- package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
- package/src/resources/extensions/shared/confirm-ui.ts +126 -0
- package/src/resources/extensions/shared/interview-ui.ts +613 -0
- package/src/resources/extensions/shared/next-action-ui.ts +197 -0
- package/src/resources/extensions/shared/progress-widget.ts +282 -0
- package/src/resources/extensions/shared/terminal.ts +23 -0
- package/src/resources/extensions/shared/thinking-widget.ts +107 -0
- package/src/resources/extensions/shared/ui.ts +400 -0
- package/src/resources/extensions/shared/wizard-ui.ts +551 -0
- package/src/resources/extensions/slash-commands/audit.ts +88 -0
- package/src/resources/extensions/slash-commands/clear.ts +10 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +297 -0
- package/src/resources/extensions/slash-commands/create-slash-command.ts +234 -0
- package/src/resources/extensions/slash-commands/index.ts +12 -0
- package/src/resources/extensions/subagent/agents.ts +126 -0
- package/src/resources/extensions/subagent/index.ts +1020 -0
- package/src/resources/extensions/voice/index.ts +195 -0
- package/src/resources/extensions/voice/speech-recognizer.swift +154 -0
- package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
- package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
- package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
- package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
- package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
- package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
- package/src/resources/skills/frontend-design/SKILL.md +45 -0
- package/src/resources/skills/swiftui/SKILL.md +208 -0
- package/src/resources/skills/swiftui/references/animations.md +921 -0
- package/src/resources/skills/swiftui/references/architecture.md +1561 -0
- package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
- package/src/resources/skills/swiftui/references/navigation.md +1492 -0
- package/src/resources/skills/swiftui/references/networking-async.md +214 -0
- package/src/resources/skills/swiftui/references/performance.md +1706 -0
- package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
- package/src/resources/skills/swiftui/references/state-management.md +1443 -0
- package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
- package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
- package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
- package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
- package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
- package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
- package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
- package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
- package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
<required_reading>
|
|
2
|
+
**Read these reference files NOW before starting:**
|
|
3
|
+
1. `../macos-apps/references/cli-workflow.md` - Test commands from CLI
|
|
4
|
+
2. `../macos-apps/references/testing-tdd.md` - TDD patterns, avoiding @main hangs
|
|
5
|
+
3. `references/testing-debugging.md` - SwiftUI-specific testing and debugging
|
|
6
|
+
</required_reading>
|
|
7
|
+
|
|
8
|
+
<process>
|
|
9
|
+
## Step 1: Identify Testing Scope
|
|
10
|
+
|
|
11
|
+
**Test business logic in view models, not views:**
|
|
12
|
+
```swift
|
|
13
|
+
// Testable view model
|
|
14
|
+
@Observable
|
|
15
|
+
final class LoginViewModel {
|
|
16
|
+
var email = ""
|
|
17
|
+
var password = ""
|
|
18
|
+
var isLoading = false
|
|
19
|
+
|
|
20
|
+
var isValidInput: Bool {
|
|
21
|
+
!email.isEmpty && password.count >= 8
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// View is just presentation
|
|
26
|
+
struct LoginView: View {
|
|
27
|
+
let viewModel: LoginViewModel
|
|
28
|
+
var body: some View {
|
|
29
|
+
Form {
|
|
30
|
+
TextField("Email", text: $viewModel.email)
|
|
31
|
+
Button("Login") { }
|
|
32
|
+
.disabled(!viewModel.isValidInput)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Step 2: Write Unit Tests
|
|
39
|
+
|
|
40
|
+
**Using Swift Testing (@Test):**
|
|
41
|
+
```swift
|
|
42
|
+
import Testing
|
|
43
|
+
@testable import MyApp
|
|
44
|
+
|
|
45
|
+
@Test("Email validation")
|
|
46
|
+
func emailValidation() {
|
|
47
|
+
let viewModel = LoginViewModel()
|
|
48
|
+
|
|
49
|
+
viewModel.email = ""
|
|
50
|
+
viewModel.password = "password123"
|
|
51
|
+
#expect(!viewModel.isValidInput)
|
|
52
|
+
|
|
53
|
+
viewModel.email = "user@example.com"
|
|
54
|
+
#expect(viewModel.isValidInput)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@Test("Async loading")
|
|
58
|
+
func asyncLoading() async {
|
|
59
|
+
let mockService = MockService()
|
|
60
|
+
let viewModel = TaskViewModel(service: mockService)
|
|
61
|
+
|
|
62
|
+
await viewModel.load()
|
|
63
|
+
|
|
64
|
+
#expect(!viewModel.tasks.isEmpty)
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Step 3: Add Accessibility Identifiers
|
|
69
|
+
|
|
70
|
+
```swift
|
|
71
|
+
TextField("Email", text: $email)
|
|
72
|
+
.accessibilityIdentifier("emailField")
|
|
73
|
+
|
|
74
|
+
SecureField("Password", text: $password)
|
|
75
|
+
.accessibilityIdentifier("passwordField")
|
|
76
|
+
|
|
77
|
+
Button("Login") { }
|
|
78
|
+
.accessibilityIdentifier("loginButton")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Step 4: Write UI Tests
|
|
82
|
+
|
|
83
|
+
```swift
|
|
84
|
+
import XCTest
|
|
85
|
+
|
|
86
|
+
final class LoginUITests: XCTestCase {
|
|
87
|
+
var app: XCUIApplication!
|
|
88
|
+
|
|
89
|
+
override func setUp() {
|
|
90
|
+
continueAfterFailure = false
|
|
91
|
+
app = XCUIApplication()
|
|
92
|
+
app.launch()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
func testLoginFlow() {
|
|
96
|
+
let emailField = app.textFields["emailField"]
|
|
97
|
+
let passwordField = app.secureTextFields["passwordField"]
|
|
98
|
+
let loginButton = app.buttons["loginButton"]
|
|
99
|
+
|
|
100
|
+
XCTAssertTrue(emailField.waitForExistence(timeout: 5))
|
|
101
|
+
|
|
102
|
+
emailField.tap()
|
|
103
|
+
emailField.typeText("user@example.com")
|
|
104
|
+
|
|
105
|
+
passwordField.tap()
|
|
106
|
+
passwordField.typeText("password123")
|
|
107
|
+
|
|
108
|
+
XCTAssertTrue(loginButton.isEnabled)
|
|
109
|
+
loginButton.tap()
|
|
110
|
+
|
|
111
|
+
let welcomeText = app.staticTexts["welcomeMessage"]
|
|
112
|
+
XCTAssertTrue(welcomeText.waitForExistence(timeout: 5))
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Step 5: Create Previews for Visual Testing
|
|
118
|
+
|
|
119
|
+
```swift
|
|
120
|
+
#Preview("Empty") { LoginView(viewModel: LoginViewModel()) }
|
|
121
|
+
|
|
122
|
+
#Preview("Filled") {
|
|
123
|
+
let viewModel = LoginViewModel()
|
|
124
|
+
viewModel.email = "user@example.com"
|
|
125
|
+
viewModel.password = "password123"
|
|
126
|
+
return LoginView(viewModel: viewModel)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
#Preview("Error") {
|
|
130
|
+
let viewModel = LoginViewModel()
|
|
131
|
+
viewModel.errorMessage = "Invalid credentials"
|
|
132
|
+
return LoginView(viewModel: viewModel)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
#Preview("Dark Mode") {
|
|
136
|
+
LoginView(viewModel: LoginViewModel())
|
|
137
|
+
.preferredColorScheme(.dark)
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Step 6: Run Tests from CLI
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Run all tests with parsed output
|
|
145
|
+
xcodebuild test -scheme AppName -destination 'platform=iOS Simulator,name=iPhone 15 Pro' 2>&1 | xcsift
|
|
146
|
+
|
|
147
|
+
# Run only unit tests
|
|
148
|
+
xcodebuild test -scheme AppName -only-testing:AppNameTests 2>&1 | xcsift
|
|
149
|
+
|
|
150
|
+
# Run only UI tests
|
|
151
|
+
xcodebuild test -scheme AppName -only-testing:AppNameUITests 2>&1 | xcsift
|
|
152
|
+
|
|
153
|
+
# Run specific test class
|
|
154
|
+
xcodebuild test -scheme AppName -only-testing:AppNameTests/LoginViewModelTests 2>&1 | xcsift
|
|
155
|
+
|
|
156
|
+
# Run specific test method
|
|
157
|
+
xcodebuild test -scheme AppName -only-testing:AppNameTests/LoginViewModelTests/testEmailValidation 2>&1 | xcsift
|
|
158
|
+
|
|
159
|
+
# Generate test coverage
|
|
160
|
+
xcodebuild test -scheme AppName -enableCodeCoverage YES -resultBundlePath TestResults.xcresult 2>&1 | xcsift
|
|
161
|
+
xcrun xccov view --report TestResults.xcresult
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**If tests hang:** The test target likely depends on the app target with `@main`. Extract testable code to a Core framework target. See `../macos-apps/references/testing-tdd.md`.
|
|
165
|
+
|
|
166
|
+
Report to user:
|
|
167
|
+
- "Tests: X pass, Y fail"
|
|
168
|
+
- "Coverage: Z% of lines"
|
|
169
|
+
- If failures: "Failed tests: [list]. Investigating..."
|
|
170
|
+
</process>
|
|
171
|
+
|
|
172
|
+
<anti_patterns>
|
|
173
|
+
## Avoid These Mistakes
|
|
174
|
+
|
|
175
|
+
**Testing view bodies:**
|
|
176
|
+
```swift
|
|
177
|
+
// Wrong: can't test views directly
|
|
178
|
+
func testView() {
|
|
179
|
+
let view = LoginView()
|
|
180
|
+
// Can't inspect SwiftUI view
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Right: test view model
|
|
184
|
+
@Test func emailInput() {
|
|
185
|
+
let viewModel = LoginViewModel()
|
|
186
|
+
viewModel.email = "test@example.com"
|
|
187
|
+
#expect(viewModel.email == "test@example.com")
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Missing accessibility identifiers:**
|
|
192
|
+
```swift
|
|
193
|
+
// Wrong: using text
|
|
194
|
+
let button = app.buttons["Login"]
|
|
195
|
+
|
|
196
|
+
// Right: stable identifier
|
|
197
|
+
let button = app.buttons["loginButton"]
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**No dependency injection:**
|
|
201
|
+
```swift
|
|
202
|
+
// Wrong: can't mock
|
|
203
|
+
@Observable
|
|
204
|
+
class ViewModel {
|
|
205
|
+
private let service = RealService()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Right: injectable
|
|
209
|
+
@Observable
|
|
210
|
+
class ViewModel {
|
|
211
|
+
private let service: ServiceProtocol
|
|
212
|
+
init(service: ServiceProtocol) {
|
|
213
|
+
self.service = service
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**No edge case testing:**
|
|
219
|
+
```swift
|
|
220
|
+
// Test empty, invalid, error states
|
|
221
|
+
@Test func emptyEmail() { }
|
|
222
|
+
@Test func shortPassword() { }
|
|
223
|
+
@Test func networkError() { }
|
|
224
|
+
```
|
|
225
|
+
</anti_patterns>
|
|
226
|
+
|
|
227
|
+
<success_criteria>
|
|
228
|
+
This workflow is complete when:
|
|
229
|
+
- [ ] Unit tests verify view model business logic
|
|
230
|
+
- [ ] UI tests verify user flows using accessibility identifiers
|
|
231
|
+
- [ ] All tests pass: `xcodebuild test -scheme YourApp`
|
|
232
|
+
- [ ] Edge cases and error states have coverage
|
|
233
|
+
- [ ] Dependencies use protocols for testability
|
|
234
|
+
- [ ] Previews exist for major UI states
|
|
235
|
+
</success_criteria>
|