Files
maestro/ui/playwright.auth.config.ts
T
oss-sync 57702b4f34
CI / build-and-test (push) Canceled after 0s
sync: update from private repo (2336949a)
2026-09-17 11:29:31 +00:00

154 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { defineConfig, devices } from '@playwright/test';
import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
import { join } from 'node:path';
import { createOwnedRunDir } from '../scripts/e2e-run-dir.mjs';
// __dirname under ESM.
const __dirname = dirname(fileURLToPath(import.meta.url));
// Repo root is one level up from ui/.
const repoRoot = resolve(__dirname, '..');
// Separate port from the auth-OFF harness (8788) so both configs can coexist.
if (!process.env.E2E_AUTH_PORT) process.env.E2E_AUTH_PORT = execFileSync(process.execPath, ['-e', "const s=require('net').createServer();s.listen(0,'127.0.0.1',()=>{console.log(s.address().port);s.close()})"], { encoding: 'utf8' }).trim();
const PORT = Number(process.env.E2E_AUTH_PORT);
const BASE_URL = `http://127.0.0.1:${PORT}`;
// Isolated throwaway data + workspace dirs — never touches real ./data/maestro.db.
//
// Mint the run directory once and propagate it through the environment. Config
// worker imports inherit the same value, while concurrent runs/worktrees never
// share or delete one another's database.
// E2E_RUN_OWNED が立っているのに実行用ディレクトリが無い状態は、手で
// 環境変数を export したときにしか作れない。ここで先に落とす: 一時
// ディレクトリを 1 つでも作った後に投げると、config が完走せず
// globalTeardown も走らないため、作った分が掃除されず残る。
if (process.env.E2E_RUN_OWNED === '1' && !process.env.E2E_RUN_TMP) {
throw new Error('E2E_RUN_OWNED=1 but E2E_RUN_TMP is not set');
}
const cache = join(repoRoot, 'node_modules', '.cache');
mkdirSync(cache, { recursive: true });
if (!process.env.E2E_AUTH_TMP) {
const ownedData = createOwnedRunDir(cache, 'maestro-e2e-auth-data-');
process.env.E2E_AUTH_TMP = ownedData.directory;
process.env.E2E_DATA_TOKEN = ownedData.token;
}
const e2eTmp = process.env.E2E_AUTH_TMP;
if (process.env.E2E_RUN_OWNED !== '1') {
const ownedRun = createOwnedRunDir(cache, 'maestro-e2e-auth-run-');
process.env.E2E_RUN_TMP = ownedRun.directory;
process.env.E2E_RUN_TOKEN = ownedRun.token;
process.env.E2E_RUN_OWNED = '1';
}
process.env.E2E_RUN_PREFIX = 'maestro-e2e-auth-run-';
// 冒頭のガードが「OWNED=1 なのに未設定」を弾き、上の分岐が「未 OWNED」を
// 必ず設定する。したがってここでは必ず値がある(TS はこの 2 段を追えない)。
const e2eRunTmp = process.env.E2E_RUN_TMP!;
process.env.E2E_CACHE_ROOT = cache;
const DB_PATH = join(e2eTmp, 'e2e-auth.db');
const WORKTREE_DIR = join(e2eTmp, 'workspaces');
// The checked-in local-auth config YAML (enables POST /auth/local login).
const AUTH_CONFIG_SOURCE = resolve(repoRoot, 'ui/e2e-auth/config.e2e-auth.yaml');
// Assemble a THROWAWAY config for the webServer: the checked-in local-auth YAML
// plus one harness-only section appended at run time. Same shape as the auth-OFF
// harness (ui/playwright.config.ts, #1394); the extra section is deliberately NOT
// written into ui/e2e-auth/config.e2e-auth.yaml, because a checked-in config that
// says "outbound network is allowed" reads like part of the production setup.
//
// What gets appended and why (#1399): WITHOUT `safety.bash_allow_network: true`
// the webServer refuses to boot on any host where bwrap cannot create a user
// namespace. `safety.bash_sandbox` defaults to `auto`, and worker-bootstrap.ts
// (#1235) treats "network blocking is configured but cannot take effect" as a
// startup error rather than a silent downgrade. The dev container hits exactly
// that (the bwrap binary is present, `--unshare-all` is denied), and whether a CI
// runner does depends on its container settings — pinning this makes the boot
// outcome the same everywhere instead of a property of the host. Nothing is
// actually opened up: these specs never reach an LLM, so no agent Bash command
// ever runs.
//
// The write is unconditional on every config evaluation (main process + worker).
// That is idempotent: e2eTmp is stable across evaluations in this process and is
// inherited by the webServer subprocess via the environment.
const AAO_CONFIG = join(e2eTmp, 'e2e-auth-config.yaml');
writeFileSync(
AAO_CONFIG,
`${readFileSync(AUTH_CONFIG_SOURCE, 'utf8')}\nsafety:\n bash_allow_network: true\n`,
);
export default defineConfig({
testDir: resolve(__dirname, 'e2e-auth'),
testMatch: '**/*.spec.ts',
// Build+boot can be slow; roomy per-test budget.
timeout: 60_000,
expect: { timeout: 15_000 },
// 走り全体の上限。1 件 60 秒 × 21 件が全部詰まると、サーバー起動の 120 秒と
// 合わせて CI のジョブ上限(30 分・ci.yml の timeout-minutes)を食い破り、
// ログが途中で切れた赤になって原因が読めなくなる。実測は 21 件で 58.6 秒
// (CI run 3212)なので 10 分は十分な余裕がありつつ、暴走はここで止まる。
globalTimeout: 600_000,
fullyParallel: false,
workers: 1,
retries: 0,
reporter: [['list']],
globalTeardown: resolve(__dirname, 'e2e-global-teardown.ts'),
use: {
baseURL: BASE_URL,
headless: true,
// 表示言語を日本語に固定する。ここが抜けていると UI は英語で描画され、
// e2e-auth の spec が持つ日本語の文字列('追加'・'リンクが無効です' 等)は
// 一つも当たらない。認証なし側(ui/playwright.config.ts)は最初からこれを
// 置いていて、こちら側だけ抜けていた(#1399)。
locale: 'ja-JP',
// NOTE: the wide viewport that keeps the desktop nav (data-testid="nav-*")
// out of the hamburger drawer is set on the chromium project below, AFTER
// the Desktop Chrome device spread (which would otherwise pin 1280×720).
// Under LOCAL AUTH the TopBar carries extra right-side chrome (user chip,
// change-password, logout) that collapses it to compact mode at 1280 — and
// the drawer nav exposes no nav-* testids.
// This sandbox disables the Chromium sandbox (bwrap/userns off). Launch
// with --no-sandbox or every browser launch fails to spawn.
launchOptions: {
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
},
trace: 'retain-on-failure',
},
projects: [
{
name: 'chromium',
// Device spread sets 1280×720; override AFTER it so the wide viewport wins.
use: { ...devices['Desktop Chrome'], viewport: { width: 1600, height: 900 } },
},
],
// Boot the real orchestrator (worker mode) with LOCAL AUTH ON (AAO_CONFIG
// points at the local-auth YAML). The bootstrap admin is seeded at startup;
// member users are seeded by the spec's beforeAll into the same temp DB.
// The webServer command builds both dist trees before starting Node.
webServer: {
command: 'node scripts/e2e-web-server.mjs',
cwd: repoRoot,
url: `${BASE_URL}/ui`,
reuseExistingServer: false,
timeout: 120_000,
stdout: 'pipe',
stderr: 'pipe',
env: {
...process.env,
PORT: String(PORT),
E2E_RUN_TMP: e2eRunTmp,
DB_PATH,
WORKTREE_DIR,
AAO_CONFIG,
AAO_MODE: 'worker',
LOG_LEVEL: 'warn',
MCP_ENCRYPTION_KEY: '00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff',
},
},
});