japa-bdd
v0.1.1
Published
BDD/Gherkin testing utilities for Japa and AdonisJS
Downloads
28
Maintainers
Readme
japa-bdd
BDD/Gherkin testing utilities for Japa, designed for AdonisJS projects.
Requires AdonisJS. This package targets AdonisJS apps (v6 or v7) that run their tests through the AdonisJS test runner. The
configurecommand and ace commands (bdd:steps,bdd:scenarios) ship as TypeScript source and rely on the AdonisJS toolchain (tsx) to load them. Plain Node/Japa projects without AdonisJS are not supported.
Installation
node ace add japa-bddOr install manually and run the configure command to scaffold config and step definition stubs:
bun add japa-bdd
node ace configure japa-bddThis will:
- Register
japa-bdd/commandsinadonisrc.ts - Create
config/bdd.ts - Create starter step definitions in
tests/steps/definitions/common/
Manual setup
If not using the configure command, add to adonisrc.ts:
commands: [
// ...
() => import('japa-bdd/commands'),
],
tests: {
suites: [
// ...
{ files: ['tests/browser/**/*.spec(.ts|.js)'], name: 'browser', timeout: 60000 },
],
},Setup
DSL file
Create a typed DSL wrapper at tests/steps/dsl.ts:
import { test } from '@japa/runner'
import type { TestContext } from '@japa/runner/core'
import {
Given as BaseGiven,
When as BaseWhen,
Then as BaseThen,
createScenarioFactory,
ScenarioContext,
} from 'japa-bdd'
interface ScenarioContextBase {
japaContext: TestContext
set(key: string, value: unknown): void
get<V = any>(key: string): V | undefined
has(key: string): boolean
require<V = any>(key: string): V
clear(): void
}
type AppScenarioContext = ScenarioContextBase & TestContext
type StepHandler = (ctx: AppScenarioContext, ...params: any[]) => Promise<void>
export const Given = BaseGiven as any as (pattern: string | RegExp, handler: StepHandler) => void
export const When = BaseWhen as any as (pattern: string | RegExp, handler: StepHandler) => void
export const Then = BaseThen as any as (pattern: string | RegExp, handler: StepHandler) => void
export const Scenario = createScenarioFactory(test as any)Bootstrap
Import step definitions in tests/bootstrap.ts:
import '#tests/steps/definitions/common/given.steps'
import '#tests/steps/definitions/common/when.steps'
import '#tests/steps/definitions/common/then.steps'Usage
Defining Steps
Step handlers receive a ScenarioContext that proxies method calls to Japa's TestContext. Access TestContext methods via ctx.japaContext:
// tests/steps/definitions/auth/given.steps.ts
import { Given } from '#tests/steps/dsl'
Given('I am logged in as a {word}', async (ctx, role) => {
const user = role === 'buyer' ? await ctx.japaContext.createBuyer() : await ctx.japaContext.createSeller()
await ctx.japaContext.loginAs(user)
ctx.set('currentUser', user)
})
Given('I am on the {string} page', async (ctx, path) => {
const page = await ctx.japaContext.visit(path)
ctx.set('page', page)
})// tests/steps/definitions/auth/when.steps.ts
import { When } from '#tests/steps/dsl'
When('I click the {string} button', async (ctx, buttonText) => {
const page = ctx.require('page')
await page.getByRole('button', { name: buttonText }).click()
})// tests/steps/definitions/auth/then.steps.ts
import { Then } from '#tests/steps/dsl'
Then('I should see {string}', async (ctx, text) => {
const page = ctx.require('page')
await page.getByText(text).waitFor()
})Writing Scenarios
// tests/browser/scenarios/login.spec.ts
import { Scenario } from '#tests/steps/dsl'
Scenario('User can login successfully')
.Given('I am on the "/login" page')
.When('I enter "[email protected]" in the email field')
.And('I enter "password123" in the password field')
.And('I click the "Sign In" button')
.Then('I should see "Welcome back"')Parameter Types
Built-in Cucumber expression parameter types:
{string}- Matches quoted strings:"hello world"→hello world{int}- Matches integers:42→42(number){float}- Matches floats:9.99→9.99(number){word}- Matches single words:buyer→buyer
Custom parameter types:
import { CucumberExpression, ParameterType } from 'japa-bdd'
CucumberExpression.defineParameterType(
new ParameterType({
name: 'userRole',
regexp: /buyer|seller/,
transformer: (value) => value,
})
)
Given('I am logged in as a {userRole}', async (ctx, role) => {
// ...
})Scenario Context
The ScenarioContext provides:
- World state: Share data between steps via
set/get/require - Japa context delegation: Access TestContext via
ctx.japaContext
Given('I create an article', async (ctx) => {
const article = await ctx.japaContext.createPublishedArticle()
ctx.set('article', article)
})
Then('the article should be visible', async (ctx) => {
const article = ctx.require('article') // Throws if not found
// or
const article = ctx.get('article') // Returns undefined if not found
})CLI Commands
List all step definitions:
node ace bdd:stepsList unused steps:
node ace bdd:steps unusedList all scenarios:
node ace bdd:scenariosFilter by feature:
node ace bdd:scenarios authHelpers
Browser Debug Capture
Automatically capture screenshots, HTML, and console logs on test failure:
import { enableDebugCapture } from 'japa-bdd/helpers/browser-debug'
Given('I am on the login page', async (ctx) => {
const page = await ctx.japaContext.visit('/login')
enableDebugCapture(page, ctx.japaContext)
ctx.set('page', page)
})Documentation Generator
Generate markdown documentation with screenshots:
GENERATE_DOCS=true bun run testConfiguration
// config/bdd.ts
import { defineConfig } from 'japa-bdd'
export default defineConfig({
stepsDirectory: 'tests/steps/definitions',
scenariosDirectory: 'tests/browser/scenarios',
outputDirectory: 'tmp',
})License
MIT
