๐Ÿ“ฆ antongolub / abstractest

๐Ÿ“„ adapter.ts ยท 110 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110import {Done, spawn, SuiteFn, Test, TestFn, TestApi} from '@abstractest/core'
import {Mocker} from '@abstractest/types'
import {expect, SnapshotState} from '@abstractest/expect'
import {mock} from '@abstractest/mock'
import {after, afterEach, before, beforeEach, describe, it} from 'node:test'
import * as path from 'node:path'

export const _api = {
  spawn,
  it,
  describe,
  after,
  afterEach,
  before,
  beforeEach,
  mock
}

const g: any = global
const currentTestNameChunks: string[]  = []
const queue: string[] = []

const trackName = (handler: (name: string, fn: any) => any): typeof handler => (name, fn) => {
  currentTestNameChunks.push(name)
  handler(name, fn)
  currentTestNameChunks.pop()
}

const adaptTest = (method: any): Test => trackName((name, fn) => {
  queue.push(currentTestNameChunks.join(' '))

  const handler = (done = (_result?: any) => {/* noop */}) => {
    let save = () => {/* noop */} // eslint-disable-line unicorn/consistent-function-scoping

    if (g.__testPath && g.__testRoot) {
      const currentTestName = queue.shift()
      const testPath = g.__testPath
      const snapshotPath = path.join(g.__testRoot, '__snapshots__', `${path.basename(testPath)}.snap`)
      const snapshotState = new SnapshotState(snapshotPath, {
        expand: undefined,
        snapshotFormat: { escapeString: false, printBasicPrototype: false },
        updateSnapshot: 'new',
        rootDir: process.cwd(),
        prettierPath: 'prettier',
        // rootDir: config.rootDir,
        // snapshotFormat: config.snapshotFormat,
        // updateSnapshot: globalConfig.updateSnapshot,
      });

      save = () => {
        snapshotState.save()
        // eslint-disable-next-line
        // @ts-ignore
        expect?.setState({snapshotState: undefined, testPath: undefined, currentTestName: undefined})
      }
      // eslint-disable-next-line
      // @ts-ignore
      expect?.setState({snapshotState, testPath, currentTestName})
    }

    let cb = (result?: any) => { save(); done(result); cb = () => {/* noop */}}
    const result = fn?.(cb)

    return typeof result?.then == 'function'
      ? result.then(() => cb()).catch(cb)
      : cb()
  }

  // TODO refactor workaround
  if (/^[^(]+\([^)]+,/.test(fn?.toString() + '')) {
    method(name, (_ctx: any, done: Done) => handler(done))
  } else {
    method(name, () => handler())
  }
})

const _it =
  Object.assign((name: string, fn?: TestFn) => { adaptTest(_api.it)(name, fn) }, {
  only(name: string, fn?: TestFn) { adaptTest(_api.it.only)(name, fn) },
  skip(name: string, fn?: TestFn) { adaptTest(_api.it.skip)(name, fn) },
  todo(name: string, fn?: TestFn) { adaptTest(_api.it.todo)(name, fn) },
})

const _describe =
  Object.assign((name: string, fn?: SuiteFn) => { trackName(_api.describe)(name, fn)}, {
  only(name: string, fn?: SuiteFn) { trackName(_api.describe.only)(name, fn) },
  skip(name: string, fn?: SuiteFn) { trackName(_api.describe.skip)(name, fn) },
  todo(name: string, fn?: SuiteFn) { trackName(_api.describe.todo)(name, fn) },
})

const _mock: Mocker = {
  // eslint-disable-next-line
  // @ts-ignore
  spyOn(...args: any[]) {        return _api.mock.spyOn(...args) },
  // eslint-disable-next-line
  // @ts-ignore
  fn(...args: any[]) { return _api.mock.fn(...args) },
}

export const api: TestApi = {
  expect,
  it: _it,
  describe: _describe,
  before(fn) {        return _api.before(fn) },
  beforeEach(fn) {    return _api.beforeEach(fn) },
  after(fn) {         return _api.after(fn) },
  afterEach(fn) {     return _api.afterEach(fn) },
  mock: _mock,
}