Skip to content

Commit

Permalink
Merge branch 'mock-api' of https://github.com/Mist3rBru/clack into mo…
Browse files Browse the repository at this point in the history
…ck-api
  • Loading branch information
orochaa committed Dec 15, 2024
2 parents 4c98bd2 + c44f005 commit c8a491b
Show file tree
Hide file tree
Showing 73 changed files with 6,236 additions and 1,936 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"editor.defaultFormatter": "biomejs.biome"
}
14 changes: 14 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/** @type {import('jest').Config} */
export default {
bail: true,
clearMocks: true,
testEnvironment: 'node',
transform: {
'^.+\\.ts$': '@swc/jest',
},
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
testRegex: ['__tests__/.+(spec|test).ts$'],
setupFiles: ['<rootDir>/setup.tests.ts'],
};
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@
"format": "biome check --write",
"lint": "biome lint --write --unsafe",
"type-check": "biome lint && tsc",
"test": "pnpm -r run test",
"test": "jest --no-cache",
"test:w": "npm test -- --watch",
"test:ci": "npm test -- --coverage",
"ci:version": "changeset version && pnpm install --no-frozen-lockfile",
"ci:publish": "changeset publish"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@changesets/cli": "^2.26.2",
"@swc/core": "^1.3.83",
"@swc/jest": "^0.2.29",
"@types/jest": "^29.5.4",
"@types/node": "^18.16.0",
"jest": "^29.6.4",
"typescript": "^5.2.2",
"unbuild": "^2.0.0"
},
Expand Down
25 changes: 25 additions & 0 deletions packages/core/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { readdirSync } from 'node:fs';
import { join } from 'node:path';
import * as packageExports from '../src/index';

describe('Package', () => {
const exportedKeys = Object.keys(packageExports);

it('should export all prompts', async () => {
const promptsPath = join(__dirname, '../src/prompts');
const promptFiles = readdirSync(promptsPath);

for (const file of promptFiles) {
const prompt = await import(join(promptsPath, file));
expect(exportedKeys).toContain(prompt.default.name);
}
});

it('should export selected utils', async () => {
const utils: string[] = ['block', 'isCancel', 'mockPrompt', 'setGlobalAliases'];

for (const util of utils) {
expect(exportedKeys).toContain(util);
}
});
});
2 changes: 2 additions & 0 deletions packages/core/__tests__/mocks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './mock-readable.js';
export * from './mock-writable.js';
File renamed without changes.
File renamed without changes.
96 changes: 96 additions & 0 deletions packages/core/__tests__/prompts/confirm.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { ConfirmPrompt, mockPrompt, setGlobalAliases } from '../../src';
import type { ConfirmOptions } from '../../src/prompts/confirm';

const makeSut = (opts?: Partial<ConfirmOptions>) => {
return new ConfirmPrompt({
render() {
return this.value;
},
active: 'yes',
inactive: 'no',
...opts,
}).prompt();
};

describe('ConfirmPrompt', () => {
const mock = mockPrompt<ConfirmPrompt>();

afterEach(() => {
mock.close();
});

it('should set a boolean value', () => {
makeSut({
// @ts-expect-error
initialValue: ' ',
});
expect(mock.value).toBe(true);
mock.close();

makeSut({
// @ts-expect-error
initialValue: '',
});
expect(mock.state).toBe('initial');
expect(mock.value).toBe(false);
});

it('should change value when cursor changes', () => {
makeSut();

expect(mock.value).toBe(false);
mock.pressKey('up', { name: 'up' });
expect(mock.cursor).toBe(0);
expect(mock.value).toBe(true);
mock.pressKey('right', { name: 'right' });
expect(mock.cursor).toBe(1);
expect(mock.value).toBe(false);
mock.pressKey('left', { name: 'left' });
expect(mock.cursor).toBe(0);
expect(mock.value).toBe(true);
});

it('should change value on cursor alias', () => {
setGlobalAliases([['u', 'up']]);
makeSut();

expect(mock.value).toBe(false);
mock.pressKey('u', { name: 'u' });
expect(mock.value).toBe(true);
});

it('should not change value on type', () => {
makeSut();

expect(mock.value).toBe(false);
mock.pressKey('t', { name: 't' });
expect(mock.value).toBe(false);
mock.pressKey('e', { name: 'e' });
expect(mock.value).toBe(false);
});

it('should submit value', () => {
makeSut();

mock.submit();

expect(mock.state).toBe('submit');
expect(mock.value).toBe(false);
});

it('should submit value on confirm alias', () => {
const aliases = [
['y', true],
['n', false],
] as const;

for (const [alias, expected] of aliases) {
makeSut();
expect(mock.state).not.toBe('submit');
mock.pressKey(alias, { name: alias });
expect(mock.state).toBe('submit');
expect(mock.value).toBe(expected);
mock.close();
}
});
});
177 changes: 177 additions & 0 deletions packages/core/__tests__/prompts/group-multiselect.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { GroupMultiSelectPrompt, mockPrompt, setGlobalAliases } from '../../src';
import type { GroupMultiSelectOptions } from '../../src/prompts/group-multiselect';

