Compare commits
4 Commits
129c58fb08
...
1e7dbea129
| Author | SHA1 | Date |
|---|---|---|
|
|
1e7dbea129 | |
|
|
b3ff589aa5 | |
|
|
21b91edc1a | |
|
|
e673f60657 |
|
|
@ -4,14 +4,13 @@ import type {
|
|||
CommandRegistry,
|
||||
PromptEvent,
|
||||
CommandRunnerContextExport,
|
||||
CommandResult
|
||||
} from '@/utils/command';
|
||||
import type { MutableSignal } from '@/utils/mutable-signal';
|
||||
import {createGameContext, IGameContext} from './game';
|
||||
import {createGameCommandRegistry, createGameContext, IGameContext} from './game';
|
||||
|
||||
export type GameHostStatus = 'created' | 'running' | 'disposed';
|
||||
|
||||
export class GameHost<TState extends Record<string, unknown>> {
|
||||
export class GameHost<TState extends Record<string, unknown>, TResult=unknown> {
|
||||
readonly context: IGameContext<TState>;
|
||||
readonly status: ReadonlySignal<GameHostStatus>;
|
||||
readonly activePromptSchema: ReadonlySignal<CommandSchema | null>;
|
||||
|
|
@ -19,6 +18,7 @@ export class GameHost<TState extends Record<string, unknown>> {
|
|||
|
||||
private _state: MutableSignal<TState>;
|
||||
private _commands: CommandRunnerContextExport<IGameContext<TState>>;
|
||||
private _start: (ctx: IGameContext<TState>) => Promise<TResult>;
|
||||
private _status: Signal<GameHostStatus>;
|
||||
private _activePromptSchema: Signal<CommandSchema | null>;
|
||||
private _activePromptPlayer: Signal<string | null>;
|
||||
|
|
@ -29,12 +29,14 @@ export class GameHost<TState extends Record<string, unknown>> {
|
|||
constructor(
|
||||
registry: CommandRegistry<IGameContext<TState>>,
|
||||
createInitialState: () => TState,
|
||||
start: (ctx: IGameContext<TState>) => Promise<TResult>
|
||||
) {
|
||||
this._createInitialState = createInitialState;
|
||||
this._eventListeners = new Map();
|
||||
|
||||
const initialState = createInitialState();
|
||||
this.context = createGameContext(registry, initialState);
|
||||
this._start = start;
|
||||
|
||||
this._status = new Signal<GameHostStatus>('created');
|
||||
this.status = this._status;
|
||||
|
|
@ -94,7 +96,7 @@ export class GameHost<TState extends Record<string, unknown>> {
|
|||
this._state.clearInterruptions();
|
||||
}
|
||||
|
||||
start(startCommand: string): Promise<CommandResult<unknown>> {
|
||||
start(): Promise<TResult> {
|
||||
if (this._isDisposed) {
|
||||
throw new Error('GameHost is disposed');
|
||||
}
|
||||
|
|
@ -104,7 +106,7 @@ export class GameHost<TState extends Record<string, unknown>> {
|
|||
const initialState = this._createInitialState();
|
||||
this._state.value = initialState as any;
|
||||
|
||||
const promise = this._commands.run(startCommand);
|
||||
const promise = this._start(this.context);
|
||||
|
||||
this._status.value = 'running';
|
||||
this._emitEvent('start');
|
||||
|
|
@ -147,16 +149,18 @@ export class GameHost<TState extends Record<string, unknown>> {
|
|||
}
|
||||
}
|
||||
|
||||
export type GameModule<TState extends Record<string, unknown>> = {
|
||||
registry: CommandRegistry<IGameContext<TState>>;
|
||||
export type GameModule<TState extends Record<string, unknown>, TResult=unknown> = {
|
||||
registry?: CommandRegistry<IGameContext<TState>>;
|
||||
createInitialState: () => TState;
|
||||
start: (ctx: IGameContext<TState>) => Promise<TResult>;
|
||||
}
|
||||
|
||||
export function createGameHost<TState extends Record<string, unknown>>(
|
||||
gameModule: GameModule<TState>
|
||||
): GameHost<TState> {
|
||||
return new GameHost(
|
||||
gameModule.registry,
|
||||
gameModule.registry || createGameCommandRegistry(),
|
||||
gameModule.createInitialState,
|
||||
gameModule.start
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export interface IGameContext<TState extends Record<string, unknown> = {} > {
|
|||
produceAsync(fn: (draft: TState) => void): Promise<void>;
|
||||
run<T>(input: string): Promise<CommandResult<T>>;
|
||||
runParsed<T>(command: Command): Promise<CommandResult<T>>;
|
||||
prompt<T>(schema: CommandSchema | string, validator: PromptValidator<T>, currentPlayer?: string | null): Promise<T>;
|
||||
prompt: <TResult,TArgs extends any[]=any[]>(schema: CommandSchema | string, validator: PromptValidator<TResult,TArgs>, currentPlayer?: string | null) => Promise<TResult>;
|
||||
addInterruption(promise: Promise<void>): void;
|
||||
|
||||
// test only
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ const checkGraduates = registry.register({
|
|||
run: handleCheckGraduates
|
||||
});
|
||||
|
||||
async function handleStart(game: BoopGame) {
|
||||
export async function start(game: BoopGame) {
|
||||
while (true) {
|
||||
const currentPlayer = game.value.currentPlayer;
|
||||
const turnOutput = await turn(game, currentPlayer);
|
||||
|
|
@ -175,10 +175,6 @@ async function handleStart(game: BoopGame) {
|
|||
|
||||
return game.value;
|
||||
}
|
||||
const start = registry.register({
|
||||
schema: 'start',
|
||||
run: handleStart
|
||||
});
|
||||
|
||||
async function handleCheckFullBoard(game: BoopGame, turnPlayer: PlayerType){
|
||||
// 检查8-piece规则: 如果玩家所有8个棋子都在棋盘上且没有获胜,强制升级一个小猫
|
||||
|
|
@ -191,8 +187,7 @@ async function handleCheckFullBoard(game: BoopGame, turnPlayer: PlayerType){
|
|||
|
||||
const partId = await game.prompt(
|
||||
'choose <player> <row:number> <col:number>',
|
||||
(command) => {
|
||||
const [player, row, col] = command.params as [PlayerType, number, number];
|
||||
(player: PlayerType, row: number, col: number) => {
|
||||
if (player !== turnPlayer) {
|
||||
throw `Invalid player: ${player}. Expected ${turnPlayer}.`;
|
||||
}
|
||||
|
|
@ -224,8 +219,7 @@ const checkFullBoard = registry.register({
|
|||
async function handleTurn(game: BoopGame, turnPlayer: PlayerType) {
|
||||
const {row, col, type} = await game.prompt(
|
||||
'play <player> <row:number> <col:number> [type:string]',
|
||||
(command) => {
|
||||
const [player, row, col, type] = command.params as [PlayerType, number, number, PieceType?];
|
||||
(player: PlayerType, row: number, col: number, type?: PieceType) => {
|
||||
const pieceType = type === 'cat' ? 'cat' : 'kitten';
|
||||
|
||||
if (player !== turnPlayer) {
|
||||
|
|
|
|||
|
|
@ -36,9 +36,7 @@ export type TicTacToeState = ReturnType<typeof createInitialState>;
|
|||
export type TicTacToeGame = IGameContext<TicTacToeState>;
|
||||
export const registry = createGameCommandRegistry<TicTacToeState>();
|
||||
|
||||
registry.register({
|
||||
schema: 'start',
|
||||
async run(game: TicTacToeGame) {
|
||||
export async function start(game: TicTacToeGame) {
|
||||
while (true) {
|
||||
const currentPlayer = game.value.currentPlayer;
|
||||
const turnNumber = game.value.turn + 1;
|
||||
|
|
@ -56,16 +54,13 @@ registry.register({
|
|||
|
||||
return game.value;
|
||||
}
|
||||
});
|
||||
|
||||
const turn = registry.register({
|
||||
schema: 'turn <player> <turnNumber:number>',
|
||||
async run(game: TicTacToeGame, turnPlayer: PlayerType, turnNumber: number) {
|
||||
const {player, row, col} = await game.prompt(
|
||||
'play <player> <row:number> <col:number>',
|
||||
(command) => {
|
||||
const [player, row, col] = command.params as [PlayerType, number, number];
|
||||
|
||||
(player: string, row: number, col: number) => {
|
||||
if (player !== turnPlayer) {
|
||||
throw `Invalid player: ${player}. Expected ${turnPlayer}.`;
|
||||
} else if (!isValidMove(row, col)) {
|
||||
|
|
|
|||
|
|
@ -147,11 +147,11 @@ export function createCommandRunnerContext<TContext>(
|
|||
}
|
||||
};
|
||||
|
||||
const prompt = <T>(
|
||||
const prompt = <TResult,TArgs extends any[]=any[]>(
|
||||
schema: CommandSchema | string,
|
||||
validator: PromptValidator<T>,
|
||||
validator: PromptValidator<TResult,TArgs>,
|
||||
currentPlayer?: string | null
|
||||
): Promise<T> => {
|
||||
): Promise<TResult> => {
|
||||
const resolvedSchema = typeof schema === 'string' ? parseCommandSchema(schema) : schema;
|
||||
return new Promise((resolve, reject) => {
|
||||
const tryCommit = (commandOrInput: Command | string) => {
|
||||
|
|
@ -161,7 +161,7 @@ export function createCommandRunnerContext<TContext>(
|
|||
return schemaResult.errors.join('; ');
|
||||
}
|
||||
try{
|
||||
const result = validator(schemaResult.command);
|
||||
const result = validator(...schemaResult.command.params as TArgs);
|
||||
resolve(result);
|
||||
return null;
|
||||
}catch(e){
|
||||
|
|
|
|||
|
|
@ -31,13 +31,13 @@ export type CommandResult<T=unknown> = {
|
|||
error: string;
|
||||
}
|
||||
|
||||
export type PromptValidator<T> = (command: Command) => T;
|
||||
export type PromptValidator<TResult,TArgs extends any[]=any[]> = (...params: TArgs) => TResult;
|
||||
|
||||
export type CommandRunnerContext<TContext> = {
|
||||
context: TContext;
|
||||
run: <T=unknown>(input: string) => Promise<CommandResult<T>>;
|
||||
runParsed: <T=unknown>(command: Command) => Promise<CommandResult<T>>;
|
||||
prompt: <T>(schema: CommandSchema | string, validator: PromptValidator<T>, currentPlayer?: string | null) => Promise<T>;
|
||||
prompt: <TResult,TArgs extends any[]=any[]>(schema: CommandSchema | string, validator: PromptValidator<TResult,TArgs>, currentPlayer?: string | null) => Promise<TResult>;
|
||||
on: <T extends keyof CommandRunnerEvents>(event: T, listener: (e: CommandRunnerEvents[T]) => void) => void;
|
||||
off: <T extends keyof CommandRunnerEvents>(event: T, listener: (e: CommandRunnerEvents[T]) => void) => void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest';
|
|||
import {
|
||||
registry,
|
||||
createInitialState,
|
||||
start,
|
||||
TicTacToeState,
|
||||
WinnerType,
|
||||
PlayerType
|
||||
|
|
@ -12,7 +13,7 @@ import { MutableSignal } from '@/utils/mutable-signal';
|
|||
|
||||
function createTestHost() {
|
||||
const host = createGameHost(
|
||||
{ registry, createInitialState }
|
||||
{ registry, createInitialState, start }
|
||||
);
|
||||
return { host };
|
||||
}
|
||||
|
|
@ -59,7 +60,7 @@ describe('GameHost', () => {
|
|||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
expect(promptEvent.schema.name).toBe('play');
|
||||
|
|
@ -73,15 +74,19 @@ describe('GameHost', () => {
|
|||
const nextPrompt = await nextPromptPromise;
|
||||
nextPrompt.cancel('test cleanup');
|
||||
|
||||
const result = await runPromise;
|
||||
expect(result.success).toBe(false); // Cancelled
|
||||
try {
|
||||
await runPromise;
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
expect(error.message).toBe('test cleanup');
|
||||
}
|
||||
});
|
||||
|
||||
it('should reject invalid input', async () => {
|
||||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
|
||||
|
|
@ -89,7 +94,12 @@ describe('GameHost', () => {
|
|||
expect(error).not.toBeNull();
|
||||
|
||||
promptEvent.cancel('test cleanup');
|
||||
try {
|
||||
await runPromise;
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
expect(error.message).toBe('test cleanup');
|
||||
}
|
||||
});
|
||||
|
||||
it('should return error when disposed', () => {
|
||||
|
|
@ -106,7 +116,7 @@ describe('GameHost', () => {
|
|||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
const schema = host.activePromptSchema.value;
|
||||
|
|
@ -115,7 +125,12 @@ describe('GameHost', () => {
|
|||
expect(schema?.name).toBe('play');
|
||||
|
||||
promptEvent.cancel('test cleanup');
|
||||
try {
|
||||
await runPromise;
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
expect(error.message).toBe('test cleanup');
|
||||
}
|
||||
});
|
||||
|
||||
it('should return null when no prompt is active', () => {
|
||||
|
|
@ -131,7 +146,7 @@ describe('GameHost', () => {
|
|||
|
||||
// First setup - make one move
|
||||
let promptPromise = waitForPromptEvent(host);
|
||||
let runPromise = host.context._commands.run('start');
|
||||
let runPromise = host.start();
|
||||
let promptEvent = await promptPromise;
|
||||
|
||||
// Make a move
|
||||
|
|
@ -142,15 +157,19 @@ describe('GameHost', () => {
|
|||
promptEvent = await promptPromise;
|
||||
promptEvent.cancel('test end');
|
||||
|
||||
let result = await runPromise;
|
||||
expect(result.success).toBe(false); // Cancelled
|
||||
try {
|
||||
await runPromise;
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
expect(error.message).toBe('test end');
|
||||
}
|
||||
expect(Object.keys(host.context._state.value.parts).length).toBe(1);
|
||||
|
||||
// Setup listener before calling start
|
||||
const newPromptPromise = waitForPromptEvent(host);
|
||||
|
||||
// Reset - should reset state and start new game
|
||||
host.start('start');
|
||||
const newRunPromise = host.start();
|
||||
|
||||
// State should be back to initial
|
||||
expect(host.context._state.value.currentPlayer).toBe('X');
|
||||
|
|
@ -162,18 +181,24 @@ describe('GameHost', () => {
|
|||
const newPrompt = await newPromptPromise;
|
||||
expect(newPrompt.schema.name).toBe('play');
|
||||
newPrompt.cancel('test end');
|
||||
|
||||
try {
|
||||
await newRunPromise;
|
||||
} catch {
|
||||
// Expected - cancelled
|
||||
}
|
||||
});
|
||||
|
||||
it('should cancel active prompt during start', async () => {
|
||||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
await promptPromise;
|
||||
|
||||
// Setup should cancel the active prompt and reset state
|
||||
host.start('start');
|
||||
host.start();
|
||||
|
||||
// The original runPromise should be rejected due to cancellation
|
||||
try {
|
||||
|
|
@ -192,7 +217,7 @@ describe('GameHost', () => {
|
|||
const { host } = createTestHost();
|
||||
host.dispose();
|
||||
|
||||
expect(() => host.start('start')).toThrow('GameHost is disposed');
|
||||
expect(() => host.start()).toThrow('GameHost is disposed');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -208,7 +233,7 @@ describe('GameHost', () => {
|
|||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
await promptPromise;
|
||||
|
||||
|
|
@ -245,7 +270,7 @@ describe('GameHost', () => {
|
|||
const promptPromise = waitForPromptEvent(host);
|
||||
|
||||
// Initial setup via reset
|
||||
host.start('start');
|
||||
const runPromise = host.start();
|
||||
expect(setupCount).toBe(1);
|
||||
|
||||
// State should be running
|
||||
|
|
@ -254,6 +279,12 @@ describe('GameHost', () => {
|
|||
// Cancel the background setup command
|
||||
const prompt = await promptPromise;
|
||||
prompt.cancel('test end');
|
||||
|
||||
try {
|
||||
await runPromise;
|
||||
} catch {
|
||||
// Expected - cancelled
|
||||
}
|
||||
});
|
||||
|
||||
it('should emit dispose event', () => {
|
||||
|
|
@ -294,7 +325,7 @@ describe('GameHost', () => {
|
|||
|
||||
// Make a move
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
promptEvent.tryCommit({ name: 'play', params: ['X', 1, 1], options: {}, flags: {} });
|
||||
|
|
@ -304,8 +335,12 @@ describe('GameHost', () => {
|
|||
const nextPrompt = await nextPromptPromise;
|
||||
nextPrompt.cancel('test end');
|
||||
|
||||
const result = await runPromise;
|
||||
expect(result.success).toBe(false); // Cancelled
|
||||
try {
|
||||
await runPromise;
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
expect(error.message).toBe('test end');
|
||||
}
|
||||
|
||||
expect(host.context._state.value.currentPlayer).toBe('O');
|
||||
expect(host.context._state.value.turn).toBe(1);
|
||||
|
|
@ -320,7 +355,7 @@ describe('GameHost', () => {
|
|||
|
||||
// Start a command that triggers prompt
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
await promptPromise;
|
||||
|
||||
|
|
@ -369,7 +404,7 @@ describe('GameHost', () => {
|
|||
});
|
||||
|
||||
// Start setup command (runs game loop until completion)
|
||||
const setupPromise = host.context._commands.run('start');
|
||||
const setupPromise = host.start();
|
||||
|
||||
for (let i = 0; i < moves.length; i++) {
|
||||
// Wait until the next prompt event arrives
|
||||
|
|
@ -383,14 +418,15 @@ describe('GameHost', () => {
|
|||
// Submit the move
|
||||
const error = host.onInput(moves[i]);
|
||||
expect(error).toBeNull();
|
||||
|
||||
// Wait for the command to complete before submitting next move
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
}
|
||||
|
||||
// Wait for setup to complete (game ended with winner)
|
||||
const result = await setupPromise;
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.result.winner).toBe('X');
|
||||
}
|
||||
try {
|
||||
const finalState = await setupPromise;
|
||||
expect(finalState.winner).toBe('X');
|
||||
|
||||
// Final state checks
|
||||
expect(host.context._state.value.winner).toBe('X');
|
||||
|
|
@ -404,6 +440,13 @@ describe('GameHost', () => {
|
|||
expect(xPieces.some(p => JSON.stringify(p.position) === JSON.stringify([0, 0]))).toBe(true);
|
||||
expect(xPieces.some(p => JSON.stringify(p.position) === JSON.stringify([1, 1]))).toBe(true);
|
||||
expect(xPieces.some(p => JSON.stringify(p.position) === JSON.stringify([2, 2]))).toBe(true);
|
||||
} catch (e) {
|
||||
// If setup fails due to cancellation, check state directly
|
||||
const error = e as Error;
|
||||
if (!error.message.includes('Cancelled')) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
host.dispose();
|
||||
expect(host.status.value).toBe('disposed');
|
||||
|
|
@ -415,14 +458,19 @@ describe('GameHost', () => {
|
|||
const { host } = createTestHost();
|
||||
|
||||
const promptPromise = waitForPromptEvent(host);
|
||||
const runPromise = host.context._commands.run('start');
|
||||
const runPromise = host.start();
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
expect(promptEvent.currentPlayer).toBe('X');
|
||||
expect(host.activePromptPlayer.value).toBe('X');
|
||||
|
||||
promptEvent.cancel('test cleanup');
|
||||
try {
|
||||
await runPromise;
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
expect(error.message).toBe('test cleanup');
|
||||
}
|
||||
});
|
||||
|
||||
it('should update activePromptPlayer reactively', async () => {
|
||||
|
|
@ -433,7 +481,7 @@ describe('GameHost', () => {
|
|||
|
||||
// First prompt - X's turn
|
||||
let promptPromise = waitForPromptEvent(host);
|
||||
let runPromise = host.context._commands.run('start');
|
||||
let runPromise = host.start();
|
||||
let promptEvent = await promptPromise;
|
||||
expect(promptEvent.currentPlayer).toBe('X');
|
||||
expect(host.activePromptPlayer.value).toBe('X');
|
||||
|
|
@ -449,7 +497,12 @@ describe('GameHost', () => {
|
|||
|
||||
// Cancel
|
||||
promptEvent.cancel('test cleanup');
|
||||
try {
|
||||
await runPromise;
|
||||
} catch (e) {
|
||||
const error = e as Error;
|
||||
expect(error.message).toBe('test cleanup');
|
||||
}
|
||||
|
||||
// After prompt ends, player should be null
|
||||
expect(host.activePromptPlayer.value).toBeNull();
|
||||
|
|
|
|||
|
|
@ -173,18 +173,17 @@ describe('TicTacToe - helper functions', () => {
|
|||
});
|
||||
|
||||
describe('TicTacToe - game flow', () => {
|
||||
it('should have setup and turn commands registered', () => {
|
||||
it('should have turn command registered', () => {
|
||||
const { registry: reg } = createTestContext();
|
||||
|
||||
expect(reg.has('start')).toBe(true);
|
||||
expect(reg.has('turn')).toBe(true);
|
||||
});
|
||||
|
||||
it('should setup board when setup command runs', async () => {
|
||||
it('should setup board when turn command runs', async () => {
|
||||
const { ctx } = createTestContext();
|
||||
|
||||
const promptPromise = waitForPrompt(ctx);
|
||||
const runPromise = ctx.run('start');
|
||||
const runPromise = ctx.run('turn X 1');
|
||||
|
||||
const promptEvent = await promptPromise;
|
||||
expect(promptEvent).not.toBeNull();
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ describe('prompt', () => {
|
|||
const chooseRunner: CommandRunner<TestContext, string> = {
|
||||
schema: parseCommandSchema('choose'),
|
||||
run: async function () {
|
||||
const result = await this.prompt('select <card>', (cmd) => cmd.params[0] as string);
|
||||
const result = await this.prompt('select <card>', (card) => card as string);
|
||||
this.context.log.push(`selected ${result}`);
|
||||
return result;
|
||||
},
|
||||
|
|
@ -317,7 +317,7 @@ describe('prompt', () => {
|
|||
schema: parseCommandSchema('choose'),
|
||||
run: async function () {
|
||||
try {
|
||||
await this.prompt('select <card>', (cmd) => cmd.params[0] as string);
|
||||
await this.prompt('select <card>', (card) => card as string);
|
||||
return 'unexpected success';
|
||||
} catch (e) {
|
||||
return (e as Error).message;
|
||||
|
|
@ -356,7 +356,7 @@ describe('prompt', () => {
|
|||
const pickRunner: CommandRunner<TestContext, string> = {
|
||||
schema: parseCommandSchema('pick'),
|
||||
run: async function () {
|
||||
const result = await this.prompt(schema, (cmd) => cmd.params[0] as string);
|
||||
const result = await this.prompt(schema, (item) => item as string);
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
|
@ -393,8 +393,8 @@ describe('prompt', () => {
|
|||
const multiPromptRunner: CommandRunner<TestContext, string[]> = {
|
||||
schema: parseCommandSchema('multi'),
|
||||
run: async function () {
|
||||
const first = await this.prompt('first <a>', (cmd) => cmd.params[0] as string);
|
||||
const second = await this.prompt('second <b>', (cmd) => cmd.params[0] as string);
|
||||
const first = await this.prompt('first <a>', (a) => a as string);
|
||||
const second = await this.prompt('second <b>', (b) => b as string);
|
||||
return [first, second];
|
||||
},
|
||||
};
|
||||
|
|
@ -440,12 +440,12 @@ describe('prompt', () => {
|
|||
run: async function () {
|
||||
const result = await this.prompt(
|
||||
'select <card>',
|
||||
(cmd) => {
|
||||
const card = cmd.params[0] as string;
|
||||
if (!['Ace', 'King', 'Queen'].includes(card)) {
|
||||
throw `Invalid card: ${card}. Must be Ace, King, or Queen.`;
|
||||
(card) => {
|
||||
const cardStr = card as string;
|
||||
if (!['Ace', 'King', 'Queen'].includes(cardStr)) {
|
||||
throw `Invalid card: ${cardStr}. Must be Ace, King, or Queen.`;
|
||||
}
|
||||
return card;
|
||||
return cardStr;
|
||||
}
|
||||
);
|
||||
return result;
|
||||
|
|
|
|||
Loading…
Reference in New Issue