1

When I try to use my pageObjects in a newly created tab (during the test run), the test tries to interact with the base page. Working with a newly created tab is working as long as I use const = [newPage], eg. newPage.locator('someLocator').click(). I want to avoid using detailed actions in the test, I just want to make function in the pageObject and reuse it with newPage.

my code:

pageObject:

export class SharedPage {
  /**
   * @param {import('@playwright/test').Page} page
   */

  constructor(page) {
    this.page = page;
    this.addToCartButton = page.locator('text=Add to cart');
  }
  async addToCartButtonClick() {
    await this.addToCartButton.click();
  }
}

Test:

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

import { LoginPage } from '../../pages/LoginPage';
import { ProductsPage } from '../../pages/ProductsPage';
import { SharedPage } from '../../pages/SharedPage';

const newPageLocators = {
  sauceLabsOnesie: 'text=Sauce Labs Onesie',
  sauceLabsBackpack: 'Sauce Labs Backpack',
};

test.beforeEach(async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goTo('inventory.html');
});

test('As a user I want to open a new tab and visit it', async ({
  page,
  context,
}) => {
  const productsPage = new ProductsPage(page);
  const [newPage] = await Promise.all([
    context.waitForEvent('page'),
    productsPage.openNewTabWithProduct(newPageLocators.sauceLabsBackpack),
  ]);
  const sharedPage = new SharedPage(newPage);
  productsPage.selectProductByText(newPage, newPageLocators.sauceLabsOnesie);
  newPage.locator(newPageLocators.sauceLabsOnesie).click(); // it works
  sharedPage.addToCartButtonClick(); // it doesn't work, test tries to perform this step on the base page
});
ggorlen
  • 44,755
  • 7
  • 76
  • 106
wojnarto
  • 411
  • 4
  • 9
  • Don't you need to `await` all of those promises? – ggorlen Feb 17 '23 at 14:38
  • Which ones? I use awaits in methods in pageObjects, one place where I can use await there is newPage.locator(newPageLocators.sauceLabsOnesie).click(); – wojnarto Feb 17 '23 at 15:45
  • 1
    Almost every line: `productsPage.selectProductByText`, `newPage.locator`, `sharedPage.addToCartButtonClick`, `page.locator`, etc. All of these return promises, and if you don't await them, they run in parallel in their own promise chains, creating a chaotic situation. Check out the [docs for `page.locator`](https://playwright.dev/docs/locators) (or just about any other Playwright method) and you'll see `await` in front of each and every line. This is a [common mistake](https://blog.appsignal.com/2023/02/08/puppeteer-in-nodejs-common-mistakes-to-avoid.html#forgetting-to-await-a-puppeteer-call). – ggorlen Feb 17 '23 at 15:49
  • Sounds like a good point. Thank you, I will check the link you provided. – wojnarto Feb 17 '23 at 19:06
  • 1
    You are right, I just added awaits. Everything work as expected now. Many thanks! – wojnarto Feb 17 '23 at 19:17

1 Answers1

1

The fundamental problem is missing awaits in front of asynchronous Playwright API calls as well as your custom wrappers on them:

// ...
const sharedPage = new SharedPage(newPage);
await productsPage.selectProductByText(newPage, newPageLocators.sauceLabsOnesie);
await newPage.locator(newPageLocators.sauceLabsOnesie).click();
await sharedPage.addToCartButtonClick();
// ...

For your constructor, async/await isn't possible. See Async/Await Class Constructor. Luckily, Playwright lets you chain locator().click() with a single await so your class code looks OK for now, but it's something to bear in mind as you add more code.

ggorlen
  • 44,755
  • 7
  • 76
  • 106