-
-
Notifications
You must be signed in to change notification settings - Fork 268
/
index.ts
185 lines (171 loc) · 4.48 KB
/
index.ts
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// module code goes here
import {
argv,
cwd,
env,
mainScript,
TapPlugin,
TestBase,
} from '@tapjs/core'
import { basename, dirname, relative, resolve } from 'node:path'
import { rimraf, rimrafSync } from 'rimraf'
import {
Fixture,
FixtureContent,
FixtureDirContent,
FixtureType,
} from './fixture.js'
export * from './fixture.js'
export interface TestFixturesOptions {
/**
* Directory to store test fixtures.
* Defaults to `./.tap/fixtures/${test name}`
*/
testdir?: string
/**
* Set to true to keep the fixture dir after the test ends.
* Otherwise, any `t.testdir()` directories are deleted when the
* test is done.
*/
saveFixture?: boolean
}
export class TestFixtures {
#t: TestBase
static #refs: Map<TestBase, TestFixtures> = new Map()
#testdir: string
#didOnEOF: boolean = false
#createdTestdir: boolean = false
#saveFixture: boolean = false
constructor(t: TestBase, opts: TestFixturesOptions) {
TestFixtures.#refs.set(t, this)
this.#testdir = opts.testdir || TestFixtures.#getTestdir(t)
if (opts.saveFixture !== undefined) {
this.#saveFixture = !!opts.saveFixture
} else {
this.#saveFixture = env.TAP_SAVE_FIXTURE === '1'
}
this.#t = t
}
/**
* Create a fixture object for use in a
* {@link @tapjs/fixture!index.TestFixtures#testdir} method.
*
* @group Spies, Mocks, and Fixtures
*/
fixture<T extends FixtureType>(
type: T,
content: FixtureContent<T>
) {
return new Fixture(type, content)
}
/**
* Set whether the fixture should be saved or not
*
* Must be set *BEFORE* calling
* {@link @tapjs/fixture!index.TestFixtures#testdir}, or it will not have any
* effect.
*/
set saveFixture(save: boolean) {
this.#saveFixture = save
}
get saveFixture() {
return this.#saveFixture
}
/**
* Create a test directory, optionally filling it up with contents
*
* If the `@tapjs/after` plugin is loaded, the testdir will be automatically
* deleted at the end of the test.
*
* To _not_ delete the directory after the test, use the
* `saveFixture: true` option when creating the test, or specify
* `--save-fixture` on the command line or in the tap configuration.
*
* @group Spies, Mocks, and Fixtures
*/
testdir(content?: FixtureDirContent) {
const dir = resolve(this.testdirName)
rimrafSync(dir)
Fixture.make(dir, content || {})
this.#createdTestdir = true
if (!this.#didOnEOF && !this.#saveFixture) {
this.#didOnEOF = true
const { onEOF } = this.#t
this.#t.onEOF = async () => {
this.#t.onEOF = onEOF
if (relative(process.cwd(), dir) === '') {
// cd out of it first, or else Windows fails with EBUSY every time
process.chdir(dirname(dir))
}
await rimraf(dir)
await onEOF()
}
}
return dir
}
/**
* The name of the folder that this test will use with
* {@link @tapjs/fixture!index.TestFixtures#testdir}.
*
* By default, it uses a folder name based on the name of the test file
* and subtest, within \`.tap/fixtures\` in the root of the project.
*
* @group Spies, Mocks, and Fixtures
*/
get testdirName() {
return this.#testdir
}
set testdirName(td: string) {
if (this.#createdTestdir && td !== this.#testdir) {
this.#didOnEOF = false
}
this.#testdir = td
}
static #getTestdir(t: TestBase) {
const re = /[^a-zA-Z0-9\._\-]+/gi
const p = t.parent && TestFixtures.#refs.get(t.parent)
if (!p) {
const main = mainScript()
/* c8 ignore start */
const root = env.TAP_CWD || cwd
/* c8 ignore stop */
const name = [
dirname(relative(root, main)),
basename(main),
...argv.slice(2),
]
.join(' ')
.trim()
.replace(re, '-')
return resolve(root, '.tap/fixtures', name)
}
/* c8 ignore start */
const name = t.name || 'unnamed test'
/* c8 ignore stop */
return `${p.testdirName}-${name.replace(re, '-')}`
}
}
export const plugin: TapPlugin<TestFixtures, TestFixturesOptions> = (
t: TestBase,
opts: TestFixturesOptions
) => new TestFixtures(t, opts)
/**
* Options added by this plugin
*
* @group Configuration
*/
export const config = {
/**
* flag
*
* Do not clean up fixtures created with `t.testdir()`
*
* @group Configuration
*/
'save-fixture': {
type: 'boolean',
short: 'F',
description:
'Do not clean up fixtures created with `t.testdir()`',
},
}