How to Block Chat Widgets During Playwright Tests (Drift, Intercom & More)

Chat widgets are great for customer support, but they can wreak havoc on your automated tests.

These floating elements often interfere with Playwright tests by covering clickable buttons, triggering unexpected popups, or causing element selection issues.

If you've ever had a test fail because a chat widget appeared at the wrong moment, you're not alone.

This guide shows you exactly how to block popular chat widgets like Drift, Intercom, Zendesk, and others during your Playwright test runs.

You'll learn practical techniques to prevent widget interference while maintaining reliable, consistent test execution.

Why chat widgets interfere with Playwright tests

Chat widgets create several challenges for automated testing that can turn reliable tests into flaky nightmares.

Understanding these issues helps you implement the right blocking strategies for your test suite.

Element visibility problems

Chat widgets often float over page content, making elements unclickable or invisible to Playwright's selectors.

This commonly happens when:

  • Widgets cover form buttons or navigation elements
  • Popup notifications appear during critical test steps
  • Widget animations trigger during element interactions

Timing and race conditions

Widgets load asynchronously and can appear at unpredictable moments during test execution.

This creates timing issues where:

  • Tests pass locally but fail in CI environments
  • Widget loading delays affect test performance
  • Race conditions occur between widget initialization and test actions

Network requests and performance

Chat widgets make additional network requests that can:

  • Slow down page load times
  • Cause timeouts in test environments
  • Interfere with network mocking or stubbing

Focus and interaction conflicts

Widgets can steal focus from form elements or trigger unwanted interactions:

  • Auto-focus on chat input fields
  • Keyboard shortcuts captured by widget
  • Mouse events intercepted by widget overlays

Blocking Drift chat widget in Playwright tests

Drift is one of the most popular chat widgets, and blocking it requires intercepting its domain requests during test execution.

Here's a comprehensive approach to prevent Drift from loading in your tests.

Basic Drift blocking function

Create a reusable helper function to block all Drift-related requests:

Code
import { Page } from '@playwright/test'

export async function blockDrift(page: Page) {
  await page.route('**/*', route => {
    const url = route.request().url()
    if (url.includes('drift.com') || url.includes('driftt.com')) {
      route.abort()
    } else {
      route.continue()
    }
  })
}

Using the blocking function in tests

Implement the blocking function in your test setup:

Code
import { Page, test, expect } from '@playwright/test'
import { blockDrift } from '../helpers/block-drift'

let page: Page

test.beforeEach(async ({ browser }) => {
  page = await browser.newPage()
  await blockDrift(page)
  await page.goto('https://your-website.com')
})

test('user can complete checkout without widget interference', async () => {
  // Your test steps here - no Drift widget will appear
  await page.click('[data-testid="checkout-button"]')
  await expect(page.locator('.checkout-form')).toBeVisible()
})

Advanced Drift blocking with multiple domains

Drift uses several domains and subdomains. Block them all for complete coverage:

Code
export async function blockDriftAdvanced(page: Page) {
  const driftDomains = [
    'drift.com',
    'driftt.com',
    'drift-widget.com',
    'js.driftt.com',
    'event.drift.com'
  ]

  await page.route('**/*', route => {
    const url = route.request().url()
    const shouldBlock = driftDomains.some(domain => url.includes(domain))
    
    if (shouldBlock) {
      route.abort()
    } else {
      route.continue()
    }
  })
}

Blocking with custom error handling

Add logging and error handling for better debugging:

Code
export async function blockDriftWithLogging(page: Page) {
  await page.route('**/*', route => {
    const url = route.request().url()
    
    if (url.includes('drift.com') || url.includes('driftt.com')) {
      console.log(`Blocked Drift request: ${url}`)
      route.abort()
    } else {
      route.continue()
    }
  })
}

Blocking other popular chat widgets

Different chat platforms use various domains and loading mechanisms. Here are blocking functions for the most common widgets.

Intercom blocking

Code
export async function blockIntercom(page: Page) {
  await page.route('**/*', route => {
    const url = route.request().url()
    if (url.includes('intercom.io') || url.includes('intercom.com')) {
      route.abort()
    } else {
      route.continue()
    }
  })
}

