From 074c50fb7fb786c63614303c7f1f4b54d30c2233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Chalk?= Date: Fri, 16 Aug 2024 15:37:37 +0200 Subject: [PATCH] feat(cli): scaffold merge-diffs command and test argument parsing --- .../tests/__snapshots__/help.e2e.test.ts.snap | 2 + packages/cli/README.md | 14 ++++ packages/cli/src/lib/commands.ts | 2 + .../lib/implementation/merge-diffs.model.ts | 3 + .../lib/implementation/merge-diffs.options.ts | 15 ++++ .../lib/merge-diffs/merge-diffs-command.ts | 30 ++++++++ .../merge-diffs-command.unit.test.ts | 72 +++++++++++++++++++ .../cli/src/lib/yargs-cli.integration.test.ts | 26 +++++++ packages/core/src/index.ts | 1 + packages/core/src/lib/merge-diffs.ts | 13 ++++ 10 files changed, 178 insertions(+) create mode 100644 packages/cli/src/lib/implementation/merge-diffs.model.ts create mode 100644 packages/cli/src/lib/implementation/merge-diffs.options.ts create mode 100644 packages/cli/src/lib/merge-diffs/merge-diffs-command.ts create mode 100644 packages/cli/src/lib/merge-diffs/merge-diffs-command.unit.test.ts create mode 100644 packages/core/src/lib/merge-diffs.ts diff --git a/e2e/cli-e2e/tests/__snapshots__/help.e2e.test.ts.snap b/e2e/cli-e2e/tests/__snapshots__/help.e2e.test.ts.snap index 7b4fbc71d..079546988 100644 --- a/e2e/cli-e2e/tests/__snapshots__/help.e2e.test.ts.snap +++ b/e2e/cli-e2e/tests/__snapshots__/help.e2e.test.ts.snap @@ -14,6 +14,8 @@ Commands: code-pushup compare Compare 2 report files and create a diff file code-pushup print-config Print config + code-pushup merge-diffs Combine many report diffs into single report-diff.md + Global Options: --progress Show progress bar in stdout. diff --git a/packages/cli/README.md b/packages/cli/README.md index a4fe15b45..42dca2a5b 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -285,3 +285,17 @@ Description: Print the resolved configuration. Refer to the [Common Command Options](#common-command-options) for the list of available options. + +### `merge-diffs` command + +Usage: +`code-pushup merge-diffs --files PATH_1 PATH_2 ... [options]` + +Description: +Combine multiple report diffs into a single `report-diff.md`. + +In addition to the [Common Command Options](#common-command-options), the following options are recognized by the `merge-diffs` command: + +| Option | Required | Type | Description | +| ------------- | :------: | ---------- | --------------------------------- | +| **`--files`** | yes | `string[]` | List of `report-diff.json` paths. | diff --git a/packages/cli/src/lib/commands.ts b/packages/cli/src/lib/commands.ts index dad41d736..46e4346c6 100644 --- a/packages/cli/src/lib/commands.ts +++ b/packages/cli/src/lib/commands.ts @@ -3,6 +3,7 @@ import { yargsAutorunCommandObject } from './autorun/autorun-command'; import { yargsCollectCommandObject } from './collect/collect-command'; import { yargsCompareCommandObject } from './compare/compare-command'; import { yargsHistoryCommandObject } from './history/history-command'; +import { yargsMergeDiffsCommandObject } from './merge-diffs/merge-diffs-command'; import { yargsConfigCommandObject } from './print-config/print-config-command'; import { yargsUploadCommandObject } from './upload/upload-command'; @@ -17,4 +18,5 @@ export const commands: CommandModule[] = [ yargsHistoryCommandObject(), yargsCompareCommandObject(), yargsConfigCommandObject(), + yargsMergeDiffsCommandObject(), ]; diff --git a/packages/cli/src/lib/implementation/merge-diffs.model.ts b/packages/cli/src/lib/implementation/merge-diffs.model.ts new file mode 100644 index 000000000..646b85456 --- /dev/null +++ b/packages/cli/src/lib/implementation/merge-diffs.model.ts @@ -0,0 +1,3 @@ +export type MergeDiffsOptions = { + files: string[]; +}; diff --git a/packages/cli/src/lib/implementation/merge-diffs.options.ts b/packages/cli/src/lib/implementation/merge-diffs.options.ts new file mode 100644 index 000000000..a8dfe624f --- /dev/null +++ b/packages/cli/src/lib/implementation/merge-diffs.options.ts @@ -0,0 +1,15 @@ +import { Options } from 'yargs'; +import type { MergeDiffsOptions } from './merge-diffs.model'; + +export function yargsMergeDiffsOptionsDefinition(): Record< + keyof MergeDiffsOptions, + Options +> { + return { + files: { + describe: 'List of report-diff.json paths', + type: 'array', + demandOption: true, + }, + }; +} diff --git a/packages/cli/src/lib/merge-diffs/merge-diffs-command.ts b/packages/cli/src/lib/merge-diffs/merge-diffs-command.ts new file mode 100644 index 000000000..13fbe97bf --- /dev/null +++ b/packages/cli/src/lib/merge-diffs/merge-diffs-command.ts @@ -0,0 +1,30 @@ +import { bold, gray } from 'ansis'; +import { CommandModule } from 'yargs'; +import { mergeDiffs } from '@code-pushup/core'; +import { PersistConfig } from '@code-pushup/models'; +import { ui } from '@code-pushup/utils'; +import { CLI_NAME } from '../constants'; +import { MergeDiffsOptions } from '../implementation/merge-diffs.model'; +import { yargsMergeDiffsOptionsDefinition } from '../implementation/merge-diffs.options'; + +export function yargsMergeDiffsCommandObject() { + const command = 'merge-diffs'; + return { + command, + describe: 'Combine many report diffs into single report-diff.md', + builder: yargsMergeDiffsOptionsDefinition(), + handler: async (args: unknown) => { + ui().logger.log(bold(CLI_NAME)); + ui().logger.info(gray(`Run ${command}...`)); + + const options = args as MergeDiffsOptions & { + persist: Required; + }; + const { files, persist } = options; + + const outputPath = await mergeDiffs(files, persist); + + ui().logger.info(`Reports diff written to ${bold(outputPath)}`); + }, + } satisfies CommandModule; +} diff --git a/packages/cli/src/lib/merge-diffs/merge-diffs-command.unit.test.ts b/packages/cli/src/lib/merge-diffs/merge-diffs-command.unit.test.ts new file mode 100644 index 000000000..1481e8e33 --- /dev/null +++ b/packages/cli/src/lib/merge-diffs/merge-diffs-command.unit.test.ts @@ -0,0 +1,72 @@ +import { bold } from 'ansis'; +import { mergeDiffs } from '@code-pushup/core'; +import { + DEFAULT_PERSIST_FILENAME, + DEFAULT_PERSIST_FORMAT, + DEFAULT_PERSIST_OUTPUT_DIR, +} from '@code-pushup/models'; +import { getLogMessages } from '@code-pushup/test-utils'; +import { ui } from '@code-pushup/utils'; +import { DEFAULT_CLI_CONFIGURATION } from '../../../mocks/constants'; +import { yargsCli } from '../yargs-cli'; +import { yargsMergeDiffsCommandObject } from './merge-diffs-command'; + +vi.mock('@code-pushup/core', async () => { + const core: object = await vi.importActual('@code-pushup/core'); + const { CORE_CONFIG_MOCK }: typeof import('@code-pushup/test-utils') = + await vi.importActual('@code-pushup/test-utils'); + return { + ...core, + autoloadRc: vi.fn().mockResolvedValue(CORE_CONFIG_MOCK), + mergeDiffs: vi.fn().mockResolvedValue('.code-pushup/report-diff.md'), + }; +}); + +describe('merge-diffs-command', () => { + it('should parse input paths from command line and output path from persist config', async () => { + await yargsCli( + [ + 'merge-diffs', + '--files', + 'frontoffice/report-diff.json', + 'backoffice/report-diff.json', + 'api/report-diff.json', + ], + { + ...DEFAULT_CLI_CONFIGURATION, + commands: [yargsMergeDiffsCommandObject()], + }, + ).parseAsync(); + + expect(mergeDiffs).toHaveBeenCalledWith>( + [ + 'frontoffice/report-diff.json', + 'backoffice/report-diff.json', + 'api/report-diff.json', + ], + { + outputDir: DEFAULT_PERSIST_OUTPUT_DIR, + filename: DEFAULT_PERSIST_FILENAME, + format: DEFAULT_PERSIST_FORMAT, + }, + ); + }); + + it('should log output path to stdout', async () => { + await yargsCli( + [ + 'merge-diffs', + '--files=frontoffice/report-diff.json', + '--files=backoffice/report-diff.json', + ], + { + ...DEFAULT_CLI_CONFIGURATION, + commands: [yargsMergeDiffsCommandObject()], + }, + ).parseAsync(); + + expect(getLogMessages(ui().logger).at(-1)).toContain( + `Reports diff written to ${bold('.code-pushup/report-diff.md')}`, + ); + }); +}); diff --git a/packages/cli/src/lib/yargs-cli.integration.test.ts b/packages/cli/src/lib/yargs-cli.integration.test.ts index ce4a6e710..a9e9e3149 100644 --- a/packages/cli/src/lib/yargs-cli.integration.test.ts +++ b/packages/cli/src/lib/yargs-cli.integration.test.ts @@ -8,6 +8,8 @@ import { UploadConfigCliOptions, } from './implementation/core-config.model'; import { GeneralCliOptions } from './implementation/global.model'; +import { MergeDiffsOptions } from './implementation/merge-diffs.model'; +import { yargsMergeDiffsOptionsDefinition } from './implementation/merge-diffs.options'; import { OnlyPluginsOptions } from './implementation/only-plugins.model'; import { yargsOnlyPluginsOptionsDefinition } from './implementation/only-plugins.options'; import { SkipPluginsOptions } from './implementation/skip-plugins.model'; @@ -175,6 +177,30 @@ describe('yargsCli', () => { ).toThrow('Missing required arguments: before, after'); }); + it('should parse merge-diffs options', async () => { + const parsedArgv = await yargsCli( + [ + '--files', + '.code-pushup/frontend/report-diff.json', + '.code-pushup/backend/report-diff.json', + ], + { options: { ...options, ...yargsMergeDiffsOptionsDefinition() } }, + ).parseAsync(); + expect(parsedArgv.files).toEqual([ + '.code-pushup/frontend/report-diff.json', + '.code-pushup/backend/report-diff.json', + ]); + }); + + it('should error if required merge-diffs option is missing', () => { + expect(() => + yargsCli([], { + options: { ...options, ...yargsMergeDiffsOptionsDefinition() }, + noExitProcess: true, + }).parse(), + ).toThrow('Missing required argument: files'); + }); + it('should provide default arguments for history command', async () => { const result = await yargsCli(['history'], { options: { ...options, ...yargsHistoryOptionsDefinition() }, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index bc828e5f4..7952d62f8 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -23,3 +23,4 @@ export { } from './lib/implementation/read-rc-file'; export { GlobalOptions } from './lib/types'; export { UploadOptions, upload } from './lib/upload'; +export { mergeDiffs } from './lib/merge-diffs'; diff --git a/packages/core/src/lib/merge-diffs.ts b/packages/core/src/lib/merge-diffs.ts new file mode 100644 index 000000000..e8bec6355 --- /dev/null +++ b/packages/core/src/lib/merge-diffs.ts @@ -0,0 +1,13 @@ +import { PersistConfig, reportsDiffSchema } from '@code-pushup/models'; +import { readJsonFile } from '@code-pushup/utils'; + +export async function mergeDiffs( + files: string[], + persistConfig: Required, +): Promise { + const diffs = await Promise.all( + files.map(file => readJsonFile(file).then(reportsDiffSchema.parseAsync)), + ); + // TODO: implement + return ''; +}