diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml
new file mode 100644
index 00000000..644576bd
--- /dev/null
+++ b/.github/workflows/e2e-tests.yml
@@ -0,0 +1,91 @@
+name: E2E Tests
+
+on:
+ pull_request:
+ branches:
+ - "*"
+ push:
+ branches:
+ - main
+ - master
+
+jobs:
+ e2e:
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "20"
+ cache: "npm"
+ cache-dependency-path: package-lock.json
+
+ - name: Install dependencies
+ # Use npm install instead of npm ci to correctly resolve platform-specific
+ # optional dependencies (e.g., @tailwindcss/oxide, lightningcss binaries)
+ run: npm install
+
+ - name: Install Linux native bindings
+ # Workaround for npm optional dependencies bug (npm/cli#4828)
+ # Explicitly install Linux bindings needed for build tools
+ run: |
+ npm install --no-save --force \
+ @rollup/rollup-linux-x64-gnu@4.53.3 \
+ @tailwindcss/oxide-linux-x64-gnu@4.1.17
+
+ - name: Install Playwright browsers
+ run: npx playwright install --with-deps chromium
+ working-directory: apps/app
+
+ - name: Build server
+ run: npm run build --workspace=apps/server
+
+ - name: Start backend server
+ run: npm run start --workspace=apps/server &
+ env:
+ PORT: 3008
+ NODE_ENV: test
+
+ - name: Wait for backend server
+ run: |
+ echo "Waiting for backend server to be ready..."
+ for i in {1..30}; do
+ if curl -s http://localhost:3008/api/health > /dev/null 2>&1; then
+ echo "Backend server is ready!"
+ exit 0
+ fi
+ echo "Waiting... ($i/30)"
+ sleep 1
+ done
+ echo "Backend server failed to start!"
+ exit 1
+
+ - name: Run E2E tests
+ # Playwright automatically starts the Next.js frontend via webServer config
+ # (see apps/app/playwright.config.ts) - no need to start it manually
+ run: npm run test --workspace=apps/app
+ env:
+ CI: true
+ NEXT_PUBLIC_SERVER_URL: http://localhost:3008
+ NEXT_PUBLIC_SKIP_SETUP: "true"
+
+ - name: Upload Playwright report
+ uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: playwright-report
+ path: apps/app/playwright-report/
+ retention-days: 7
+
+ - name: Upload test results
+ uses: actions/upload-artifact@v4
+ if: failure()
+ with:
+ name: test-results
+ path: apps/app/test-results/
+ retention-days: 7
diff --git a/apps/app/package.json b/apps/app/package.json
index a5bafda6..ad9100db 100644
--- a/apps/app/package.json
+++ b/apps/app/package.json
@@ -27,15 +27,19 @@
"postinstall": "electron-builder install-app-deps",
"start": "next start",
"lint": "eslint",
+ "pretest": "node scripts/setup-e2e-fixtures.js",
"test": "playwright test",
"test:headed": "playwright test --headed",
"dev:electron:wsl": "concurrently \"next dev -p 3007\" \"wait-on http://localhost:3007 && electron . --no-sandbox --disable-gpu\"",
"dev:electron:wsl:gpu": "concurrently \"next dev -p 3007\" \"wait-on http://localhost:3007 && MESA_D3D12_DEFAULT_ADAPTER_NAME=NVIDIA electron . --no-sandbox --disable-gpu-sandbox\""
},
"dependencies": {
+ "@codemirror/lang-xml": "^6.1.0",
+ "@codemirror/theme-one-dark": "^6.1.3",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
+ "@lezer/highlight": "^1.2.3",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
@@ -46,6 +50,7 @@
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@tanstack/react-query": "^5.90.12",
+ "@uiw/react-codemirror": "^4.25.4",
"@xterm/addon-fit": "^0.10.0",
"@xterm/addon-webgl": "^0.18.0",
"@xterm/xterm": "^5.5.0",
@@ -122,7 +127,9 @@
{
"from": "../../.env",
"to": ".env",
- "filter": ["**/*"]
+ "filter": [
+ "**/*"
+ ]
}
],
"mac": {
diff --git a/apps/app/playwright.config.ts b/apps/app/playwright.config.ts
index d01cb870..d4d0f866 100644
--- a/apps/app/playwright.config.ts
+++ b/apps/app/playwright.config.ts
@@ -30,6 +30,10 @@ export default defineConfig({
url: `http://localhost:${port}`,
reuseExistingServer: !process.env.CI,
timeout: 120000,
+ env: {
+ ...process.env,
+ NEXT_PUBLIC_SKIP_SETUP: "true",
+ },
},
}),
});
diff --git a/apps/app/scripts/setup-e2e-fixtures.js b/apps/app/scripts/setup-e2e-fixtures.js
new file mode 100644
index 00000000..63ad5a02
--- /dev/null
+++ b/apps/app/scripts/setup-e2e-fixtures.js
@@ -0,0 +1,49 @@
+#!/usr/bin/env node
+
+/**
+ * Setup script for E2E test fixtures
+ * Creates the necessary test fixture directories and files before running Playwright tests
+ */
+
+import * as fs from "fs";
+import * as path from "path";
+import { fileURLToPath } from "url";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+// Resolve workspace root (apps/app/scripts -> workspace root)
+const WORKSPACE_ROOT = path.resolve(__dirname, "../../..");
+const FIXTURE_PATH = path.join(WORKSPACE_ROOT, "test/fixtures/projectA");
+const SPEC_FILE_PATH = path.join(FIXTURE_PATH, ".automaker/app_spec.txt");
+
+const SPEC_CONTENT = `