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.
npm install
npx playwright install chromium
npm run check
npm testtests/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
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
{
"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
{
"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
node_modules/
playwright-report/
test-results/
playwright/.auth/
.env
3. Trace the code and check the result
- click waits for the button to be actionable.
- toHaveText waits for the asynchronous outcome.
- 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.
