Bridge Shim Port Configuration for Multi-Partition Test Execution
Bridge Shim Port Configuration for Multi-Partition Test Execution
Problem
When replacing native IPC (e.g., Tauri’s invoke()) with an HTTP bridge for testing, the bridge shim needs to know
which backend instance to target. Hardcoding the port in the shim breaks multi-partition setups where tests run against
different bridge instances in parallel.
Symptoms:
- All test partitions route to the same bridge port
- Tests in partition B modify data on partition A’s bridge
- Port configuration in
playwright.config.tshas no effect on actual HTTP calls
Root Cause
The bridge shim (bridge-inject.js) is a static JavaScript file injected into the page before the app loads. It
intercepts window.__TAURI_INTERNALS__.invoke() and replaces it with fetch() calls. If the target URL is hardcoded in
the shim, all test projects hit the same bridge regardless of their bridgePort configuration.
Solution
Use Playwright’s page.addInitScript() to inject port configuration before the shim, then have the shim read the
configuration at runtime.
1. Define a fixture option for the bridge port
export const test = base.extend<{ bridgePort: number; appPage: Page;}>({ bridgePort: [9990, { option: true }], // Default port, overridable per project
appPage: async ({ page, bridgePort }, use) => { // Step 1: Set the target URL BEFORE the shim loads await page.addInitScript( `window.__BRIDGE_BASE_URL = 'http://localhost:${bridgePort}';` );
// Step 2: Inject the shim (reads __BRIDGE_BASE_URL at runtime) if (bridgeShim) { await page.addInitScript(bridgeShim); }
// Step 3: Navigate (shim is active, routes to correct port) await page.goto('/'); await page.waitForSelector('aside', { timeout: 15_000 }); await use(page); },});2. Configure per-project ports in playwright.config.ts
projects: [ { name: 'workspace-pages', use: { ...devices['Desktop Chrome'], bridgePort: 9990 } as any, testMatch: '**/workspace-*.spec.ts', }, { name: 'editor-content', use: { ...devices['Desktop Chrome'], bridgePort: 9991 } as any, testMatch: '**/editor-*.spec.ts', }, // ...],3. Bridge shim reads the runtime configuration
const BASE_URL = window.__BRIDGE_BASE_URL || 'http://localhost:9990';
window.__TAURI_INTERNALS__ = { invoke: async (cmd, args) => { const response = await fetch(`${BASE_URL}/invoke/${cmd}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(args || {}), }); // ... error handling, response parsing },};Why addInitScript Order Matters
page.addInitScript() scripts execute in registration order, before page scripts load. Registering the port config
first ensures window.__BRIDGE_BASE_URL is set when the shim reads it:
1. addInitScript: window.__BRIDGE_BASE_URL = 'http://localhost:9991'2. addInitScript: bridge-inject.js (reads __BRIDGE_BASE_URL)3. Page scripts load (see __TAURI_INTERNALS__ already shimmed)API-Level Tests Can Also Use the Port
Tests that call bridge routes directly (bypassing the UI) can access bridgePort from the fixture:
test('folder status check', async ({ bridgePort }) => { const res = await fetch(`http://localhost:${bridgePort}/invoke/check_folder_status`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: '/some/path' }), }); expect(res.ok).toBe(true);});Prevention
Best Practices
- Never hardcode ports in the bridge shim — always use a runtime configuration variable
- Register port config before the shim in
addInitScriptorder - Use Playwright’s
option: truepattern for fixture values that vary per project - Type the fixture option with
as anyon theuseobject (TypeScript doesn’t know about custom options)
Warning Signs
- Test creates a page but it doesn’t appear — may be hitting the wrong bridge
- Tests “pass” but verify stale data — bridge instance has data from a different partition’s tests
- Port conflicts on startup — infrastructure script may be missing a partition
References
tests/e2e/seed.spec.ts— fixture implementation withbridgePortoptiontests/e2e/playwright.config.ts— per-project port configurationapps/http-bridge/bridge-inject.js— bridge shim reading__BRIDGE_BASE_URL
Bounded Loop with UUID Fallback for Unique Names Next
Partitioned Test Execution with Backend Data Isolation
Was this page helpful?
Thanks for your feedback!