With vitest we cannot put logic inside describe for GIVEN/WHEN/THEN setup as with-describe

However, we can use beforeAll to power GIVEN/WHEN/THEN:

GT-Sandbox-Snapshot

Code

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
 
/**
 * NOTE: There is a quirk in Vitest where console.log() statements inside beforeAll()
 * hooks get buffered separately and appear under "unknown test" in the output, making
 * them appear out of order even though execution happens correctly. This executionLog
 * array tracks the actual execution order and prints it at the end via afterAll() to
 * prove the GIVEN/WHEN/THEN flow executes as expected.
 */
const executionLog: string[] = [];
 
describe('GIVEN a user account', () => {
  let account: any;
 
  beforeAll(() => {
    executionLog.push("1) GIVEN - Setting up account context");
    account = {balance: 0, transactions: []};
  });
 
  describe('WHEN depositing money', () => {
    beforeAll(() => {
      executionLog.push("2) WHEN - Depositing $100");
      account.balance += 100;
      account.transactions.push({type: 'deposit', amount: 100});
    });
 
    it('THEN balance should increase', () => {
      executionLog.push("3) THEN - Checking balance");
      expect(account.balance).toBe(100);
    });
 
    it('THEN transaction should be recorded', () => {
      executionLog.push("4) THEN - Checking transaction");
      expect(account.transactions).toHaveLength(1);
      expect(account.transactions[0].type).toBe('deposit');
    });
 
    describe('WHEN withdrawing money', () => {
      beforeAll(() => {
        executionLog.push("5) WHEN - Withdrawing $30");
        account.balance -= 30;
        account.transactions.push({type: 'withdrawal', amount: 30});
      });
 
      it('THEN balance should reflect withdrawal', () => {
        executionLog.push("6) THEN - Checking new balance");
        expect(account.balance).toBe(70);
      });
 
      it('THEN both transactions should be recorded', () => {
        executionLog.push("7) THEN - Checking all transactions");
        expect(account.transactions).toHaveLength(2);
      });
    });
  });
 
  describe('WHEN checking balance without deposits', () => {
    let cleanAccount: any;
 
    beforeAll(() => {
      executionLog.push("8) WHEN - Creating clean account");
      cleanAccount = {balance: 0, transactions: []};
    });
 
    it('THEN balance should be zero', () => {
      executionLog.push("9) THEN - Checking zero balance");
      expect(cleanAccount.balance).toBe(0);
    });
  });
 
  afterAll(() => {
    console.log("\n=== EXECUTION ORDER ===");
    executionLog.forEach(log => console.log(log));
    console.log("======================\n");
  });
});

Command to reproduce:

gt.sandbox.checkout.commit 2f99590ccd576a626a1c \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./run.sh"

Recorded output of command:

 
up to date, audited 182 packages in 614ms
 
33 packages are looking for funding
  run `npm fund` for details
 
2 moderate severity vulnerabilities
 
To address all issues (including breaking changes), run:
  npm audit fix --force
 
Run `npm audit` for details.
 
> glassthought-sandbox@1.0.0 test
> vitest run
 
 
 RUN  v0.34.6 /home/nickolaykondratyev/git_repos/glassthought-sandbox
 
 ✓ src/main.test.ts  (5 tests) 3ms
stdout | unknown test
 
=== EXECUTION ORDER ===
1) GIVEN - Setting up account context
2) WHEN - Depositing $100
3) THEN - Checking balance
4) THEN - Checking transaction
5) WHEN - Withdrawing $30
6) THEN - Checking new balance
7) THEN - Checking all transactions
8) WHEN - Creating clean account
9) THEN - Checking zero balance
======================
 
 
 
 Test Files  1 passed (1)
      Tests  5 passed (5)
   Start at  11:39:40
   Duration  463ms (transform 33ms, setup 0ms, collect 16ms, tests 3ms, environment 0ms, prepare 79ms)

Notes

  • it blocks that are in the outer level THEN describe.beforeAll will have priority in order of execution.
  • describe.beforeAll will execute once for that describe block.
  • Any code that you would like to put directly into describe block likely belongs in describe.beforeAll to act as you would expect (See with-describe)