Skip to main content
Version: 2x

Hooks

qavajs supports two layers of hooks: Cucumber's scenario-level hooks (Before/After) and qavajs execution-level hooks (BeforeExecution/AfterExecution).

Scenario Hooks

Scenario hooks run before or after each individual scenario and are imported from @qavajs/core.

Before / After

import { Before, After } from '@qavajs/core';

Before(async function() {
await this.playwright.page.goto('/');
});

After(async function() {
await this.playwright.context.clearCookies();
});

BeforeAll / AfterAll

Run once per worker process — before the first scenario and after the last scenario in that worker.

import { BeforeAll, AfterAll } from '@qavajs/core';

BeforeAll(async function() {
// runs once in this worker before any scenarios
});

AfterAll(async function() {
// runs once in this worker after all scenarios
});

Execution Hooks

Execution hooks run once in the main process, outside of any worker. Use them for services shared across all tests.

import { BeforeExecution, AfterExecution } from '@qavajs/core';

BeforeExecution(async function() {
await mockServer.start();
});

AfterExecution(async function() {
await mockServer.stop();
});

Tagged Hooks

Restrict a hook to scenarios with a specific tag using a tag expression:

import { Before, After } from '@qavajs/core';

Before({ tags: '@authenticated' }, async function() {
await this.playwright.page.goto('/login');
await this.playwright.page.fill('#username', 'admin');
await this.playwright.page.fill('#password', 'password');
await this.playwright.page.click('#submit');
});

After({ tags: '@db-cleanup' }, async function() {
await this.playwright.page.request.delete('/api/test-data');
});

Feature file:

@authenticated
Scenario: Admin views dashboard
Then I expect 'Dashboard Header' to be visible

World Context in Hooks

Hooks run in the same world context as steps, giving access to this.playwright, this.wdio, this.getValue, this.setValue, etc.

import { Before, After } from '@qavajs/core';

Before(async function() {
const baseUrl = await this.getValue('$baseUrl');
await this.playwright.page.goto(baseUrl);
});

After(async function() {
if (this.result?.status === 'FAILED') {
const screenshot = await this.playwright.page.screenshot();
this.attach(screenshot, 'image/png');
}
});

Common Patterns

Screenshot on failure

import { After } from '@qavajs/core';

After(async function() {
if (this.result?.status === 'FAILED') {
const screenshot = await this.playwright.page.screenshot({ fullPage: true });
this.attach(screenshot, 'image/png');
}
});

Reset browser state between scenarios

import { Before } from '@qavajs/core';

Before(async function() {
await this.playwright.context.clearCookies();
await this.playwright.context.clearPermissions();
await this.playwright.page.goto('about:blank');
});

Seed and clean database

import { Before, After } from '@qavajs/core';
import { db } from './support/database';

Before({ tags: '@seed' }, async function() {
await db.seed('test-fixtures');
});

After({ tags: '@seed' }, async function() {
await db.truncate('test-fixtures');
});

Start a local server

import { BeforeExecution, AfterExecution } from '@qavajs/core';
import { createServer } from './support/server';

const server = createServer();

BeforeExecution(async function() {
await server.listen(3000);
});

AfterExecution(async function() {
await server.close();
});