@osfactory/har 1.15.1 → 1.16.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.
@@ -0,0 +1,25 @@
1
+ # Cypress end-to-end tests — official GitHub Action
2
+ # https://github.com/cypress-io/github-action
3
+ #
4
+ # Adapt `start` and `wait-on` to this repo's dev server. The action installs
5
+ # dependencies, downloads the Cypress binary, and runs `cypress run`.
6
+ name: Cypress tests
7
+
8
+ on:
9
+ push:
10
+ pull_request:
11
+
12
+ jobs:
13
+ cypress-run:
14
+ runs-on: ubuntu-24.04
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v4
18
+ - name: Cypress run
19
+ uses: cypress-io/github-action@v7
20
+ with:
21
+ start: npm run dev
22
+ wait-on: http://localhost:3000
23
+ browser: chrome
24
+ env:
25
+ CYPRESS_BASE_URL: http://localhost:3000
@@ -0,0 +1,81 @@
1
+ # Cypress (cypress-e2e)
2
+
3
+ Adapt specs under `cypress/e2e/` for your application. Full verification (`verify --full`) runs this stage when it is listed in `verificationStages` (the plugin adds it).
4
+
5
+ | Path | Purpose |
6
+ |------|---------|
7
+ | `cypress/e2e/` | End-to-end specs — extend when you add UI |
8
+ | `cypress/support/e2e.js` | Shared commands loaded before every spec |
9
+ | `cypress.config.js` | Base URL, spec pattern, artifact folders |
10
+
11
+ ## Run
12
+
13
+ After `har env launch <id>` and `npm install`:
14
+
15
+ ```bash
16
+ npx cypress verify
17
+ ./.har/stages/cypress-e2e.sh <id>
18
+ # included in:
19
+ har env verify <id> --full
20
+ ```
21
+
22
+ `cypress run` is headless. Cypress 16 deprecates the bundled Electron browser, so the stage uses Chrome when `google-chrome` is on PATH (or Google Chrome.app on macOS), then Chromium, then Electron. Set `HARNESS_CYPRESS_BROWSER` (`chrome`, `chromium`, `firefox`, `electron`) to override. Electron still runs in Cypress 16 and prints a deprecation warning.
23
+
24
+ ## Ports
25
+
26
+ The stage reads the slot env file and points Cypress at that slot:
27
+
28
+ | Variable | Source | Cypress |
29
+ |----------|--------|---------|
30
+ | Frontend | `FE_PORT` (or `HARNESS_FE_BASE_PORT + id * 10`) | `CYPRESS_BASE_URL` → `cy.visit('/')` |
31
+ | API | `API_PORT` (or `HARNESS_API_BASE_PORT + id * 10`) | `CYPRESS_API_URL` → `Cypress.expose('API_URL')` |
32
+ | Health path | `HARNESS_HEALTH_CHECK_PATH` (default `/health`) | `Cypress.expose('HEALTH_PATH')` |
33
+
34
+ Do not hardcode slot ports in specs. A monolith that serves UI and API on one port should set both bases to that origin in `.har/harness.env` (or only assert against `baseUrl`).
35
+
36
+ The scaffold `api.cy.js` expects `GET /health` to return 200. Change the path, or set `HARNESS_HEALTH_CHECK_PATH`, to match your app.
37
+
38
+ ## Artifacts
39
+
40
+ Written under the main repo's `.har/artifacts/cypress-e2e/` (not the worktree):
41
+
42
+ | Path | Contents |
43
+ |------|----------|
44
+ | `cypress.log` | Full `cypress run` output |
45
+ | `screenshots/` | Automatic failure screenshots |
46
+ | `videos/` | Present when you set `video: true` in `cypress.config.js` |
47
+
48
+ Video recording is off by default (`video: false`), matching current Cypress. Turn it on in config when you want a video per spec.
49
+
50
+ Exit codes use `--posix-exit-codes`: `0` when every spec passes, `1` when Cypress fails. The stage propagates that code. Without the flag, Cypress exits with the number of failed tests.
51
+
52
+ ## Linux libraries
53
+
54
+ `npx cypress verify` checks that the Electron binary can start. If it reports missing libraries, install the packages listed in the [Cypress Linux prerequisites](https://docs.cypress.io/app/get-started/install-cypress#Linux-Prerequisites) and verify again.
55
+
56
+ ## Existing Cypress projects
57
+
58
+ `add-plugin` will not overwrite a `cypress.config.js` you already have. Point `specPattern` at your specs, keep artifact folders under `.har/artifacts/cypress-e2e/`, and let the stage export `CYPRESS_BASE_URL` rather than a fixed `baseUrl`.
59
+
60
+ ## New UI features
61
+
62
+ Add or update a spec so `cypress-e2e` covers the change. Prefer one file per feature under `cypress/e2e/<feature>.cy.js`. Full verification must pass before done.
63
+
64
+ ## CI
65
+
66
+ `har env add-plugin cypress --with-ci` copies `.github/workflows/cypress.yml`, the official `cypress-io/github-action@v7` recipe. Change `start` and `wait-on` to this repo's dev command and URL.
67
+
68
+ ## Plugin updates
69
+
70
+ When HAR ships a new plugin template version, merge drift from:
71
+
72
+ ```bash
73
+ har env maintain
74
+ # review .har/maintain/plugins/cypress/
75
+ ```
76
+
77
+ Or refresh all plugin-owned files:
78
+
79
+ ```bash
80
+ har env add-plugin cypress --force
81
+ ```
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env bash
2
+ # Cypress end-to-end tests for an agent slot.
3
+ # Outputs JSON to stdout, human-readable progress to stderr.
4
+ #
5
+ # Usage: ./.har/stages/cypress-e2e.sh <agent-id>
6
+ # Prerequisite: har env launch <agent-id> AND npm install (Cypress binary)
7
+ # See: ./.har/stages/CYPRESS.md
8
+ set -euo pipefail
9
+
10
+ # 1.0 stage surface: the runner exports WORK_DIR, ENV_FILE, AGENT_ID and
11
+ # HAR_HARNESS_DIR, with harness.env and the slot env file already sourced —
12
+ # agent-slot.sh is retired (1.0 migration).
13
+ HARNESS_DIR="${HAR_HARNESS_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
14
+
15
+ AGENT_ID="${1:-${AGENT_ID:?Usage: cypress-e2e.sh <agent-id>}}"
16
+
17
+ # Direct invocation (the manifest nextSteps) has no runner in front of it.
18
+ # Recover the slot work dir from the registry launch wrote.
19
+ if [ -z "${ENV_FILE:-}" ] || [ -z "${WORK_DIR:-}" ]; then
20
+ SLOT_FILE="$HARNESS_DIR/slots/agent-${AGENT_ID}.json"
21
+ if [ -f "$SLOT_FILE" ]; then
22
+ SLOT_WORK="$(node -e 'const fs=require("fs"); const s=JSON.parse(fs.readFileSync(process.argv[1],"utf8")); process.stdout.write(s.workDir||"")' "$SLOT_FILE" 2>/dev/null || true)"
23
+ if [ -n "$SLOT_WORK" ] && [ -f "$SLOT_WORK/.env.agent.${AGENT_ID}" ]; then
24
+ WORK_DIR="$SLOT_WORK"
25
+ ENV_FILE="$SLOT_WORK/.env.agent.${AGENT_ID}"
26
+ set -a
27
+ # shellcheck disable=SC1091
28
+ [ -f "$HARNESS_DIR/harness.env" ] && . "$HARNESS_DIR/harness.env"
29
+ # shellcheck disable=SC1090
30
+ . "$ENV_FILE"
31
+ set +a
32
+ fi
33
+ fi
34
+ fi
35
+
36
+ now_ms() { node -e 'process.stdout.write(String(Date.now()))' 2>/dev/null || echo 0; }
37
+
38
+ log() { echo "==> [cypress-e2e agent-$AGENT_ID] $*" >&2; }
39
+
40
+ ENV_FILE="${ENV_FILE:?No slot env for agent ${AGENT_ID} — run har env launch ${AGENT_ID} first}"
41
+ WORK_DIR="${WORK_DIR:?No slot work dir for agent ${AGENT_ID} — run har env launch ${AGENT_ID} first}"
42
+
43
+ FE_PORT="${FE_PORT:-$(( ${HARNESS_FE_BASE_PORT:-3000} + AGENT_ID * 10 ))}"
44
+ API_PORT="${API_PORT:-$(( ${HARNESS_API_BASE_PORT:-8000} + AGENT_ID * 10 ))}"
45
+
46
+ export BASE_URL="${BASE_URL:-http://localhost:${FE_PORT}}"
47
+ export API_URL="${API_URL:-http://localhost:${API_PORT}}"
48
+ export CYPRESS_BASE_URL="${CYPRESS_BASE_URL:-$BASE_URL}"
49
+ export CYPRESS_API_URL="${CYPRESS_API_URL:-$API_URL}"
50
+ HEALTH_PATH="${HARNESS_HEALTH_CHECK_PATH:-${HARNESS_HEALTH_PATH:-/health}}"
51
+ export CYPRESS_HEALTH_PATH="${CYPRESS_HEALTH_PATH:-$HEALTH_PATH}"
52
+
53
+ # Artifacts land next to this harness (main checkout), not inside a worktree
54
+ # whose .env.agent file may rewrite REPO_ROOT.
55
+ ARTIFACT_DIR="$(cd "$HARNESS_DIR/.." && pwd)/.har/artifacts/cypress-e2e"
56
+ mkdir -p "$ARTIFACT_DIR"
57
+ export HARNESS_CYPRESS_ARTIFACT_DIR="$ARTIFACT_DIR"
58
+
59
+ # Cypress 16 deprecates the bundled Electron browser. Prefer an installed Chrome
60
+ # or Chromium; fall back to Electron so `npm install` is still enough to run.
61
+ if [ -n "${HARNESS_CYPRESS_BROWSER:-}" ]; then
62
+ BROWSER="$HARNESS_CYPRESS_BROWSER"
63
+ elif command -v google-chrome >/dev/null 2>&1 || command -v google-chrome-stable >/dev/null 2>&1 || [ -d "/Applications/Google Chrome.app" ]; then
64
+ BROWSER="chrome"
65
+ elif command -v chromium >/dev/null 2>&1 || command -v chromium-browser >/dev/null 2>&1; then
66
+ BROWSER="chromium"
67
+ else
68
+ BROWSER="electron"
69
+ fi
70
+ export HARNESS_CYPRESS_BROWSER="$BROWSER"
71
+
72
+ if [ ! -x "$WORK_DIR/node_modules/.bin/cypress" ]; then
73
+ echo "Error: Cypress is not installed in $WORK_DIR." >&2
74
+ echo " npm install" >&2
75
+ echo " npx cypress verify" >&2
76
+ echo " Then re-run: ./.har/stages/cypress-e2e.sh ${AGENT_ID}" >&2
77
+ exit 1
78
+ fi
79
+
80
+ log "Running Cypress ($BROWSER) against $CYPRESS_BASE_URL (API: $CYPRESS_API_URL)"
81
+ log "Work dir: $WORK_DIR"
82
+ log "Artifacts: $ARTIFACT_DIR"
83
+
84
+ START_TOTAL=$(now_ms)
85
+ LOG_FILE="$ARTIFACT_DIR/cypress.log"
86
+
87
+ set +e
88
+ (
89
+ cd "$WORK_DIR"
90
+ npx cypress run \
91
+ --e2e \
92
+ --browser "$BROWSER" \
93
+ --posix-exit-codes
94
+ ) >"$LOG_FILE" 2>&1
95
+ CYPRESS_EXIT=$?
96
+ set -e
97
+
98
+ END_TOTAL=$(now_ms)
99
+ TOTAL_MS=$(( END_TOTAL - START_TOTAL ))
100
+
101
+ if [ "$CYPRESS_EXIT" -eq 0 ]; then
102
+ log "Cypress passed (${TOTAL_MS}ms)"
103
+ else
104
+ log "Cypress failed with exit $CYPRESS_EXIT (${TOTAL_MS}ms) — see $LOG_FILE"
105
+ tail -30 "$LOG_FILE" | sed 's/^/ /' >&2
106
+ fi
107
+
108
+ HARNESS_CYPRESS_ARTIFACT_DIR="$ARTIFACT_DIR" node -e "
109
+ const fs = require('fs');
110
+ const artifactDir = process.env.HARNESS_CYPRESS_ARTIFACT_DIR;
111
+ const out = {
112
+ status: ${CYPRESS_EXIT} === 0 ? 'pass' : 'fail',
113
+ stageId: 'cypress-e2e',
114
+ kind: 'test',
115
+ agent_id: ${AGENT_ID},
116
+ total_ms: ${TOTAL_MS},
117
+ exit_code: ${CYPRESS_EXIT},
118
+ browser: process.env.HARNESS_CYPRESS_BROWSER || 'electron',
119
+ urls: [
120
+ { label: 'frontend', url: process.env.CYPRESS_BASE_URL || '' },
121
+ { label: 'api', url: process.env.CYPRESS_API_URL || '' },
122
+ ],
123
+ artifacts: [
124
+ { path: '.har/artifacts/cypress-e2e', kind: 'directory' },
125
+ { path: '.har/artifacts/cypress-e2e/cypress.log', kind: 'log' },
126
+ ],
127
+ };
128
+ if (fs.existsSync(artifactDir + '/screenshots')) {
129
+ out.artifacts.push({ path: '.har/artifacts/cypress-e2e/screenshots', kind: 'directory' });
130
+ }
131
+ process.stdout.write(JSON.stringify(out, null, 2) + '\n');
132
+ "
133
+
134
+ exit "$CYPRESS_EXIT"
@@ -0,0 +1,14 @@
1
+ # Cypress HAR plugin
2
+
3
+ This plugin was added by `har env add-plugin cypress`. It registers a `cypress-e2e` stage and scaffolds a minimal Cypress end-to-end suite.
4
+
5
+ ## Next steps
6
+
7
+ ```bash
8
+ npm install
9
+ npx cypress verify
10
+ har env launch 1
11
+ ./.har/stages/cypress-e2e.sh 1
12
+ ```
13
+
14
+ See `.har/stages/CYPRESS.md` for adaptation checklist, ports, and artifact layout.
@@ -0,0 +1,13 @@
1
+ // TODO: adapt the path for your app's health endpoint.
2
+ // Public config comes from cypress.config.js `expose` (Cypress.env was removed in Cypress 16).
3
+ describe('api health', () => {
4
+ it('health endpoint responds', () => {
5
+ const healthPath = Cypress.expose('HEALTH_PATH') || '/health';
6
+ const apiUrl = String(Cypress.expose('API_URL') || Cypress.config('baseUrl') || '').replace(
7
+ /\/$/,
8
+ '',
9
+ );
10
+ const requestPath = String(healthPath).startsWith('/') ? String(healthPath) : `/${healthPath}`;
11
+ cy.request(`${apiUrl}${requestPath}`).its('status').should('eq', 200);
12
+ });
13
+ });
@@ -0,0 +1,7 @@
1
+ // TODO: adapt routes and selectors for your app.
2
+ describe('smoke', () => {
3
+ it('homepage loads', () => {
4
+ cy.visit('/');
5
+ cy.get('body').should('be.visible');
6
+ });
7
+ });
@@ -0,0 +1 @@
1
+ // Loaded before every spec. Add app-wide commands here.
@@ -0,0 +1,68 @@
1
+ // Cypress config for the HAR `cypress-e2e` verification stage.
2
+ //
3
+ // Harness integration
4
+ // -------------------
5
+ // - Stage id: `cypress-e2e` (`.har/stages/cypress-e2e.sh`, registered in `stages.json`).
6
+ // - Runs on FULL verify only — not on quick verify:
7
+ // har env verify <id> --full
8
+ // - Quick verify stops at the quick-tier stages (typecheck, unit tests, api-health).
9
+ //
10
+ // Prerequisites
11
+ // -------------
12
+ // 1. Launch a slot first: har env launch <id>
13
+ // 2. Install Cypress: npm install && npx cypress verify
14
+ // The Cypress npm package downloads the Electron binary. On Linux, system
15
+ // libraries from the Cypress docs may also be required.
16
+ //
17
+ // Environment (injected by cypress-e2e.sh — never hardcode slot ports in specs)
18
+ // ------------------------------------------------------------------------------
19
+ // CYPRESS_BASE_URL Frontend origin for cy.visit('/') (slot FE port)
20
+ // CYPRESS_API_URL API origin (Cypress.expose('API_URL'); Cypress.env was removed in Cypress 16)
21
+ // CYPRESS_HEALTH_PATH Health path (Cypress.expose('HEALTH_PATH'), default /health)
22
+ // HARNESS_CYPRESS_ARTIFACT_DIR Absolute artifact directory for this run
23
+ // HARNESS_CYPRESS_BROWSER Browser passed to `cypress run` (chrome when installed, else electron)
24
+ //
25
+ // Test layout — agents must add or update specs for every UI change
26
+ // ------------------------------------------------------------------
27
+ // cypress/e2e/<feature>.cy.js End-to-end flows (prefer one file per feature)
28
+ //
29
+ // Full verify only proves existing specs pass. New UI behavior needs new or
30
+ // updated specs here — the harness does not auto-generate flows per feature.
31
+ // Widen `specPattern` to `cypress/e2e/**/*.cy.{js,jsx,ts,tsx}` when you add
32
+ // TypeScript specs.
33
+ //
34
+ // Artifacts (gitignored under .har/artifacts/)
35
+ // --------------------------------------------
36
+ // cypress-e2e/cypress.log Runner log
37
+ // cypress-e2e/screenshots/ Failure screenshots (cypress run)
38
+ // cypress-e2e/videos/ Videos when `video: true` (off by default)
39
+ //
40
+ // Local-only (without cypress-e2e.sh): npm run test:cypress
41
+ // Set CYPRESS_BASE_URL (and CYPRESS_API_URL if split) when the app is not on
42
+ // the default below.
43
+ const path = require('path');
44
+ const { defineConfig } = require('cypress');
45
+
46
+ const baseUrl = process.env.CYPRESS_BASE_URL || process.env.BASE_URL || 'http://localhost:3000';
47
+ const apiUrl = process.env.CYPRESS_API_URL || process.env.API_URL || baseUrl;
48
+ const healthPath = process.env.CYPRESS_HEALTH_PATH || '/health';
49
+ const artifactDir =
50
+ process.env.HARNESS_CYPRESS_ARTIFACT_DIR || path.join('.har', 'artifacts', 'cypress-e2e');
51
+
52
+ module.exports = defineConfig({
53
+ expose: {
54
+ API_URL: apiUrl,
55
+ HEALTH_PATH: healthPath,
56
+ },
57
+ e2e: {
58
+ baseUrl,
59
+ specPattern: 'cypress/e2e/**/*.cy.js',
60
+ supportFile: 'cypress/support/e2e.js',
61
+ fixturesFolder: false,
62
+ screenshotsFolder: path.join(artifactDir, 'screenshots'),
63
+ videosFolder: path.join(artifactDir, 'videos'),
64
+ downloadsFolder: path.join(artifactDir, 'downloads'),
65
+ video: false,
66
+ screenshotOnRunFailure: true,
67
+ },
68
+ });
@@ -0,0 +1,10 @@
1
+ {
2
+ "scripts": {
3
+ "test:cypress": "cypress run",
4
+ "test:cypress:open": "cypress open",
5
+ "test:cypress:ci": "cypress run --posix-exit-codes"
6
+ },
7
+ "devDependencies": {
8
+ "cypress": "^16.1.1"
9
+ }
10
+ }
@@ -0,0 +1,74 @@
1
+ {
2
+ "id": "cypress",
3
+ "stageId": "cypress-e2e",
4
+ "verificationStages": [
5
+ "cypress-e2e"
6
+ ],
7
+ "stage": {
8
+ "id": "cypress-e2e",
9
+ "kind": "test",
10
+ "description": "Cypress end-to-end tests against the slot's running app",
11
+ "script": "stages/cypress-e2e.sh",
12
+ "requiresAgentId": true,
13
+ "artifacts": [
14
+ {
15
+ "path": ".har/artifacts/cypress-e2e",
16
+ "kind": "directory",
17
+ "description": "Cypress run log, screenshots, and videos"
18
+ },
19
+ {
20
+ "path": ".har/artifacts/cypress-e2e/cypress.log",
21
+ "kind": "log"
22
+ }
23
+ ],
24
+ "tier": "full"
25
+ },
26
+ "files": [
27
+ {
28
+ "src": ".har/stages/cypress-e2e.sh",
29
+ "dest": ".har/stages/cypress-e2e.sh",
30
+ "executable": true
31
+ },
32
+ {
33
+ "src": ".har/stages/CYPRESS.md",
34
+ "dest": ".har/stages/CYPRESS.md"
35
+ },
36
+ {
37
+ "src": "cypress.config.js",
38
+ "dest": "cypress.config.js"
39
+ },
40
+ {
41
+ "src": "cypress/e2e/smoke.cy.js",
42
+ "dest": "cypress/e2e/smoke.cy.js"
43
+ },
44
+ {
45
+ "src": "cypress/e2e/api.cy.js",
46
+ "dest": "cypress/e2e/api.cy.js"
47
+ },
48
+ {
49
+ "src": "cypress/support/e2e.js",
50
+ "dest": "cypress/support/e2e.js"
51
+ },
52
+ {
53
+ "src": "README.md",
54
+ "dest": "cypress/README.md"
55
+ }
56
+ ],
57
+ "optionalFiles": [
58
+ {
59
+ "src": ".github/workflows/cypress.yml",
60
+ "dest": ".github/workflows/cypress.yml",
61
+ "skipFlag": "skipCi"
62
+ }
63
+ ],
64
+ "merge": {
65
+ "package.json": "package.fragment.json"
66
+ },
67
+ "nextSteps": [
68
+ "npm install",
69
+ "npx cypress verify",
70
+ "har env launch 1",
71
+ "./.har/stages/cypress-e2e.sh 1"
72
+ ],
73
+ "docsPath": ".har/stages/CYPRESS.md"
74
+ }
@@ -104,7 +104,8 @@ committed.
104
104
  playwright` (web), `har env add-plugin rocketsim` (iOS), or `har env
105
105
  add-plugin kerno` (backend), `har env add-plugin gitleaks` (secrets scanning, any
106
106
  stack), or `har env add-plugin trivy` (dependency + IaC security scan, any stack),
107
- or `har env add-plugin semgrep` (SAST, any stack)
107
+ or `har env add-plugin semgrep` (SAST, any stack),
108
+ or `har env add-plugin cypress` (browser e2e)
108
109
  installs one. A
109
110
  plugin is just packaging: it copies files, merges `package.json` fragments,
110
111
  and registers stages through the exact same registry as everything else.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osfactory/har",
3
- "version": "1.15.1",
3
+ "version": "1.16.0",
4
4
  "description": "HAR: open agent harness CLI and MCP server for coding agents, isolated worktrees, and software-factory workflows",
5
5
  "keywords": [
6
6
  "har",