Testing recipe
Test Firebase phone authentication with real SMS
Test your Firebase sign-in page with a real company number and let Phonekit supply the received code. Keep Firebase’s app-verification step in the flow, then assert that your application opens the authenticated account.
Choose the coverage you want
| Path | What the fixture exercises |
|---|---|
| Fictional number and configured code | Application form, auth-session handling and signed-in behavior with Firebase's test mode |
| Real number and real SMS | Sender request, real receipt, code submission and the application's signed-in result |
Scroll horizontally to see all columns.
Use fictional-number coverage for repeatable unattended runs. This recipe adds a headed real-delivery acceptance run with a human app-verification checkpoint. Firebase's testing switch for disabled app verification works with fictional numbers, not a real Phonekit number. Firebase phone authentication, testing switch reference.
Prepare the real sign-in page
Enable Firebase Phone sign-in, configure the permitted SMS region, and authorize the staging domain. Use a deployed domain for this web flow; Firebase's current phone-auth guide excludes localhost as a hosted domain. Use a real test number outside the fictional-number list.
Keep your application's RecaptchaVerifier and signInWithPhoneNumber flow. The returned ConfirmationResult belongs to that attempt; its confirm(code) completes verification. Verifier reference, confirmation reference.
For the sample, expose the visible reCAPTCHA on /signin before Send code. Label the fields Phone number and Verification code, use Send code and Verify buttons, and show Your account on /account after successful confirmation. Adapt these labels and the final assertion to your existing application. If you use invisible reCAPTCHA, move the checkpoint to where your application's challenge is presented.
Receive and submit after app verification
Install the server-side JS/TS SDK in your test project. Install it with npm install @phonekit/sdk. Keep its key in the test runner environment.
Set PHONEKIT_API_KEY, TEST_PHONE_NUMBER, and TEST_BASE_URL. Use the Playwright configuration, one worker, and a dedicated staging account whose state is reset before the run.
import { test, expect } from "@playwright/test"
import { Phonekit } from "@phonekit/sdk"
function required(name: string): string {
const value = process.env[name]
if (!value) throw new Error(`Set ${name}`)
return value
}
test("Firebase accepts the delivered code after app verification", async ({
page,
}) => {
test.setTimeout(300_000)
const phone = required("TEST_PHONE_NUMBER")
const number = new Phonekit({ apiKey: required("PHONEKIT_API_KEY") }).number(
phone
)
await page.goto("/signin")
await page.getByLabel("Phone number", { exact: true }).fill(phone)
const after = await number.mark()
// Complete any visible reCAPTCHA in the browser, then click Resume in Inspector.
await page.pause()
await page.getByRole("button", { name: "Send code", exact: true }).click()
await page.getByLabel("Verification code", { exact: true }).waitFor({
state: "visible",
timeout: 60_000,
})
const code = await number.waitForOtp({ after, timeoutMs: 120_000 })
await page.getByLabel("Verification code", { exact: true }).fill(code)
await page.getByRole("button", { name: "Verify", exact: true }).click()
await expect(page).toHaveURL(/\/account(?:[/?#]|$)/)
await expect(
page.getByRole("heading", { name: "Your account", exact: true })
).toBeVisible()
})npx playwright test tests/firebase-phone.spec.ts --headed --workers=1At the pause, solve the page's visible challenge and resume in Playwright Inspector. The test then requests the SMS, receives its code and verifies the application's signed-in page. This checkpoint makes the dependency visible instead of disguising the run as an unattended test.
Triage a failed attempt
| Failed stage | First check |
|---|---|
| App verification or send | Authorized domain, challenge result, permitted SMS region and sender response |
| Code field never opens | Application's handling of the Firebase send result |
| Receipt wait times out | Accepted send and the intended test number |
| Code submission is rejected | Attempt's confirmation state and code validity |
| Account assertion fails | Application's Firebase session handling and navigation |
Scroll horizontally to see all columns.
Respect Firebase’s sender limits instead of retrying sends in a tight loop. Record stage/status metadata, and keep codes and auth tokens out of screenshots and logs. Use the reliability guide to decide reset and resend behavior.
Common questions
Can I make the real Firebase web test completely unattended?
A real request keeps Firebase’s app verification. Invisible verification may finish without interaction, but a challenge can require a person. The recipe uses an explicit checkpoint. Use fictional-number tests for dependable unattended UI coverage and keep real-delivery acceptance as a separate lane.
Will appVerificationDisabledForTesting send an SMS to Phonekit?
No. Firebase documents this test path for fictional numbers. It does not establish receipt at your company number. Keep the normal verification path when the test’s purpose is actual delivery.
Why do I need a deployed staging URL?
Firebase’s current web phone-auth guide excludes localhost as a hosted phone-auth domain. Configure and use the authorized staging domain for this real-delivery browser test.
Can I reuse the same ConfirmationResult after clicking resend?
Track the attempt state in your application. The acceptance test should submit the code using the confirmation state associated with its intended send. Test resend behavior explicitly instead of reusing whichever state happens to remain.
Does Phonekit change my Firebase sign-in implementation?
No. Your application still requests and confirms phone verification through Firebase. Phonekit supplies the received SMS code to the test runner; the final browser assertion checks the application’s authenticated outcome.