Form Testing Guide

How to Test Typeform Embed with Playwright: Complete 2026 Guide

A scenario-by-scenario walkthrough of testing Typeform embedded forms with Playwright. Full iframe isolation, logic jump branches, hidden fields, multi-step navigation, thank you screen redirects, and webhook response verification.

800M+

β€œTypeform has collected over 800 million responses across millions of forms, making it one of the most widely used conversational form platforms for lead capture, surveys, and product signups.”

Typeform

0Scenarios covered
0Iframe nesting levels
0Logic jump branches
0%Fewer lines with Assrt

Typeform Embed Response Flow

BrowserHost PageTypeform IframeTypeform APIWebhook EndpointLoad page with embedRender Typeform iframeDisplay first questionAnswer + press OKEvaluate logic jumpShow next question (or skip)Submit final answerPOST /responsesFire webhook payloadShow thank you screen

1. Why Testing Typeform Embeds Is Harder Than It Looks

Typeform embeds your form inside a sandboxed iframe served from form.typeform.com. That single architectural decision cascades into six distinct testing challenges. First, the iframe is cross-origin, which means Playwright's default page context cannot reach any elements inside it. You must use frameLocator() to obtain a handle into the Typeform DOM, and every subsequent locator call must be scoped to that frame.

Second, Typeform forms are single-page applications inside that iframe. Each question is rendered one at a time with smooth CSS transitions. There are no URL changes between questions, no page navigations, and no network requests you can intercept to determine when the next question is ready. Your test must rely on DOM visibility checks rather than network or URL watchers.

Third, Typeform logic jumps can skip questions, loop back to earlier questions, or branch into entirely different paths based on previous answers. A form with 10 questions might have 12 or more distinct paths through it, and the DOM for skipped questions never renders at all. You cannot just fill every field sequentially; you must account for conditional routing at each step.

Fourth, hidden fields are injected via URL parameters on the embed URL, passed into the iframe, and included in the final response payload. They never appear in the visible DOM, so you cannot assert their values through standard locator checks. Fifth, the thank you screen can either show a Typeform default message, render a custom ending, or redirect the parent page to an external URL. Each behavior requires a different assertion strategy. Sixth, webhook payloads fire asynchronously after submission and contain the full response data including hidden fields, making them critical to verify but impossible to check from the browser alone.

Typeform Embed Architecture

🌐

Host Page

Your app with embed script

βš™οΈ

Embed SDK

Injects iframe element

πŸ“¦

Typeform Iframe

form.typeform.com

🌐

Question SPA

Single-page form engine

βš™οΈ

Typeform API

Stores responses

πŸ””

Webhook

POST to your endpoint

Logic Jump Decision Tree

🌐

Question 1

Company size

βš™οΈ

Evaluate Rule

If > 50 employees

β†ͺ️

Branch A

Enterprise questions

β†ͺ️

Branch B

SMB questions

βœ…

Merge Point

Final question

A robust Typeform embed test suite addresses all six of these challenges. The sections below walk through each scenario with runnable Playwright TypeScript you can copy directly into your project.

2. Setting Up a Reliable Test Environment

Before writing any scenarios, you need a Typeform workspace with a form configured specifically for testing. Create a dedicated workspace (Typeform allows multiple workspaces on all paid plans) so your test forms stay isolated from production surveys. Build a test form with at least six questions covering text input, multiple choice, dropdown, number, email, and a rating question. Add two logic jumps: one that branches on a multiple choice answer and one that skips a question based on a number threshold. Configure three hidden fields: user_id, session_id, and utm_source. Set the thank you screen to redirect to a specific URL on your test domain. Finally, create a webhook endpoint that logs payloads so you can verify submission data.

