Bridge mode
Use bridge mode when you need to prove that the app can talk to the real Nimiq Pay mini-app provider surface.
Do not make bridge mode your only CI signal. Local scenarios catch package and app regressions faster and do not depend on host availability.
Choose bridge for provider integration
Use bridge mode when one of these changed:
- provider wait behavior
- mini-app host integration
- account reads around
window.nimiq - signing calls made through the injected provider
- remote Better Auth endpoint wiring
Do not use bridge mode for pure nonce, signature, QR-state, or profile parsing tests. Local mode covers those paths deterministically.
Run the package bridge script
Use the package script when testing this workspace.
pnpm --filter @onmax/better-auth-nimiq-pay-e2e test:e2e:bridge
Expected behavior:
NIMIQ_PAY_E2E_NETWORK=testnet
NIMIQ_PAY_E2E_MODE=bridge
NIMIQ_PAY_E2E_PROJECTS=e2e-direct-sign-in
The script builds package dependencies, waits for the injected provider, and runs the direct sign-in project.
Configure a bridge run explicitly
Use explicit environment variables in CI or one-off debugging so the test profile is visible in logs.
NIMIQ_PAY_E2E_NETWORK=testnet \
NIMIQ_PAY_E2E_MODE=bridge \
NIMIQ_PAY_E2E_PLAYGROUND_URL=http://127.0.0.1:3000 \
NIMIQ_PAY_E2E_NUXT_AUTH_BASE=http://127.0.0.1:3000/api/auth \
NIMIQ_PAY_E2E_PROJECTS=e2e-direct-sign-in \
pnpm --filter @onmax/better-auth-nimiq-pay-e2e test:e2e:bridge
Use NIMIQ_PAY_E2E_ENDPOINT_PREFIX when your direct auth endpoints are not under /nimiq. Use NIMIQ_PAY_E2E_QR_ENDPOINT_PREFIX when QR endpoints are not under /mobile-qr.
Write a bridge-focused test
Use a bridge profile and keep the test narrow. The bridge provider must resolve before the remote fetcher is useful.
import { describe, expect, it } from 'vitest'
import {
defineE2EProfile,
runSignInScenario,
} from '@onmax/better-auth-nimiq-pay-e2e'
describe('Nimiq Pay bridge auth', () => {
it('signs in through the injected provider', async () => {
const profile = defineE2EProfile({
mode: 'bridge',
network: 'testnet',
})
const result = await runSignInScenario({
profile,
bridgeProviderOptions: {
timeoutMs: 10_000,
intervalMs: 250,
},
})
expect(result.ok, result.error).toBe(true)
expect(result.mode).toBe('bridge')
expect(result.network).toBe('testnet')
expect(result.token).toBeTruthy()
})
})
Expected behavior:
The test waits for window.nimiq.
The sign-in flow uses remote auth endpoints.
The returned token comes from the app, not the local token generator.
Diagnose provider timeouts
Bridge mode fails before auth calls when no provider appears.
import { expect, it } from 'vitest'
import {
defineE2EProfile,
runSignInScenario,
} from '@onmax/better-auth-nimiq-pay-e2e'
it('reports missing provider clearly', async () => {
const result = await runSignInScenario({
profile: defineE2EProfile({ mode: 'bridge', env: {} }),
authBaseUrl: 'https://example.test/api/auth',
fetcher: async () => {
throw new Error('fetcher should not run before provider resolves')
},
bridgeProviderOptions: {
source: () => null,
timeoutMs: 20,
intervalMs: 5,
},
})
expect(result.ok).toBe(false)
expect(result.error).toContain('window.nimiq provider')
})
If this fails in a real browser run, check that the app is loaded inside the Nimiq Pay host or simulator that injects window.nimiq.
Treat mainnet as an explicit manual run
Use mainnet only for manual bridge checks.
NIMIQ_PAY_E2E_NETWORK=mainnet \
NIMIQ_PAY_E2E_MODE=bridge \
NIMIQ_PAY_E2E_ALLOW_MAINNET=true \
NIMIQ_PAY_E2E_PROJECTS=e2e-direct-sign-in \
pnpm --filter @onmax/better-auth-nimiq-pay-e2e test:e2e:mainnet
Expected guardrails:
mainnet without NIMIQ_PAY_E2E_ALLOW_MAINNET=true is blocked
mainnet in CI is blocked