diff --git a/README.md b/README.md index 59dedf6..31c1333 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,8 @@ This MVP uses Drizzle with Bun SQLite for local file-backed persistence. - `bun run test`: run the unit test stage in all workspaces. - `bun run test:unit`: run Vitest explicitly as the unit test stage. - `bun run test:e2e`: build and run Playwright browser smoke and QA checks. -- `bun run test:qa`: run the release gate through Turbo. +- `bun run test:qa`: run the release gate through Turbo across check, unit, + build, Storybook, and e2e tasks. - `bun run build`: build the UI package, production website, and Bun server. - `bun run preview`: preview the production web build. - `bun run storybook`: start the UI package component explorer on port 6006. @@ -102,7 +103,9 @@ bun run test:qa The gate runs TypeScript checks, Vitest coverage for model, persistence, renderer, datasource mocks, and presentation boundaries, the production build, the static Storybook build, and Playwright desktop/mobile -smoke checks against the built adapter output. Playwright also performs +smoke checks against the built adapter output. Turbo owns the release task +graph; app package scripts stay as leaf commands and do not re-run the QA +pipeline internally. Playwright also performs baseline screenshot checks, keyboard navigation checks, reduced-motion checks, landmark checks, and axe accessibility checks against the real model-driven route. diff --git a/apps/web/package.json b/apps/web/package.json index 7875aa8..c2c183f 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -11,7 +11,6 @@ "test": "vitest run", "test:unit": "vitest run", "test:e2e": "env -u NO_COLOR playwright test", - "test:qa": "bun run check && bun run test:unit && bun run build && bun run test:e2e", "db:generate": "drizzle-kit generate", "db:check": "drizzle-kit check" }, diff --git a/apps/web/playwright.config.ts b/apps/web/playwright.config.ts index afba557..0dbd949 100644 --- a/apps/web/playwright.config.ts +++ b/apps/web/playwright.config.ts @@ -27,7 +27,7 @@ export default defineConfig({ timeout: 120_000, }, { - command: `cd ../.. && bun run build-storybook && STORYBOOK_STATIC_PORT=${storybookPort} bun apps/web/tests/e2e/storybook-server.ts`, + command: `cd ../.. && STORYBOOK_STATIC_PORT=${storybookPort} bun apps/web/tests/e2e/storybook-server.ts`, url: storybookURL, reuseExistingServer: false, timeout: 120_000, diff --git a/apps/web/src/lib/workspace-boundary.test.ts b/apps/web/src/lib/workspace-boundary.test.ts index 66a11c5..5707a8c 100644 --- a/apps/web/src/lib/workspace-boundary.test.ts +++ b/apps/web/src/lib/workspace-boundary.test.ts @@ -18,10 +18,61 @@ describe("workspace boundaries", () => { expect(packageJson.private).toBe(true); expect(packageJson.workspaces).toEqual(["apps/*", "packages/*"]); - expect(packageJson.scripts?.build).toBe("turbo build"); + expect(packageJson.scripts?.build).toBe("turbo run build"); expect(existsSync(join(root, "turbo.json"))).toBe(true); }); + test("keeps release orchestration in the root turbo task graph", () => { + const rootPackage = JSON.parse( + readFileSync(join(root, "package.json"), "utf8"), + ) as { + scripts?: Record; + }; + const webPackage = JSON.parse( + readFileSync(join(root, "apps/web/package.json"), "utf8"), + ) as { scripts?: Record }; + const turboConfig = JSON.parse( + readFileSync(join(root, "turbo.json"), "utf8"), + ) as { + globalDependencies?: string[]; + tasks?: Record; + }; + + expect(rootPackage.scripts?.["test:qa"]).toBe( + "turbo run check test:unit build @dimensionlab/ui#build-storybook @dimensionlab/web#test:e2e", + ); + expect(webPackage.scripts).not.toHaveProperty("test:qa"); + expect(turboConfig.tasks).not.toHaveProperty("test:qa"); + expect(turboConfig.globalDependencies).toEqual( + expect.arrayContaining(["bun.lock", "tsconfig.base.json"]), + ); + expect(turboConfig.tasks?.["test:e2e"]?.env).toEqual( + expect.arrayContaining([ + "CI", + "PLAYWRIGHT_DATABASE_URL", + "PLAYWRIGHT_PORT", + "PLAYWRIGHT_STORYBOOK_PORT", + ]), + ); + }); + + test("lets turbo build Storybook before e2e serves the static artifact", () => { + const playwrightConfig = readFileSync( + join(root, "apps/web/playwright.config.ts"), + "utf8", + ); + const turboConfig = JSON.parse( + readFileSync(join(root, "turbo.json"), "utf8"), + ) as { + tasks?: Record; + }; + + expect(playwrightConfig).not.toContain("bun run build-storybook"); + expect(turboConfig.tasks?.["test:e2e"]?.dependsOn).toEqual( + expect.arrayContaining(["@dimensionlab/ui#build-storybook"]), + ); + }); + test("keeps the website app and reusable UI library as separate packages", () => { const webPackage = JSON.parse( readFileSync(join(root, "apps/web/package.json"), "utf8"), diff --git a/package.json b/package.json index 5b94c8f..e33d3aa 100644 --- a/package.json +++ b/package.json @@ -9,18 +9,18 @@ "packages/*" ], "scripts": { - "dev": "turbo dev --filter=@dimensionlab/web", - "build": "turbo build", - "preview": "bun run --cwd apps/web preview", - "storybook": "turbo storybook --filter=@dimensionlab/ui", - "build-storybook": "turbo build-storybook --filter=@dimensionlab/ui", - "check": "turbo check", - "test": "turbo test:unit", - "test:unit": "turbo test:unit", - "test:e2e": "turbo test:e2e --filter=@dimensionlab/web", - "test:qa": "turbo test:qa", - "db:generate": "bun run --cwd apps/web db:generate", - "db:check": "bun run --cwd apps/web db:check" + "dev": "turbo run dev --filter=@dimensionlab/web", + "build": "turbo run build", + "preview": "turbo run preview --filter=@dimensionlab/web", + "storybook": "turbo run storybook --filter=@dimensionlab/ui", + "build-storybook": "turbo run build-storybook --filter=@dimensionlab/ui", + "check": "turbo run check", + "test": "turbo run test:unit", + "test:unit": "turbo run test:unit", + "test:e2e": "turbo run test:e2e --filter=@dimensionlab/web", + "test:qa": "turbo run check test:unit build @dimensionlab/ui#build-storybook @dimensionlab/web#test:e2e", + "db:generate": "turbo run db:generate --filter=@dimensionlab/web", + "db:check": "turbo run db:check --filter=@dimensionlab/web" }, "devDependencies": { "turbo": "^2.5.0" diff --git a/turbo.json b/turbo.json index ee027a8..446d6fe 100644 --- a/turbo.json +++ b/turbo.json @@ -1,9 +1,16 @@ { "$schema": "https://turbo.build/schema.json", + "globalDependencies": [ + "bun.lock", + "package.json", + "tsconfig.base.json", + "tsconfig.json" + ], "tasks": { "build": { "dependsOn": ["^build"], - "outputs": ["dist/**", "build/**"] + "outputs": ["dist/**", "build/**"], + "env": ["NODE_ENV", "VITE_*"] }, "check": { "dependsOn": ["^build"], @@ -11,33 +18,78 @@ }, "test:unit": { "dependsOn": ["^build"], - "outputs": [] + "outputs": [], + "env": [ + "AGENT_CONFIG_TOKEN", + "DASHBOARD_MIGRATIONS_DIR", + "DATABASE_URL", + "DISABLE_LIVE_DATASOURCES", + "PROMETHEUS_BASE_URL" + ] }, "build-storybook": { "dependsOn": ["^build"], - "outputs": ["storybook-static/**"] + "outputs": ["storybook-static/**"], + "env": ["NODE_ENV", "STORYBOOK_*", "VITE_*"] }, "test:e2e": { - "dependsOn": ["build", "^build"], - "outputs": ["test-results/**", "playwright-report/**"] - }, - "test:qa": { "dependsOn": [ - "check", - "test:unit", "build", - "build-storybook", - "test:e2e" + "^build", + "@dimensionlab/ui#build-storybook" ], - "outputs": [] + "outputs": ["test-results/**", "playwright-report/**"], + "env": [ + "AGENT_CONFIG_TOKEN", + "CI", + "DASHBOARD_MIGRATIONS_DIR", + "DATABASE_URL", + "DISABLE_LIVE_DATASOURCES", + "PLAYWRIGHT_DATABASE_URL", + "PLAYWRIGHT_PORT", + "PLAYWRIGHT_STORYBOOK_PORT", + "PROMETHEUS_BASE_URL", + "STORYBOOK_STATIC_PORT" + ] + }, + "db:generate": { + "cache": false + }, + "db:check": { + "outputs": [], + "env": ["DATABASE_URL"] }, "dev": { "cache": false, - "persistent": true + "persistent": true, + "env": [ + "AGENT_CONFIG_TOKEN", + "DASHBOARD_DEV_API_HOST", + "DASHBOARD_DEV_API_PORT", + "DASHBOARD_DEV_API_TARGET", + "DASHBOARD_MIGRATIONS_DIR", + "DATABASE_URL", + "HOST", + "PORT", + "PROMETHEUS_BASE_URL" + ] + }, + "preview": { + "cache": false, + "persistent": true, + "env": [ + "AGENT_CONFIG_TOKEN", + "DASHBOARD_MIGRATIONS_DIR", + "DATABASE_URL", + "HOST", + "PORT", + "PROMETHEUS_BASE_URL" + ] }, "storybook": { "cache": false, - "persistent": true + "persistent": true, + "env": ["HOST", "PORT", "STORYBOOK_*", "VITE_*"] } } }