I have a spec file that has a test:
test.describe('mobile', () => {
test.beforeEach(async ({ page }) => {
await page.goto(url);
});
test('Mobile check - (Optimal on desktop / tablet) only shown on smaller screens (i.e mobile breakpoint)', async ({ page })=>{
await page.goto(url);
await expect(page.getByText('(Optimal on desktop / tablet)')).toBeVisible();
});
});
I have in the playwright config setup 2 mobile browsers to test iPhone and Android emulation:
/* Test against mobile viewports. */
{
name: 'Mobile Chrome',
grep: /mobile/,
use: { ...devices['Pixel 5'] },
},
{
name: 'Mobile Safari',
grep: /mobile/,
use: { ...devices['iPhone 14 Pro'] },
},
When I run this, it runs the tests in the desktop browser, not the mobile window. I've seen examples of setting the viewport on a per test basis, but not the emulator itself.
I'd like to be able to have 1 specs file describing both mobile and desktop emulation, rather than separate by device type.
Can I set the target emulator on per test basis?
I don't want to have to run with filters; rather I run the tests, they execute individually against a targeted browser/s.
test.spec.ts
import { test, expect, devices } from '@playwright/test';
test.use({ storageState: 'playwright/.auth.json' });
const url = 'https://test.com/custom-library';
test.describe('desktop', () => {
test('Hide No Data (shown twice) on radar whilst awaiting an input', async ({ page, isMobile }) => {
test.skip(isMobile)
await page.goto(url);
const buttonsCount = await page.getByText('No Data').count();
expect(buttonsCount).toBe(0);
});
test('Desktop check - (Optimal on desktop / tablet) only shown on smaller screens (i.e mobile breakpoint)', async ({ page, isMobile }) => {
test.skip(isMobile)
await page.goto(url);
const buttonsCount = await page.getByText('(Optimal on desktop / tablet)').count();
expect(buttonsCount).toBe(0);
});
});
test.describe('mobile', () => {
test('Mobile check - (Optimal on desktop / tablet) only shown on smaller screens (i.e mobile breakpoint)', async ({ page, isMobile })=>{
console.log(!isMobile);
test.skip(!isMobile)
await page.goto(url);
await expect(page.getByText('(Optimal on desktop / tablet)')).toBeVisible();
});
});
My playwright.config.ts file is:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
trace: 'on-first-retry',
},
projects: [
{
name: 'setup',
testMatch: /.*\.setup\.ts/
},
{
name: 'chromium',
use: { ...devices['Desktop Chrome'], storageState: './playwright/.auth.json' },
dependencies: ['setup'],
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'], storageState: './playwright/.auth.json' },
dependencies: ['setup'],
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'], storageState: './playwright/.auth.json' },
dependencies: ['setup'],
},
/* Test against mobile viewports. */
{
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'], storageState: './playwright/.auth.json' },
dependencies: ['setup'],
},
{
name: 'Mobile Safari',
use: { ...devices['iPhone 14 Pro'], storageState: './playwright/.auth.json' },
dependencies: ['setup'],
},
],
});
Your approach looks basically OK, with some improvements available. For starters, here's your code as a fully working, reproducible example.
grep
playwright.config.js:
import {defineConfig, devices} from "@playwright/test"; // ^1.51.0
export default defineConfig({
projects: [
{
name: "Mobile Chrome",
grep: /mobile/,
use: {...devices["Pixel 5"]},
},
{
name: "Mobile Safari",
grep: /mobile/,
use: {...devices["iPhone 14 Pro"]},
},
],
});
example.test.js:
import {expect, test} from "@playwright/test";
const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="text"></div>
<script>
function updateText() {
const textElement = document.getElementById("text");
textElement.textContent =
window.innerWidth <= 900 ? "mobile" : "desktop";
}
updateText();
window.addEventListener("resize", updateText);
</script>
</body>
</html>`;
test("mobile check", async ({page}) => {
await page.setContent(html);
await expect(page.locator("#text")).toHaveText("mobile");
});
test("desktop check", async ({page}) => {
await page.setContent(html);
await expect(page.locator("#text")).toHaveText("desktop");
});
Sample run:
% npx playwright test
Running 2 tests using 2 workers
✓ 1 [Mobile Chrome] › example.test.js:23:5 › mobile check (261ms)
✓ 2 [Mobile Safari] › example.test.js:23:5 › mobile check (465ms)
2 passed (930ms)
The first note to mention is that grep
applies to the whole test name, including the project name and file name. So if your project name happens to match the grep
pattern, every test will run regardless of whether the test is mobile or not!
As such, the preferred approach is to use tags which are less prone to inaccuracies like this.
playwright.config.js:
import {defineConfig, devices} from "@playwright/test";
export default defineConfig({
projects: [
{
name: "Mobile Chrome",
grep: /@mobile/,
use: {...devices["Pixel 5"]},
},
{
name: "Mobile Safari",
grep: /@mobile/,
use: {...devices["iPhone 14 Pro"]},
},
],
});
example.test.js:
test("mobile check", {tag: "@mobile"}, async ({page}) => {
await page.setContent(html);
await expect(page.locator("#text")).toHaveText("mobile");
});
test("desktop check", {tag: "@desktop"}, async ({page}) => {
await page.setContent(html);
await expect(page.locator("#text")).toHaveText("desktop");
});
Output:
% npx playwright test
Running 2 tests using 2 workers
✓ 1 [Mobile Safari] › example.test.js:23:5 › mobile check @mobile (531ms)
✓ 2 [Mobile Chrome] › example.test.js:23:5 › mobile check @mobile (290ms)
2 passed (1.1s)
isMobile
and test.skip()
isMobile
and test.skip()
are another options:
playwright.config.js
import {defineConfig, devices} from "@playwright/test";
export default defineConfig({
projects: [
{
name: "Mobile Chrome",
use: {...devices["Pixel 5"]},
},
{
name: "Desktop",
use: {...devices["Desktop Chrome"]},
},
],
});
example.test.js
test("mobile check", async ({page, isMobile}) => {
test.skip(!isMobile)
await page.setContent(html);
await expect(page.locator("#text")).toHaveText("mobile");
});
test("desktop check", async ({page, isMobile}) => {
test.skip(isMobile)
await page.setContent(html);
await expect(page.locator("#text")).toHaveText("desktop");
});
Output:
% npx playwright test
Running 4 tests using 2 workers
✓ 1 [Mobile Chrome] › example.test.js:23:5 › mobile check (171ms)
- 2 [Desktop] › example.test.js:23:5 › mobile check
✓ 3 [Desktop] › example.test.js:29:5 › desktop check (78ms)
- 4 [Mobile Chrome] › example.test.js:29:5 › desktop check
2 skipped
2 passed (747ms)
You can use any of these methods at a describe
block level to create groups of tests if you prefer.