@whook/whook
Version:
Build strong and efficient REST web services.
2,097 lines (2,063 loc) • 52.5 kB
JavaScript
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, test, beforeEach, jest, expect } from '@jest/globals';
import initCreateCommand from './create.js';
import { definition as getPingDefinition } from '../routes/getPing.js';
describe('createCommand', () => {
const PROJECT_DIR = '/hom/whoiam/project';
const API = {
openapi: '3.1.0',
info: {
version: '1.0.0',
title: 'Sample OpenAPI',
description: 'A sample OpenAPI file for testing purpose.',
},
paths: {
[getPingDefinition.path]: {
[getPingDefinition.method]: getPingDefinition.operation,
},
},
tags: [{ name: 'system' }],
};
const ensureDir = jest.fn();
const writeFile = jest.fn();
const pathExists = jest.fn();
const inquirer = { prompt: jest.fn() };
const log = jest.fn();
beforeEach(() => {
ensureDir.mockReset();
writeFile.mockReset();
pathExists.mockReset();
inquirer.prompt.mockReset();
log.mockReset();
});
describe('for routes', () => {
test('should work with get and no dependencies', async () => {
inquirer.prompt.mockResolvedValueOnce({
services: [],
});
inquirer.prompt.mockResolvedValueOnce({
method: 'get',
path: '/lol',
description: 'yolo',
tags: [],
});
pathExists.mockResolvedValueOnce(false);
const createCommand = await initCreateCommand({
PROJECT_DIR,
API,
ensureDir,
writeFile,
pathExists,
inquirer: inquirer,
log,
});
const result = await createCommand({
command: 'whook',
rest: ['create'],
namedArguments: {
name: 'getHandler',
type: 'route',
},
});
expect({
result,
ensureDirCalls: ensureDir.mock.calls,
writeFileCalls: writeFile.mock.calls,
pathExistsCalls: pathExists.mock.calls,
inquirerPromptCalls: inquirer.prompt.mock.calls,
logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
}).toMatchInlineSnapshot(`
{
"ensureDirCalls": [
[
"/hom/whoiam/project/src/routes",
],
],
"inquirerPromptCalls": [
[
[
{
"choices": [
{
"value": "codeGenerator",
},
{
"value": "counter",
},
{
"value": "delay",
},
{
"value": "importer",
},
{
"value": "lock",
},
{
"value": "log",
},
{
"value": "random",
},
{
"value": "resolve",
},
{
"value": "time",
},
{
"value": "APP_CONFIG",
},
{
"value": "ENV",
},
{
"value": "process",
},
{
"value": "PROJECT_DIR",
},
{
"value": "MAIN_FILE_URL",
},
{
"value": "DEBUG_NODE_ENVS",
},
{
"value": "BASE_URL",
},
{
"value": "HOST",
},
{
"value": "PORT",
},
{
"value": "DEFINITIONS",
},
{
"value": "APM",
},
],
"message": "Which services do you want to use?",
"name": "services",
"type": "checkbox",
},
],
],
[
[
{
"choices": [
{
"name": "get",
"value": "get",
},
{
"name": "put",
"value": "put",
},
{
"name": "post",
"value": "post",
},
{
"name": "delete",
"value": "delete",
},
{
"name": "options",
"value": "options",
},
{
"name": "head",
"value": "head",
},
{
"name": "patch",
"value": "patch",
},
{
"name": "trace",
"value": "trace",
},
],
"default": "get",
"message": "Give the handler method",
"name": "method",
"type": "list",
},
{
"message": "Give the handler path",
"name": "path",
"type": "input",
},
{
"message": "Give the handler description",
"name": "description",
"type": "input",
},
{
"choices": [
{
"name": "system",
"value": "system",
},
],
"message": "Assign one or more tags to the handler",
"name": "tags",
"type": "checkbox",
},
],
],
],
"logCalls": [],
"pathExistsCalls": [
[
"/hom/whoiam/project/src/routes/getHandler.ts",
],
],
"result": undefined,
"writeFileCalls": [
[
"/hom/whoiam/project/src/routes/getHandler.ts",
"import { autoService, location } from 'knifecycle';
import {
type WhookRouteDefinition,
type WhookRouteTypedHandler,
} from '@whook/whook';
export const definition = {
path: '/lol',
method: 'get',
operation: {
operationId: 'getHandler',
summary: 'yolo',
tags: [],
parameters: [
{
name: 'param',
in: 'query',
required: false,
schema: { type: 'number' },
},
],
responses: {
200: {
description: 'Success',
content: {
'application/json': {
schema: {
type: 'object',
},
},
},
},
},
},
} as const satisfies WhookRouteDefinition;
export type HandlerDependencies = {};
async function initGetHandler(_: HandlerDependencies) {
const handler: WhookRouteTypedHandler<
operations[typeof definition.operation.operationId],
typeof definition
> = async ({
query: { param },
}) => {
return {
status: 200,
headers: {},
body: { param },
};
};
return handler;
}
export default location(
autoService(initGetHandler),
import.meta.url,
);
",
],
],
}
`);
});
test('should work with an existing get and dependencies but no erase allowed', async () => {
inquirer.prompt.mockResolvedValueOnce({
services: ['PROJECT_DIR', 'log', 'ENV'],
});
inquirer.prompt.mockResolvedValueOnce({
method: 'get',
path: '/lol',
description: 'yolo',
tags: [],
});
pathExists.mockResolvedValueOnce(true);
inquirer.prompt.mockResolvedValueOnce({
erase: false,
});
const createCommand = await initCreateCommand({
PROJECT_DIR,
API,
ensureDir,
writeFile,
pathExists,
inquirer: inquirer,
log,
});
const result = await createCommand({
command: 'whook',
rest: ['create'],
namedArguments: {
name: 'getHandler',
type: 'route',
},
});
expect({
result,
ensureDirCalls: ensureDir.mock.calls,
writeFileCalls: writeFile.mock.calls,
pathExistsCalls: pathExists.mock.calls,
inquirerPromptCalls: inquirer.prompt.mock.calls,
logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
}).toMatchInlineSnapshot(`
{
"ensureDirCalls": [
[
"/hom/whoiam/project/src/routes",
],
],
"inquirerPromptCalls": [
[
[
{
"choices": [
{
"value": "codeGenerator",
},
{
"value": "counter",
},
{
"value": "delay",
},
{
"value": "importer",
},
{
"value": "lock",
},
{
"value": "log",
},
{
"value": "random",
},
{
"value": "resolve",
},
{
"value": "time",
},
{
"value": "APP_CONFIG",
},
{
"value": "ENV",
},
{
"value": "process",
},
{
"value": "PROJECT_DIR",
},
{
"value": "MAIN_FILE_URL",
},
{
"value": "DEBUG_NODE_ENVS",
},
{
"value": "BASE_URL",
},
{
"value": "HOST",
},
{
"value": "PORT",
},
{
"value": "DEFINITIONS",
},
{
"value": "APM",
},
],
"message": "Which services do you want to use?",
"name": "services",
"type": "checkbox",
},
],
],
[
[
{
"choices": [
{
"name": "get",
"value": "get",
},
{
"name": "put",
"value": "put",
},
{
"name": "post",
"value": "post",
},
{
"name": "delete",
"value": "delete",
},
{
"name": "options",
"value": "options",
},
{
"name": "head",
"value": "head",
},
{
"name": "patch",
"value": "patch",
},
{
"name": "trace",
"value": "trace",
},
],
"default": "get",
"message": "Give the handler method",
"name": "method",
"type": "list",
},
{
"message": "Give the handler path",
"name": "path",
"type": "input",
},
{
"message": "Give the handler description",
"name": "description",
"type": "input",
},
{
"choices": [
{
"name": "system",
"value": "system",
},
],
"message": "Assign one or more tags to the handler",
"name": "tags",
"type": "checkbox",
},
],
],
[
[
{
"message": "Erase ?",
"name": "erase",
"type": "confirm",
},
],
],
],
"logCalls": [
[
"warning",
"⚠️ - The file already exists !",
],
],
"pathExistsCalls": [
[
"/hom/whoiam/project/src/routes/getHandler.ts",
],
],
"result": undefined,
"writeFileCalls": [],
}
`);
});
test('should work with an existing get and dependencies and erase allowed', async () => {
inquirer.prompt.mockResolvedValueOnce({
services: ['PROJECT_DIR', 'log', 'ENV'],
});
inquirer.prompt.mockResolvedValueOnce({
method: 'get',
path: '/lol',
description: 'yolo',
tags: [],
});
pathExists.mockResolvedValueOnce(true);
inquirer.prompt.mockResolvedValueOnce({
erase: true,
});
const createCommand = await initCreateCommand({
PROJECT_DIR,
API,
ensureDir,
writeFile,
pathExists,
inquirer: inquirer,
log,
});
const result = await createCommand({
command: 'whook',
rest: ['create'],
namedArguments: {
name: 'getHandler',
type: 'route',
},
});
expect({
result,
ensureDirCalls: ensureDir.mock.calls,
writeFileCalls: writeFile.mock.calls,
pathExistsCalls: pathExists.mock.calls,
inquirerPromptCalls: inquirer.prompt.mock.calls,
logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
}).toMatchInlineSnapshot(`
{
"ensureDirCalls": [
[
"/hom/whoiam/project/src/routes",
],
],
"inquirerPromptCalls": [
[
[
{
"choices": [
{
"value": "codeGenerator",
},
{
"value": "counter",
},
{
"value": "delay",
},
{
"value": "importer",
},
{
"value": "lock",
},
{
"value": "log",
},
{
"value": "random",
},
{
"value": "resolve",
},
{
"value": "time",
},
{
"value": "APP_CONFIG",
},
{
"value": "ENV",
},
{
"value": "process",
},
{
"value": "PROJECT_DIR",
},
{
"value": "MAIN_FILE_URL",
},
{
"value": "DEBUG_NODE_ENVS",
},
{
"value": "BASE_URL",
},
{
"value": "HOST",
},
{
"value": "PORT",
},
{
"value": "DEFINITIONS",
},
{
"value": "APM",
},
],
"message": "Which services do you want to use?",
"name": "services",
"type": "checkbox",
},
],
],
[
[
{
"choices": [
{
"name": "get",
"value": "get",
},
{
"name": "put",
"value": "put",
},
{
"name": "post",
"value": "post",
},
{
"name": "delete",
"value": "delete",
},
{
"name": "options",
"value": "options",
},
{
"name": "head",
"value": "head",
},
{
"name": "patch",
"value": "patch",
},
{
"name": "trace",
"value": "trace",
},
],
"default": "get",
"message": "Give the handler method",
"name": "method",
"type": "list",
},
{
"message": "Give the handler path",
"name": "path",
"type": "input",
},
{
"message": "Give the handler description",
"name": "description",
"type": "input",
},
{
"choices": [
{
"name": "system",
"value": "system",
},
],
"message": "Assign one or more tags to the handler",
"name": "tags",
"type": "checkbox",
},
],
],
[
[
{
"message": "Erase ?",
"name": "erase",
"type": "confirm",
},
],
],
],
"logCalls": [
[
"warning",
"⚠️ - The file already exists !",
],
],
"pathExistsCalls": [
[
"/hom/whoiam/project/src/routes/getHandler.ts",
],
],
"result": undefined,
"writeFileCalls": [
[
"/hom/whoiam/project/src/routes/getHandler.ts",
"import { autoService, location } from 'knifecycle';
import {
type WhookRouteDefinition,
type WhookRouteTypedHandler,
} from '@whook/whook';
import { type LogService } from 'common-services';
import { type AppEnvVars, type ProjectDirService } from 'application-services';
export const definition = {
path: '/lol',
method: 'get',
operation: {
operationId: 'getHandler',
summary: 'yolo',
tags: [],
parameters: [
{
name: 'param',
in: 'query',
required: false,
schema: { type: 'number' },
},
],
responses: {
200: {
description: 'Success',
content: {
'application/json': {
schema: {
type: 'object',
},
},
},
},
},
},
} as const satisfies WhookRouteDefinition;
export type HandlerDependencies = {
ENV: AppEnvVars;
PROJECT_DIR: ProjectDirService;
log: LogService;
};
async function initGetHandler({
ENV,
PROJECT_DIR,
log,
}: HandlerDependencies) {
const handler: WhookRouteTypedHandler<
operations[typeof definition.operation.operationId],
typeof definition
> = async ({
query: { param },
}) => {
return {
status: 200,
headers: {},
body: { param },
};
};
return handler;
}
export default location(
autoService(initGetHandler),
import.meta.url,
);
",
],
],
}
`);
});
});
describe('for services', () => {
test('should work with no dependencies', async () => {
inquirer.prompt.mockResolvedValueOnce({
services: [],
});
pathExists.mockResolvedValueOnce(false);
const createCommand = await initCreateCommand({
PROJECT_DIR,
API,
ensureDir,
writeFile,
pathExists,
inquirer: inquirer,
log,
});
const result = await createCommand({
command: 'whook',
rest: ['create'],
namedArguments: {
name: 'aService',
type: 'service',
},
});
expect({
result,
ensureDirCalls: ensureDir.mock.calls,
writeFileCalls: writeFile.mock.calls,
pathExistsCalls: pathExists.mock.calls,
inquirerPromptCalls: inquirer.prompt.mock.calls,
logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
}).toMatchInlineSnapshot(`
{
"ensureDirCalls": [
[
"/hom/whoiam/project/src/services",
],
],
"inquirerPromptCalls": [
[
[
{
"choices": [
{
"value": "codeGenerator",
},
{
"value": "counter",
},
{
"value": "delay",
},
{
"value": "importer",
},
{
"value": "lock",
},
{
"value": "log",
},
{
"value": "random",
},
{
"value": "resolve",
},
{
"value": "time",
},
{
"value": "APP_CONFIG",
},
{
"value": "ENV",
},
{
"value": "process",
},
{
"value": "PROJECT_DIR",
},
{
"value": "MAIN_FILE_URL",
},
{
"value": "DEBUG_NODE_ENVS",
},
{
"value": "BASE_URL",
},
{
"value": "HOST",
},
{
"value": "PORT",
},
{
"value": "DEFINITIONS",
},
{
"value": "APM",
},
],
"message": "Which services do you want to use?",
"name": "services",
"type": "checkbox",
},
],
],
],
"logCalls": [],
"pathExistsCalls": [
[
"/hom/whoiam/project/src/services/aService.ts",
],
],
"result": undefined,
"writeFileCalls": [
[
"/hom/whoiam/project/src/services/aService.ts",
"import { autoService, location } from 'knifecycle';
export type AServiceService = {};
export type AServiceDependencies = {};
async function initAService(_: AServiceDependencies): Promise<AServiceService> {
// Instantiate and return your service
return {};
}
export default location(
autoService(initAService),
import.meta.url,
);
",
],
],
}
`);
});
test('should work when existing with dependencies but no erase allowed', async () => {
inquirer.prompt.mockResolvedValueOnce({
services: ['PROJECT_DIR', 'log', 'ENV'],
});
pathExists.mockResolvedValueOnce(true);
inquirer.prompt.mockResolvedValueOnce({
erase: false,
});
const createCommand = await initCreateCommand({
PROJECT_DIR,
API,
ensureDir,
writeFile,
pathExists,
inquirer: inquirer,
log,
});
const result = await createCommand({
command: 'whook',
rest: ['create'],
namedArguments: {
name: 'aService',
type: 'service',
},
});
expect({
result,
ensureDirCalls: ensureDir.mock.calls,
writeFileCalls: writeFile.mock.calls,
pathExistsCalls: pathExists.mock.calls,
inquirerPromptCalls: inquirer.prompt.mock.calls,
logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
}).toMatchInlineSnapshot(`
{
"ensureDirCalls": [
[
"/hom/whoiam/project/src/services",
],
],
"inquirerPromptCalls": [
[
[
{
"choices": [
{
"value": "codeGenerator",
},
{
"value": "counter",
},
{
"value": "delay",
},
{
"value": "importer",
},
{
"value": "lock",
},
{
"value": "log",
},
{
"value": "random",
},
{
"value": "resolve",
},
{
"value": "time",
},
{
"value": "APP_CONFIG",
},
{
"value": "ENV",
},
{
"value": "process",
},
{
"value": "PROJECT_DIR",
},
{
"value": "MAIN_FILE_URL",
},
{
"value": "DEBUG_NODE_ENVS",
},
{
"value": "BASE_URL",
},
{
"value": "HOST",
},
{
"value": "PORT",
},
{
"value": "DEFINITIONS",
},
{
"value": "APM",
},
],
"message": "Which services do you want to use?",
"name": "services",
"type": "checkbox",
},
],
],
[
[
{
"message": "Erase ?",
"name": "erase",
"type": "confirm",
},
],
],
],
"logCalls": [
[
"warning",
"⚠️ - The file already exists !",
],
],
"pathExistsCalls": [
[
"/hom/whoiam/project/src/services/aService.ts",
],
],
"result": undefined,
"writeFileCalls": [],
}
`);
});
test('should work when existing with dependencies and erase allowed', async () => {
inquirer.prompt.mockResolvedValueOnce({
services: ['PROJECT_DIR', 'log', 'ENV'],
});
pathExists.mockResolvedValueOnce(true);
inquirer.prompt.mockResolvedValueOnce({
erase: true,
});
const createCommand = await initCreateCommand({
PROJECT_DIR,
API,
ensureDir,
writeFile,
pathExists,
inquirer: inquirer,
log,
});
const result = await createCommand({
command: 'whook',
rest: ['create'],
namedArguments: {
name: 'aService',
type: 'service',
},
});
expect({
result,
ensureDirCalls: ensureDir.mock.calls,
writeFileCalls: writeFile.mock.calls,
pathExistsCalls: pathExists.mock.calls,
inquirerPromptCalls: inquirer.prompt.mock.calls,
logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
}).toMatchInlineSnapshot(`
{
"ensureDirCalls": [
[
"/hom/whoiam/project/src/services",
],
],
"inquirerPromptCalls": [
[
[
{
"choices": [
{
"value": "codeGenerator",
},
{
"value": "counter",
},
{
"value": "delay",
},
{
"value": "importer",
},
{
"value": "lock",
},
{
"value": "log",
},
{
"value": "random",
},
{
"value": "resolve",
},
{
"value": "time",
},
{
"value": "APP_CONFIG",
},
{
"value": "ENV",
},
{
"value": "process",
},
{
"value": "PROJECT_DIR",
},
{
"value": "MAIN_FILE_URL",
},
{
"value": "DEBUG_NODE_ENVS",
},
{
"value": "BASE_URL",
},
{
"value": "HOST",
},
{
"value": "PORT",
},
{
"value": "DEFINITIONS",
},
{
"value": "APM",
},
],
"message": "Which services do you want to use?",
"name": "services",
"type": "checkbox",
},
],
],
[
[
{
"message": "Erase ?",
"name": "erase",
"type": "confirm",
},
],
],
],
"logCalls": [
[
"warning",
"⚠️ - The file already exists !",
],
],
"pathExistsCalls": [
[
"/hom/whoiam/project/src/services/aService.ts",
],
],
"result": undefined,
"writeFileCalls": [
[
"/hom/whoiam/project/src/services/aService.ts",
"import { autoService, location } from 'knifecycle';
import { type LogService } from 'common-services';
import { type AppEnvVars, type ProjectDirService } from 'application-services';
export type AServiceService = {};
export type AServiceDependencies = {
ENV: AppEnvVars;
PROJECT_DIR: ProjectDirService;
log: LogService;
};
async function initAService({
ENV,
PROJECT_DIR,
log,
}: AServiceDependencies): Promise<AServiceService> {
// Instantiate and return your service
return {};
}
export default location(
autoService(initAService),
import.meta.url,
);
",
],
],
}
`);
});
});
describe('for providers', () => {
test('should work with no dependencies', async () => {
inquirer.prompt.mockResolvedValueOnce({
services: [],
});
pathExists.mockResolvedValueOnce(false);
const createCommand = await initCreateCommand({
PROJECT_DIR,
API,
ensureDir,
writeFile,
pathExists,
inquirer: inquirer,
log,
});
const result = await createCommand({
command: 'whook',
rest: ['create'],
namedArguments: {
name: 'aProvider',
type: 'provider',
},
});
expect({
result,
ensureDirCalls: ensureDir.mock.calls,
writeFileCalls: writeFile.mock.calls,
pathExistsCalls: pathExists.mock.calls,
inquirerPromptCalls: inquirer.prompt.mock.calls,
logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
}).toMatchInlineSnapshot(`
{
"ensureDirCalls": [
[
"/hom/whoiam/project/src/services",
],
],
"inquirerPromptCalls": [
[
[
{
"choices": [
{
"value": "codeGenerator",
},
{
"value": "counter",
},
{
"value": "delay",
},
{
"value": "importer",
},
{
"value": "lock",
},
{
"value": "log",
},
{
"value": "random",
},
{
"value": "resolve",
},
{
"value": "time",
},
{
"value": "APP_CONFIG",
},
{
"value": "ENV",
},
{
"value": "process",
},
{
"value": "PROJECT_DIR",
},
{
"value": "MAIN_FILE_URL",
},
{
"value": "DEBUG_NODE_ENVS",
},
{
"value": "BASE_URL",
},
{
"value": "HOST",
},
{
"value": "PORT",
},
{
"value": "DEFINITIONS",
},
{
"value": "APM",
},
],
"message": "Which services do you want to use?",
"name": "services",
"type": "checkbox",
},
],
],
],
"logCalls": [],
"pathExistsCalls": [
[
"/hom/whoiam/project/src/services/aProvider.ts",
],
],
"result": undefined,
"writeFileCalls": [
[
"/hom/whoiam/project/src/services/aProvider.ts",
"import { autoProvider, location, type Provider } from 'knifecycle';
export type AProviderService = {};
export type AProviderProvider = Provider<AProviderService>;
export type AProviderDependencies = {};
async function initAProvider(_: AProviderDependencies): Promise<AProviderProvider> {
// Instantiate and return your service
return {
service: {},
dispose: async () => {
// Do any action before the process shutdown
// (closing db connections... etc)
},
// You can also set a promise for unexpected errors
// that shutdown the app when it happens
// errorPromise: new Promise(),
};
}
export default location(
autoProvider(initAProvider),
import.meta.url,
);
",
],
],
}
`);
});
test('should work when existing with dependencies but no erase allowed', async () => {
inquirer.prompt.mockResolvedValueOnce({
services: ['PROJECT_DIR', 'log', 'ENV'],
});
pathExists.mockResolvedValueOnce(true);
inquirer.prompt.mockResolvedValueOnce({
erase: false,
});
const createCommand = await initCreateCommand({
PROJECT_DIR,
API,
ensureDir,
writeFile,
pathExists,
inquirer: inquirer,
log,
});
const result = await createCommand({
command: 'whook',
rest: ['create'],
namedArguments: {
name: 'aProvider',
type: 'provider',
},
});
expect({
result,
ensureDirCalls: ensureDir.mock.calls,
writeFileCalls: writeFile.mock.calls,
pathExistsCalls: pathExists.mock.calls,
inquirerPromptCalls: inquirer.prompt.mock.calls,
logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
}).toMatchInlineSnapshot(`
{
"ensureDirCalls": [
[
"/hom/whoiam/project/src/services",
],
],
"inquirerPromptCalls": [
[
[
{
"choices": [
{
"value": "codeGenerator",
},
{
"value": "counter",
},
{
"value": "delay",
},
{
"value": "importer",
},
{
"value": "lock",
},
{
"value": "log",
},
{
"value": "random",
},
{
"value": "resolve",
},
{
"value": "time",
},
{
"value": "APP_CONFIG",
},
{
"value": "ENV",
},
{
"value": "process",
},
{
"value": "PROJECT_DIR",
},
{
"value": "MAIN_FILE_URL",
},
{
"value": "DEBUG_NODE_ENVS",
},
{
"value": "BASE_URL",
},
{
"value": "HOST",
},
{
"value": "PORT",
},
{
"value": "DEFINITIONS",
},
{
"value": "APM",
},
],
"message": "Which services do you want to use?",
"name": "services",
"type": "checkbox",
},
],
],
[
[
{
"message": "Erase ?",
"name": "erase",
"type": "confirm",
},
],
],
],
"logCalls": [
[
"warning",
"⚠️ - The file already exists !",
],
],
"pathExistsCalls": [
[
"/hom/whoiam/project/src/services/aProvider.ts",
],
],
"result": undefined,
"writeFileCalls": [],
}
`);
});
test('should work when existing with dependencies and erase allowed', async () => {
inquirer.prompt.mockResolvedValueOnce({
services: ['PROJECT_DIR', 'log', 'ENV'],
});
pathExists.mockResolvedValueOnce(true);
inquirer.prompt.mockResolvedValueOnce({
erase: true,
});
const createCommand = await initCreateCommand({
PROJECT_DIR,
API,
ensureDir,
writeFile,
pathExists,
inquirer: inquirer,
log,
});
const result = await createCommand({
command: 'whook',
rest: ['create'],
namedArguments: {
name: 'aProvider',
type: 'provider',
},
});
expect({
result,
ensureDirCalls: ensureDir.mock.calls,
writeFileCalls: writeFile.mock.calls,
pathExistsCalls: pathExists.mock.calls,
inquirerPromptCalls: inquirer.prompt.mock.calls,
logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
}).toMatchInlineSnapshot(`
{
"ensureDirCalls": [
[
"/hom/whoiam/project/src/services",
],
],
"inquirerPromptCalls": [
[
[
{
"choices": [
{
"value": "codeGenerator",
},
{
"value": "counter",
},
{
"value": "delay",
},
{
"value": "importer",
},
{
"value": "lock",
},
{
"value": "log",
},
{
"value": "random",
},
{
"value": "resolve",
},
{
"value": "time",
},
{
"value": "APP_CONFIG",
},
{
"value": "ENV",
},
{
"value": "process",
},
{
"value": "PROJECT_DIR",
},
{
"value": "MAIN_FILE_URL",
},
{
"value": "DEBUG_NODE_ENVS",
},
{
"value": "BASE_URL",
},
{
"value": "HOST",
},
{
"value": "PORT",
},
{
"value": "DEFINITIONS",
},
{
"value": "APM",
},
],
"message": "Which services do you want to use?",
"name": "services",
"type": "checkbox",
},
],
],
[
[
{
"message": "Erase ?",
"name": "erase",
"type": "confirm",
},
],
],
],
"logCalls": [
[
"warning",
"⚠️ - The file already exists !",
],
],
"pathExistsCalls": [
[
"/hom/whoiam/project/src/services/aProvider.ts",
],
],
"result": undefined,
"writeFileCalls": [
[
"/hom/whoiam/project/src/services/aProvider.ts",
"import { autoProvider, location, type Provider } from 'knifecycle';
import { type LogService } from 'common-services';
import { type AppEnvVars, type ProjectDirService } from 'application-services';
export type AProviderService = {};
export type AProviderProvider = Provider<AProviderService>;
export type AProviderDependencies = {
ENV: AppEnvVars;
PROJECT_DIR: ProjectDirService;
log: LogService;
};
async function initAProvider({
ENV,
PROJECT_DIR,
log,
}: AProviderDependencies): Promise<AProviderProvider> {
// Instantiate and return your service
return {
service: {},
dispose: async () => {
// Do any action before the process shutdown
// (closing db connections... etc)
},
// You can also set a promise for unexpected errors
// that shutdown the app when it happens
// errorPromise: new Promise(),
};
}
export default location(
autoProvider(initAProvider),
import.meta.url,
);
",
],
],
}
`);
});
});
describe('for commands', () => {
test('should work with no dependencies', async () => {
inquirer.prompt.mockResolvedValueOnce({
services: [],
});
inquirer.prompt.mockResolvedValueOnce({
description: 'yolo',
});
pathExists.mockResolvedValueOnce(false);
const createCommand = await initCreateCommand({
PROJECT_DIR,
API,
ensureDir,
writeFile,
pathExists,
inquirer: inquirer,
log,
});
const result = await createCommand({
command: 'whook',
rest: ['create'],
namedArguments: {
name: 'aCommand',
type: 'command',
},
});
expect({
result,
ensureDirCalls: ensureDir.mock.calls,
writeFileCalls: writeFile.mock.calls,
pathExistsCalls: pathExists.mock.calls,
inquirerPromptCalls: inquirer.prompt.mock.calls,
logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
}).toMatchInlineSnapshot(`
{
"ensureDirCalls": [
[
"/hom/whoiam/project/src/commands",
],
],
"inquirerPromptCalls": [
[
[
{
"choices": [
{
"value": "codeGenerator",
},
{
"value": "counter",
},
{
"value": "delay",
},
{
"value": "importer",
},
{
"value": "lock",
},
{
"value": "log",
},
{
"value": "random",
},
{
"value": "resolve",
},
{
"value": "time",
},
{
"value": "APP_CONFIG",
},
{
"value": "ENV",
},
{
"value": "process",
},
{
"value": "PROJECT_DIR",
},
{
"value": "MAIN_FILE_URL",
},
{
"value": "DEBUG_NODE_ENVS",
},
{
"value": "BASE_URL",
},
{
"value": "HOST",
},
{
"value": "PORT",
},
{
"value": "DEFINITIONS",
},
{
"value": "APM",
},
],
"message": "Which services do you want to use?",
"name": "services",
"type": "checkbox",
},
],
],
[
[
{
"message": "Give the command description",
"name": "description",
"type": "input",
},
],
],
],
"logCalls": [],
"pathExistsCalls": [
[
"/hom/whoiam/project/src/commands/aCommand.ts",
],
],
"result": undefined,
"writeFileCalls": [
[
"/hom/whoiam/project/src/commands/aCommand.ts",
"import { location, autoService } from 'knifecycle';
import {
type WhookCommand,
type WhookCommandDefinition,
} from '@whook/whook';
export const definition = {
name: 'aCommand',
description: 'yolo',
example: \`whook aCommand --param "value"\`,
arguments: [{
name: 'param',
required: true,
description: 'A parameter',
schema: {
type: 'string',
default: 'A default value',
},
}],
} as const satisfies WhookCommandDefinition;
async function initACommandCommand({
promptArgs,
}: {
promptArgs: WhookPromptArgs;
}): Promise<WhookCommand<{ param: string; }>> {
return async ({ param }) => {
// Implement your command here
}
}
export default location(
autoService(initACommandCommand),
import.meta.url,
);
",
],
],
}
`);
});
test('should work when existing with dependencies but no erase allowed', async () => {
inquirer.prompt.mockResolvedValueOnce({
services: ['PROJECT_DIR', 'log', 'ENV'],
});
inquirer.prompt.mockResolvedValueOnce({
description: 'yolo',
});
pathExists.mockResolvedValueOnce(true);
inquirer.prompt.mockResolvedValueOnce({
erase: false,
});
const createCommand = await initCreateCommand({
PROJECT_DIR,
API,
ensureDir,
writeFile,
pathExists,
inquirer: inquirer,
log,
});
const result = await createCommand({
command: 'whook',
rest: ['create'],
namedArguments: {
name: 'aCommand',
type: 'command',
},
});
expect({
result,
ensureDirCalls: ensureDir.mock.calls,
writeFileCalls: writeFile.mock.calls,
pathExistsCalls: pathExists.mock.calls,
inquirerPromptCalls: inquirer.prompt.mock.calls,
logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
}).toMatchInlineSnapshot(`
{
"ensureDirCalls": [
[
"/hom/whoiam/project/src/commands",
],
],
"inquirerPromptCalls": [
[
[
{
"choices": [
{
"value": "codeGenerator",
},
{
"value": "counter",
},
{
"value": "delay",
},
{
"value": "importer",
},
{
"value": "lock",
},
{
"value": "log",
},
{
"value": "random",
},
{
"value": "resolve",
},
{
"value": "time",
},
{
"value": "APP_CONFIG",
},
{
"value": "ENV",
},
{
"value": "process",
},
{
"value": "PROJECT_DIR",
},
{
"value": "MAIN_FILE_URL",
},
{
"value": "DEBUG_NODE_ENVS",
},
{
"value": "BASE_URL",
},
{
"value": "HOST",
},
{
"value": "PORT",
},
{
"value": "DEFINITIONS",
},
{
"value": "APM",
},
],
"message": "Which services do you want to use?",
"name": "services",
"type": "checkbox",
},
],
],
[
[
{
"message": "Give the command description",
"name": "description",
"type": "input",
},
],
],
[
[
{
"message": "Erase ?",
"name": "erase",
"type": "confirm",
},
],
],
],
"logCalls": [
[
"warning",
"⚠️ - The file already exists !",
],
],
"pathExistsCalls": [
[
"/hom/whoiam/project/src/commands/aCommand.ts",
],
],
"result": undefined,
"writeFileCalls": [],
}
`);
});
test('should work when existing with dependencies and erase allowed', async () => {
inquirer.prompt.mockResolvedValueOnce({
services: ['PROJECT_DIR', 'log', 'ENV'],
});
inquirer.prompt.mockResolvedValueOnce({
description: 'yolo',
});
pathExists.mockResolvedValueOnce(true);
inquirer.prompt.mockResolvedValueOnce({
erase: true,
});
const createCommand = await initCreateCommand({
PROJECT_DIR,
API,
ensureDir,
writeFile,
pathExists,
inquirer: inquirer,
log,
});
const result = await createCommand({
command: 'whook',
rest: ['create'],
namedArguments: {
name: 'aCommand',
type: 'command',
},
});
expect({
result,
ensureDirCalls: ensureDir.mock.calls,
writeFileCalls: writeFile.mock.calls,
pathExistsCalls: pathExists.mock.calls,
inquirerPromptCalls: inquirer.prompt.mock.calls,
logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
}).toMatchInlineSnapshot(`
{
"ensureDirCalls": [
[
"/hom/whoiam/project/src/commands",
],
],
"inquirerPromptCalls": [
[
[
{
"choices": [
{
"value": "codeGenerator",
},
{
"value": "counter",
},
{
"value": "delay",
},
{
"value": "importer",
},
{
"value": "lock",
},
{
"value": "log",
},
{
"value": "random",
},
{
"value": "resolve",
},
{
"value": "time",
},
{
"value": "APP_CONFIG",
},
{
"value": "ENV",