Typeform Test Environment Checklist

  • Create a dedicated Typeform workspace for testing
  • Build a test form with 6+ question types (text, choice, dropdown, number, email, rating)
  • Add two logic jump rules (branch on choice, skip on number threshold)
  • Configure three hidden fields: user_id, session_id, utm_source
  • Set thank you screen redirect to your test domain
  • Create a webhook endpoint to capture submission payloads
  • Note the form ID from the Typeform URL (e.g., abc123XY)
  • Generate a Personal Access Token for the Typeform API

Environment Variables

.env.test

Playwright Configuration for Iframe Testing

Typeform iframes load from form.typeform.com, a different origin than your application. Playwright's frameLocator() handles cross-origin iframes automatically, but you need generous timeouts because the Typeform embed SDK loads asynchronously and the iframe content can take several seconds to render, especially on CI runners with limited bandwidth.

playwright.config.ts

Webhook Receiver Setup

To verify that Typeform fires the correct webhook payload after submission, run a lightweight Express server that captures and stores incoming payloads. Start this server before your test suite and query it from your tests to assert on the response data, including hidden field values.

test/helpers/webhook-receiver.ts
Starting the Test Environment

3. Scenario: Accessing the Typeform Iframe

The first and most fundamental scenario is simply reaching into the Typeform iframe and interacting with any element. If you cannot reliably locate and interact with elements inside the embed, none of the subsequent scenarios will work. The Typeform embed SDK injects an iframe with a data-testid attribute of iframe (on the standard embed) or a title attribute containing the form name. The iframe's src points to form.typeform.com/to/FORM_ID. You use Playwright's frameLocator() to get a handle, and all subsequent queries run inside that frame context.

One common mistake is trying to use page.locator() to find elements inside the Typeform iframe. This will always fail because the elements live in a separate browsing context. Another mistake is querying the iframe before it has fully loaded; the Typeform SDK inserts the iframe asynchronously, and the iframe itself may take a few seconds to render the first question. Always wait for a known element inside the frame before proceeding.

1

Accessing the Typeform Iframe

Straightforward

Goal

Load a page containing an embedded Typeform, get a handle to the iframe, and confirm the first question is visible and interactive.

Preconditions

  • App running at APP_BASE_URL with a page that embeds the Typeform
  • The Typeform embed uses the standard SDK (@typeform/embed)
  • Form ID is configured in TYPEFORM_FORM_ID

Playwright Implementation

typeform-iframe.spec.ts

What to Assert Beyond the UI

  • The iframe src attribute contains your form ID
  • No console errors related to CORS or Content Security Policy appear
  • The Typeform SDK script tag is present in the host page DOM

Iframe Access: Playwright vs Assrt

import { test, expect } from '@playwright/test';

test('access Typeform iframe', async ({ page }) => {
  await page.goto('/contact');
  const iframeSelector = 'iframe[data-testid="iframe"]';
  await page.waitForSelector(iframeSelector, { timeout: 15_000 });

  const typeform = page.frameLocator(iframeSelector);

  const firstQuestion = typeform
    .locator('[data-qa="question-container"]').first();
  await expect(firstQuestion).toBeVisible({ timeout: 10_000 });

  const inputField = typeform.locator('input[type="text"]').first();
  await expect(inputField).toBeEnabled();
});
45% fewer lines

4. Scenario: Multi-Step Form Navigation

Typeform presents one question at a time. After answering a question, the user either clicks an β€œOK” button, presses Enter, or the form auto-advances (depending on question type and settings). Each transition involves a CSS animation that slides the current question out and the next question in. There is no URL change, no page navigation, and no new iframe load. From Playwright's perspective, you are clicking and waiting for DOM mutations inside the same frame context.

The tricky part is timing. If you type an answer and immediately try to click β€œOK,” the button may not be enabled yet because Typeform validates the input asynchronously. If you click β€œOK” and immediately try to interact with the next question, the transition animation may still be running and the next input may not be interactive yet. The reliable approach is to wait for each question container to become visible and its primary input to become enabled before interacting.

2

Multi-Step Form Navigation

