Local scenarios
Use @onmax/better-auth-nimiq-pay-e2e local scenarios when you need deterministic Nimiq Pay Better Auth flow tests without a real mini-app host, injected provider, or network calls.
Do not use local scenarios as the only signal for host integration. They prove your app wiring and auth flow shape, not the real window.nimiq bridge.
Start with a profile
Use defineE2EProfile() to keep local, CI, and bridge runs on the same configuration surface.
import { defineE2EProfile } from '@onmax/better-auth-nimiq-pay-e2e'
export const profile = defineE2EProfile({
env: process.env,
playgroundUrl: 'http://127.0.0.1:3000',
endpointPrefix: '/nimiq',
qrEndpointPrefix: '/cross-device',
})
Default profile values:
network: testnet
mode: local
endpointPrefix: /nimiq
qrEndpointPrefix: /mobile-qr
playgroundUrl: http://127.0.0.1:3000
nuxtAuthBaseUrl: http://127.0.0.1:3000/api/auth
Mainnet E2E runs are blocked unless NIMIQ_PAY_E2E_ALLOW_MAINNET=true, and mainnet runs are disabled in CI.
Test direct sign-in locally
Use runSignInScenario() for the full nonce -> sign -> verify loop.
import { describe, expect, it } from 'vitest'
import { runSignInScenario } from '@onmax/better-auth-nimiq-pay-e2e'
import { profile } from './profile'
describe('Nimiq Pay auth', () => {
it('signs in with a deterministic local provider', async () => {
const result = await runSignInScenario({
profile,
origin: 'https://example.test',
requestAddressBeforeVerify: false,
})
expect(result).toMatchObject({
ok: true,
network: 'testnet',
mode: 'local',
token: 'local-token-1',
})
})
})
Expected behavior:
No remote fetch is used in local mode.
The token is local-token-1 for the first successful verification.
Test signature failures
Use a custom provider when you need to prove that invalid signatures return useful failure details.
import { expect, it } from 'vitest'
import { isNimiqProviderError } from '@onmax/better-auth-nimiq/provider'
import {
createStubNimiqProvider,
runSignInScenario,
} from '@onmax/better-auth-nimiq-pay-e2e'
it('reports invalid signatures', async () => {
const stub = createStubNimiqProvider()
const invalidProvider = {
listAccounts: () => stub.listAccounts(),
async sign(message: string) {
const result = await stub.sign(message)
if (isNimiqProviderError(result))
return result
const suffix = result.signature.endsWith('0') ? '1' : '0'
return {
publicKey: result.publicKey,
signature: `${result.signature.slice(0, -1)}${suffix}`,
}
},
}
const result = await runSignInScenario({
provider: invalidProvider as any,
origin: 'https://example.test',
requestAddressBeforeVerify: false,
})
expect(result.ok).toBe(false)
expect(result.error).toContain('Invalid signature')
})
Test cross-device sign-in locally
Use runQrSignInScenario() when desktop and phone flows need to complete in one test process.
import { describe, expect, it } from 'vitest'
import { runQrSignInScenario } from '@onmax/better-auth-nimiq-pay-e2e'
describe('QR sign-in', () => {
it('starts, approves, and finalizes locally', async () => {
const result = await runQrSignInScenario()
expect(result).toMatchObject({
ok: true,
mode: 'local',
token: 'local-finalized-order-1',
})
})
})
Expected behavior:
order-1 is created
the stub provider approves the challenge
desktop finalize returns local-finalized-order-1
Replace HTTP calls with local fetchers
Use local fetchers when you want to test lower-level client calls without the scenario runner.
import { expect, it } from 'vitest'
import { isNimiqProviderError } from '@onmax/better-auth-nimiq/provider'
import {
createLocalAuthFetcher,
createStubNimiqProvider,
} from '@onmax/better-auth-nimiq-pay-e2e'
it('issues and verifies a local nonce', async () => {
const fetcher = createLocalAuthFetcher({
appName: 'Test App',
origin: 'https://example.test',
})
const provider = createStubNimiqProvider()
const nonce = await fetcher<{
nonceId: string
message: string
}>('/nimiq/nonce', { method: 'POST', body: {} })
const signed = await provider.sign(nonce.message)
if (isNimiqProviderError(signed))
throw new Error(signed.error.message)
const verified = await fetcher<{ ok: true, token: string }>('/nimiq/verify', {
method: 'POST',
body: {
nonceId: nonce.nonceId,
publicKeyHex: signed.publicKey,
signatureHex: signed.signature,
},
})
expect(verified).toEqual({
ok: true,
token: 'local-token-1',
})
})
Use local scenarios in CI first. Add bridge mode only for the smaller set of tests that must prove injected-provider behavior.