PluginProbe
Extendify / 3.1.5
Extendify v3.1.5
3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 0.7.0 All 126 releases
extendify / playwright.config.ts

playwright.config.ts in Extendify 3.1.5, at playwright.config.ts

149 lines 5.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
2 import { dirname, join, relative, resolve, sep } from 'node:path';
3 import { defineConfig, devices } from '@playwright/test';
4
5 const ROOT = resolve(__dirname);
6 const TEST_DIR = resolve(ROOT, 'tests/playwright');
7 const BASE_PORT = Number(process.env.BASE_PORT ?? 9400);
8 const WP_VERSION = process.env.WP_VERSION ?? 'latest';
9 const RUN_PROJECT = process.env.RUN_PROJECT;
10
11 const walkSpecs = (dir: string): string[] => {
12 const out: string[] = [];
13 for (const entry of readdirSync(dir)) {
14 const full = join(dir, entry);
15 const stat = statSync(full);
16 if (stat.isDirectory()) {
17 out.push(...walkSpecs(full));
18 continue;
19 }
20 if (entry.endsWith('.spec.ts')) out.push(full);
21 }
22 return out;
23 };
24
25 const closestBlueprint = (specPath: string): string | null => {
26 let dir = dirname(specPath);
27 while (dir.startsWith(TEST_DIR) || dir === TEST_DIR) {
28 const candidate = join(dir, 'blueprint.json');
29 if (existsSync(candidate)) return candidate;
30 if (dir === TEST_DIR) return null;
31 dir = dirname(dir);
32 }
33 return null;
34 };
35
36 const projectName = (specPath: string): string =>
37 relative(TEST_DIR, specPath)
38 .replace(/\.spec\.ts$/, '')
39 .split(sep)
40 .join('/');
41
42 // First-line `// e2e:disabled` marker — see tests/playwright/scripts/discover-playwright-projects.mjs.
43 // RUN_PROJECT explicitly opting in to a disabled spec still wins (for local debug).
44 const isDisabled = (spec: string): boolean =>
45 readFileSync(spec, 'utf8').split('\n', 1)[0].includes('e2e:disabled');
46
47 const specs = existsSync(TEST_DIR)
48 ? walkSpecs(TEST_DIR).filter(
49 (s) => !isDisabled(s) || projectName(s) === RUN_PROJECT,
50 )
51 : [];
52
53 const blueprintPort = new Map<string, number>();
54 for (const spec of specs) {
55 const blueprint = closestBlueprint(spec);
56 if (!blueprint) continue;
57 if (!blueprintPort.has(blueprint)) {
58 blueprintPort.set(blueprint, BASE_PORT + blueprintPort.size);
59 }
60 }
61
62 const projectBlueprint = new Map<string, string>();
63
64 const allProjects = specs
65 .map((spec) => {
66 const blueprint = closestBlueprint(spec);
67 const port = blueprint ? blueprintPort.get(blueprint) : undefined;
68 if (!port || !blueprint) return null;
69 const name = projectName(spec);
70 projectBlueprint.set(name, blueprint);
71 return {
72 name,
73 testMatch: relative(ROOT, spec),
74 use: {
75 ...devices['Desktop Chrome'],
76 baseURL: `http://127.0.0.1:${port}`,
77 },
78 };
79 })
80 .filter((p): p is NonNullable<typeof p> => p !== null);
81
82 const projects = RUN_PROJECT
83 ? allProjects.filter((p) => p.name === RUN_PROJECT)
84 : allProjects;
85
86 // Only spin up the webServer(s) the selected projects actually need. Spawning
87 // every blueprint's playground when only one project will run wastes boot time
88 // and burns ports.
89 const activeBlueprints = new Set(
90 projects.map((p) => projectBlueprint.get(p.name)).filter(Boolean) as string[],
91 );
92 const webServers = [...blueprintPort.entries()]
93 .filter(([blueprint]) => activeBlueprints.has(blueprint))
94 .map(([blueprint, port]) => ({
95 // PHP version is pinned per-blueprint via `preferredVersions.php` — the
96 // CLI `--php` flag is silently ignored by wp-playground-cli. Pinned to
97 // 8.3 to dodge a PHP 8.5 deprecation in vendor/react/promise that
98 // breaks ?action=rest-nonce by printing inline when WP_DEBUG_DISPLAY is
99 // on. Drop the pin once react/promise ships an 8.5-compatible release.
100 command: `npx wp-playground-cli server --auto-mount --blueprint=${relative(ROOT, blueprint)} --wp=${WP_VERSION} --port=${port} --internal-cookie-store=true --login=false`,
101 url: `http://127.0.0.1:${port}`,
102 reuseExistingServer: !process.env.CI,
103 timeout: 120_000,
104 stdout: 'pipe' as const,
105 stderr: 'pipe' as const,
106 }));
107
108 // RequestUtils caches the discovered REST root to this file and reuses it
109 // whenever it is already populated, skipping rediscovery. The default path is
110 // one shared file and playground ports shift between runs, so a later run can
111 // POST to whichever server held that port before. Scope it to the process.
112 if (!process.env.STORAGE_STATE_PATH) {
113 process.env.STORAGE_STATE_PATH = resolve(
114 ROOT,
115 `artifacts/storage-states/admin-${process.pid}.json`,
116 );
117 }
118
119 // @wordpress/e2e-test-utils-playwright's RequestUtils HEADs process.env.WP_BASE_URL
120 // (default http://localhost:8889 — wp-env's default port) to find the REST root,
121 // regardless of each request context's configured baseURL. Pin it to the selected
122 // project's playground port so requestUtils.login() reaches the right server.
123 if (projects.length > 0 && !process.env.WP_BASE_URL) {
124 const firstPort = new URL(projects[0].use.baseURL).port;
125 process.env.WP_BASE_URL = `http://127.0.0.1:${firstPort}`;
126 }
127
128 export default defineConfig({
129 testDir: TEST_DIR,
130 globalSetup: './tests/playwright/global-setup.ts',
131 forbidOnly: !!process.env.CI,
132 retries: process.env.CI ? 1 : 0,
133 workers: 1,
134 // 30s default is too tight for this wp-playground suite on cold CI — the
135 // reload-based save specs run ~25-30s even locally.
136 timeout: 120_000,
137 // Raise the per-assertion floor from 5s to the 15s specs already hand-patch.
138 expect: { timeout: 15_000 },
139 reporter: process.env.CI ? [['github'], ['html', { open: 'never' }]] : 'list',
140 use: {
141 trace: 'on-first-retry',
142 video: 'retain-on-failure',
143 screenshot: 'only-on-failure',
144 testIdAttribute: 'data-test',
145 },
146 webServer: webServers.length > 0 ? webServers : undefined,
147 projects,
148 });
149