const makeSut = (opts?: Partial<GroupMultiSelectOptions<{ value: string }>>) => {
return new GroupMultiSelectPrompt<any>({
render() {
return this.value;
},
options: {
'changed packages': [{ value: '@scope/a' }, { value: '@scope/b' }, { value: '@scope/c' }],
'unchanged packages': [{ value: '@scope/x' }, { value: '@scope/y' }, { value: '@scope/z' }],
},
...opts,
}).prompt();
};

describe('GroupMultiSelectPrompt', () => {
const mock = mockPrompt<GroupMultiSelectPrompt<{ value: string }>>();

afterEach(() => {
mock.close();
});

it('should set options', () => {
makeSut();

expect(mock.options).toStrictEqual([
{ label: 'changed packages', value: 'changed packages', group: true },
{ value: '@scope/a', group: 'changed packages' },
{ value: '@scope/b', group: 'changed packages' },
{ value: '@scope/c', group: 'changed packages' },
{ label: 'unchanged packages', value: 'unchanged packages', group: true },
{ value: '@scope/x', group: 'unchanged packages' },
{ value: '@scope/y', group: 'unchanged packages' },
{ value: '@scope/z', group: 'unchanged packages' },
]);
});

it('should set initialValues', () => {
makeSut({
initialValues: ['@scope/a', 'unchanged packages'],
});

expect(mock.value).toStrictEqual(['@scope/a', '@scope/x', '@scope/y', '@scope/z']);
});

it('should set initial cursor position', () => {
makeSut({
cursorAt: '@scope/b',
});

expect(mock.cursor).toBe(2);
});

it('should set default cursor position', () => {
makeSut();

expect(mock.cursor).toBe(0);
});

it('should change cursor position on cursor', () => {
makeSut({
options: {
groupA: [{ value: '1' }],
groupB: [{ value: '1' }],
},
});
const moves = [
['down', 1],
['right', 2],
['down', 3],
['right', 0],
['left', 3],
['up', 2],
] as const;

for (const [cursor, index] of moves) {
mock.emit('cursor', cursor);
expect(mock.cursor).toBe(index);
}
});

it('should change cursor position on cursor alias', () => {
setGlobalAliases([
['d', 'down'],
['u', 'up'],
]);
makeSut();
const moves = [
['d', 1],
['u', 0],
] as const;

for (const [cursor, index] of moves) {
mock.pressKey(cursor, { name: cursor });
expect(mock.cursor).toBe(index);
}
});

it('should toggle option', () => {
makeSut();

mock.emit('cursor', 'down');
mock.emit('cursor', 'space');

expect(mock.value).toStrictEqual(['@scope/a']);
});

it('should toggle multiple options', () => {
makeSut();

mock.emit('cursor', 'down');
mock.emit('cursor', 'space');
mock.emit('cursor', 'down');
mock.emit('cursor', 'space');

expect(mock.value).toStrictEqual(['@scope/a', '@scope/b']);
});

it('should untoggle option', () => {
makeSut();

mock.emit('cursor', 'down');
mock.emit('cursor', 'space');
mock.emit('cursor', 'space');

expect(mock.value).toStrictEqual([]);
});

it('should toggle group', () => {
makeSut();

mock.emit('cursor', 'space');

expect(mock.value).toStrictEqual(['@scope/a', '@scope/b', '@scope/c']);
});

it('should toggle multiple groups', () => {
makeSut();

mock.emit('cursor', 'space');
mock.emit('cursor', 'down');
mock.emit('cursor', 'down');
mock.emit('cursor', 'down');
mock.emit('cursor', 'down');
mock.emit('cursor', 'space');

expect(mock.value).toStrictEqual([
'@scope/a',
'@scope/b',
'@scope/c',
'@scope/x',
'@scope/y',
'@scope/z',
]);
});

it('should untoggle group', () => {
makeSut();

mock.emit('cursor', 'space');
mock.emit('cursor', 'space');

expect(mock.value).toStrictEqual([]);
});

it('should submit value', () => {
makeSut({
initialValues: ['changed packages'],
});

mock.submit();

expect(mock.state).toBe('submit');
expect(mock.value).toStrictEqual(['@scope/a', '@scope/b', '@scope/c']);
});
});
Loading

0 comments on commit c8a491b

Please sign in to comment.