Moderate

Goal

Fill out three consecutive questions (text, multiple choice, email) and verify each transition completes before interacting with the next question.

Playwright Implementation

typeform-multistep.spec.ts

What to Assert Beyond the UI

  • The progress bar (if enabled) advances with each answered question
  • Pressing the keyboard Enter key also advances the form, not just the OK button
  • The back button or keyboard shortcut navigates to the previous question without losing answers

Multi-Step Navigation: Playwright vs Assrt

import { test, expect } from '@playwright/test';

test('multi-step Typeform navigation', async ({ page }) => {
  await page.goto('/contact');
  const typeform = page.frameLocator('iframe[data-testid="iframe"]');

  async function waitForQuestion(idx: number) {
    const q = typeform.locator('[data-qa="question-container"]').nth(idx);
    await expect(q).toBeVisible({ timeout: 10_000 });
    return q;
  }

  await waitForQuestion(0);
  await typeform.locator('input[type="text"]').first().fill('Jane Doe');
  await typeform.getByRole('button', { name: /ok/i }).click();
  await page.waitForTimeout(800);

  await waitForQuestion(1);
  await typeform.getByRole('button', { name: /51-200/i }).click();
  await page.waitForTimeout(800);

  await waitForQuestion(2);
  await typeform.locator('input[type="email"]')
    .fill(`test+${Date.now()}@example.com`);
  await typeform.getByRole('button', { name: /ok/i }).click();
});
57% fewer lines

Try Assrt for free

Open-source AI testing framework. No signup required.

Get Started β†’

5. Scenario: Logic Jump Branching

Logic jumps are Typeform's conditional routing feature. Based on the answer to one question, the form can skip ahead to a specific question, jump to a different branch of questions, or go directly to the thank you screen. This is the feature that makes Typeform forms powerful for segmentation, but it also makes them challenging to test because a single form can have many distinct paths through it.

The key testing strategy is to identify all logic jump rules in your form and test each branch explicitly. For a form with two logic jumps that each have two outcomes, you need four test cases to achieve full branch coverage. Each test case must provide the specific answer that triggers the target branch and then verify that the correct follow-up question appears (and that skipped questions do not appear).

You can review your form's logic jumps in the Typeform builder under the β€œLogic” panel, or fetch them programmatically via the Typeform Create API. The API response includes a logic array on each field that defines the conditions and destination references. Use this to generate your test matrix.

3

Logic Jump: Enterprise Branch

Complex

Goal

Answer the company size question with β€œ201+” to trigger the enterprise logic jump, and verify that the enterprise-specific follow-up questions appear while the SMB questions are skipped.

Playwright Implementation

typeform-logic-jumps.spec.ts

Fetching Logic Rules via the Typeform API

To programmatically discover all branches in your form, query the Typeform Create API. The response includes the logic configuration for each field with conditions and jump destinations.

test/helpers/typeform-logic.ts

6. Scenario: Hidden Field Injection and Verification

Hidden fields are one of Typeform's most valuable features for connecting form responses to your application data. You pass values via URL parameters on the embed URL, and Typeform includes them in the response payload. Common use cases include passing a user ID, session ID, UTM parameters, or an A/B test variant. The challenge for testing is that hidden fields never appear in the visible DOM. You cannot see them, click them, or assert their values through standard Playwright locators.

There are two verification strategies. First, you can intercept the network request that Typeform sends when submitting the form and inspect the request body for hidden field values. Second, you can check the webhook payload that Typeform fires to your endpoint after submission. The webhook approach is more reliable because it represents the final, server-side view of the response data. The network interception approach gives you faster feedback during the test but depends on Typeform's internal API structure, which can change without notice.

4

Hidden Field Injection and Verification

Complex

Goal

Load the Typeform embed with hidden field values injected via URL parameters, complete the form, and verify the hidden fields appear in both the intercepted network request and the webhook payload.

