Skip to content

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

PathWhat the fixture exercises
Fictional number and configured codeApplication form, auth-session handling and signed-in behavior with Firebase's test mode
Real number and real SMSSender 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.

tests/firebase-phone.spec.tsTypeScript
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()
})
Terminal — run the headed Firebase acceptance testShell
npx playwright test tests/firebase-phone.spec.ts --headed --workers=1

At 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 stageFirst check
App verification or sendAuthorized domain, challenge result, permitted SMS region and sender response
Code field never opensApplication's handling of the Firebase send result
Receipt wait times outAccepted send and the intended test number
Code submission is rejectedAttempt's confirmation state and code validity
Account assertion failsApplication'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.