Search code examples
typescriptplaywrightplaywright-testplaywright-typescript

Interacting with a new tab when clicking a button


So before I ask this question I think its good to understand how ive generally laid my project out, I use the page object model in my project and below is an example of my project structure

I create the page objects in this file:

import { Locator, Page } from "@playwright/test";

export class checkoutProcess{
    readonly page: Page;
    readonly proceedButton: Locator;

    constructor(page:Page) {
        this.page = page;
        this.proceedButton = page.locator('//a[@id="btnProceedToOrder"]');
    }
}

Ill use this page object in another file I've labeled as 'Actions'

import { checkoutProgress } from "../../Page Objects/checkoutProgress";

export class checkoutActions extends checkoutProgress{
    async completeCheckout() {
      //stuff using page objects
      await this.proceedButton.click(); //example  
    }
   

}

I use the actions within the spec file:

import { expect, test } from "@playwright/test";
import { checkoutActions } from "../../Actions/checkoutActions";

test.describe('Survey Management Tests', () => {
    let checkout;
   

    test.beforeEach(async ({page}) => {
        checkout = new checkoutActions(page);
        await page.goto('/');
    });
    test('Checkout', async () => { 
        await checkout.completeCheckout();
    });
});

So now in one of my actions I have to click on a link that opens a new tab(page) and when I try to assert anything on the new tab it fails. Im assuming my page objects are using the wrong instance of the page because the locators are accurate, and the problem lies when I write this.page.locator('') in the page object file.

Is this assumption correct? When I run the project in debug mode, clicking the link immediately puts focus on it so I dont have to switch tabs or anything.

when i run the debugger i see that the assertion was detected: enter image description here

when i try to step over: enter image description here


Solution

  • Yes, looks like you have to switch to a new tab before starting any action or checks on it. the code below may help you to do this:

    import { Locator, Page, BrowserContext } from "@playwright/test";
    
    export class checkoutProcess{
      readonly page: Page;
      readonly context: BrowserContext;
      readonly proceedButton: Locator;
    
      constructor(page:Page, context:BrowserContext) {
        this.page = page;
        this.context = context;
        this.proceedButton = page.locator('//a[@id="btnProceedToOrder"]');
      }
    }
    

    Then in checkoutActions:

      async completeCheckout() {
         const [newPage] = await Promise.all([
           this.context.waitForEvent('page'),
           this.proceedButton.click()
        ]);
        return newPage;
        // or
        // return new someNewTabPage(newPage, this.context); // if you have it as a separately described page
      }
    

    And, finally, the spec file:

    import { expect, test, Page, BrowserContext } from "@playwright/test";
    import { checkoutActions } from "../../Actions/checkoutActions";
    let context: BrowserContext;
    let page: Page;
    
    test.describe('Survey Management Tests', () => {
      let checkout;
    
    
      test.beforeEach(async ({browser}) => {
        context = await browser.newContext()
        page = await context.newPage()
        checkout = new checkoutActions(page);
        await page.goto('/');
      });
      test('Checkout', async () => { 
        const someNewTabPage = await checkout.completeCheckout();
        await expect(someNewTabPage.locator('//mylocator').toBeVisible();
      });
    });