Messenger Widget Testing Guide
How to Test Intercom Messenger with Playwright: Complete 2026 Guide
A scenario-by-scenario walkthrough of testing Intercom Messenger with Playwright. Iframe traversal, launcher button rendering, bot auto-replies, conversation threads, custom actions, article suggestions, and the pitfalls that break real messenger test suites.
“Intercom serves over 25,000 businesses globally, with the Messenger widget embedded on hundreds of thousands of web applications handling millions of conversations daily.”
Intercom
Intercom Messenger Conversation Flow
1. Why Testing Intercom Messenger Is Harder Than It Looks
Intercom Messenger is a third-party widget that injects itself into your page via a JavaScript snippet, then renders its entire UI inside one or more nested iframes. The launcher button, the conversation panel, and the article viewer each live inside separate iframe boundaries. From Playwright's perspective, every interaction requires crossing those iframe boundaries using frameLocator before you can locate any element. Standard page.getByRole() calls will silently find nothing if you forget to scope them to the correct frame.
The second structural challenge is timing. Intercom boots asynchronously after your page loads. The SDK fetches configuration from Intercom's servers, determines whether the current user qualifies for messenger visibility (based on segments, audience rules, and launcher settings), and only then injects the iframe. There is no DOM event your test can listen for to know the widget is ready. You must poll for the iframe's existence or wait for the launcher button to appear inside it.
Third, bot auto-replies arrive on a server-driven schedule. When a user sends a message, the Intercom backend evaluates workflow rules, matches resolution bot paths, and pushes a response back through a WebSocket connection. That response can take anywhere from 500 milliseconds to several seconds. Your tests must wait for the reply to appear in the conversation thread without using brittle fixed-duration sleeps.
Fourth, the Messenger DOM is heavily obfuscated. Class names are hashed and change between Intercom SDK releases. Data attributes are sparse. Intercom uses React internally, so the DOM structure shifts when they refactor components. Your selectors need to target semantic content (text, ARIA roles, structural position) rather than class names.
Fifth, custom actions, article cards, and conversation cards are rendered dynamically based on bot configuration. Your test environment needs a predictable bot workflow configured in Intercom so that sending a specific message always triggers the same response path. Without that, tests become nondeterministic.
Intercom Messenger Iframe Architecture
Page Load
Your app HTML renders
SDK Boot
Intercom snippet loads
Config Fetch
GET /messenger/web/config
Launcher Iframe
Button injected into DOM
Click Launcher
User opens messenger
Messenger Iframe
Conversation panel opens
WebSocket
Real-time message delivery
2. Setting Up a Reliable Test Environment
Intercom provides separate workspaces for development and production. Always test against a dedicated development workspace to avoid polluting production analytics and conversation data. You will need your workspace's app_id, and if you use Identity Verification (which you should in production), you will also need the HMAC secret to generate user hashes.
Environment Variables
Intercom Snippet Integration
Your application should already include the Intercom snippet. For tests, ensure the snippet boots with identity verification enabled. The HMAC hash is computed server-side using the user's email and your HMAC secret. Here is a minimal setup:
Playwright Configuration
Intercom loads scripts from widget.intercom.io and js.intercomcdn.com. Your Playwright config must not block these domains. Also, set a generous default timeout for iframe operations since the messenger can take several seconds to boot on slow networks.
Helper: Wait for Intercom Messenger Frame
The single most reused utility in any Intercom test suite is a function that waits for the messenger iframe to exist and returns a scoped frame locator. Intercom injects an iframe with the name intercom-messenger-frame for the main panel, and a separate frame for the launcher button. Here is a robust helper:
Test Environment Setup Flow
Dev Workspace
Create in Intercom
Identity Verification
Enable HMAC
Bot Workflow
Configure test bot
Env Variables
app_id + HMAC secret
Playwright Config
Allow Intercom domains
Helpers
Frame locator utilities
Launcher Button Renders and Opens
Straightforward3. Scenario: Launcher Button Renders and Opens
Goal: Verify the Intercom launcher button appears on the page and clicking it opens the messenger panel.
Preconditions: The Intercom snippet is loaded with a valid app_id. The current user matches the audience rules for messenger visibility.
Playwright Implementation
What to Assert Beyond the UI
Check that the Intercom boot request completed successfully by monitoring network traffic. The SDK sends a POST to https://api-iam.intercom.io/messenger/web/ping on boot. Verify the response status is 200 and the response body contains the expected app configuration. You can also assert that window.Intercom is defined and callable on the page by evaluating typeof window.Intercom === 'function'.
Sending a Message and Receiving a Bot Reply
Moderate4. Scenario: Sending a Message and Receiving a Bot Reply
Goal: Send a user message through the Intercom Messenger and verify the resolution bot delivers an automated reply.
Preconditions:A resolution bot workflow is configured in your development workspace that triggers on all inbound conversations. The bot should send at least one auto-reply message, such as “Thanks for reaching out! Let me find the right answer for you.”
Playwright Implementation
What to Assert Beyond the UI
Intercept the WebSocket frames to confirm the message was delivered to the Intercom backend. You can use page.on('websocket') to capture outgoing frames and verify the message payload includes the correct conversation ID and body text. This catches silent delivery failures where the UI appears to send but the backend never receives the message.
Navigating a Conversation Thread
Moderate5. Scenario: Navigating a Conversation Thread
Goal: Open an existing conversation from the conversation list, scroll through the message history, and verify messages render in chronological order.
Preconditions: The test user has at least one previous conversation in their Intercom history. You can create this in a beforeAll hook by sending a message via the Intercom REST API using the POST /conversations endpoint.
Playwright Implementation
What to Assert Beyond the UI
Verify the Intercom API call that fetches conversation history returns a 200 status. The SDK requests /messenger/web/conversations/:id when a thread is opened. Intercept this request and validate that the response payload includes the expected number of message parts.
Conversation thread navigation
const messenger = await openMessenger(page);
const previousConversations = messenger.getByText(
/previous conversations|see all/i
);
if (await previousConversations.isVisible()) {
await previousConversations.click();
}
const conversationItem = messenger
.locator('[data-testid="conversation-list-item"]')
.first();
await expect(conversationItem).toBeVisible({ timeout: 8_000 });
await conversationItem.click();
const messageList = messenger.locator(
'[data-testid="conversation-container"]'
);
await expect(messageList).toBeVisible();
await expect(
messenger.getByRole('textbox', { name: /write a reply/i })
).toBeVisible();Custom Bot Flow with Buttons and Branching
Complex6. Scenario: Custom Bot Flow with Buttons and Branching
Goal:Trigger a custom bot workflow, click through button options in the conversation, and verify the bot follows the correct branching path based on the user's selection.
Preconditions:Your Intercom workspace has a custom bot configured with at least two branching paths. For example, a “How can I help?” bot that offers “Billing”, “Technical Support”, and “Other” as button options, each leading to a different follow-up message.
Playwright Implementation
What to Assert Beyond the UI
Monitor the API calls to verify the button click is registered as a “quick reply” in the conversation payload. Intercom records button clicks as a specific message part type. Assert the request body includes the correct reply_option value corresponding to the button the user clicked. This ensures the branching logic will work correctly even when the button label text changes.
Article Suggestions and Help Center Search
Moderate7. Scenario: Article Suggestions and Help Center Search
Goal: Trigger an article suggestion from the bot, click through to the article viewer within the messenger, and verify the article content renders correctly. Also test the help center search functionality.
Preconditions: Your Intercom workspace has at least one published help center collection with articles. A resolution bot workflow is configured to suggest articles when it matches certain keywords.
Playwright Implementation
What to Assert Beyond the UI
Verify that the article fetch request to /messenger/web/articles/:id returns a 200 status and that the article body HTML is not empty. For search, intercept the /messenger/web/search/articles request and confirm the query parameter matches what the user typed and that the response includes at least one result object.
Article suggestion and reading
const messenger = await openMessenger(page);
const composer = messenger.getByRole('textbox', {
name: /write a reply|type a message/i,
});
await composer.fill('How do I reset my password?');
await composer.press('Enter');
const articleCard = messenger.locator(
'[data-testid="article-card"]'
).first();
await expect(articleCard).toBeVisible({ timeout: 12_000 });
await articleCard.click();
const articleViewer = messenger.locator(
'[data-testid="article-viewer"]'
);
await expect(articleViewer).toBeVisible({ timeout: 8_000 });
const articleText = await articleViewer.textContent();
expect(articleText!.length).toBeGreaterThan(50);Custom Actions and Launcher Visibility Rules
Complex8. Scenario: Custom Actions and Launcher Visibility Rules
Goal: Test that the Intercom launcher respects visibility rules (showing or hiding based on page URL, user segment, or custom data attributes) and that custom actions triggered via the Intercom JavaScript API work correctly.
Preconditions: Your workspace has launcher visibility rules configured. For example, the messenger is hidden on the /pricing page but visible on /dashboard. You also have a custom action that opens the messenger to a specific conversation or bot workflow when called programmatically.
Playwright Implementation
What to Assert Beyond the UI
For visibility rules, assert that the Intercom boot config response includes the correct launcher_enabled value for each page. Intercept /messenger/web/ping and inspect the response JSON. For custom actions, verify the JavaScript API call resolves without errors by wrapping it in a try/catch inside page.evaluate and checking the return value.
9. Common Pitfalls That Break Intercom Messenger Tests
These pitfalls come from real-world Intercom test suites, GitHub issues, and community forum posts. Every one of them has caused flaky or permanently broken tests in production CI pipelines.
Pitfall 1: Forgetting to Use frameLocator
The most common mistake is using page.getByRole() directly to find elements inside the Intercom widget. Since the widget renders inside an iframe, these locators will time out with no useful error message. You must always scope your locators through page.frameLocator('iframe[name="intercom-messenger-frame"]') first. This is the single most reported issue in Playwright forum threads about Intercom testing.
Pitfall 2: Using Fixed Timeouts Instead of Waiting for Elements
Intercom's boot time varies based on network latency, workspace configuration complexity, and whether the CDN cache is warm. Tests that use await page.waitForTimeout(3000) before interacting with the messenger will either be too slow (wasting CI time) or too fast (failing intermittently). Always wait for the actual iframe element to appear in the DOM using waitForSelector.
Pitfall 3: Class Name Selectors Break on SDK Updates
Intercom's class names are generated by their build process and change between SDK versions. Selectors like .intercom-1x2y3z will break silently when Intercom deploys a new version of the widget (which happens frequently, since the SDK is loaded from their CDN). Use ARIA roles, text content, and data-testid attributes instead.
Pitfall 4: Bot Replies Depend on Workspace Configuration
If someone modifies the resolution bot workflow in your development workspace, every test that asserts on specific bot reply text will break. Treat your Intercom workspace configuration as infrastructure code. Document the expected bot flows, restrict editing permissions on the development workspace, and version-control your bot workflow exports using the Intercom API.
Pitfall 5: Identity Verification HMAC Mismatches
If your application enables Identity Verification but your test environment computes the HMAC hash with a different secret (or skips it entirely), the messenger will boot in an “unverified” state. Intercom may reject API calls, block conversations, or display a degraded experience. Ensure your test environment uses the same Identity Verification secret as your development workspace.
Pitfall 6: Parallel Tests Create Overlapping Conversations
If multiple test workers share the same test user identity, they will see each other's conversations in the messenger. This causes assertions on message count or conversation list order to become nondeterministic. Use unique user identities per test worker by appending the worker index to the email address: test+worker${workerIndex}@yourcompany.com.
Pre-flight Checklist Before Running Intercom Tests
- Use frameLocator for all messenger element access
- Wait for iframe attachment, not fixed timeouts
- Target ARIA roles and text, not class names
- Verify bot workflow configuration is stable
- Match Identity Verification HMAC secret
- Use unique user identities per parallel worker
- Use fixed timeouts like waitForTimeout(3000)
- Select elements by Intercom class names
- Share one test user across parallel workers
10. Writing These Scenarios in Plain English with Assrt
Every scenario above requires you to know Playwright's frameLocator API, understand Intercom's iframe naming conventions, handle variable bot reply timing, and maintain selectors that break when Intercom updates their SDK. Assrt lets you describe what you want to test in plain English and compiles it into the same Playwright TypeScript you saw throughout this guide.
Here is the “send a message and receive a bot reply” scenario from Section 4, rewritten as an Assrt scenario file:
Assrt compiles each scenario block into the Playwright TypeScript you saw in the preceding sections, committed to your repo as real tests you can read, run, and modify. When Intercom renames an iframe, changes their launcher button markup, or restructures the conversation thread DOM, Assrt detects the failure, analyzes the new DOM, and opens a pull request with the updated selectors and frame locators. Your scenario files stay untouched.
Bot reply: Playwright vs Assrt
import { test, expect } from '@playwright/test';
import { openMessenger } from './helpers';
test('sends a message and receives bot reply', async ({ page }) => {
await page.goto('/');
const messenger = await openMessenger(page);
const composer = messenger.getByRole('textbox', {
name: /write a reply|type a message/i,
});
await composer.fill('I need help with billing');
await composer.press('Enter');
await expect(
messenger.getByText('I need help with billing')
).toBeVisible();
const botReply = messenger.locator(
'[data-testid="operator-message"]'
).first();
await expect(botReply).toBeVisible({ timeout: 10_000 });
await expect(
messenger.getByText(/thanks for reaching out|let me find/i)
).toBeVisible({ timeout: 10_000 });
});Start with the launcher visibility scenario. Once it is green in your CI, add the message and bot reply test, then the custom bot branching flow, then article suggestions, then custom actions and visibility rules. In a single afternoon you can have complete Intercom Messenger coverage that most applications never manage to achieve by hand. The iframe traversal, bot reply timing, and selector maintenance are all handled for you.
Related Guides
How to Test Ably Realtime
A practical guide to testing Ably Realtime messaging with Playwright. Covers token auth...
How to Test AI Chat Streaming UI
A practical guide to testing AI chat streaming interfaces with Playwright. Covers...
How to Test Collaborative Cursors
A practical guide to testing collaborative cursors with Playwright. Covers Liveblocks and...
Ready to automate your testing?
Assrt discovers test scenarios, writes Playwright tests from plain English, and self-heals when your UI changes.