Lesson 13 / الدرس 13

Keeping the locators in one place / حفظ المحدِّدات في مكان واحد

When forty tests each contain their own copy of the same locator, a renamed button is forty edits. Putting the page's vocabulary in one file makes it one — and makes the tests read like sentences.

حين يحوي أربعون اختبارًا كلٌّ منها نسخته من المحدِّد نفسه، يصير زرٌّ أُعيدت تسميته أربعين تعديلًا. ووضع مفردات الصفحة في ملف واحد يجعلها تعديلًا واحدًا — ويجعل الاختبارات تُقرأ جملًا.

By the twentieth test, getByRole('button', { name: 'Sign in' }) appears in twenty files. The day it becomes "Log in" you have twenty failing tests and twenty edits, and you will miss one. The fix is not clever; it is a file that knows the page.

A page object is a vocabulary

// sign-in.page.js — everything that knows what the page looks like.
export class SignInPage {
  constructor(page) {
    this.page     = page;
    this.email    = page.getByLabel('Email');
    this.password = page.getByLabel('Password');
    this.submit   = page.getByRole('button', { name: 'Sign in' });
    this.error    = page.getByTestId('form-error');
  }

  async open()                { await this.page.goto('/sign-in'); }
  async signIn(email, pass)   {
    await this.email.fill(email);
    await this.password.fill(pass);
    await this.submit.click();
  }
}

// the test — now a description of behaviour, with no markup in it at all
test('a wrong password is refused', async ({ page }) => {
  const signIn = new SignInPage(page);
  await signIn.open();

  await signIn.signIn('sara@example.com', 'totallywrong');

  await expect(signIn.error).toHaveText('Email or password is incorrect');
});
The test now contains no locators, no selectors and no knowledge of the markup. When the page is rebuilt, one file changes and forty tests carry on — because they were never about the markup, and now they no longer mention it.

What does not belong in it

  • Assertions. The page object knows where things are; the test decides what is correct. Put an expect inside it and every test inherits a judgement it never asked for.
  • Test data. A page object that knows the password of a test account has bound the page to one scenario.
  • Whole journeys. A method called buySomething() that spans six pages is not a page object; it is a test hiding in a helper, and when it fails nobody can see where.

Check yourself / اختبر نفسك

1. What problem does a page object solve?

2. Why should assertions stay out of a page object?

3. When should you extract a page object?