Search code examples
typescriptautomated-testsplaywright

page object breaks waitForElement on Playwright


Watching some lessons I wanted to implement an universal importer for page objects, instead of calling the my_file.page.ts on each test, I wanted to import all *.page.ts in a single index.ts file but I can no longer call the locators and use native Playwright methods with this.

Here is index.ts

const { test: base, expect } = require('@playwright/test')

import { LoginPage } from '../pages/login.page';
import { AssignFields } from '../pages/assignFields.page';

const test = base.extend({
    page: async ({ page }: any, use: (arg0: any) => any) => {
        await use({
            ...page,
            login: new LoginPage(page),
            assignfields: new AssignFields(page),
        })
    }
})

export { test, expect };

I didn't change anything into the assignFields.page.ts

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

const selectors = {
    btnAssignFields: 'selector1',
    btnEdit: 'selector2',
    tableFields: 'selector3',
    tableFieldsItems: 'selector4',
};

export class AssignFields {
    readonly page: Page;
    readonly selectors : any;

    constructor(page: Page) {
        this.page = page;
        this.selectors = selectors
    }

    async assignFields() {
        await this.page.locator(this.selectors.btnAssignFields).click();
    }

    async edit() {
        if (await this.page.isVisible(this.selectors.btnEdit)) {
            await this.page.locator(this.selectors.btnEdit).click();
        }
    }

    async searchByField(fieldName: string) {
        await this.page.getByPlaceholder('Search').fill(fieldName);
    }
}

With this I import the index.ts file and do my tests on assignFields.spec.ts

import { test, expect } from '../support/index';

test('Filter by text', async ({ page }) => {
    await page.login.goto();
    await page.login.fillEmail('my_email@gmail.com');
    await page.login.fillPass('my_password');
    await page.login.clickLogin();

    await page.locator(page.assignfields.selectors.btnAssignFields).waitFor();

    await page.assignfields.assignFields();
    await page.waitForSelector(page.assignfields.selectors.tableFields);
    await page.assignfields.edit();

    await page.assignfields.searchByField('my_field');

    await expect(
        page.locator(page.assignfields.selectors.tableFieldsItems)
    ).toHaveCount(1);
});

When running the project I got the following message:

@assignFields.spec.ts:9
TypeError: page.locator is not a function

That line is the first await page.locator and what I understand from this is that something is not being correctly imported, maybe the Playwright itself, maybe something else but I cannot proceed from this point.


Solution

  • Looks like you mixed up the pages - which are the real page object provided by Playwright, and the ones you created named as LoginPage and AssignFields.

    Instead of using page.locator, you can call page.page.locator to fix the issue, but it may require some additional changes on the index.ts.

    I would go and change naming convention, instead.

    Here is my suggestions:

    • Do not overwrite the page, you can call it app or something else.
    • In the index.ts, keep the original page object.
    const test = base.extend({
        app: async ({ page }, use) => {
            await use({
                page,  // Keep the original page object
                login: new LoginPage(page),
                assignfields: new AssignFields(page),
            });
        }
    });
    
    • Then I would update the my spec file to use app instead of page.
    test('Filter by text', async ({ app }) => {
        await app.login.goto();
        await app.login.fillEmail('my_email@gmail.com');
        await app.login.fillPass('my_password');
        await app.login.clickLogin();
    
        await app.page.locator(app.assignfields.selectors.btnAssignFields).waitFor();
    
        ...
    
    

    Those changes should give you an idea to fix the issue.