RELIABLE SUITES · LESSON 7 OF 24
Isolation, hooks and safe resets
Keep tests independent and reset only owned practice data.
- browser context
- beforeEach
- isolation
- reset
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
Playwright gives each test a fresh browser context by default. Cookies and local storage from one test do not leak into the next. A new page inside the same context shares that context’s storage; a new context does not.
beforeEach is useful for repeated navigation or setup. Avoid a beforeAll that shares one mutable page across tests. Independent tests can be reordered, retried or run in parallel without depending on an earlier scenario.
Browser isolation does not reset a server database. For shared services, create a unique record or namespace and clean up only what the test owns. Never call a broad production reset endpoint from a tutorial test.
The lab’s Reset practice tasks button clears only its own task array and storage key. It asks for confirmation; register the dialog handler before clicking. The server API lessons use a separate per-test namespace rather than a global reset.
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.beforeEach(async ({ page }) => { await page.goto('/'); });
test('reset only the lab tasks', async ({ page }) => {
await page.getByLabel('Task title', { exact: true }).fill('Disposable practice task');
await page.getByRole('button', { name: 'Add task', exact: true }).click();
page.once('dialog', async dialog => { await dialog.accept(); });
await page.getByRole('button', { name: 'Reset practice tasks' }).click();
await expect(page.getByTestId('task-row')).toHaveCount(0);
await expect(page.locator('#task-status')).toHaveText('Practice tasks reset');
});
test('another test starts with fresh storage', async ({ page }) => {
await expect(page.getByTestId('task-row')).toHaveCount(0);
});
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-isolation-hooks-and-reset",
"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
- The hook receives a different test-scoped page each time.
- The dialog listener is installed before the triggering action.
- The second test proves the expected starting state without relying on the first.
Verify your result
- Both tests pass in either order.
- Reset clears the task list and reports completion.
4. Practice independently
Dismiss the reset confirmation and verify the task remains.
Show the explanation
Use dialog.dismiss() in the one-time handler and assert the row count stays one.
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.