Playwright Implementation

typeform-hidden-fields.spec.ts

What to Assert Beyond the UI

  • The iframe src attribute includes all hidden field key/value pairs
  • The Typeform API submission request body contains the hidden fields
  • The webhook payload's form_response.hidden object matches the injected values exactly
  • Missing or empty hidden fields default to empty strings, not null

7. Scenario: Thank You Screen and Redirect

After the user submits their final answer, Typeform shows a thank you screen. This screen can behave in three distinct ways depending on your form configuration. The default behavior shows a Typeform-branded β€œThanks for completing this typeform!” message inside the iframe. A custom ending replaces that with your own message, image, and optional button. A redirect ending navigates the parent page (not just the iframe) to an external URL. Each of these requires a different assertion strategy.

The redirect ending is the most complex to test. When configured, Typeform uses window.top.location.href (or postMessage to the parent) to navigate the entire browser window away from your application to the target URL. In Playwright, this means the page context changes entirely. You must use page.waitForURL() on the main page (not the frame) to detect the redirect, and your assertion runs against the new page, not the original embed host.

5

Thank You Screen and Redirect

Moderate

Goal

Complete a form and verify all three thank you screen variants: default Typeform ending, custom ending with message and button, and redirect to an external URL.

Playwright Implementation

typeform-thankyou.spec.ts

Thank You Redirect: Playwright vs Assrt

test('thank you: redirect to external URL', async ({ page }) => {
  await page.goto('/survey-redirect');
  const typeform = page.frameLocator('iframe[data-testid="iframe"]');

  // Fill all questions
  const nameInput = typeform.locator('input[type="text"]').first();
  await expect(nameInput).toBeVisible({ timeout: 10_000 });
  await nameInput.fill('Redirect Tester');
  await typeform.getByRole('button', { name: /ok/i }).click();
  await page.waitForTimeout(800);

  const choice = typeform.getByRole('button', { name: /1-50/i });
  await choice.click();
  await page.waitForTimeout(800);

  const emailInput = typeform.locator('input[type="email"]');
  await emailInput.fill(`test+${Date.now()}@example.com`);
  await typeform.getByRole('button', { name: /ok/i }).click();
  await page.waitForTimeout(800);

  await typeform.getByRole('button', { name: /submit/i }).click();

  await page.waitForURL('**/thank-you**', { timeout: 15_000 });
  expect(page.url()).toContain('/thank-you');
});
57% fewer lines

8. Scenario: Webhook Response Verification

Typeform webhooks fire after a response is submitted. The webhook payload contains the full response data: all answers, hidden field values, metadata (user agent, landed at, submitted at), and the form definition reference. For many applications, the webhook is where the real business logic begins: creating a CRM lead, sending a follow-up email, triggering an onboarding sequence, or updating a database record. Testing the webhook payload is therefore just as important as testing the form UI.

Typeform signs every webhook request with an HMAC SHA-256 signature in the Typeform-Signature header. Your webhook receiver should verify this signature to prevent spoofed payloads. In your test environment, the webhook receiver (from Section 2) captures and stores every incoming payload so your Playwright tests can query the most recent payload and assert on its structure and values.

6

Webhook Response Verification

Complex

Goal

Submit a Typeform response and verify the webhook payload contains the correct answers, hidden fields, signature, and metadata.

Playwright Implementation

typeform-webhooks.spec.ts

Verifying Webhook Signature

test/helpers/verify-signature.ts

9. Common Pitfalls That Break Typeform Test Suites

The following pitfalls come from real issues reported by developers testing Typeform embeds in production CI pipelines. Each one can cause intermittent test failures that are difficult to diagnose without understanding Typeform's internal behavior.

