|
16 | 16 |
|
17 | 17 | import { expect, request as playwrightRequest, test } from '@playwright/test'; |
18 | 18 | import type { APIRequestContext } from '@playwright/test'; |
19 | | -import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from 'node:child_process'; |
| 19 | +import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'; |
20 | 20 | import * as fs from 'node:fs'; |
21 | 21 | import * as net from 'node:net'; |
22 | 22 | import * as os from 'node:os'; |
23 | 23 | import * as path from 'node:path'; |
24 | 24 | import { fileURLToPath } from 'node:url'; |
| 25 | +import { ensureDdxE2EBinary } from './ddx-binary'; |
25 | 26 |
|
26 | 27 | const FRONTEND_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); |
27 | | -const CLI_DIR = path.resolve(FRONTEND_DIR, '../../..'); |
28 | 28 | const FIXTURE_DIR = path.resolve(FRONTEND_DIR, 'e2e/fixtures'); |
29 | 29 |
|
30 | | -let ddxBinary: string | null = null; |
31 | | - |
32 | | -function ensureDdxBinary(): string { |
33 | | - if (ddxBinary) return ddxBinary; |
34 | | - const binDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ddx-fed-e2e-bin-')); |
35 | | - ddxBinary = path.join(binDir, process.platform === 'win32' ? 'ddx-fed-e2e.exe' : 'ddx-fed-e2e'); |
36 | | - const result = spawnSync('go', ['build', '-o', ddxBinary, '.'], { |
37 | | - cwd: CLI_DIR, |
38 | | - env: process.env, |
39 | | - encoding: 'utf8' |
40 | | - }); |
41 | | - if (result.status !== 0) { |
42 | | - throw new Error(`failed to build ddx test binary\n${result.stdout}\n${result.stderr}`); |
43 | | - } |
44 | | - return ddxBinary; |
45 | | -} |
46 | | - |
47 | 30 | async function freePort(): Promise<number> { |
48 | 31 | return new Promise((resolve, reject) => { |
49 | 32 | const srv = net.createServer(); |
@@ -125,18 +108,11 @@ interface SpawnOpts { |
125 | 108 | } |
126 | 109 |
|
127 | 110 | async function spawnServer(opts: SpawnOpts): Promise<SpawnedServer> { |
128 | | - const bin = ensureDdxBinary(); |
| 111 | + const bin = ensureDdxE2EBinary(); |
129 | 112 | const port = await freePort(); |
130 | 113 | const bindAddr = opts.bindAddr ?? '127.0.0.1'; |
131 | 114 | const root = opts.reuseRoot ?? copyFixture(); |
132 | | - const args = [ |
133 | | - 'server', |
134 | | - '--port', |
135 | | - String(port), |
136 | | - '--addr', |
137 | | - bindAddr, |
138 | | - '--tsnet=false' |
139 | | - ]; |
| 115 | + const args = ['server', '--port', String(port), '--addr', bindAddr, '--tsnet=false']; |
140 | 116 | if (opts.hubMode) args.push('--hub-mode'); |
141 | 117 | if (opts.allowPlainHTTP) args.push('--federation-allow-plain-http'); |
142 | 118 | if (opts.hubURL) args.push('--hub-address', opts.hubURL); |
@@ -198,13 +174,12 @@ async function nodeIdOf(s: SpawnedServer): Promise<string> { |
198 | 174 | return body.data.nodeInfo.id; |
199 | 175 | } |
200 | 176 |
|
201 | | -async function federationNodes(s: SpawnedServer): Promise< |
202 | | - Array<{ nodeId: string; status: string; name: string }> |
203 | | -> { |
| 177 | +async function federationNodes( |
| 178 | + s: SpawnedServer |
| 179 | +): Promise<Array<{ nodeId: string; status: string; name: string }>> { |
204 | 180 | const r = await s.api.post('/graphql', { |
205 | 181 | data: { |
206 | | - query: |
207 | | - '{ federationNodes { nodeId status name } }' |
| 182 | + query: '{ federationNodes { nodeId status name } }' |
208 | 183 | } |
209 | 184 | }); |
210 | 185 | const body = (await r.json()) as { |
@@ -292,10 +267,9 @@ test.describe('federation 2-node e2e', () => { |
292 | 267 | await expect(page.getByTestId('scope-toggle')).toContainText('federation'); |
293 | 268 | // Both fixture beads exist; expect at least one row per node by |
294 | 269 | // their fixture title prefix. |
295 | | - await expect.poll( |
296 | | - async () => await page.getByText('Open ready bead').count(), |
297 | | - { timeout: 10_000 } |
298 | | - ).toBeGreaterThanOrEqual(2); |
| 270 | + await expect |
| 271 | + .poll(async () => await page.getByText('Open ready bead').count(), { timeout: 10_000 }) |
| 272 | + .toBeGreaterThanOrEqual(2); |
299 | 273 |
|
300 | 274 | // Toggle switches LOCAL vs FEDERATION. |
301 | 275 | await page.getByTestId('scope-toggle').click(); |
@@ -329,9 +303,9 @@ test.describe('federation 2-node e2e', () => { |
329 | 303 | await page.goto(`${hub.baseURL}/federation`); |
330 | 304 | const offlineRow = page.locator('[data-testid="federation-row"][data-status="offline"]'); |
331 | 305 | await expect(offlineRow).toHaveCount(1); |
332 | | - await expect( |
333 | | - offlineRow.locator('[data-testid="federation-status-badge"]') |
334 | | - ).toContainText(/offline/i); |
| 306 | + await expect(offlineRow.locator('[data-testid="federation-status-badge"]')).toContainText( |
| 307 | + /offline/i |
| 308 | + ); |
335 | 309 |
|
336 | 310 | // Restart the spoke — registration alone (handshake → StatusActive) |
337 | 311 | // returns it to active without waiting on a heartbeat tick. |
|
0 commit comments