Skip to content

Commit fe20db0

Browse files
committed
test(core): colocate tests and migrate to test-utils
1 parent 90c7065 commit fe20db0

15 files changed

Lines changed: 718 additions & 831 deletions

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
"license": "MIT",
4949
"packageManager": "pnpm@9.14.2",
5050
"scripts": {
51-
"build": "bsh build",
51+
"build": "bsh build \"src/**/*.ts\" \"!src/**/*.test.ts\"",
5252
"prepack": "pnpm build",
5353
"test": "bsh test",
5454
"lint": "bsh lint"

packages/core/test/prompts/autocomplete.test.ts renamed to packages/core/src/prompts/autocomplete.test.ts

Lines changed: 45 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import { cursor } from 'sisteransi';
2-
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
3-
import { default as AutocompletePrompt } from '../../src/prompts/autocomplete.js';
4-
import { MockReadable } from '../mock-readable.js';
5-
import { MockWritable } from '../mock-writable.js';
2+
import { beforeEach, describe, expect, test } from 'vitest';
3+
import { default as AutocompletePrompt } from './autocomplete.js';
4+
import { createMocks, type Mocks } from '@bomb.sh/tools/test-utils';
65

76
describe('AutocompletePrompt', () => {
8-
let input: MockReadable;
9-
let output: MockWritable;
7+
let mocks: Mocks<{ input: true; output: true }>;
108
const testOptions = [
119
{ value: 'apple', label: 'Apple' },
1210
{ value: 'banana', label: 'Banana' },
@@ -16,29 +14,24 @@ describe('AutocompletePrompt', () => {
1614
];
1715

1816
beforeEach(() => {
19-
input = new MockReadable();
20-
output = new MockWritable();
21-
});
22-
23-
afterEach(() => {
24-
vi.restoreAllMocks();
17+
mocks = createMocks({ input: true, output: true });
2518
});
2619

2720
test('renders render() result', () => {
2821
const instance = new AutocompletePrompt({
29-
input,
30-
output,
22+
input: mocks.input,
23+
output: mocks.output,
3124
render: () => 'foo',
3225
options: testOptions,
3326
});
3427
instance.prompt();
35-
expect(output.buffer).to.deep.equal([cursor.hide, 'foo']);
28+
expect(mocks.output.buffer).to.deep.equal([cursor.hide, 'foo']);
3629
});
3730

3831
test('initial options match provided options', () => {
3932
const instance = new AutocompletePrompt({
40-
input,
41-
output,
33+
input: mocks.input,
34+
output: mocks.output,
4235
render: () => 'foo',
4336
options: testOptions,
4437
});
@@ -52,8 +45,8 @@ describe('AutocompletePrompt', () => {
5245

5346
test('cursor navigation with event emitter', () => {
5447
const instance = new AutocompletePrompt({
55-
input,
56-
output,
48+
input: mocks.input,
49+
output: mocks.output,
5750
render: () => 'foo',
5851
options: testOptions,
5952
});
@@ -78,8 +71,8 @@ describe('AutocompletePrompt', () => {
7871

7972
test('initialValue selects correct option', () => {
8073
const instance = new AutocompletePrompt({
81-
input,
82-
output,
74+
input: mocks.input,
75+
output: mocks.output,
8376
render: () => 'foo',
8477
options: testOptions,
8578
initialValue: ['cherry'],
@@ -95,8 +88,8 @@ describe('AutocompletePrompt', () => {
9588

9689
test('initialValue defaults to first option when non-multiple', () => {
9790
const instance = new AutocompletePrompt({
98-
input,
99-
output,
91+
input: mocks.input,
92+
output: mocks.output,
10093
render: () => 'foo',
10194
options: testOptions,
10295
});
@@ -107,8 +100,8 @@ describe('AutocompletePrompt', () => {
107100

108101
test('initialValue is empty when multiple', () => {
109102
const instance = new AutocompletePrompt({
110-
input,
111-
output,
103+
input: mocks.input,
104+
output: mocks.output,
112105
render: () => 'foo',
113106
options: testOptions,
114107
multiple: true,
@@ -120,8 +113,8 @@ describe('AutocompletePrompt', () => {
120113

121114
test('filtering through user input', () => {
122115
const instance = new AutocompletePrompt({
123-
input,
124-
output,
116+
input: mocks.input,
117+
output: mocks.output,
125118
render: () => 'foo',
126119
options: testOptions,
127120
});
@@ -132,7 +125,7 @@ describe('AutocompletePrompt', () => {
132125
expect(instance.filteredOptions.length).to.equal(testOptions.length);
133126

134127
// Simulate typing 'a' by emitting keypress event
135-
input.emit('keypress', 'a', { name: 'a' });
128+
mocks.input.emit('keypress', 'a', { name: 'a' });
136129

137130
// Check that filtered options are updated to include options with 'a'
138131
expect(instance.filteredOptions.length).to.be.lessThan(testOptions.length);
@@ -144,37 +137,37 @@ describe('AutocompletePrompt', () => {
144137

145138
test('default filter function works correctly', () => {
146139
const instance = new AutocompletePrompt({
147-
input,
148-
output,
140+
input: mocks.input,
141+
output: mocks.output,
149142
render: () => 'foo',
150143
options: testOptions,
151144
});
152145

153146
instance.prompt();
154147

155-
input.emit('keypress', 'a', { name: 'a' });
156-
input.emit('keypress', 'p', { name: 'p' });
148+
mocks.input.emit('keypress', 'a', { name: 'a' });
149+
mocks.input.emit('keypress', 'p', { name: 'p' });
157150

158151
expect(instance.filteredOptions).toEqual([
159152
{ value: 'apple', label: 'Apple' },
160153
{ value: 'grape', label: 'Grape' },
161154
]);
162155

163-
input.emit('keypress', 'z', { name: 'z' });
156+
mocks.input.emit('keypress', 'z', { name: 'z' });
164157

165158
expect(instance.filteredOptions).toEqual([]);
166159
});
167160

168161
test('submit without nav resolves to first option in non-multiple', async () => {
169162
const instance = new AutocompletePrompt({
170-
input,
171-
output,
163+
input: mocks.input,
164+
output: mocks.output,
172165
render: () => 'foo',
173166
options: testOptions,
174167
});
175168

176169
const promise = instance.prompt();
177-
input.emit('keypress', '', { name: 'return' });
170+
mocks.input.emit('keypress', '', { name: 'return' });
178171
const result = await promise;
179172

180173
expect(instance.selectedValues).to.deep.equal(['apple']);
@@ -183,15 +176,15 @@ describe('AutocompletePrompt', () => {
183176

184177
test('submit without nav resolves to [] in multiple', async () => {
185178
const instance = new AutocompletePrompt({
186-
input,
187-
output,
179+
input: mocks.input,
180+
output: mocks.output,
188181
render: () => 'foo',
189182
options: testOptions,
190183
multiple: true,
191184
});
192185

193186
const promise = instance.prompt();
194-
input.emit('keypress', '', { name: 'return' });
187+
mocks.input.emit('keypress', '', { name: 'return' });
195188
const result = await promise;
196189

197190
expect(instance.selectedValues).to.deep.equal([]);
@@ -200,16 +193,16 @@ describe('AutocompletePrompt', () => {
200193

201194
test('Tab with empty input and placeholder fills input and submit returns matching option', async () => {
202195
const instance = new AutocompletePrompt({
203-
input,
204-
output,
196+
input: mocks.input,
197+
output: mocks.output,
205198
render: () => 'foo',
206199
options: testOptions,
207200
placeholder: 'apple',
208201
});
209202

210203
const promise = instance.prompt();
211-
input.emit('keypress', '\t', { name: 'tab' });
212-
input.emit('keypress', '', { name: 'return' });
204+
mocks.input.emit('keypress', '\t', { name: 'tab' });
205+
mocks.input.emit('keypress', '', { name: 'return' });
213206
const result = await promise;
214207

215208
expect(instance.userInput).to.equal('apple');
@@ -222,15 +215,15 @@ describe('AutocompletePrompt', () => {
222215
{ value: 'banana', label: 'Banana' },
223216
];
224217
const instance = new AutocompletePrompt({
225-
input,
226-
output,
218+
input: mocks.input,
219+
output: mocks.output,
227220
render: () => 'foo',
228221
options: () => dynamicOptions,
229222
});
230223

231224
instance.prompt();
232225

233-
input.emit('keypress', 'z', { name: 'z' });
226+
mocks.input.emit('keypress', 'z', { name: 'z' });
234227

235228
expect(instance.filteredOptions).toEqual(dynamicOptions);
236229
});
@@ -242,32 +235,32 @@ describe('AutocompletePrompt', () => {
242235
{ value: 'cherry', label: 'Cherry' },
243236
];
244237
const instance = new AutocompletePrompt({
245-
input,
246-
output,
238+
input: mocks.input,
239+
output: mocks.output,
247240
render: () => 'foo',
248241
options: () => dynamicOptions,
249242
filter: (search, opt) => (opt.label ?? '').toLowerCase().endsWith(search.toLowerCase()),
250243
});
251244

252245
instance.prompt();
253246

254-
input.emit('keypress', 'a', { name: 'a' });
247+
mocks.input.emit('keypress', 'a', { name: 'a' });
255248

256249
// 'endsWith' matches Banana but not Apple or Cherry
257250
expect(instance.filteredOptions).toEqual([{ value: 'banana', label: 'Banana' }]);
258251
});
259252

260253
test('Tab with non-matching placeholder does not fill input', async () => {
261254
const instance = new AutocompletePrompt({
262-
input,
263-
output,
255+
input: mocks.input,
256+
output: mocks.output,
264257
render: () => 'foo',
265258
options: testOptions,
266259
placeholder: 'Type to search...',
267260
});
268261

269262
instance.prompt();
270-
input.emit('keypress', '\t', { name: 'tab' });
263+
mocks.input.emit('keypress', '\t', { name: 'tab' });
271264

272265
// Placeholder does not match any option, so input must not be filled with placeholder
273266
expect(instance.userInput).not.to.equal('Type to search...');

packages/core/test/prompts/confirm.test.ts renamed to packages/core/src/prompts/confirm.test.ts

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,56 @@
11
import { cursor } from 'sisteransi';
2-
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
3-
import { default as ConfirmPrompt } from '../../src/prompts/confirm.js';
4-
import { MockReadable } from '../mock-readable.js';
5-
import { MockWritable } from '../mock-writable.js';
2+
import { beforeEach, describe, expect, test } from 'vitest';
3+
import { default as ConfirmPrompt } from './confirm.js';
4+
import { createMocks, type Mocks } from '@bomb.sh/tools/test-utils';
65

76
describe('ConfirmPrompt', () => {
8-
let input: MockReadable;
9-
let output: MockWritable;
7+
let mocks: Mocks<{ input: true; output: true }>;
108

119
beforeEach(() => {
12-
input = new MockReadable();
13-
output = new MockWritable();
14-
});
15-
16-
afterEach(() => {
17-
vi.restoreAllMocks();
10+
mocks = createMocks({ input: true, output: true });
1811
});
1912

2013
test('renders render() result', () => {
2114
const instance = new ConfirmPrompt({
22-
input,
23-
output,
15+
input: mocks.input,
16+
output: mocks.output,
2417
render: () => 'foo',
2518
active: 'yes',
2619
inactive: 'no',
2720
});
2821
instance.prompt();
29-
expect(output.buffer).to.deep.equal([cursor.hide, 'foo']);
22+
expect(mocks.output.buffer).to.deep.equal([cursor.hide, 'foo']);
3023
});
3124

3225
test('sets value and submits on confirm (y)', () => {
3326
const instance = new ConfirmPrompt({
34-
input,
35-
output,
27+
input: mocks.input,
28+
output: mocks.output,
3629
render: () => 'foo',
3730
active: 'yes',
3831
inactive: 'no',
3932
initialValue: true,
4033
});
4134

4235
instance.prompt();
43-
input.emit('keypress', 'y', { name: 'y' });
36+
mocks.input.emit('keypress', 'y', { name: 'y' });
4437

4538
expect(instance.value).to.equal(true);
4639
expect(instance.state).to.equal('submit');
4740
});
4841

4942
test('sets value and submits on confirm (n)', () => {
5043
const instance = new ConfirmPrompt({
51-
input,
52-
output,
44+
input: mocks.input,
45+
output: mocks.output,
5346
render: () => 'foo',
5447
active: 'yes',
5548
inactive: 'no',
5649
initialValue: true,
5750
});
5851

5952
instance.prompt();
60-
input.emit('keypress', 'n', { name: 'n' });
53+
mocks.input.emit('keypress', 'n', { name: 'n' });
6154

6255
expect(instance.value).to.equal(false);
6356
expect(instance.state).to.equal('submit');
@@ -66,31 +59,31 @@ describe('ConfirmPrompt', () => {
6659
describe('cursor', () => {
6760
test('cursor is 1 when inactive', () => {
6861
const instance = new ConfirmPrompt({
69-
input,
70-
output,
62+
input: mocks.input,
63+
output: mocks.output,
7164
render: () => 'foo',
7265
active: 'yes',
7366
inactive: 'no',
7467
initialValue: false,
7568
});
7669

7770
instance.prompt();
78-
input.emit('keypress', '', { name: 'return' });
71+
mocks.input.emit('keypress', '', { name: 'return' });
7972
expect(instance.cursor).to.equal(1);
8073
});
8174

8275
test('cursor is 0 when active', () => {
8376
const instance = new ConfirmPrompt({
84-
input,
85-
output,
77+
input: mocks.input,
78+
output: mocks.output,
8679
render: () => 'foo',
8780
active: 'yes',
8881
inactive: 'no',
8982
initialValue: true,
9083
});
9184

9285
instance.prompt();
93-
input.emit('keypress', '', { name: 'return' });
86+
mocks.input.emit('keypress', '', { name: 'return' });
9487
expect(instance.cursor).to.equal(0);
9588
});
9689
});

0 commit comments

Comments
 (0)