@systemfsoftware/effect-gherkin-spec
v7.0.1
Published
Gherkin BDD for Effect-TS and Vitest — Given/When/Then steps that compose as Effects, with Scenario Outline expansion and typed step errors.
Downloads
16,892
Maintainers
Readme
@systemfsoftware/effect-gherkin-spec
Write Gherkin-style behaviour tests with Effect.
makeFeature({ it }) gives you a Feature builder for organising integration tests as Given/When/Then step pipelines. A Then body is (state, expect) => one check: it asserts the state the last Given or When produced, exactly once — several facts about that state become one check over a record. Every scenario runs under the simulation kernel by default (the zero-preemption schedule, then the profile's seeded schedules); a scenario that must stay on the live clock declares a required reason, which the run report lists.
import { Gherkin, Given, it, makeFeature, Then, When } from '@systemfsoftware/effect-gherkin-spec'
import { Effect } from 'effect'
const Feature = makeFeature({ it })
Feature('Borrowing a library book')
.withScenarioLayer(BookCheckoutLive)
.body(({ scenario }) => {
scenario(
'A member borrows an available book',
Gherkin.Do.pipe(
Given('an available book on the shelf')('book', () => BookCheckoutLive.current()),
When('the member borrows the book')(() => BookCheckoutLive.borrow('member-42')),
Then('the book is checked out to the member')((_scope, expect) =>
Effect.map(BookCheckoutLive.holder(), (holder) => expect(holder).toBe('member-42'))
),
),
)
scenario(
'A notification that leaves the building uses the live clock',
{ live: 'the notification leaves the process for a real socket' },
Gherkin.Do.pipe(
Given('a notification on its way out')('notice', () => Effect.succeed('due-reminder')),
Then('the socket receives the notice')((scope, expect) => expect(scope.notice).toBe('due-reminder')),
),
)
})A whole feature stays on the live clock with Feature('Name').live('the reason it waits on real I/O'), and a scenario outline takes the same reason through opts.live. A live declaration without a reason fails type-checking; there is no boolean form.
Scenario outlines parameterise the same behaviour across example rows, each row running through the same kernel exploration:
Feature('Calculating late fees')
.withScenarioLayer(LoanLedgerLive)
.body(({ scenarioOutline }) => {
scenarioOutline(
'A late book accrues its daily fee',
[{ daysLate: 1, fee: 25 }, { daysLate: 3, fee: 75 }],
({ daysLate, fee }) =>
Gherkin.Do.pipe(
Given('a book returned late')(() => LoanLedgerLive.recordReturn({ daysLate })),
Then('the ledger shows the accrued fee')((_scope, expect) =>
Effect.map(LoanLedgerLive.fee(), (charged) => expect(charged).toBe(fee))
),
),
)
})Install
pnpm add -D @systemfsoftware/effect-gherkin-spec@workspace:^[!NOTE] This package is
private: true— it exists to exercise the effect v4 RC inside the workspace and is never published.
[!NOTE]
effect,@systemfsoftware/vitest, andvitestare peer dependencies — you bring your own.
