{"id":4265,"date":"2026-03-16T12:38:37","date_gmt":"2026-03-16T07:08:37","guid":{"rendered":"https:\/\/www.getpanto.ai\/blog\/?p=4265"},"modified":"2026-05-18T11:30:12","modified_gmt":"2026-05-18T06:00:12","slug":"playwright-react-native-hybrid-testing","status":"publish","type":"post","link":"https:\/\/www.getpanto.ai\/blog\/playwright-react-native-hybrid-testing","title":{"rendered":"How to Test Hybrid React Native Apps with Playwright"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Hybrid React Native apps combine native UI with web content (WebViews), and testing those mixed flows is uniquely challenging.<\/strong> <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Failures in embedded web pages \u2014 OAuth redirects, third-party checkouts, or analytics scripts \u2014 break user journeys even when native screens are fine. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide shows you how to use <a href=\"https:\/\/www.getpanto.ai\/blog\/playwright-mcp-for-mobile-app-testing#why-playwright-mcp-does-not-fully-solve-native-mobile-app-testing\">Playwright to reliably automate<\/a> the web portion of hybrid apps on Android: setup, best practices, sample tests, CI integration, and common pitfalls to avoid.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By the end you\u2019ll have a repeatable strategy to test webviews alongside native tests and keep CI fast and actionable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n<h2 class=\"wp-block-heading\" id=\"why-hybrid-testing-matters\"><span class=\"ez-toc-section\" id=\"why-hybrid-testing-matters\"><\/span><strong>Why Hybrid Testing Matters<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n<p class=\"wp-block-paragraph\">Hybrid components are common in production React Native apps. <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>OAuth and SSO<\/strong> pages often live on the web.<br><\/li>\n\n\n\n<li><strong>Payment and checkout<\/strong> flows may be hosted by a third party in a WebView.<br><\/li>\n\n\n\n<li><strong>Support portals, marketing, and <\/strong><a href=\"https:\/\/docs.getpanto.ai\/code-review\/overview\" target=\"_blank\" rel=\"noopener\"><strong>documentation<\/strong><\/a> can be rendered inside the app.<br><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">When the web layer fails (redirects break, third-party scripts error, or responsive layout regresses), the user journey stops even though native screens may be fine. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hybrid testing ensures those cross-layer journeys work end-to-end by validating the web content and its interaction with the native shell.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the web layer fails (redirects break, responsive layout fails, third-party script errors), the user journey is interrupted\u2014even when <a href=\"https:\/\/www.getpanto.ai\/blog\/native-mobile-app-testing#top-5-tools-for-native-mobile-app-testing\">native screens are fine<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hybrid testing validates the <em><strong>integration points<\/strong><\/em> and user journeys that span both native and web layers.<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"key-testing-tools-for-react-native-hybrid-apps\"><span class=\"ez-toc-section\" id=\"key-testing-tools-for-react-native-hybrid-apps\"><\/span><strong>Key Testing Tools for React Native Hybrid Apps<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n<p class=\"wp-block-paragraph\">Before diving into Playwright, it helps to compare popular frameworks for React Native apps:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Testing Framework<\/th><th class=\"has-text-align-left\" data-align=\"left\">Focus<\/th><th class=\"has-text-align-left\" data-align=\"left\">Strengths<\/th><th class=\"has-text-align-left\" data-align=\"left\">Limitations<\/th><\/tr><\/thead><tbody><tr><td><a href=\"https:\/\/www.getpanto.ai\/blog\/playwright-alternatives\"><strong>Playwright<\/strong><\/a><\/td><td>Web and Hybrid (Android WebViews)<\/td><td>Fast, unified web API; excellent for webviews. Auto-waits and parallel tests.<\/td><td><strong>No native UI control<\/strong>&nbsp;\u2013 can\u2019t tap RN views outside webviews. Android-only for WebView (iOS requires workarounds).<\/td><\/tr><tr><td><a href=\"https:\/\/www.getpanto.ai\/blog\/detox-alternatives\"><strong>Detox<\/strong><\/a><\/td><td>React Native (iOS &amp; Android)<\/td><td>Designed for RN; can tap native components directly. Integrates with Metro.<\/td><td>Limited to RN; cannot drive webview content inside the app.<\/td><\/tr><tr><td><a href=\"https:\/\/www.getpanto.ai\/blog\/appium-alternatives\"><strong>Appium<\/strong><\/a><\/td><td>Native and Hybrid (Android\/iOS)<\/td><td>WebView + native support; cross-platform; widely used. Can automate any element via accessibility IDs.<\/td><td>Setup can be complex; slower; test scripts tend to be more brittle.<\/td><\/tr><tr><td><a href=\"https:\/\/www.getpanto.ai\/blog\/maestro-alternatives\"><strong>Maestro<\/strong><\/a><\/td><td>Mobile (Android\/iOS)<\/td><td>Supports hybrid apps and gestures; simpler syntax.<\/td><td>Emerging tool, less mature ecosystem.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">In React Native projects,&nbsp;<strong>Detox<\/strong>&nbsp;and&nbsp;<strong>Appium<\/strong>&nbsp;are commonly used for end-to-end tests. Appium can also drive WebViews but requires special configuration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Playwright shines when your app\u2019s core logic lives in web code (Cordova\/Capacitor style) or when using complex web-based flows (e.g. embedded PWAs). <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It treats the WebView as a browser context, letting you write tests in JavaScript\/TypeScript with the familiar&nbsp;<code>page.click()<\/code>,&nbsp;<code>page.fill()<\/code>, etc.<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"where-playwright-fits-in\"><span class=\"ez-toc-section\" id=\"where-playwright-fits-in\"><\/span><strong>Where Playwright Fits In<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n<p class=\"wp-block-paragraph\">Playwright excels at automating and <a href=\"https:\/\/www.getpanto.ai\/blog\/why-playwright-mcp-isnt-enough-and-what-mobile-qa-teams-actually-need#playwrightmcp-is-webcentric-not-mobilecentric\">validating browser-based functionality<\/a>: rendering, navigation, form submission, redirects, cookies, network conditions, and cross-origin flows. That makes it ideal for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Web authentication and SSO flows.<br><\/li>\n\n\n\n<li>WebView content verification (HTML\/CSS\/JS behavior inside the app).<br><\/li>\n\n\n\n<li>Device-emulated mobile web tests for responsive behavior.<br><\/li>\n\n\n\n<li>Testing hosted external integrations (payment gateways, analytics redirects).<br><\/li>\n<\/ul>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-3a88641f wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:100%\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Test Layer<\/th><th>Typical Tool<\/th><\/tr><\/thead><tbody><tr><td>Unit Tests<\/td><td>Jest<\/td><\/tr><tr><td>Component Tests<\/td><td>React Native Testing Library<\/td><\/tr><tr><td>Web \/ Hybrid (WebViews)<\/td><td>Playwright<\/td><\/tr><tr><td>Native E2E (gestures, hardware)<\/td><td><a href=\"https:\/\/www.getpanto.ai\/blog\/detox-vs-appium\">Detox \/ Appium<\/a><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Where Playwright helps:<\/strong> web authentication flows, WebView content verification, device-emulated responsive checks, and testing third-party web integrations.<br><\/p>\n<\/div>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"setting-up-the-playwright-test-environment\"><span class=\"ez-toc-section\" id=\"setting-up-the-playwright-test-environment\"><\/span><strong>Setting Up The Playwright Test Environment<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n<h3 class=\"wp-block-heading\" id=\"step-1-basic-requirements\"><span class=\"ez-toc-section\" id=\"step-1-basic-requirements\"><\/span><strong>Step 1: Basic Requirements<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n<p class=\"wp-block-paragraph\">Before writing tests, ensure your development environment meets Playwright\u2019s <a href=\"https:\/\/www.getpanto.ai\/blog\/native-mobile-app-testing#what-is-native-mobile-app-testing\">mobile testing<\/a> requirements. Below are the key requirements and short guidance for each item so you can get started quickly.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Node.js and Playwright<\/strong> \u2013 Install Node (v14+) and Playwright via <code>npm init playwright@latest<\/code> or <code>npm i -D @playwright\/test<\/code>. If you plan to test Android WebViews, run <code>npx playwright install android<\/code>.<br><\/li>\n\n\n\n<li><strong>Android SDK \/ Emulator<\/strong> \u2013 Install Android SDK (Platform-Tools) and an emulator (AVD) or connect a real device via USB. Make sure <em>USB debugging<\/em> is enabled in Developer Options on the device.<br><\/li>\n\n\n\n<li><strong>ADB (Android Debug Bridge)<\/strong> \u2013 Add <code>adb<\/code> to your <code>PATH<\/code>. Verify connectivity with <code>adb devices<\/code> \u2014 your device or emulator should be listed.<br><\/li>\n\n\n\n<li><strong>Chrome \/ WebView Version \u2265 87<\/strong> \u2013 The Android system WebView or Chrome on the device must be recent (Playwright requires WebView \/ Chrome 87 or newer).<br><\/li>\n\n\n\n<li><strong>Chrome Debugging Flag<\/strong> \u2013 On non-rooted devices, enable the flag <code>Enable command line on non-rooted devices<\/code> in <code>chrome:\/\/flags<\/code> so Playwright can talk to Chrome DevTools.<br><\/li>\n\n\n\n<li><strong>iOS (Optional)<\/strong> \u2013 Playwright does <em>not<\/em> natively support automating WKWebViews on iOS. If you must test iOS WebViews, consider tools like Appium or community tools such as Canter. This guide focuses on Android.<br><\/li>\n<\/ul>\n\n\n<h4 class=\"wp-block-heading\" id=\"requirements-checklist\"><strong>Requirements Checklist<\/strong><\/h4>\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Requirement<\/th><th>Description<\/th><\/tr><\/thead><tbody><tr><td>Node.js &amp; Playwright<\/td><td>Latest Node.js (v14+) and Playwright (<code>npm i -D @playwright\/test<\/code>).<\/td><\/tr><tr><td>Android SDK &amp; AVD<\/td><td>Android SDK with emulator or a real device. Enable USB debugging.<\/td><\/tr><tr><td>Android Debug Bridge (ADB)<\/td><td><code>adb<\/code> must be running and list devices with <code>adb devices<\/code>.<\/td><\/tr><tr><td>Chrome 87+ on Device<\/td><td>Install or update Android System WebView \/ Chrome to Chrome 87 or newer.<\/td><\/tr><tr><td>Chrome Debugging Flag<\/td><td>Enable <code>chrome:\/\/flags\/#enable-command-line-on-non-rooted<\/code> on the device.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n<h4 class=\"wp-block-heading\" id=\"install-playwright-android-dependencies\"><strong>Install Playwright Android Dependencies<\/strong><\/h4>\n\n\n<p class=\"wp-block-paragraph\">To set up Playwright\u2019s test runner and Android support, run these commands in your project directory:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Install Playwright test runner\nnpm init -y\nnpm i -D @playwright\/test\n\n# Install Android support (Chromium\/WebView)\nnpx playwright install android\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Short notes:<\/strong> If <code>npx playwright install android<\/code> fails (commonly on CI\/Linux runners due to missing system libraries), try:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npx playwright install --with-deps<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Or ensure required system libraries are present on your runner.<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"step-2-launching-the-react-native-app-and-attaching-playwright\"><span class=\"ez-toc-section\" id=\"step-2-launching-the-react-native-app-and-attaching-playwright\"><\/span><strong>Step 2: Launching The React Native App And Attaching Playwright<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n<p class=\"wp-block-paragraph\">When the environment is ready, the next step is to start your React Native app on an Android device\/emulator and then <a href=\"https:\/\/www.getpanto.ai\/blog\/playwright-mcp-for-mobile-app-testing#2-playwright-installation-and-configuration\">attach Playwright<\/a> to the app&#8217;s WebView. The general flow is:<\/p>\n\n\n<h4 class=\"wp-block-heading\" id=\"run-or-install-the-app\"><strong>Run Or Install The App<\/strong><\/h4>\n\n\n<p class=\"wp-block-paragraph\">You can sideload the APK or launch your app using ADB. Example ADB commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>adb install path\/to\/app.apk\nadb shell am start -n com.example.myapp\/.MainActivity\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Replace <code>com.example.myapp\/.MainActivity<\/code> with your app\u2019s package and main activity. If using React Native local development you can also run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npx react-native run-android<\/code><\/pre>\n\n\n<h4 class=\"wp-block-heading\" id=\"connect-playwright-to-the-device\"><strong>Connect Playwright To The Device<\/strong><\/h4>\n\n\n<p class=\"wp-block-paragraph\">In your test file, use Playwright\u2019s Android API to discover and connect to the device. Example (Node\/JS):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { _android as android } from '@playwright\/test';\n\n(async () =&gt; {\n  \/\/ Connect to the first available Android device\/emulator\n  const &#91;device] = await android.devices();\n  console.log(`Connected to ${device.model()} (S\/N: ${device.serial()})`);\n  \/\/ Optional: screenshot device home\n  await device.screenshot({ path: 'device-home.png' });\n})();<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The call <code>android.devices()<\/code> returns <a href=\"https:\/\/www.getpanto.ai\/blog\/automated-mobile-qa-ai-testing#real-devices-and-cloud-labs\">connected Android devices<\/a> that Playwright can use.<\/p>\n\n\n<h4 class=\"wp-block-heading\" id=\"launch-the-app-with-adb-shell-if-needed\"><strong>Launch The App With ADB Shell (If Needed)<\/strong><\/h4>\n\n\n<p class=\"wp-block-paragraph\">If the app isn&#8217;t running, you can start it from Playwright via shell commands on the device:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Stop then start the app\nawait device.shell('am force-stop com.example.myapp');\nawait device.shell('am start -n com.example.myapp\/.MainActivity');<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Adjust package and activity names as required.<\/p>\n\n\n<h4 class=\"wp-block-heading\" id=\"attach-to-the-apps-webview\"><strong>Attach To The App\u2019s WebView<\/strong><\/h4>\n\n\n<p class=\"wp-block-paragraph\">Once the app is running, request Playwright to find the WebView instance by package name and obtain a Playwright <code>page<\/code> that represents the WebView content:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const webview = await device.webView({ pkg: 'com.example.myapp' });\nconst page = await webview.page(); \/\/ Playwright Page for WebView content<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After this you have a normal Playwright <code>page<\/code> object and can interact with the web UI inside the WebView as if it were a mobile browser page.<\/p>\n\n\n<h4 class=\"wp-block-heading\" id=\"handling-multiple-webviews\"><strong>Handling Multiple WebViews<\/strong><\/h4>\n\n\n<p class=\"wp-block-paragraph\">If your app creates multiple WebViews, <code>device.webView()<\/code> may hang or attach to the wrong instance. Options to handle this:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ensure only one WebView is active at attach time (close or pause others).<\/li>\n\n\n\n<li>Use newer Playwright versions (<a href=\"https:\/\/www.getpanto.ai\/blog\/mobile-app-testing-ai-top-bugs#understanding-why-these-five-bugs-never-disappear\">bug fixes<\/a> often land that address multiple-webview issues).<br><\/li>\n\n\n\n<li>Specify additional criteria at attach time (where supported) such as process name.<br><\/li>\n<\/ul>\n\n\n<h3 class=\"wp-block-heading\" id=\"step-3-writing-playwright-tests-for-the-webview\"><span class=\"ez-toc-section\" id=\"step-3-writing-playwright-tests-for-the-webview\"><\/span><strong>Step 3: Writing Playwright Tests For The WebView<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n<p class=\"wp-block-paragraph\">Once you have <code>page = await webview.page()<\/code>, you use Playwright\u2019s web API normally. Below is a simple example that interacts with a login form inside the WebView.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Wait for the login form inside the WebView\nawait page.waitForSelector('#username');\nawait page.fill('#username', 'testuser');\nawait page.fill('#password', 'secret123');\n\n\/\/ Use tap() to simulate touch\nawait page.tap('#login-button');\n\nawait page.waitForSelector('#welcome-message');\nconst welcomeText = await page.textContent('#welcome-message');\nconsole.log(`Logged in, message: ${welcomeText}`);<\/code><\/pre>\n\n\n<h4 class=\"wp-block-heading\" id=\"key-points-for-mobile-web-testing\"><strong>Key Points For Mobile Web Testing<\/strong><\/h4>\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Use Touch Events<\/strong> \u2013 Prefer <code>page.tap()<\/code> or <code>locator.tap()<\/code> over <code>page.click()<\/code> so mobile touch events (touchstart\/touchend) fire properly.<br><\/li>\n\n\n\n<li><strong>Selectors<\/strong> \u2013 Use standard CSS\/text selectors; switch frames where necessary if the WebView loads iframes.<br><\/li>\n\n\n\n<li><strong>Navigation &amp; Waits<\/strong> \u2013 <a href=\"https:\/\/www.getpanto.ai\/blog\/playwright-vs-maestro#1-auto-waiting-flakiness-tolerance\">Playwright auto-waits<\/a> for many actions, but use explicit <code>waitForNavigation<\/code> or selector waits if pages take longer to load on mobile.<br><\/li>\n\n\n\n<li><strong>Multiple Pages\/Popups<\/strong> \u2013 If WebView spawns popups, capture them using <code>page.context().on('page', ...)<\/code>.<br><\/li>\n\n\n\n<li><strong>Screenshots<\/strong> \u2013 Use <code>await page.screenshot()<\/code> for the WebView content and <code>await device.screenshot()<\/code> for the full device screen (native + web).<br><\/li>\n\n\n\n<li><strong>Browser Context<\/strong> \u2013 The WebView page runs in its own browser context. Manage cookies and sessions with Playwright APIs as needed.<br><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Example verifying a product page inside the WebView:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Navigate inside the WebView\nawait page.goto('https:\/\/example.com\/product\/42');\n\n\/\/ Validate product title\nawait page.waitForSelector('h1.product-title');\nawait expect(page.locator('h1.product-title')).toHaveText('Cool Widget');<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id=\"step-4-setting-up-playwright-test-runner\"><span class=\"ez-toc-section\" id=\"step-4-setting-up-playwright-test-runner\"><\/span><strong>Step 4: Setting Up Playwright Test Runner<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n<p class=\"wp-block-paragraph\">Rather than running ad-hoc scripts, use <em>Playwright Test<\/em> to organize, run and report on <a href=\"https:\/\/www.getpanto.ai\/products\/self-healing-test-automation\">test suites<\/a>. Example <code>playwright.config.ts<\/code> snippet that targets Android:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { defineConfig, devices } from '@playwright\/test';\n\nexport default defineConfig({\n  projects: &#91;\n    {\n      name: 'AndroidWebView',\n      use: {\n        \/\/ Target Android devices; this preset uses the first available device via ADB.\n        channel: 'chrome',\n      },\n    },\n  ],\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And a sample test file using the Android fixtures:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { test, expect, _android as android } from '@playwright\/test';\n\ntest('hybrid app login flow', async () =&gt; {\n  const &#91;device] = await android.devices();\n  \/\/ assume the app is installed &amp; launched\n  const webview = await device.webView({ pkg: 'com.example.myapp' });\n  const page = await webview.page();\n\n  \/\/ interact with page\n  await page.waitForSelector('#username');\n  await page.fill('#username', 'testuser');\n  await page.tap('#login-button');\n  await expect(page.locator('#welcome-message')).toBeVisible();\n});<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run tests with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npx playwright test<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id=\"continuous-integration-and-reporting\"><span class=\"ez-toc-section\" id=\"continuous-integration-and-reporting\"><\/span><strong>Continuous Integration and Reporting<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n<p class=\"wp-block-paragraph\">Integrating your hybrid app tests into CI is similar to any Playwright workflow. You commit code, a CI runner starts an emulator\/device, and runs your Playwright tests, producing a report. For example, you might add a <a href=\"https:\/\/docs.getpanto.ai\/code-review\/installations\/github\" target=\"_blank\" rel=\"noopener\">GitHub Actions workflow<\/a> like:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">yamlCopy<code>name: Playwright Android Tests<br>on: [push, pull_request]<br>jobs:<br>  test:<br>    runs-on: ubuntu-latest<br>    steps:<br>      - uses: actions\/checkout@v3<br>      - uses: actions\/setup-node@v4<br>        with:<br>          node-version: '18'<br>      - run: npm ci<br>      - run: npx playwright install --with-deps<br>      - run: |<br>          adb start-server<br>          adb devices<br>      - run: npx playwright test<br>      - uses: actions\/upload-artifact@v3<br>        with:<br>          name: playwright-report<br>          path: playwright-report\/<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"763\" height=\"498\" src=\"https:\/\/www.getpanto.ai\/blog\/wp-content\/uploads\/2026\/03\/image.png\" alt=\"\" class=\"wp-image-4266\" srcset=\"https:\/\/www.getpanto.ai\/blog\/wp-content\/uploads\/2026\/03\/image.png 763w, https:\/\/www.getpanto.ai\/blog\/wp-content\/uploads\/2026\/03\/image-300x196.png 300w, https:\/\/www.getpanto.ai\/blog\/wp-content\/uploads\/2026\/03\/image-200x131.png 200w\" sizes=\"auto, (max-width: 763px) 100vw, 763px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The diagram above illustrates a typical CI workflow: <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>developers\u00a0<strong>commit code<\/strong>\u00a0(Step 1)<\/li>\n\n\n\n<li>the\u00a0<strong>Playwright test suite<\/strong>\u00a0runs on Android emulators or devices (Step 2)<\/li>\n\n\n\n<li>then a\u00a0<strong>report<\/strong>\u00a0is generated (Step 3). <\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Playwright Test produces HTML reports and traces by default; <a href=\"https:\/\/www.getpanto.ai\/blog\/how-to-reduce-ci-test-runtime#the-future-of-ci-runtime-management\">CI systems<\/a> can archive these for review.<\/p>\n\n\n<h3 class=\"wp-block-heading\" id=\"common-challenges-and-tips\"><span class=\"ez-toc-section\" id=\"common-challenges-and-tips\"><\/span><strong>Common Challenges and Tips<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Multiple WebViews<\/strong>: As noted, if your app creates more than one WebView, Playwright\u2019s\u00a0<code>device.webView()<\/code>\u00a0might attach to the wrong one or hang. <br><br>In Playwright v1.34+ this issue is fixed, so ensure you use an up-to-date version. Alternatively, streamline the app so only one WebView is active during tests.<br><br><\/li>\n\n\n\n<li><strong>Native dialogs and interactions<\/strong>: Playwright cannot tap native dialogs (permission prompts, etc.). <br><br>If your app triggers an Android permission dialog (e.g. camera access), you must automate it with ADB or <a href=\"https:\/\/www.getpanto.ai\/blog\/appium-mcp-for-mobile-app-qa-testing\">Appium<\/a>, or grant permissions ahead of time. You can use\u00a0<code>device.grantPermissions({ package: '...', permissions: [...] })<\/code>\u00a0if needed.<br><br><\/li>\n\n\n\n<li><strong>Page.waitForXXX<\/strong>: Because Playwright waits for network and rendering, your tests can be reliable. However, <a href=\"https:\/\/www.getpanto.ai\/blog\/automated-mobile-qa-ai-testing#visual-and-performance-regression-detection\">mobile performance<\/a> can be slower; consider increasing timeouts if pages take longer to load.<br><br><\/li>\n\n\n\n<li><strong>Context and Cookies<\/strong>: Be aware that <a href=\"https:\/\/www.getpanto.ai\/blog\/selenium-vs-playwright#real-device-testing-where-each-leads\">Playwright\u2019s WebView<\/a> context is separate from your desktop browser context. If the web content requires login or cookies, manage them via standard Playwright APIs.<br><br> (Note: some Chromium\/WebView versions may have quirks with cookie storage. If you encounter errors like \u201cBrowser context management is not supported,\u201d it\u2019s due to a low-level WebView limitation. Updating the Android WebView system component on the device often helps.)<br><br><\/li>\n\n\n\n<li><strong>Debugging<\/strong>: Use&nbsp;<code>await device.screenshot()<\/code>&nbsp;and&nbsp;<code>await webview.page().screenshot()<\/code>&nbsp;to debug. Playwright\u2019s trace viewer can also record sessions to inspect step-by-step.<br><br><\/li>\n\n\n\n<li><strong>Mobile Gestures<\/strong>: For advanced gestures (swipe, pinch), Playwright doesn\u2019t have one-liners, but you can simulate drag with&nbsp;<code>page.mouse.down()<\/code>&nbsp;\/&nbsp;<code>page.mouse.move()<\/code>&nbsp;\/&nbsp;<code>page.mouse.up()<\/code>. There are no built-in pinch, but you could call&nbsp;<code>page.evaluate<\/code>&nbsp;with JavaScript to trigger zoom if needed.<br><br><\/li>\n\n\n\n<li><strong>iOS Hybrid<\/strong>: If you need to test an iOS React Native app\u2019s WebView, Playwright alone won\u2019t suffice. Tools like\u00a0Canter\u00a0(for Safari\/WKWebView automation on macOS) or <a href=\"https:\/\/www.getpanto.ai\/blog\/appium-vs-playwright\">Appium must be used<\/a>. <br><br>Currently, Playwright\u2019s WebKit engine is only accessible via emulation of desktop Safari or mobile Safari (i.e., it cannot attach to an iPhone app\u2019s WebView in the same way).<\/li>\n<\/ul>\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\"><span class=\"ez-toc-section\" id=\"conclusion\"><\/span><strong>Conclusion<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n<p class=\"wp-block-paragraph\">Testing hybrid React Native apps involves bridging the native and web worlds. Playwright provides a powerful way to automate the\u00a0<em>web<\/em>\u00a0portion of a hybrid app by leveraging Android\u2019s <a href=\"https:\/\/www.getpanto.ai\/blog\/vibe-debugging-effortless-engineering#understanding-the-vibe-in-vibe-debugging\">debugging<\/a> interfaces.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By setting up the Android device\/emulator, launching the React Native app, and attaching to its WebView, you can write Playwright tests that fill forms, tap buttons, and assert content as if in a mobile browser. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This complements <a href=\"https:\/\/www.getpanto.ai\/\">native-focused tools<\/a> (like Detox\/Appium) by covering the webview logic and UI.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In summary, to test a hybrid RN app with Playwright:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Prepare the environment<\/strong>&nbsp;(Node.js, Playwright, Android SDK, Chrome 87+).<\/li>\n\n\n\n<li><strong>Launch the app<\/strong>&nbsp;on a device or emulator (via ADB\/<code>run-android<\/code>).<\/li>\n\n\n\n<li><strong>Attach to the WebView<\/strong>&nbsp;using&nbsp;<code>device.webView({ pkg: 'your.app.pkg' })<\/code>&nbsp;and get the&nbsp;<code>page<\/code>.<\/li>\n\n\n\n<li><strong>Drive tests in the&nbsp;<code>page<\/code>&nbsp;context<\/strong>&nbsp;using regular Playwright commands (<code>page.fill<\/code>,&nbsp;<code>page.tap<\/code>,&nbsp;<code>page.waitForSelector<\/code>, etc.), simulating mobile user actions.<\/li>\n\n\n\n<li><strong>Integrate into CI<\/strong>&nbsp;by running your Playwright tests on emulators\/devices and publishing reports.<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">By following this approach and watching for known issues (multiple webviews, permission dialogs, etc.), you can build a robust <a href=\"https:\/\/www.getpanto.ai\/products\/no-code-test-automation-tools\">end-to-end testing strategy<\/a> for React Native hybrid apps.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This not only validates your web views but also ensures that the hybrid user experience remains reliable across releases.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hybrid React Native apps combine native UI with web content (WebViews), and testing those mixed flows is uniquely challenging. Failures in embedded web pages \u2014 OAuth redirects, third-party checkouts, or analytics scripts \u2014 break user journeys even when native screens are fine. This guide shows you how to use Playwright to reliably automate the web [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":4268,"comment_status":"open","ping_status":"open","sticky":false,"template":"wp-custom-template-panto-blogs-v3","format":"standard","meta":{"footnotes":""},"categories":[110],"tags":[],"class_list":["post-4265","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai-qa-testing"],"_links":{"self":[{"href":"https:\/\/www.getpanto.ai\/blog\/wp-json\/wp\/v2\/posts\/4265","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.getpanto.ai\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.getpanto.ai\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.getpanto.ai\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.getpanto.ai\/blog\/wp-json\/wp\/v2\/comments?post=4265"}],"version-history":[{"count":0,"href":"https:\/\/www.getpanto.ai\/blog\/wp-json\/wp\/v2\/posts\/4265\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.getpanto.ai\/blog\/wp-json\/wp\/v2\/media\/4268"}],"wp:attachment":[{"href":"https:\/\/www.getpanto.ai\/blog\/wp-json\/wp\/v2\/media?parent=4265"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.getpanto.ai\/blog\/wp-json\/wp\/v2\/categories?post=4265"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.getpanto.ai\/blog\/wp-json\/wp\/v2\/tags?post=4265"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}