Pitfalls to Avoid

  • Using page.locator() instead of frameLocator() for iframe elements. Every Typeform element lives inside a cross-origin iframe; page-level locators will never find them.
  • Not waiting for the CSS transition between questions. Typeform uses 300-500ms slide animations. Interacting during the transition causes flaky clicks on stale elements.
  • Assuming sequential question order when logic jumps are configured. The DOM only renders the current question; skipped questions never appear at all.
  • Hardcoding question indices instead of using content-based selectors. Typeform reorders questions when you edit the form in the builder, breaking nth-child selectors.
  • Ignoring the embed SDK loading delay. The Typeform SDK script is async; the iframe may not exist in the DOM for 2-5 seconds after page load on slow CI runners.
  • Testing hidden fields by looking at the visible DOM. Hidden fields are never rendered; verify them via network interception or webhook payloads only.
  • Forgetting that redirect endings navigate the parent window, not the iframe. Use page.waitForURL() on the main page context, not on the frame locator.
  • Running tests in parallel against the same webhook endpoint without clearing payloads between runs. Stale payloads from previous tests cause false positives.

Iframe Selector Fragility

Typeform updates their embed SDK and internal DOM structure periodically. The data-testid="iframe" attribute has been stable across recent SDK versions, but the internal question container selectors like [data-qa="question-container"] can change between major Typeform releases. Pin your Typeform embed SDK version in package.json to avoid surprise breakages, and add a smoke test that asserts the existence of your critical selectors before running the full suite.

Webhook Delivery Latency

Typeform's webhook delivery is eventually consistent. In testing, payloads typically arrive within 1 to 3 seconds of submission, but during high-traffic periods, delivery can take up to 30 seconds. Your test should poll the webhook receiver with retries rather than using a fixed waitForTimeout. If the webhook never arrives within your timeout window, check the Typeform dashboard's webhook log for delivery failures, which commonly occur when the webhook endpoint is unreachable from Typeform's servers (a frequent issue in local development without a tunnel).

Common Typeform Test Errors

Content Security Policy Blocking the Embed

If your application has a strict Content Security Policy, the Typeform embed script and iframe may be blocked entirely. Your CSP must include frame-src form.typeform.com and script-src embed.typeform.com at minimum. Add a test that checks for CSP violation errors in the browser console. In Playwright, use page.on('console', ...) to capture and assert that no CSP errors appear when the embed loads.

Full Typeform Test Suite Run (All Passing)

10. Writing These Scenarios in Plain English with Assrt

Every scenario above requires careful iframe handling, precise timing around CSS transitions, and awareness of Typeform's internal DOM structure. The multi-step navigation scenario alone is over 40 lines of Playwright TypeScript. The webhook verification scenario adds network polling, signature verification, and payload structure assertions. Multiply all of that by the number of logic jump branches in your form and you have a substantial test maintenance burden.

Assrt eliminates the iframe boilerplate entirely. When you describe a scenario that involves a Typeform embed, Assrt automatically detects the iframe, acquires the frame locator, scopes all interactions to the correct context, and handles the transition timing between questions. You write what you want to test in plain English; Assrt generates the Playwright code that handles the implementation details.

Here is the complete Typeform test suite from this guide, compiled into Assrt scenario format. Each scenario block maps to one of the sections above.

scenarios/typeform-embed-suite.assrt

Assrt compiles each scenario block into the same Playwright TypeScript from the preceding sections. The generated code is committed to your repo as standard test files you can read, run with npx playwright test, and modify. When Typeform updates their embed SDK or changes the internal DOM structure, Assrt detects the test failure, analyzes the new DOM, and opens a pull request with updated selectors and wait logic. Your scenario files remain unchanged.

Start with the iframe access scenario. Once that is green, add multi-step navigation, then logic jump branches, then hidden fields, then the thank you screen, then webhook verification. Within a single afternoon you can have complete Typeform embed coverage that would take days to build and maintain by hand.

Related Guides

Ready to automate your testing?

Assrt discovers test scenarios, writes Playwright tests from plain English, and self-heals when your UI changes.

$npm install @assrt/sdk