8

I have an E2E tests suite with Playwright that runs on different browsers and viewports.

I specified the different browsers and viewports in my playwright.config.ts:

projects: [
  {
    name: 'chromium',
    use: {
      ...devices['Desktop Chrome'],
    },
  },

  {
    name: 'firefox',
    use: {
      ...devices['Desktop Firefox'],
    },
  },

  {
    name: 'webkit',
    use: {
      ...devices['Desktop Safari'],
    },
  },

  /* Test against mobile viewports. */
  {
    name: 'Mobile Chrome',
    use: {
      ...devices['Pixel 5'],
    },
  },
  {
    name: 'Mobile Safari',
    use: {
      ...devices['iPhone 12'],
    },
  },
]

There are certain tests that need to execute different assertions and clicks based on the viewport.

For example, when the viewport gets small enough, the header shrinks into a burger menu. On desktop, there is a "log out" button in the header, but on mobile, you have to open the burger menu first to see the "log out" button.

How can I write a test that runs only on mobile viewports (and vice versa, a test that runs only on desktop viewports)?

I was able to make it work with Playwright, but I didn't like my solution and suspect there is a better way to do this.

I grabbed the isMobile property from the test function, and then added a conditional click to open the burger menu on mobile like this:

test('given the user is logged in: lets the user log out', async ({
    page,
    baseURL,
    isMobile,
    browserName,
}) => {
    const { id } = await loginAndSaveUserProfileToDatabase({ page });
    await page.goto('./home');

    if (isMobile) {
      await page.getByRole('button', { name: /open main menu/i }).click();
    }

    // Logging the user out should redirect you to the landing page.
    await page.getByRole('button', { name: /open user menu/i }).click();
    await page.getByRole('menuitem', { name: /log out/i }).click();
    expect(page.url()).toEqual(baseURL + '/');
    // ...
}));

I don't like this approach, because it introduces a conditional into the test, which is an anti-pattern (and gets called out by the Playwright ESLint plugin). Also isMobile doesn't work for Firefox.

Ideally, there'd be a feature in Playwright like TestCafe's meta tags. In TestCafe you can add a meta to the test and filter out tests through this tag.

test
    .meta('mobile', true)
    ('My test that only runs on mobile', async t => { /* ... */});

But I couldn't find anything like that in the docs (other than grep tagging, which I'm unsure if its well suited for this use-case), so I'm asking this question.

J. Hesters
  • 13,117
  • 31
  • 133
  • 249

1 Answers1

7

You could use test.skip in your tests to avoid the Lint errors for conditionals so something like this: test.skip(!isMobile || browserName==firefox, 'is not mobile');

Other then that only grep or grepInvert comes to my mind. Maybe you can collect your mobile tests in describe blocks

test.describe('mobile', () => {
 test.beforeEach(async ({ page }) => {
    await page.goto(pageUrl);
  });

  test('some test', async ({ page })=>{
   
  });
});

And then in your playwright config you just add a grep or grepInvert to your projects. E.g.:

{
    name: 'chromium',
    grepInvert: /mobile/,
    use: {
      ...devices['Desktop Chrome'],
    },
},
{
    name: 'Mobile Chrome',
    grep: /mobile/,
    use: {
      ...devices['Pixel 5'],
    },
},
Basti
  • 449
  • 2
  • 13
  • 1
    I tried out both versions and the grep / grepInvert option is better. With test skipping, the skipped tests show up in reports. Test counts are increased because of the skipped tests. With describe blocks, you also get a nice hierarchy in the report viewer such as `mobile > `. – deviantintegral Mar 22 '23 at 00:01
  • 1
    This is a brilliant answer and works perfectly. I was hoping not to have to break up `npx playwright test` commands in our pipelines. This allows us to run our entire suite and execute some files against chromium and others against the playwright emulator. Thank you. – Phil Aug 03 '23 at 20:53