Deep Links & Web Views
Jump straight to any screen with deep links, and drive embedded web views with the full Playwright API.
Deep Links: Skip the Tapping
A deep link opens a specific screen of your app directly via a URL. In tests this is gold: instead of tapping through five screens to reach the one you want to test, you jump straight there with device.openUrl(). Each test becomes faster and more focused:
import { test, expect } from '@mobilewright/test';
test.describe('order history', () => {
test.beforeEach(async ({ device }) => {
// Jump straight to the screen under test — no tapping through menus
await device.openUrl('myapp://account/orders');
});
test('shows past orders', async ({ screen }) => {
await expect(screen.getByText('Your Orders')).toBeVisible();
});
test('empty state shown when no orders', async ({ screen }) => {
await expect(screen.getByTestId('empty-orders')).toBeVisible();
});
});
const { test, expect } = require('@mobilewright/test');
test.describe('order history', () => {
test.beforeEach(async ({ device }) => {
// Jump straight to the screen under test — no tapping through menus
await device.openUrl('myapp://account/orders');
});
test('shows past orders', async ({ screen }) => {
await expect(screen.getByText('Your Orders')).toBeVisible();
});
test('empty state shown when no orders', async ({ screen }) => {
await expect(screen.getByTestId('empty-orders')).toBeVisible();
});
});
Want to verify your setup works before wiring up your own scheme? The tel: scheme exists on every device:
import { test, expect } from '@mobilewright/test';
test('tel: scheme opens the phone dialer', async ({ device, screen }) => {
await device.openUrl('tel:+15550001234');
await expect(screen.getByText('Call')).toBeVisible();
});
const { test, expect } = require('@mobilewright/test');
test('tel: scheme opens the phone dialer', async ({ device, screen }) => {
await device.openUrl('tel:+15550001234');
await expect(screen.getByText('Call')).toBeVisible();
});
Passing Parameters
Deep links support path segments and query parameters — whatever your app's routing understands:
import { test } from '@mobilewright/test';
test('deep link parameters', async ({ device }) => {
// Path parameter
await device.openUrl('myapp://users/99');
// Query parameters
await device.openUrl('myapp://search?q=headphones&sort=price');
// Combined
await device.openUrl('myapp://products/42?variant=blue&size=M');
});
const { test } = require('@mobilewright/test');
test('deep link parameters', async ({ device }) => {
// Path parameter
await device.openUrl('myapp://users/99');
// Query parameters
await device.openUrl('myapp://search?q=headphones&sort=price');
// Combined
await device.openUrl('myapp://products/42?variant=blue&size=M');
});
beforeEach, and each test stays short and independent.Web Views: The Full Playwright API
Many apps embed web content in a native web view (WKWebView on iOS, System WebView on Android). Mobilewright lets you drive that content with the real Playwright API — the same locators, actions, and web-first assertions. Under the hood it injects Playwright's own engine into the web view, so the Page and Locator objects you get back behave exactly like Playwright's.
import { test, expect } from '@mobilewright/test';
test('drive the in-app web view', async ({ device, screen }) => {
await device.launchApp('com.example.app');
// Navigate to the screen that hosts the web view
await screen.getByText('Web View').tap();
// Attach and get a Playwright-style Page
const page = await screen.getByWebView().page();
await page.goto('https://example.com/login');
await page.getByPlaceholder('Email').fill('user@example.com');
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page).toHaveURL(/\/dashboard/);
await expect(page.getByRole('heading')).toHaveText('Welcome');
});
const { test, expect } = require('@mobilewright/test');
test('drive the in-app web view', async ({ device, screen }) => {
await device.launchApp('com.example.app');
// Navigate to the screen that hosts the web view
await screen.getByText('Web View').tap();
// Attach and get a Playwright-style Page
const page = await screen.getByWebView().page();
await page.goto('https://example.com/login');
await page.getByPlaceholder('Email').fill('user@example.com');
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page).toHaveURL(/\/dashboard/);
await expect(page.getByRole('heading')).toHaveText('Welcome');
});
One important requirement: the app must be a debug build. On Android that means android:debuggable="true"; on iOS the app needs the get-task-allow entitlement (any development or Simulator build). Release builds cannot be attached to, and getByWebView() will not find a web view in them.
page.route), page.screenshot, dialogs/downloads, and multiple tabs.Written by PV
© 2026 All Rights Reserved