Skip to content

Commit

Permalink
test: GroupMultiSelectPrompt
Browse files Browse the repository at this point in the history
  • Loading branch information
orochaa committed Sep 12, 2023
1 parent d1c6f56 commit 1f9b77b
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 6 deletions.
160 changes: 160 additions & 0 deletions packages/core/__tests__/prompts/group-multiselect.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { GroupMultiSelectPrompt, mockPrompt } from '../../src';
import { 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 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']);
});
});
37 changes: 31 additions & 6 deletions packages/core/src/prompts/group-multiselect.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import { exposeTestUtils } from '../utils';
import Prompt, { PromptOptions } from './prompt';

interface GroupMultiSelectOptions<T extends { value: any }>
export interface GroupMultiSelectOptions<T extends { value: any }>
extends PromptOptions<GroupMultiSelectPrompt<T>> {
options: Record<string, T[]>;
initialValues?: T['value'][];
required?: boolean;
cursorAt?: T['value'];
}

export default class GroupMultiSelectPrompt<T extends { value: any }> extends Prompt {
options: (T & { group: string | boolean })[];
cursor: number = 0;
public options: (T & { group: string | boolean })[];
public cursor: number;

getGroupItems(group: string): T[] {
return this.options.filter((o) => o.group === group);
}

isGroupSelected(group: string) {
isGroupSelected(group: string): boolean {
const items = this.getGroupItems(group);
return items.every((i) => this.value.includes(i.value));
}

private toggleValue() {
private toggleValue(): void {
const item = this.options[this.cursor];
if (item.group === true) {
const group = item.value;
Expand All @@ -43,17 +45,31 @@ export default class GroupMultiSelectPrompt<T extends { value: any }> extends Pr

constructor(opts: GroupMultiSelectOptions<T>) {
super(opts, false);

const { options } = opts;
this.options = Object.entries(options).flatMap(([key, option]) => [
{ value: key, group: true, label: key },
...option.map((opt) => ({ ...opt, group: key })),
]) as any;
this.value = [...(opts.initialValues ?? [])];

this.value = [];
if (opts.initialValues) {
for (const value of opts.initialValues) {
const optionIndex = this.options.findIndex((o) => o.value === value);
if (optionIndex !== -1) {
this.cursor = optionIndex;
this.toggleValue();
}
}
}

this.cursor = Math.max(
this.options.findIndex(({ value }) => value === opts.cursorAt),
0
);

this.exposeTestUtils();

this.on('cursor', (key) => {
switch (key) {
case 'left':
Expand All @@ -68,6 +84,15 @@ export default class GroupMultiSelectPrompt<T extends { value: any }> extends Pr
this.toggleValue();
break;
}
this.exposeTestUtils();
});
}

private exposeTestUtils() {
exposeTestUtils<GroupMultiSelectPrompt<any>>({
options: this.options,
cursor: this.cursor,
value: this.value,
});
}
}

0 comments on commit 1f9b77b

Please sign in to comment.