2

I want to be able to use a locator variable within all the tests without having to define it every time inside each test. Something like:

// @ts-check
const { test, expect } = require('@playwright/test');

test.beforeEach( async ({ page }) => {
  await page.goto('[desired URL]');  
});

// I want to make this variable global to be able to use it within all the tests.
const signInBtn = page.getByTestId('some-button'); // how to resolve 'page' here??

test.describe('My set of tests', () => {

  test('My test 1', async ({ page }) => {
    await expect(page).toHaveTitle(/Some-Title/);
    await expect(signInBtn).toBeEnabled();     // I wanna use the variable here...
  });
  
  test('My test 2', async ({ page }) => {
    await signInBtn.click();   // ...and here, without having to define it every time inside each test.
  });

});

PS: This snippet is just an example to pass the idea, not the actual project, pls don't be attached to it.

Gus
  • 39
  • 2
  • Don't use globals shared between tests like this. Each test should be self-contained. The page does a totally fresh navigation between tests. You could use a `beforeEach` block though. – ggorlen Jan 10 '23 at 22:39
  • You might want to check out the page object pattern. – Christian Baumann Jan 11 '23 at 09:14

2 Answers2

3

You don't have to .Use Page Object Model.. Keep the tests clean.

By using page object model which is an well known & time tested automation design pattern we separate out locator definitions and test method definitions from the actual test to keep it simple, clean & reusable.

See below example:

//Page Object

// playwright-dev-page.js
const { expect } = require('@playwright/test');

exports.PlaywrightDevPage = class PlaywrightDevPage {

  /**
   * @param {import('@playwright/test').Page} page
   */
  constructor(page) {
    this.page = page;
    this.getStartedLink = page.locator('a', { hasText: 'Get started' });
    this.gettingStartedHeader = page.locator('h1', { hasText: 'Installation' });
    this.pomLink = page.locator('li', { hasText: 'Guides' }).locator('a', { hasText: 'Page Object Model' });
    this.tocList = page.locator('article div.markdown ul > li > a');
  }

  async goto() {
    await this.page.goto('https://playwright.dev');
  }

  async getStarted() {
    await this.getStartedLink.first().click();
    await expect(this.gettingStartedHeader).toBeVisible();
  }

  async pageObjectModel() {
    await this.getStarted();
    await this.pomLink.click();
  }
}

Now we can use the PlaywrightDevPage class in our tests.

// example.spec.js
const { test, expect } = require('@playwright/test');
const { PlaywrightDevPage } = require('./playwright-dev-page');

test('getting started should contain table of contents', async ({ page }) => {
  const playwrightDev = new PlaywrightDevPage(page);
  await playwrightDev.goto();
  await playwrightDev.getStarted();
  await expect(playwrightDev.tocList).toHaveText([
    `How to install Playwright`,
    `What's Installed`,
    `How to run the example test`,
    `How to open the HTML test report`,
    `Write tests using web first assertions, page fixtures and locators`,
    `Run single test, multiple tests, headed mode`,
    `Generate tests with Codegen`,
    `See a trace of your tests`
  ]);
});

test('should show Page Object Model article', async ({ page }) => {
  const playwrightDev = new PlaywrightDevPage(page);
  await playwrightDev.goto();
  await playwrightDev.pageObjectModel();
  await expect(page.locator('article')).toContainText('Page Object Model is a common pattern');
});

Reference: https://playwright.dev/docs/pom https://www.selenium.dev/documentation/test_practices/encouraged/page_object_models/

Vishal Aggarwal
  • 1,929
  • 1
  • 13
  • 23
-1

You could move it all into a describe block. So something like this should work:

test.describe('My set of tests', () => {
   let signInBtn:Locator;

  test.beforeEach( async ({ page }) => {
    await page.goto('[desired URL]');
    signInBtn = page.getByTestId('some-button');
  });

  test('My test 1', async ({ page }) => {
    await expect(page).toHaveTitle(/Some-Title/);
    await expect(signInBtn).toBeEnabled();     
  });

  test('My test 2', async ({ page }) => {
    await signInBtn.click();  
  });

});
Basti
  • 449
  • 2
  • 13
  • This is a bad practice to store locators directly in tests. If we do that , then we have to redefine it in each test file where ever its used where as if we define in page object it can be reused virtually in any number of tests . Also modification is easy as only need to be done in one place which is in page object. – Vishal Aggarwal Jan 12 '23 at 19:04