Page Object Model Instance in Playwright (TS) Doesn't Use the Global Config Session Storage

932 Views Asked by At

I am developing tests for a website that requires login for the whole page. This website uses Google Sign-In for authentication. All of the Google accounts used for authentication require two-factor authentication.

This means to test one part of the website, the test suite needs to sign in for each test three times - which eventually leads to problems with authentication requiring human interaction, or even requiring extra steps to authenticate.

To resolve this problem, I followed the Reuse Signed-In State part of the Playwright documentation so that sign in would happen once and then saved and shared among the tests.

This works fine for just normal tests. However, I also started using Page Object Models to describe my pages and allow for easier maintenance of the test suite. For some reason, in tests that create new instances of Page Object Model classes, the tests do not make use of the saved state and thus are not logged in.

How can this saved state be passed onto a POM instance in Playwright so that signed-in state can be reused? Perhaps I've missed something simple?

Used a global-setup.ts file to declare login:

async function globalSetup(config: FullConfig) {
  const browser = await chromium.launch({ headless: false });
  const page = await browser.newPage();
  await page.goto("http://localhost:8000/login");
  await page.getByRole("button", { name: "Connexion" }).click();
  await page.getByRole("button", { name: "Continue with Google" }).click();
  await page.getByRole("textbox", { name: "Adresse e-mail ou numéro de téléphone" }).fill(process.env.USERNAME);
  await page.getByRole("textbox", { name: "Adresse e-mail ou numéro de téléphone" }).press("Enter");
  await page.getByRole("textbox", { name: "Saisissez votre mot de passe" }).fill(process.env.PASSWORD);
  await page.locator("#passwordNext").click();
  await page.goto("http://localhost:8000");
  // Save signed-in state to 'storageState.json'.
  await page.context().storageState({ path: "storageState.json" });
  await browser.close();
}

I also added this to the playwright.config.ts file:

const config: PlaywrightTestConfig = {
  globalSetup: require.resolve('./global-setup'),
  use: {
    storageState: 'storageState.json'
  }
};

Here is an example of a test making use of the POM, which then doesn't use the state created in global setup. The login still happens, but then the new browser opened doesn't have the state and thus gets stuck on the login page:

test("apply a filter", async ({ page }) => {
  const dashboardHome = new DashboardHome(page);
  await new LoginPage(page).login();
  await dashboardHome.bookingLink.waitFor();
  await dashboardHome.bookingLink.click();
  const reservationsPage = new ReservationsPage(page);
  await reservationsPage.orderNumberFilter.waitFor();
  reservationsPage.filter({ orderID: "22222" });
  await expect(page).toHaveURL("http://localhost:8000/booking/22222");
});

4

There are 4 best solutions below

0
On

I had similar issues with global setup and re-write my global setup in fixture (kinda global) where I used it everywhere. I amn't very experienced in test automation and can't explain why globalsetup didn't work. I also believe it's something simple, that i can't catch. Hope it help you.

0
On

That definitely seems confusing, especially since switching to a POM shouldn’t affect it (I have successfully used the POM and this one-time saved and reused auth).

I can’t think how switching to use page objects would affect it, and would be curious to see your setup. But one potential reason for your problem is that I’ve seen and worked with sites where if you go to the login page, you’re automatically “logged out” in some way, thus negating your login during setup. If you already logged in during setup and saved that auth state, you should be able to bypass/skip the login page altogether in the test and go directly to the page you need to work with. I would actually expect/be curious if you run into the same problem when not using the POM.

Let me know if that solution works for you, or if you even still have the issue at this point, but if that’s not the issue I may need more clarification or info to help further.

0
On

I am running into a similar issue. It doesn't matter about the page object on my end. When i run the test while showing browser I see that when page initially loads the state is default and not honoring storageState.json even though it appears that it's saving the state successfully when i check the file. In the test I would expect it to navigate to the page set in storageState.json

Test:

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

Global setup:

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

async function globalSetup(config: FullConfig) {
  const browser = await chromium.launch({ headless: false });

  const page = await browser.newPage();
  console.log('global setup test');
  await page.goto('http://localhost:8000');
  await page.getByTestId('get-started-button').click();
  await page.getByTestId('phone-form-input').fill('1111111');
  await page.getByTestId('submit-button').click();
  await page.getByTestId('smsCheckbox').click()
  await page.getByTestId('submit-button').click();
  await page.getByTestId('pin-form-1').fill('0000000');
  await page.getByTestId('verify-account-button').click();
  // Save signed-in state to 'storageState.json'.
  await page.context().storageState({ path: 'storageState.json' });
  await browser.close();
}

export default globalSetup;

playwright.config:

use: {
  actionTimeout: 0,
  baseURL: 'http://localhost:8000',
  storageState: 'storageState.json',
  trace: 'on-first-retry',
  ignoreHTTPSErrors: true
}
0
On

I've used this solution successfully with POM and Cucumber but not for 2fa. See if this works.

Save your storage state:

await this.page.context().storageState({ path: 'newStorageState.json' })

Reuse saved state in a Before Hook for all other tests:

global.context = await global.browser.newContext({storageState:'newStorageState.json'});
global.page = await global.context.newPage();