Skip to content

FOUNDATIONS · LESSON 4 OF 24

Assertions and automatic waiting

Wait for observable results instead of guessing delays.

  • web-first assertions
  • actionability
  • timeout
  • await

Your runnable checkpoint

Extract into a new practice folder. Each download contains tests, configuration, the complete local practice app and an offline lesson. Use Node.js 22 or 24. Each checkpoint is independent.

Install dependencies and browser binaries using the commands below. The test runner starts the practice server on port 4174. Tests target localhost, never the production EduPoly site.

1. Understand the concepts

Before clicking, Playwright checks relevant actionability conditions such as visibility, stability, receiving events and enabled state. These checks are not a guarantee that an application’s business operation has finished after the click.

Use retrying locator assertions for changing UI. await expect(locator).toHaveText(...) repeatedly checks the current value. expect(await locator.textContent()).toBe(...) captures one moment and does not retry that browser observation.

The practice button updates a message after a short timer. The test should assert the message, not waitForTimeout(1000). A fixed delay can waste time on a fast machine and still fail on a slow one.

Keep assertion timeouts aligned with expected behavior. A timeout identifies a missing condition; increasing it indefinitely can hide a broken selector or failed request. await every asynchronous action and assertion.

2. Build and run

These are complete test files and configuration. The download also includes app/ and server.mjs; preserve their paths. Stop any manually started practice server before running the suite.

Terminal
npm install
npx playwright install chromium
npm run check
npm test

tests/lesson.spec.ts

tests/lesson.spec.ts
import { test, expect } from '@playwright/test';
test('wait for the result, not a sleep', async ({ page }) => {
  await page.goto('/');
  await page.getByRole('button', { name: 'Show delayed message' }).click();
  await expect(page.getByTestId('delayed-result')).toHaveText('Ready to test');
  await expect(page.getByRole('button', { name: 'Show delayed message' })).toBeEnabled();
});

playwright.config.ts

playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 1 : 0,
  workers: process.env.CI ? 2 : undefined,
  timeout: 30000,
  expect: { timeout: 5000 },
  reporter: [['list'], ['html', { open: 'never' }]],
  use: { baseURL: 'http://127.0.0.1:4174', trace: 'retain-on-failure', screenshot: 'only-on-failure' },
  projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
  webServer: { command: 'node server.mjs', url: 'http://127.0.0.1:4174', reuseExistingServer: false, timeout: 15000 }
});

package.json

package.json
{
  "name": "edupoly-playwright-assertions-and-auto-waiting",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "test": "playwright test",
    "test:headed": "playwright test --headed",
    "test:ui": "playwright test --ui",
    "check": "tsc --noEmit",
    "report": "playwright show-report",
    "serve": "node server.mjs"
  },
  "devDependencies": {
    "@playwright/test": "1.56.1",
    "typescript": "5.9.3",
    "@types/node": "22.20.5"
  }
}

tsconfig.json

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "CommonJS",
    "moduleResolution": "Node",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "types": [
      "node"
    ],
    "noEmit": true
  },
  "include": [
    "tests/**/*.ts",
    "pages/**/*.ts",
    "playwright.config.ts"
  ]
}

.gitignore

.gitignore
node_modules/
playwright-report/
test-results/
playwright/.auth/
.env

3. Trace the code and check the result

  1. click waits for the button to be actionable.
  2. toHaveText waits for the asynchronous outcome.
  3. A second assertion verifies that the app is usable again.

Verify your result

  • The delayed text becomes Ready to test.
  • The button becomes enabled again.

4. Practice independently

Make the expected text wrong and inspect the assertion’s call log.

Show the explanation

The call log shows repeated observations and the unmet expected text. Fix the expectation or application behavior; adding a sleep does not repair a wrong value.

When something goes wrong

If a browser executable is missing, run npx playwright install chromium in the project folder. If port 4174 is occupied, stop your manual practice server. Read assertion call logs for mismatched locators or expectations. Use npm run report to inspect failures; do not add fixed sleeps or broad resets to hide them.

Read the related official Playwright documentation ↗