Zendesk Chat blocking

Code
export async function blockZendeskChat(page: Page) {
  await page.route('**/*', route => {
    const url = route.request().url()
    if (url.includes('zendesk.com') || url.includes('zdassets.com')) {
      route.abort()
    } else {
      route.continue()
    }
  })
}

HubSpot Chat blocking

Code
export async function blockHubSpotChat(page: Page) {
  await page.route('**/*', route => {
    const url = route.request().url()
    if (url.includes('hubspot.com') || url.includes('hs-scripts.com')) {
      route.abort()
    } else {
      route.continue()
    }
  })
}

Crisp Chat blocking

Code
export async function blockCrispChat(page: Page) {
  await page.route('**/*', route => {
    const url = route.request().url()
    if (url.includes('crisp.chat') || url.includes('crisp.help')) {
      route.abort()
    } else {
      route.continue()
    }
  })
}

LiveChat blocking

Code
export async function blockLiveChat(page: Page) {
  await page.route('**/*', route => {
    const url = route.request().url()
    if (url.includes('livechatinc.com') || url.includes('livechat.com')) {
      route.abort()
    } else {
      route.continue()
    }
  })
}

Freshchat blocking

Code
export async function blockFreshchat(page: Page) {
  await page.route('**/*', route => {
    const url = route.request().url()
    if (url.includes('freshchat.com') || url.includes('freshworks.com')) {
      route.abort()
    } else {
      route.continue()
    }
  })
}

Universal chat widget blocker

For maximum coverage, create a universal function that blocks multiple chat widgets simultaneously:

Code
export async function blockAllChatWidgets(page: Page) {
  const chatWidgetDomains = [
    // Drift
    'drift.com', 'driftt.com', 'drift-widget.com',
    // Intercom
    'intercom.io', 'intercom.com',
    // Zendesk
    'zendesk.com', 'zdassets.com',
    // HubSpot
    'hubspot.com', 'hs-scripts.com',
    // Crisp
    'crisp.chat', 'crisp.help',
    // LiveChat
    'livechatinc.com', 'livechat.com',
    // Freshchat
    'freshchat.com', 'freshworks.com',
    // Tawk.to
    'tawk.to',
    // Olark
    'olark.com',
    // Tidio
    'tidio.co',
    // Smartsupp
    'smartsupp.com'
  ]

  await page.route('**/*', route => {
    const url = route.request().url()
    const shouldBlock = chatWidgetDomains.some(domain => url.includes(domain))
    
    if (shouldBlock) {
      console.log(`Blocked chat widget request: ${url}`)
      route.abort()
    } else {
      route.continue()
    }
  })
}

Alternative blocking methods

Sometimes request interception isn't enough. Here are additional techniques for stubborn widgets.

CSS-based hiding

Hide widgets using CSS injection:

Code
export async function hideChatWidgets(page: Page) {
  await page.addStyleTag({
    content: `
      /* Hide common chat widget selectors */
      #drift-widget,
      .intercom-launcher,
      #zendesk_widget,
      .crisp-client,
      #livechat-widget,
      .freshchat-widget {
        display: none !important;
        visibility: hidden !important;
      }
    `
  })
}

JavaScript-based blocking

Prevent widgets from initializing with JavaScript:

Code
export async function preventChatWidgetInit(page: Page) {
  await page.addInitScript(() => {
    // Block common chat widget globals
    window.drift = undefined
    window.Intercom = undefined
    window.zE = undefined
    window.$crisp = undefined
  })
}

Combined approach

Use multiple blocking methods for maximum effectiveness:

Code
export async function comprehensiveChatBlock(page: Page) {
  // Block network requests
  await blockAllChatWidgets(page)
  
  // Hide with CSS
  await hideChatWidgets(page)
  
  // Prevent JavaScript initialization
  await preventChatWidgetInit(page)
}

Best practices for chat widget blocking

Implementing chat widget blocking effectively requires following proven practices that ensure reliable test execution.

Environment-specific blocking

Only block widgets in test environments:

Code
export async function conditionalChatBlock(page: Page) {
  if (process.env.NODE_ENV === 'test' || process.env.CI) {
    await blockAllChatWidgets(page)
  }
}

Selective blocking

Block widgets only for specific test suites:

Code
// In tests that need widget blocking
test.describe('Checkout flow tests', () => {
  test.beforeEach(async ({ page }) => {
    await blockDrift(page)
  })
  
  // Tests that require clean UI
})

// In tests where widgets are acceptable
test.describe('General navigation tests', () => {
  // No widget blocking needed
})

Performance monitoring

Track the impact of widget blocking on test performance:

Code
export async function blockWithMetrics(page: Page) {
  const startTime = Date.now()
  let blockedRequests = 0
  
  await page.route('**/*', route => {
    const url = route.request().url()
    if (url.includes('drift.com')) {
      blockedRequests++
      route.abort()
    } else {
      route.continue()
    }
  })
  
  console.log(`Blocked ${blockedRequests} widget requests in ${Date.now() - startTime}ms`)
}

Error handling

Gracefully handle blocking failures:

Code
export async function safeBlockChatWidgets(page: Page) {
  try {
    await page.route('**/*', route => {
      const url = route.request().url()
      if (url.includes('drift.com')) {
        route.abort()
      } else {
        route.continue()
      }
    })
  } catch (error) {
    console.warn('Failed to block chat widgets:', error)
    // Continue with test execution
  }
}

Testing your blocking implementation

Verify that your chat widget blocking works correctly with these validation techniques.

Visual verification

Check that widgets don't appear in screenshots:

Code
test('verify no chat widgets appear', async ({ page }) => {
  await blockDrift(page)
  await page.goto('https://your-website.com')
  
  // Take screenshot and verify no widget elements
  await expect(page).toHaveScreenshot('page-without-widgets.png')
  
  // Verify specific widget elements don't exist
  await expect(page.locator('#drift-widget')).not.toBeVisible()
})

Network request verification

Confirm that widget requests are actually blocked:

Code
test('verify widget requests are blocked', async ({ page }) => {
  const blockedUrls: string[] = []
  
  page.on('requestfailed', request => {
    if (request.url().includes('drift.com')) {
      blockedUrls.push(request.url())
    }
  })
  
  await blockDrift(page)
  await page.goto('https://your-website.com')
  
  expect(blockedUrls.length).toBeGreaterThan(0)
})

Performance impact testing

Measure the performance improvement from blocking widgets:

Code
test('measure performance improvement', async ({ page }) => {
  // Test without blocking
  const startWithWidgets = Date.now()
  await page.goto('https://your-website.com')
  const timeWithWidgets = Date.now() - startWithWidgets
  
  // Test with blocking
  await page.goto('about:blank')
  await blockDrift(page)
  const startWithoutWidgets = Date.now()
  await page.goto('https://your-website.com')
  const timeWithoutWidgets = Date.now() - startWithoutWidgets
  
  console.log(`Performance improvement: ${timeWithWidgets - timeWithoutWidgets}ms`)
})

Conclusion

Chat widgets shouldn't break your automated tests. With the blocking techniques covered in this guide, you can eliminate widget interference and create more reliable test suites.

Key takeaways for implementing chat widget blocking:

  • Use request interception to block widget domains at the network level
  • Implement environment-specific blocking to avoid affecting production
  • Combine multiple blocking methods for stubborn widgets
  • Test your blocking implementation to ensure it works correctly

The examples provided work for Drift, Intercom, Zendesk, HubSpot, and many other popular chat platforms.

Start with the universal blocker function and customize it based on your specific widget requirements.

Your Playwright tests will run more consistently, and you'll spend less time debugging flaky test failures caused by chat widget interference.

For comprehensive monitoring of your applications and services, consider using Hyperping to track uptime and performance alongside your automated testing strategy.

Article by
Léo Baecker
I'm Léo Baecker, the heart and soul behind Hyperping, steering our ship through the dynamic seas of the monitoring industry.
Get Started Free
Create your account
Blue check.15 day trialBlue check.No credit card required