Skip to content
Documentation GitHub
Patterns

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.ts has 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

seed.spec.ts
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

bridge-inject.js
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 addInitScript order
  • Use Playwright’s option: true pattern for fixture values that vary per project
  • Type the fixture option with as any on the use object (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 with bridgePort option
  • tests/e2e/playwright.config.ts — per-project port configuration
  • apps/http-bridge/bridge-inject.js — bridge shim reading __BRIDGE_BASE_URL

Was this page helpful?