{"version":3,"sources":["../src/private/isPromise.ts","../src/givenWhenThen.ts","../src/mergeInto.ts"],"sourcesContent":["export default function isPromise(promise: unknown): promise is Promise<unknown> {\n  return !!promise && typeof promise === 'object' && 'then' in promise && typeof promise.then === 'function';\n}\n","import isPromise from './private/isPromise';\n\ninterface BehaviorDrivenDevelopment {\n  given: Given<void>;\n}\n\ntype GivenPermutation<TPrecondition, TNextPrecondition> =\n  | readonly [\n      string,\n      (precondition: TPrecondition) => Promise<TNextPrecondition> | TNextPrecondition,\n      ((precondition: TNextPrecondition) => Promise<void> | void) | undefined\n    ]\n  | readonly [string, (precondition: TPrecondition) => Promise<TNextPrecondition> | TNextPrecondition];\n\ninterface Given<TPrecondition> {\n  <TNextPrecondition>(\n    message: string,\n    setup: (precondition: TPrecondition) => Promise<TNextPrecondition> | TNextPrecondition,\n    teardown?: ((precondition: TNextPrecondition) => Promise<void> | void) | undefined\n  ): {\n    and: Given<TNextPrecondition>;\n    when: When<TNextPrecondition, void>;\n  };\n\n  oneOf<TNextPrecondition>(permutations: readonly GivenPermutation<TPrecondition, TNextPrecondition>[]): {\n    and: Given<TNextPrecondition>;\n    when: When<TNextPrecondition, void>;\n  };\n}\n\ntype WhenPermutation<TPrecondition, TOutcome, TNextOutcome> =\n  | readonly [\n      string,\n      (precondition: TPrecondition, outcome: TOutcome) => Promise<TNextOutcome> | TNextOutcome,\n      ((precondition: TPrecondition, outcome: TNextOutcome) => Promise<void> | void) | undefined\n    ]\n  | readonly [string, (precondition: TPrecondition, outcome: TOutcome) => Promise<TNextOutcome> | TNextOutcome];\n\ninterface When<TPrecondition, TOutcome> {\n  <TNextOutcome>(\n    message: string,\n    setup: (precondition: TPrecondition, outcome: TOutcome) => Promise<TNextOutcome> | TNextOutcome,\n    teardown?: ((precondition: TPrecondition, outcome: TNextOutcome) => Promise<void> | void) | undefined\n  ): {\n    then: Then<TPrecondition, TNextOutcome>;\n  };\n\n  oneOf<TNextOutcome>(permutations: readonly WhenPermutation<TPrecondition, TOutcome, TNextOutcome>[]): {\n    then: Then<TPrecondition, TNextOutcome>;\n  };\n}\n\ninterface Then<TPrecondition, TOutcome> {\n  (\n    message: string,\n    fn: (precondition: TPrecondition, outcome: TOutcome) => void\n  ): {\n    and: Then<TPrecondition, TOutcome>;\n    when: When<TPrecondition, TOutcome>;\n  };\n}\n\ninterface TestFacility {\n  afterEach: (fn: () => Promise<void> | void) => void;\n  beforeEach: (fn: () => Promise<void> | void) => void;\n  describe: (message: string, fn: () => void) => void;\n  it: (message: string, fn: (() => PromiseLike<void>) | (() => void)) => void;\n}\n\ntype Ref<T> = { value: T };\n\ntype GivenFrame<TPrecondition, TNextPrecondition> = {\n  isConjunction: boolean;\n  operation: 'given';\n  permutations: readonly GivenPermutation<TPrecondition, TNextPrecondition>[];\n};\n\ntype WhenFrame<TPrecondition, TOutcome, TNextOutcome> = {\n  isConjunction: boolean;\n  permutations: readonly WhenPermutation<TPrecondition, TOutcome, TNextOutcome>[];\n  operation: 'when';\n};\n\ntype ThenFrame<TPrecondition, TOutcome> = {\n  isConjunction: boolean;\n  message: string;\n  operation: 'then';\n  fn: (precondition: TPrecondition, outcome: TOutcome) => Promise<void> | void;\n};\n\ntype Frame = GivenFrame<unknown, any> | ThenFrame<unknown, unknown> | WhenFrame<unknown, unknown, any>;\n\nfunction createChain(mutableStacks: (readonly Frame[])[], stack: readonly Frame[]) {\n  const given: (isConjunction: boolean) => Given<unknown> = (isConjunction: boolean) => {\n    const given: Given<unknown> = <TNextPrecondition>(\n      message: string,\n      setup: (value: unknown) => Promise<TNextPrecondition> | TNextPrecondition,\n      teardown?: ((value: TNextPrecondition) => Promise<void> | void) | undefined\n    ) => {\n      const nextChain = createChain(\n        mutableStacks,\n        Object.freeze([\n          ...stack,\n          {\n            isConjunction,\n            operation: 'given',\n            permutations: [[message, setup, teardown]]\n          } satisfies GivenFrame<unknown, TNextPrecondition>\n        ])\n      );\n\n      return {\n        and: nextChain.given(true) as Given<Awaited<ReturnType<typeof setup>>>,\n        when: nextChain.when(false) as When<Awaited<ReturnType<typeof setup>>, void>\n      } satisfies ReturnType<Given<Awaited<ReturnType<typeof setup>>>>;\n    };\n\n    given.oneOf = (<TNextPrecondition>(permutations: readonly GivenPermutation<unknown, TNextPrecondition>[]) => {\n      const nextChain = createChain(\n        mutableStacks,\n        Object.freeze([\n          ...stack,\n          {\n            isConjunction,\n            operation: 'given',\n            permutations\n          } satisfies GivenFrame<unknown, TNextPrecondition>\n        ])\n      );\n\n      return {\n        and: nextChain.given(true) as Given<TNextPrecondition>,\n        when: nextChain.when(false) as When<TNextPrecondition, void>\n      } satisfies ReturnType<Given<TNextPrecondition>>;\n    }) satisfies Given<unknown>['oneOf'];\n\n    return given;\n  };\n\n  const when: (isConjunction: boolean) => When<unknown, unknown> = (isConjunction: boolean) => {\n    const when: When<unknown, unknown> = (message, setup, teardown) => {\n      const nextChain = createChain(\n        mutableStacks,\n        Object.freeze([...stack, { isConjunction, operation: 'when', permutations: [[message, setup, teardown]] }])\n      );\n\n      return {\n        then: nextChain.then(false) as Then<unknown, Awaited<ReturnType<typeof setup>>>\n      } satisfies ReturnType<When<unknown, Awaited<ReturnType<typeof setup>>>>;\n    };\n\n    when.oneOf = (<TNextOutcome>(permutations: readonly WhenPermutation<unknown, unknown, TNextOutcome>[]) => {\n      const nextChain = createChain(\n        mutableStacks,\n        Object.freeze([\n          ...stack,\n          {\n            isConjunction,\n            operation: 'when',\n            permutations\n          } satisfies WhenFrame<unknown, unknown, TNextOutcome>\n        ])\n      );\n\n      return {\n        then: nextChain.then(false) as Then<unknown, TNextOutcome>\n      } satisfies ReturnType<When<unknown, TNextOutcome>>;\n    }) satisfies When<unknown, unknown>['oneOf'];\n\n    return when;\n  };\n\n  const then: (isConjunction: boolean) => Then<unknown, unknown> = isConjunction => {\n    return ((message, fn) => {\n      mutableStacks.push(\n        Object.freeze([\n          ...stack,\n          {\n            isConjunction,\n            fn,\n            message,\n            operation: 'then'\n          } satisfies ThenFrame<unknown, unknown>\n        ])\n      );\n\n      const currentChain = createChain(mutableStacks, stack);\n\n      return {\n        and: currentChain.then(true),\n        when: currentChain.when(false)\n      } satisfies ReturnType<Then<unknown, unknown>>;\n    }) satisfies Then<unknown, unknown>;\n  };\n\n  return { given, then, when };\n}\n\nfunction scenario(\n  message: string,\n  fn: (bdd: BehaviorDrivenDevelopment) => void,\n  options?: Partial<TestFacility>\n): void {\n  const stacks: (readonly Frame[])[] = [];\n\n  const fnResult = fn({\n    given: createChain(stacks, Object.freeze([])).given(false) as Given<void>\n  });\n\n  if (isPromise(fnResult)) {\n    // This is a soft block.\n    // While we can technically allow fn() to be asynchronous, we are blocking it\n    // to prevent potentially bad code patterns\n    throw new Error('The function passed to scenario() cannot asynchronous');\n  } else if (!stacks.some(frames => frames.some(frame => frame.operation === 'then'))) {\n    throw new Error('Scenario should contains at least one then clause');\n  }\n\n  const facility = {\n    afterEach: options?.afterEach || globalThis.afterEach,\n    beforeEach: options?.beforeEach || globalThis.beforeEach,\n    describe: options?.describe || globalThis.describe,\n    it: options?.it || globalThis.it\n  };\n\n  facility.describe(message, () => {\n    for (const stack of stacks) {\n      runStack(stack, { value: undefined }, { value: undefined }, facility);\n    }\n  });\n}\n\nfunction runStack(\n  stack: readonly Frame[],\n  preconditionRef: Ref<unknown>,\n  outcomeRef: Ref<unknown>,\n  facility: TestFacility\n): void {\n  const frame = stack.at(0);\n\n  if (!frame) {\n    return;\n  }\n\n  if (frame.operation === 'given') {\n    for (const [message, setup, teardown] of frame.permutations) {\n      facility.describe(`${frame.isConjunction ? 'and' : 'given'} ${message}`, () => {\n        let currentPreconditionRef: Ref<unknown>;\n\n        facility.beforeEach(() => {\n          const save = (value: unknown): void => {\n            currentPreconditionRef = { value };\n            preconditionRef.value = value;\n          };\n\n          const value = setup(preconditionRef.value);\n\n          return isPromise(value) ? value.then(save) : save(value);\n        });\n\n        facility.afterEach(() => teardown?.(currentPreconditionRef.value));\n\n        return runStack(stack.slice(1), preconditionRef, outcomeRef, facility);\n      });\n    }\n  } else if (frame.operation === 'when') {\n    for (const [message, setup, teardown] of frame.permutations) {\n      facility.describe(`${frame.isConjunction ? 'and' : 'when'} ${message}`, () => {\n        let currentOutcomeRef: { value: unknown };\n\n        facility.beforeEach(() => {\n          const save = (value: unknown): void => {\n            currentOutcomeRef = { value };\n            outcomeRef.value = value;\n          };\n\n          const value = setup(preconditionRef.value, outcomeRef.value);\n\n          return isPromise(value) ? value.then(save) : save(value);\n        });\n\n        facility.afterEach(() => teardown?.(preconditionRef.value, currentOutcomeRef.value));\n\n        return runStack(stack.slice(1), preconditionRef, outcomeRef, facility);\n      });\n    }\n  } else if (frame.operation === 'then') {\n    facility.it(`${frame.isConjunction ? 'and' : 'then'} ${frame.message}`, () =>\n      frame.fn(preconditionRef.value, outcomeRef.value)\n    );\n\n    return runStack(stack.slice(1), preconditionRef, outcomeRef, facility);\n  }\n}\n\nexport { scenario, type TestFacility };\n","export default function mergeInto<T, TMerge>(objectToMerge: TMerge): (scope: T) => T & TMerge {\n  return scope => ({ ...scope, ...objectToMerge });\n}\n"],"mappings":";AAAe,SAAR,UAA2B,SAA+C;AAC/E,SAAO,CAAC,CAAC,WAAW,OAAO,YAAY,YAAY,UAAU,WAAW,OAAO,QAAQ,SAAS;AAClG;;;AC0FA,SAAS,YAAY,eAAqC,OAAyB;AACjF,QAAM,QAAoD,CAAC,kBAA2B;AACpF,UAAMA,SAAwB,CAC5B,SACA,OACA,aACG;AACH,YAAM,YAAY;AAAA,QAChB;AAAA,QACA,OAAO,OAAO;AAAA,UACZ,GAAG;AAAA,UACH;AAAA,YACE;AAAA,YACA,WAAW;AAAA,YACX,cAAc,CAAC,CAAC,SAAS,OAAO,QAAQ,CAAC;AAAA,UAC3C;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,QACL,KAAK,UAAU,MAAM,IAAI;AAAA,QACzB,MAAM,UAAU,KAAK,KAAK;AAAA,MAC5B;AAAA,IACF;AAEA,IAAAA,OAAM,QAAS,CAAoB,iBAA0E;AAC3G,YAAM,YAAY;AAAA,QAChB;AAAA,QACA,OAAO,OAAO;AAAA,UACZ,GAAG;AAAA,UACH;AAAA,YACE;AAAA,YACA,WAAW;AAAA,YACX;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,QACL,KAAK,UAAU,MAAM,IAAI;AAAA,QACzB,MAAM,UAAU,KAAK,KAAK;AAAA,MAC5B;AAAA,IACF;AAEA,WAAOA;AAAA,EACT;AAEA,QAAM,OAA2D,CAAC,kBAA2B;AAC3F,UAAMC,QAA+B,CAAC,SAAS,OAAO,aAAa;AACjE,YAAM,YAAY;AAAA,QAChB;AAAA,QACA,OAAO,OAAO,CAAC,GAAG,OAAO,EAAE,eAAe,WAAW,QAAQ,cAAc,CAAC,CAAC,SAAS,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC;AAAA,MAC5G;AAEA,aAAO;AAAA,QACL,MAAM,UAAU,KAAK,KAAK;AAAA,MAC5B;AAAA,IACF;AAEA,IAAAA,MAAK,QAAS,CAAe,iBAA6E;AACxG,YAAM,YAAY;AAAA,QAChB;AAAA,QACA,OAAO,OAAO;AAAA,UACZ,GAAG;AAAA,UACH;AAAA,YACE;AAAA,YACA,WAAW;AAAA,YACX;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,QACL,MAAM,UAAU,KAAK,KAAK;AAAA,MAC5B;AAAA,IACF;AAEA,WAAOA;AAAA,EACT;AAEA,QAAM,OAA2D,mBAAiB;AAChF,WAAQ,CAAC,SAAS,OAAO;AACvB,oBAAc;AAAA,QACZ,OAAO,OAAO;AAAA,UACZ,GAAG;AAAA,UACH;AAAA,YACE;AAAA,YACA;AAAA,YACA;AAAA,YACA,WAAW;AAAA,UACb;AAAA,QACF,CAAC;AAAA,MACH;AAEA,YAAM,eAAe,YAAY,eAAe,KAAK;AAErD,aAAO;AAAA,QACL,KAAK,aAAa,KAAK,IAAI;AAAA,QAC3B,MAAM,aAAa,KAAK,KAAK;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,MAAM,KAAK;AAC7B;AAEA,SAAS,SACP,SACA,IACA,SACM;AACN,QAAM,SAA+B,CAAC;AAEtC,QAAM,WAAW,GAAG;AAAA,IAClB,OAAO,YAAY,QAAQ,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK;AAAA,EAC3D,CAAC;AAED,MAAI,UAAU,QAAQ,GAAG;AAIvB,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE,WAAW,CAAC,OAAO,KAAK,YAAU,OAAO,KAAK,WAAS,MAAM,cAAc,MAAM,CAAC,GAAG;AACnF,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AAEA,QAAM,WAAW;AAAA,IACf,YAAW,mCAAS,cAAa,WAAW;AAAA,IAC5C,aAAY,mCAAS,eAAc,WAAW;AAAA,IAC9C,WAAU,mCAAS,aAAY,WAAW;AAAA,IAC1C,KAAI,mCAAS,OAAM,WAAW;AAAA,EAChC;AAEA,WAAS,SAAS,SAAS,MAAM;AAC/B,eAAW,SAAS,QAAQ;AAC1B,eAAS,OAAO,EAAE,OAAO,OAAU,GAAG,EAAE,OAAO,OAAU,GAAG,QAAQ;AAAA,IACtE;AAAA,EACF,CAAC;AACH;AAEA,SAAS,SACP,OACA,iBACA,YACA,UACM;AACN,QAAM,QAAQ,MAAM,GAAG,CAAC;AAExB,MAAI,CAAC,OAAO;AACV;AAAA,EACF;AAEA,MAAI,MAAM,cAAc,SAAS;AAC/B,eAAW,CAAC,SAAS,OAAO,QAAQ,KAAK,MAAM,cAAc;AAC3D,eAAS,SAAS,GAAG,MAAM,gBAAgB,QAAQ,OAAO,IAAI,OAAO,IAAI,MAAM;AAC7E,YAAI;AAEJ,iBAAS,WAAW,MAAM;AACxB,gBAAM,OAAO,CAACC,WAAyB;AACrC,qCAAyB,EAAE,OAAAA,OAAM;AACjC,4BAAgB,QAAQA;AAAA,UAC1B;AAEA,gBAAM,QAAQ,MAAM,gBAAgB,KAAK;AAEzC,iBAAO,UAAU,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,KAAK;AAAA,QACzD,CAAC;AAED,iBAAS,UAAU,MAAM,qCAAW,uBAAuB,MAAM;AAEjE,eAAO,SAAS,MAAM,MAAM,CAAC,GAAG,iBAAiB,YAAY,QAAQ;AAAA,MACvE,CAAC;AAAA,IACH;AAAA,EACF,WAAW,MAAM,cAAc,QAAQ;AACrC,eAAW,CAAC,SAAS,OAAO,QAAQ,KAAK,MAAM,cAAc;AAC3D,eAAS,SAAS,GAAG,MAAM,gBAAgB,QAAQ,MAAM,IAAI,OAAO,IAAI,MAAM;AAC5E,YAAI;AAEJ,iBAAS,WAAW,MAAM;AACxB,gBAAM,OAAO,CAACA,WAAyB;AACrC,gCAAoB,EAAE,OAAAA,OAAM;AAC5B,uBAAW,QAAQA;AAAA,UACrB;AAEA,gBAAM,QAAQ,MAAM,gBAAgB,OAAO,WAAW,KAAK;AAE3D,iBAAO,UAAU,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,KAAK;AAAA,QACzD,CAAC;AAED,iBAAS,UAAU,MAAM,qCAAW,gBAAgB,OAAO,kBAAkB,MAAM;AAEnF,eAAO,SAAS,MAAM,MAAM,CAAC,GAAG,iBAAiB,YAAY,QAAQ;AAAA,MACvE,CAAC;AAAA,IACH;AAAA,EACF,WAAW,MAAM,cAAc,QAAQ;AACrC,aAAS;AAAA,MAAG,GAAG,MAAM,gBAAgB,QAAQ,MAAM,IAAI,MAAM,OAAO;AAAA,MAAI,MACtE,MAAM,GAAG,gBAAgB,OAAO,WAAW,KAAK;AAAA,IAClD;AAEA,WAAO,SAAS,MAAM,MAAM,CAAC,GAAG,iBAAiB,YAAY,QAAQ;AAAA,EACvE;AACF;;;ACrSe,SAAR,UAAsC,eAAiD;AAC5F,SAAO,YAAU,EAAE,GAAG,OAAO,GAAG,cAAc;AAChD;","names":["given","when","value"]}