Search code examples
javascripttypescriptplaywright

Cannot access element in Playwright global setup script


I'm trying to use Playwright to automate authentication in my web application. When I did the authentication test in a typical .spec.ts file, it succeeded:

    test('bnblnlnnl', async ({ page }) => {
        await page.goto('/');

        await page.getByTestId('auth-github-auth-button').click();
        await page.getByLabel('Username or email address').fill('automations@blabla');
        await page.getByLabel('Password').fill('sdfgsdgsdfgfgf');
        await page.getByRole('button', { name: 'Sign in' }).click();
        const authorizeElement = page.getByRole('button', { name: 'Authorize blabla' });
        const shouldAuthorize = await authorizeElement.isVisible();

        if (shouldAuthorize) {
            await authorizeElement.click();
        }

        const navElemnt = page.getByTestId('nav');

        await expect(navElemnt).toBeVisible();
        await expect(page).toHaveURL('/');
    });

So this test successfully completes. Then, according to this documentation: https://playwright.dev/docs/auth

I can authenticate already in the global setup script, instead of authenticating before each test block. To do so, I have this script for my global setup file:

import { chromium } from '@playwright/test';

const globalSetup = async () => {
    const browser = await chromium.launch();
    const page = await browser.newPage();

    await page.goto('http://localhost:8080/');
    await page.getByTestId('auth-github-auth-button').click();

    await page.getByLabel('Username or email address').fill('gfsdagdf');
    await page.getByLabel('Password').fill('sadfsdfsdfs');
    await page.getByRole('button', { name: 'Sign in' }).click();

    const authorizeElement = page.getByRole('button', { name: 'Authorize dfssd' });
    const shouldAuthorize = await authorizeElement.isVisible();

    if (shouldAuthorize) {
        await authorizeElement.click();
    }

    await page.context().storageState({ path: 'storageState.json' });
    await browser.close();
};

export default globalSetup;

But when I run playwright test I get a timeout from this statement: await page.getByTestId('auth-github-auth-button').click();. The error message:

{
  "name": "TimeoutError"
}

So I checked, during test process- I browsed to http://localhost:8080 and I saw my web app is running, and the element with id auth-github-auth-button does present, including its data-test-id attribute. So why playwright fails to locate it?

This is my playwright.config.ts file:

import { defineConfig } from '@playwright/test';

const configuration = defineConfig({
    testDir: './tests',
    testIgnore: 'scripts',
    globalSetup: './tests/scripts/global-setup.ts',
    globalTeardown: './tests/scripts/global-teardown.ts',
    reporter: [['html', { open: 'never' }]],
    use: {
        testIdAttribute: 'data-test-id',
        baseURL: 'http://localhost:8080',
        storageState: 'storageState.json',
    },
});

export default configuration;

Solution

  • As you noted in your answer, the issue was that the config doesn’t affect the global setup, and so Playwright tried to use the default data-testid attribute instead of your custom attribute.

    While one solution would be to switch to using data-testid attributes instead to match the default, I wanted to offer up an alternative to keep your custom attribute. According to the Playwright docs on setting a custom test id attribute, “you can configure it in your test config or by calling selectors.setTestIdAttribute().” While the config option won’t automatically work for the global setup as you mentioned in your answer, you should be able to use it as passed into your setup along with selectors.setTestIdAttribute() to use your custom attribute as expected.

    So this suggested change to the top of your setup file should theoretically make it work as you expected:

    import { chromium, selectors, FullConfig } from '@playwright/test';
    
    const globalSetup = async (config: FullConfig) => {
        const { testIdAttribute } = config.projects[0].use;
        selectors.setTestIdAttribute(testIdAttribute);
        const browser = await chromium.launch();
    

    See the docs about global setup for their example of using the config object inside the setup to reuse values. Theirs uses baseURL and storageState, which you may find value in as well.

    Hope that helps!