codebyplan 1.11.1 → 1.12.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/dist/cli.js +602 -345
- package/package.json +1 -1
- package/templates/README.md +1 -1
- package/templates/agents/cbp-cc-executor.md +1 -1
- package/templates/agents/cbp-e2e-maestro.md +202 -0
- package/templates/agents/cbp-e2e-playwright.md +229 -0
- package/templates/agents/cbp-e2e-tauri.md +184 -0
- package/templates/agents/cbp-e2e-vscode.md +203 -0
- package/templates/agents/cbp-e2e-xcuitest.md +224 -0
- package/templates/agents/cbp-improve-claude.md +1 -1
- package/templates/agents/cbp-round-executor.md +11 -11
- package/templates/agents/cbp-task-check.md +1 -1
- package/templates/agents/cbp-task-planner.md +2 -0
- package/templates/agents/cbp-testing-qa-agent.md +9 -9
- package/templates/context/testing/e2e.md +303 -0
- package/templates/hooks/cbp-statusline.mjs +44 -0
- package/templates/hooks/cbp-statusline.py +24 -2
- package/templates/hooks/cbp-statusline.sh +22 -2
- package/templates/hooks/validate-structure-lengths.sh +2 -0
- package/templates/hooks/validate-structure-smoke.sh +2 -1
- package/templates/hooks/validate-structure-templates.sh +1 -0
- package/templates/rules/README.md +8 -1
- package/templates/rules/context-file-loading.md +4 -1
- package/templates/rules/e2e-mandatory.md +70 -0
- package/templates/rules/supabase-branch-lifecycle.md +99 -0
- package/templates/settings.project.base.json +1 -2
- package/templates/skills/cbp-build-cc-agent/SKILL.md +16 -14
- package/templates/skills/cbp-build-cc-agent/reference/cbp-quality.md +4 -4
- package/templates/skills/cbp-build-cc-agent/scripts/validate-agent.sh +8 -6
- package/templates/skills/cbp-build-cc-mode/SKILL.md +4 -4
- package/templates/skills/cbp-build-cc-settings/reference/cbp-conventions.md +1 -2
- package/templates/skills/cbp-checkpoint-check/SKILL.md +12 -8
- package/templates/skills/cbp-checkpoint-create/SKILL.md +2 -0
- package/templates/skills/cbp-checkpoint-end/SKILL.md +27 -5
- package/templates/skills/cbp-checkpoint-plan/SKILL.md +2 -2
- package/templates/skills/cbp-checkpoint-plan/reference/e2e-discovery-probe.md +5 -5
- package/templates/skills/cbp-e2e-setup/SKILL.md +254 -0
- package/templates/skills/cbp-e2e-setup/reference/maestro.md +200 -0
- package/templates/skills/cbp-e2e-setup/reference/playwright.md +212 -0
- package/templates/skills/cbp-e2e-setup/reference/tauri.md +147 -0
- package/templates/skills/cbp-e2e-setup/reference/vscode.md +154 -0
- package/templates/skills/cbp-e2e-setup/reference/xcuitest.md +185 -0
- package/templates/skills/cbp-frontend-ui/SKILL.md +6 -6
- package/templates/skills/cbp-frontend-ux/SKILL.md +1 -1
- package/templates/skills/cbp-git-worktree-remove/SKILL.md +17 -1
- package/templates/skills/cbp-round-execute/SKILL.md +30 -17
- package/templates/skills/cbp-session-start/SKILL.md +27 -2
- package/templates/skills/cbp-ship-main/SKILL.md +13 -0
- package/templates/skills/cbp-supabase-branch-check/SKILL.md +12 -5
- package/templates/skills/cbp-supabase-migrate/SKILL.md +139 -9
- package/templates/skills/cbp-supabase-migrate/reference/preflight-dry-run.md +1 -1
- package/templates/skills/cbp-supabase-setup/SKILL.md +13 -7
- package/templates/skills/cbp-supabase-setup/reference/branching-setup.md +2 -2
- package/templates/skills/cbp-task-check/SKILL.md +2 -2
- package/templates/skills/cbp-task-start/SKILL.md +2 -0
- package/templates/agents/cbp-test-e2e-agent.md +0 -363
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Tauri Reference
|
|
2
|
+
|
|
3
|
+
Full install, config, and test walkthrough for Tauri desktop apps using WebDriverIO +
|
|
4
|
+
tauri-driver. Source: upstream WebDriverIO docs + `.claude/context/testing/e2e.md`.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
|
|
8
|
+
- Rust toolchain: `rustup --version` (install via https://rustup.rs)
|
|
9
|
+
- `tauri-driver` binary: installed via Cargo (see Install below)
|
|
10
|
+
- Built Tauri binary: `cargo build` must complete before any tests run
|
|
11
|
+
- Node.js / pnpm available
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# WebDriverIO runner + framework
|
|
17
|
+
pnpm add -D @wdio/cli @wdio/local-runner @wdio/mocha-framework @wdio/spec-reporter
|
|
18
|
+
|
|
19
|
+
# Tauri driver (native binary — needs Cargo)
|
|
20
|
+
cargo install tauri-driver
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Verify:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
which tauri-driver
|
|
27
|
+
tauri-driver --version
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## wdio.conf.ts
|
|
31
|
+
|
|
32
|
+
Place at `apps/desktop/wdio.conf.ts` (or repo root for single-app repos):
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { spawn, spawnSync } from "child_process";
|
|
36
|
+
import type { Options } from "@wdio/types";
|
|
37
|
+
|
|
38
|
+
// Path to your built Tauri binary
|
|
39
|
+
const BINARY_PATH = "./src-tauri/target/debug/your-app-name";
|
|
40
|
+
|
|
41
|
+
let tauriDriver: ReturnType<typeof spawn>;
|
|
42
|
+
|
|
43
|
+
export const config: Options.Testrunner = {
|
|
44
|
+
specs: ["./e2e/**/*.spec.ts"],
|
|
45
|
+
maxInstances: 1,
|
|
46
|
+
capabilities: [
|
|
47
|
+
{
|
|
48
|
+
"tauri:options": { application: BINARY_PATH },
|
|
49
|
+
maxInstances: 1,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
services: ["chromedriver"],
|
|
53
|
+
framework: "mocha",
|
|
54
|
+
reporters: ["spec"],
|
|
55
|
+
mochaOpts: { timeout: 60_000 },
|
|
56
|
+
|
|
57
|
+
beforeSession: async () => {
|
|
58
|
+
// Start tauri-driver before each session
|
|
59
|
+
tauriDriver = spawn("tauri-driver", [], {
|
|
60
|
+
stdio: [null, process.stdout, process.stderr],
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
afterSession: async () => {
|
|
65
|
+
// Kill tauri-driver after each session
|
|
66
|
+
tauriDriver.kill();
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Build before running
|
|
72
|
+
|
|
73
|
+
Tests will fail if the binary is stale or absent. Always build before running tests:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Build the Tauri app
|
|
77
|
+
cargo build --manifest-path apps/desktop/src-tauri/Cargo.toml
|
|
78
|
+
|
|
79
|
+
# Then run WebDriverIO
|
|
80
|
+
pnpm --filter @codebyplan/desktop wdio run wdio.conf.ts
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Or as a combined pnpm script:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"scripts": {
|
|
88
|
+
"e2e": "cargo build --manifest-path src-tauri/Cargo.toml && wdio run wdio.conf.ts",
|
|
89
|
+
"e2e:test": "wdio run wdio.conf.ts"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Writing tests
|
|
95
|
+
|
|
96
|
+
Use `data-testid` attributes for stable targeting (Tauri WebView renders HTML):
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { browser, $ } from "@wdio/globals";
|
|
100
|
+
import { expect } from "@wdio/globals";
|
|
101
|
+
|
|
102
|
+
describe("Desktop app", () => {
|
|
103
|
+
it("opens the main window", async () => {
|
|
104
|
+
const navBar = await $("[data-testid='nav']");
|
|
105
|
+
await expect(navBar).toBeDisplayed();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("navigates to settings", async () => {
|
|
109
|
+
await $("[data-testid='settings-link']").click();
|
|
110
|
+
await expect($("[data-testid='settings-panel']")).toBeDisplayed();
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Prefer `data-testid` over CSS class selectors — SCSS Modules mangle class names.
|
|
116
|
+
|
|
117
|
+
## Auth probe
|
|
118
|
+
|
|
119
|
+
`apps/desktop/e2e/_probe/auth.spec.ts`:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { browser, $ } from "@wdio/globals";
|
|
123
|
+
import { expect } from "@wdio/globals";
|
|
124
|
+
|
|
125
|
+
describe("auth probe", () => {
|
|
126
|
+
it("can reach the main window", async () => {
|
|
127
|
+
// Tauri desktop apps often skip network auth — adapt if your app has auth
|
|
128
|
+
const root = await $("[data-testid='app-root']");
|
|
129
|
+
await expect(root).toBeDisplayed();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Pitfalls
|
|
135
|
+
|
|
136
|
+
**Must build before run** — tauri-driver launches the binary; if the binary doesn't
|
|
137
|
+
exist or is stale the session fails immediately with a confusing error.
|
|
138
|
+
|
|
139
|
+
**Binary path** — the `application` path in `capabilities` must be the exact path
|
|
140
|
+
to the compiled binary. Debug builds are under `src-tauri/target/debug/`, release
|
|
141
|
+
builds under `src-tauri/target/release/`.
|
|
142
|
+
|
|
143
|
+
**Port conflicts** — tauri-driver listens on port 4444 by default. Ensure no other
|
|
144
|
+
WebDriver session is running on the same port.
|
|
145
|
+
|
|
146
|
+
**CI** — Tauri desktop E2E on CI requires a display (Xvfb on Linux) and the full Rust
|
|
147
|
+
build toolchain. Use GitHub-hosted `ubuntu-latest` or `macos-latest` runners.
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# VS Code Extension Reference
|
|
2
|
+
|
|
3
|
+
Full install, config, and test walkthrough for VS Code extension testing using
|
|
4
|
+
`@vscode/test-cli` and `@vscode/test-electron`. Source: upstream VS Code extension
|
|
5
|
+
testing docs.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- VS Code installed (used as the test host)
|
|
10
|
+
- Node.js / pnpm available
|
|
11
|
+
- On Linux CI: Xvfb for a display server (extensions require a GUI)
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add -D @vscode/test-cli @vscode/test-electron
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Verify:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm exec vscode-test --version
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## .vscode-test.mjs
|
|
26
|
+
|
|
27
|
+
Create `.vscode-test.mjs` at the extension package root (e.g. `apps/vscode/`):
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import { defineConfig } from "@vscode/test-cli";
|
|
31
|
+
|
|
32
|
+
export default defineConfig({
|
|
33
|
+
files: "e2e/**/*.test.js", // compiled output path (JS, not TS)
|
|
34
|
+
extensionDevelopmentPath: ".", // path to the extension package root
|
|
35
|
+
workspaceFolder: "test-fixtures/workspace", // optional: open a fixture workspace
|
|
36
|
+
mocha: {
|
|
37
|
+
timeout: 20_000,
|
|
38
|
+
ui: "bdd",
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For TypeScript source, compile tests before running:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"scripts": {
|
|
48
|
+
"test:e2e": "tsc -p tsconfig.test.json && vscode-test",
|
|
49
|
+
"test:e2e:watch": "vscode-test --watch"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Extension host lifecycle
|
|
55
|
+
|
|
56
|
+
`@vscode/test-electron` downloads an isolated VS Code instance, installs your extension,
|
|
57
|
+
opens the workspace, and runs the Mocha suite inside the extension host process.
|
|
58
|
+
|
|
59
|
+
Tests import from `vscode` — the module is available because they run inside VS Code:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import * as vscode from "vscode";
|
|
63
|
+
import * as assert from "assert";
|
|
64
|
+
|
|
65
|
+
suite("Extension", () => {
|
|
66
|
+
test("extension activates", async () => {
|
|
67
|
+
const ext = vscode.extensions.getExtension("yourpublisher.yourextension");
|
|
68
|
+
assert.ok(ext, "extension not found");
|
|
69
|
+
await ext.activate();
|
|
70
|
+
assert.ok(ext.isActive);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("command is registered", async () => {
|
|
74
|
+
const commands = await vscode.commands.getCommands();
|
|
75
|
+
assert.ok(
|
|
76
|
+
commands.includes("yourextension.yourCommand"),
|
|
77
|
+
"command not registered"
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The test file runs inside the VS Code extension host — full `vscode` API is available,
|
|
84
|
+
including workspace, editors, commands, and diagnostics.
|
|
85
|
+
|
|
86
|
+
## Directory structure
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
apps/vscode/
|
|
90
|
+
.vscode-test.mjs
|
|
91
|
+
e2e/
|
|
92
|
+
_probe/
|
|
93
|
+
activation.test.ts
|
|
94
|
+
commands/
|
|
95
|
+
my-command.test.ts
|
|
96
|
+
test-fixtures/
|
|
97
|
+
workspace/ # optional: committed fixture files opened in tests
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Activation probe
|
|
101
|
+
|
|
102
|
+
`apps/vscode/e2e/_probe/activation.test.ts`:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import * as vscode from "vscode";
|
|
106
|
+
import * as assert from "assert";
|
|
107
|
+
|
|
108
|
+
suite("Activation probe", () => {
|
|
109
|
+
test("extension activates without error", async () => {
|
|
110
|
+
// Replace with your publisher.extensionname from package.json
|
|
111
|
+
const ext = vscode.extensions.getExtension("yourpublisher.yourextension");
|
|
112
|
+
assert.ok(ext, "Extension not installed in test host");
|
|
113
|
+
if (!ext.isActive) {
|
|
114
|
+
await ext.activate();
|
|
115
|
+
}
|
|
116
|
+
assert.ok(ext.isActive, "Extension did not activate");
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## CI (GitHub Actions)
|
|
122
|
+
|
|
123
|
+
Linux runners require Xvfb. Use the `xvfb-run` wrapper:
|
|
124
|
+
|
|
125
|
+
```yaml
|
|
126
|
+
- name: Install dependencies
|
|
127
|
+
run: pnpm install
|
|
128
|
+
|
|
129
|
+
- name: Compile extension tests
|
|
130
|
+
run: pnpm --filter @codebyplan/vscode test:compile
|
|
131
|
+
|
|
132
|
+
- name: Run VS Code extension tests
|
|
133
|
+
run: xvfb-run -a pnpm --filter @codebyplan/vscode test:e2e
|
|
134
|
+
env:
|
|
135
|
+
DISPLAY: ':99.0'
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
On macOS/Windows runners, Xvfb is not needed — `vscode-test` uses the native display.
|
|
139
|
+
|
|
140
|
+
## Pitfalls
|
|
141
|
+
|
|
142
|
+
**Wrong extensionDevelopmentPath** — if the path in `.vscode-test.mjs` doesn't point
|
|
143
|
+
to the package root (where `package.json` has the `contributes` block), VS Code won't
|
|
144
|
+
find the extension and activation tests will fail silently.
|
|
145
|
+
|
|
146
|
+
**TypeScript source vs compiled output** — `@vscode/test-cli` runs compiled JS.
|
|
147
|
+
Always compile before invoking `vscode-test` in CI.
|
|
148
|
+
|
|
149
|
+
**Extension host isolation** — each test run downloads a fresh VS Code binary into a
|
|
150
|
+
temp dir. This is intentional; do not try to reuse the system VS Code installation.
|
|
151
|
+
|
|
152
|
+
**`vscode` module availability** — tests run inside the extension host, so `import
|
|
153
|
+
* as vscode from "vscode"` resolves correctly. The same import will fail if you try
|
|
154
|
+
to run these files with plain Node.js outside the host.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# XCUITest Reference
|
|
2
|
+
|
|
3
|
+
Full walkthrough for iOS native E2E testing with XCUITest via the Expo `withXCUITests`
|
|
4
|
+
plugin. Source: Apple XCUITest docs + Expo prebuild docs.
|
|
5
|
+
|
|
6
|
+
## When to use XCUITest vs Maestro
|
|
7
|
+
|
|
8
|
+
| Scenario | Use |
|
|
9
|
+
| --- | --- |
|
|
10
|
+
| Standard UI flows (login, navigation, forms) | Maestro — simpler, cross-platform |
|
|
11
|
+
| Apple Watch companion app testing | XCUITest — Maestro can't target watchOS |
|
|
12
|
+
| HealthKit permission dialogs | XCUITest — system dialogs not reachable by Maestro |
|
|
13
|
+
| iOS system sheet interactions (share sheet, notification permissions) | XCUITest |
|
|
14
|
+
| Face ID / Touch ID prompts | XCUITest |
|
|
15
|
+
| Camera/microphone permission dialogs | XCUITest |
|
|
16
|
+
|
|
17
|
+
Choose Maestro first; escalate to XCUITest only when Maestro genuinely cannot reach
|
|
18
|
+
the target UI.
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
- macOS with Xcode 15+ installed
|
|
23
|
+
- An active Apple Developer account (free tier sufficient for Simulator testing)
|
|
24
|
+
- Expo managed workflow with prebuild enabled
|
|
25
|
+
- `xcbeautify` for readable output: `brew install xcbeautify`
|
|
26
|
+
|
|
27
|
+
## Setup — Expo withXCUITests plugin
|
|
28
|
+
|
|
29
|
+
Add the plugin to `app.config.ts` (or `app.config.js`):
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
export default {
|
|
33
|
+
expo: {
|
|
34
|
+
plugins: [
|
|
35
|
+
["expo-build-properties", { ios: { useFrameworks: "static" } }],
|
|
36
|
+
// Add your withXCUITests plugin config
|
|
37
|
+
["./plugins/withXCUITests", {}],
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If using the community `expo-xcuitest` plugin:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pnpm add -D expo-xcuitest
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then in `app.config.ts`:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
plugins: [
|
|
53
|
+
["expo-xcuitest", { testTargetName: "AppUITests" }]
|
|
54
|
+
]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Prebuild
|
|
58
|
+
|
|
59
|
+
After updating `app.config.ts`, regenerate the native project:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
expo prebuild --platform ios --clean
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`--clean` ensures a fresh native project from the current config. Commit the generated
|
|
66
|
+
`ios/` directory so CI can build without running prebuild.
|
|
67
|
+
|
|
68
|
+
## Swift test class
|
|
69
|
+
|
|
70
|
+
Create `ios/AppUITests/AppUITests.swift`:
|
|
71
|
+
|
|
72
|
+
```swift
|
|
73
|
+
import XCTest
|
|
74
|
+
|
|
75
|
+
class AppUITests: XCTestCase {
|
|
76
|
+
|
|
77
|
+
var app: XCUIApplication!
|
|
78
|
+
|
|
79
|
+
override func setUpWithError() throws {
|
|
80
|
+
continueAfterFailure = false
|
|
81
|
+
app = XCUIApplication()
|
|
82
|
+
|
|
83
|
+
// Inject credentials via scheme environment variables
|
|
84
|
+
app.launchEnvironment["TEST_EMAIL"] = ProcessInfo.processInfo.environment["TEST_EMAIL"] ?? ""
|
|
85
|
+
app.launchEnvironment["TEST_PASSWORD"] = ProcessInfo.processInfo.environment["TEST_PASSWORD"] ?? ""
|
|
86
|
+
|
|
87
|
+
app.launch()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
func testLoginFlow() throws {
|
|
91
|
+
// Wait for the login screen
|
|
92
|
+
let emailField = app.textFields["email-input"]
|
|
93
|
+
XCTAssertTrue(emailField.waitForExistence(timeout: 10))
|
|
94
|
+
|
|
95
|
+
emailField.tap()
|
|
96
|
+
emailField.typeText(app.launchEnvironment["TEST_EMAIL"]!)
|
|
97
|
+
|
|
98
|
+
let passwordField = app.secureTextFields["password-input"]
|
|
99
|
+
passwordField.tap()
|
|
100
|
+
passwordField.typeText(app.launchEnvironment["TEST_PASSWORD"]!)
|
|
101
|
+
|
|
102
|
+
app.buttons["sign-in-button"].tap()
|
|
103
|
+
|
|
104
|
+
// Assert post-login element
|
|
105
|
+
let dashboard = app.staticTexts["Dashboard"]
|
|
106
|
+
XCTAssertTrue(dashboard.waitForExistence(timeout: 15))
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## accessibilityIdentifier targeting
|
|
112
|
+
|
|
113
|
+
Set `accessibilityIdentifier` in your React Native components so XCUITest can find them:
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
// In React Native
|
|
117
|
+
<TextInput
|
|
118
|
+
testID="email-input" // becomes accessibilityIdentifier on iOS
|
|
119
|
+
accessibilityLabel="Email"
|
|
120
|
+
/>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
In XCUITest, query by identifier:
|
|
124
|
+
|
|
125
|
+
```swift
|
|
126
|
+
app.textFields["email-input"] // TextInput
|
|
127
|
+
app.buttons["sign-in-button"] // TouchableOpacity / Pressable
|
|
128
|
+
app.staticTexts["Dashboard"] // Text component
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Credentials via scheme environment variables
|
|
132
|
+
|
|
133
|
+
Rather than hardcoding credentials, inject them via the Xcode scheme.
|
|
134
|
+
|
|
135
|
+
In Xcode: Product → Scheme → Edit Scheme → Run → Arguments → Environment Variables.
|
|
136
|
+
Add `TEST_EMAIL` and `TEST_PASSWORD` pointing to your local values.
|
|
137
|
+
|
|
138
|
+
For CI, pass them via `xcodebuild`:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
xcodebuild test \
|
|
142
|
+
-workspace ios/YourApp.xcworkspace \
|
|
143
|
+
-scheme YourApp \
|
|
144
|
+
-destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
|
|
145
|
+
TEST_EMAIL="$TEST_EMAIL" \
|
|
146
|
+
TEST_PASSWORD="$TEST_PASSWORD" \
|
|
147
|
+
| xcbeautify
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Running tests
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
xcodebuild test \
|
|
154
|
+
-workspace ios/YourApp.xcworkspace \
|
|
155
|
+
-scheme YourApp \
|
|
156
|
+
-destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
|
|
157
|
+
| xcbeautify
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## pnpm script
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"scripts": {
|
|
165
|
+
"xcuitest": "xcodebuild test -workspace ios/YourApp.xcworkspace -scheme YourApp -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' | xcbeautify"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Pitfalls
|
|
171
|
+
|
|
172
|
+
**Simulator not booted** — `xcodebuild` will boot the simulator if needed, but the
|
|
173
|
+
first run is slow. Pre-boot with `xcrun simctl boot "iPhone 16"` in CI setup.
|
|
174
|
+
|
|
175
|
+
**accessibilityIdentifier vs testID** — React Native maps `testID` to
|
|
176
|
+
`accessibilityIdentifier` on iOS. Ensure the component renders the prop all the way
|
|
177
|
+
through; some wrappers drop it.
|
|
178
|
+
|
|
179
|
+
**waitForExistence timeout** — always use `waitForExistence(timeout:)` rather than
|
|
180
|
+
asserting element existence immediately. React Native renders asynchronously; the
|
|
181
|
+
element may not be in the view hierarchy at the instant of the assertion.
|
|
182
|
+
|
|
183
|
+
**Derived data cache** — stale derived data can cause confusing failures. Clear with
|
|
184
|
+
`rm -rf ~/Library/Developer/Xcode/DerivedData` if tests pass locally but fail after
|
|
185
|
+
a schema change.
|
|
@@ -10,7 +10,7 @@ effort: xhigh
|
|
|
10
10
|
Invoked twice per round in non-`claude_only` profiles:
|
|
11
11
|
|
|
12
12
|
1. `round-executor` Step 3.8 — `phase: 'style_only'`, no e2e screenshots. Reviews token/spacing/typography/color/cohesion against the just-written code.
|
|
13
|
-
2. `/cbp-round-execute` Step 5b — `phase: 'screenshot_review'`, with screenshots from `
|
|
13
|
+
2. `/cbp-round-execute` Step 5b — `phase: 'screenshot_review'`, with screenshots from the `cbp-e2e-*` specialists. Reviews rendered output and detects baseline regressions.
|
|
14
14
|
|
|
15
15
|
Default `phase: 'full'` runs everything (back-compat for any caller not yet migrated). Inline counterpart of the up-front `frontend-design` skill — `frontend-design` decides direction before code; `frontend-ui` reviews and polishes after code.
|
|
16
16
|
|
|
@@ -36,7 +36,7 @@ input:
|
|
|
36
36
|
context:
|
|
37
37
|
checkpoint_goal: string
|
|
38
38
|
round_requirements: string
|
|
39
|
-
e2e_screenshots: # Required for phase 'screenshot_review' or 'full' (when present); empty / omitted for 'style_only'. Sourced from round.context.
|
|
39
|
+
e2e_screenshots: # Required for phase 'screenshot_review' or 'full' (when present); empty / omitted for 'style_only'. Sourced from the aggregated round.context.e2e_outputs[*].screenshots (populated by the cbp-e2e-* specialists at /cbp-round-execute Step 5).
|
|
40
40
|
- test_name: string
|
|
41
41
|
path: string # Repo-relative or absolute path to PNG
|
|
42
42
|
page_or_screen: string
|
|
@@ -213,7 +213,7 @@ The skill's auto-fix capability is for in-scope polish, not opportunistic sweeps
|
|
|
213
213
|
**Specifically forbidden** (always out of scope, never edited regardless of `files_changed`):
|
|
214
214
|
|
|
215
215
|
- `.claude/**` — managed infrastructure under user-level governance
|
|
216
|
-
- Project test infrastructure (e.g., `playwright.config.*`, `e2e/**`) — governed by `
|
|
216
|
+
- Project test infrastructure (e.g., `playwright.config.*`, `e2e/**`) — governed by the `cbp-e2e-*` specialist agents
|
|
217
217
|
- DB migrations (e.g., `supabase/migrations/**`) — governed by `database-agent`
|
|
218
218
|
- Vendor mirrors and read-only reference trees
|
|
219
219
|
|
|
@@ -254,9 +254,9 @@ Go beyond fixing violations — actively improve visual quality. If spacing coul
|
|
|
254
254
|
|
|
255
255
|
- **Loaded twice per round** (non-`claude_only` profiles):
|
|
256
256
|
1. `round-executor` Step 3.8 with `phase: 'style_only'` and empty `e2e_screenshots[]` — reviews the just-written code's tokens/spacing/typography/color/cohesion (mandatory when files_changed contains UI / styling files)
|
|
257
|
-
2. `/cbp-round-execute` Step 5b with `phase: 'screenshot_review'` and screenshots from `round.context.
|
|
258
|
-
- **Also invoked by**: `/cbp-checkpoint-check`
|
|
259
|
-
- **Consumes**: `e2e_screenshots[]` from `round.context.
|
|
257
|
+
2. `/cbp-round-execute` Step 5b with `phase: 'screenshot_review'` and screenshots aggregated from `round.context.e2e_outputs[*].screenshots` — runs Phase 6.5 only (rendered-output review + baseline regressions). Skipped when no e2e ran (`claude_only` / `backend`, or no eligible framework in `.codebyplan/e2e.json`).
|
|
258
|
+
- **Also invoked by**: `/cbp-checkpoint-check` with screenshots aggregated from a whole-checkpoint e2e run
|
|
259
|
+
- **Consumes**: `e2e_screenshots[]` aggregated from `round.context.e2e_outputs[*].screenshots` (populated by the `cbp-e2e-*` specialists at `/cbp-round-execute` Step 5)
|
|
260
260
|
- **Output written to**: `round.context.frontend_ui_review` — when invoked twice per round, the second invocation merges with the first
|
|
261
261
|
- **Downstream gate**: this skill emits `findings[]` only. Baseline-regression findings surface as a BLOCKING gate at `/cbp-round-end` Step 7 (baselines never auto-accepted); rendered-visual critical findings are surfaced in the Step 7 findings presentation.
|
|
262
262
|
- **Paired with**: `frontend-design` (pre-implementation aesthetic decision), `frontend-ux` (interaction-quality self-review, also Step 3.8)
|
|
@@ -148,7 +148,7 @@ This rule applies to every file. The skill's auto-fix surface exists because in-
|
|
|
148
148
|
**Specifically forbidden** (always out of scope, never edited regardless of `files_changed`):
|
|
149
149
|
|
|
150
150
|
- `.claude/**` — managed infrastructure under user-level governance
|
|
151
|
-
- Project test infrastructure (e.g., `playwright.config.*`, `e2e/**`) — governed by `
|
|
151
|
+
- Project test infrastructure (e.g., `playwright.config.*`, `e2e/**`) — governed by the `cbp-e2e-*` specialist agents
|
|
152
152
|
- DB migrations (e.g., `supabase/migrations/**`) — governed by `database-agent`
|
|
153
153
|
- Vendor mirrors and read-only reference trees
|
|
154
154
|
|
|
@@ -116,7 +116,12 @@ Only use `--force` if the user confirms.
|
|
|
116
116
|
|
|
117
117
|
### Step 9: Delete Branch (if requested)
|
|
118
118
|
|
|
119
|
-
**Protected branch check:**
|
|
119
|
+
**Protected branch check:** Read the protected set from `.codebyplan/git.json`:
|
|
120
|
+
```bash
|
|
121
|
+
PRODUCTION=$(jq -r '.branch_config.production // "main"' .codebyplan/git.json)
|
|
122
|
+
PROTECTED=$(jq -r '.branch_config.protected[]? // empty' .codebyplan/git.json)
|
|
123
|
+
```
|
|
124
|
+
If `$BRANCH_NAME` equals `$PRODUCTION` or appears in `$PROTECTED` — refuse deletion and stop.
|
|
120
125
|
|
|
121
126
|
**Checkpoint verification:** Before deleting a feat branch, verify that the associated checkpoint has completed via `/cbp-checkpoint-end`. If the checkpoint is still active, warn the user that unshipped work may be lost.
|
|
122
127
|
|
|
@@ -126,6 +131,17 @@ git branch -d "$BRANCH_NAME" && git push origin --delete "$BRANCH_NAME"
|
|
|
126
131
|
|
|
127
132
|
Use `-d` (not `-D`) to prevent deleting unmerged work. If it fails because the branch is not fully merged, inform the user and ask if they want to force delete with `-D`.
|
|
128
133
|
|
|
134
|
+
After the git branch delete succeeds, run a conditional Supabase preview-branch teardown for `$BRANCH_NAME`:
|
|
135
|
+
|
|
136
|
+
> Lifecycle contract: see [[supabase-branch-lifecycle]].
|
|
137
|
+
|
|
138
|
+
- Call `mcp__supabase__list_branches` with `project_id: rrvtrumtkhrsbhcyrwvf`.
|
|
139
|
+
- Scan the returned list for an entry whose `name` exactly equals `$BRANCH_NAME`.
|
|
140
|
+
- If found: call `mcp__supabase__delete_branch` with its `branch_id`. Report "Supabase preview branch deleted: `$BRANCH_NAME`".
|
|
141
|
+
- If not found: no-op silently — the GitHub integration may have already removed it on PR close; not-found is success, NOT an error.
|
|
142
|
+
- If the `list_branches` call itself fails (network, auth, or a non-success response — distinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$BRANCH_NAME` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
|
|
143
|
+
- Never delete the parent project `rrvtrumtkhrsbhcyrwvf` itself or any persistent/production branch.
|
|
144
|
+
|
|
129
145
|
### Step 10: Show Result
|
|
130
146
|
|
|
131
147
|
```
|