1.1.2Updated a month ago
interface I_Test {
  name: string
  fn: TestFunction
}

class TestbedInstance {
  readonly name: string
  private tests: I_Test[] = []
  private setup_fn?: SetupFunction
  private clean_up_fn?: SetupFunction
  private before_each_fn?: BeforeEachFunction

  constructor(name: string) {
    this.name = name;
  }

  setup(fn: SetupFunction) {
    this.setup_fn = fn;
  }

  clean_up(fn: CleanUpFunction) {
    this.clean_up_fn = fn;
  }

  before_each(fn: BeforeEachFunction) {
    this.before_each_fn = fn;
  }

  test(name: string, fn: () => void) {
    this.tests.push({
      name,
      fn
    });
    return this;
  }

  async run() {
    console.log(this.name);
    if(!this.tests.length) {
      console.log(` '- No tests provided. Skipping...`);
      return;
    }

    if(this.setup_fn) {
      console.log(` '- Setup()`);
      this.setup_fn();
    }

    console.log(` '- Loading Tests...`)
    for(const test of this.tests) {
      let pad = '';
      if(this.before_each_fn) {
        pad = '  ';
        console.log(`   '- BeforeEach()`);
        this.before_each_fn();
      }

      console.log(`   ${pad}'- ${test.name}`)
      await Deno.test(`${this.name} - ${test.name}`, test.fn);
    }

    if(this.clean_up_fn) {
      console.log(` '- CleanUp()`);
      this.clean_up_fn();
    }
  }
}

type TestFunction = () => void | Promise<void>
type SetupFunction = () => void | Promise<void>
type CleanUpFunction = () => void | Promise<void>
type BeforeEachFunction = () => void | Promise<void>

type CreateTestFunction = (name: string, fn: TestFunction) => void
type SetSetupFunction = (fn: SetupFunction) => void
type SetCleanUpFunction = (fn: CleanUpFunction) => void
type SetBeforeEachFunction = (fn: BeforeEachFunction) => void

export type TestbedInitializeParams = {
  Test: CreateTestFunction,
  Setup: SetSetupFunction,
  CleanUp: SetCleanUpFunction,
  BeforeEach: SetBeforeEachFunction,
}
export type TestbedInitializer = (bed_handlers : TestbedInitializeParams) => void | Promise<void>

export const Testbed = async (name: string, initializer: TestbedInitializer) => {
  const testbed = new TestbedInstance(name);

  await initializer({
    Test: testbed.test.bind(testbed),
    CleanUp: testbed.clean_up.bind(testbed),
    Setup: testbed.setup.bind(testbed),
    BeforeEach: testbed.before_each.bind(testbed),
  });

  testbed.run();
}