Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 16
feat(cli): implement compare command#556
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
b119c1ac047699a09da12becdb44File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -9,6 +9,7 @@ Commands: | ||
| code-pushup autorun Shortcut for running collect followed by upload | ||
| code-pushup collect Run Plugins and collect results | ||
| code-pushup upload Upload report results to the portal | ||
| code-pushup compare Compare 2 report files and create a diff file | ||
matejchalk marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| code-pushup print-config Print config | ||
| Options: | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| import { simpleGit } from 'simple-git'; | ||
| import { ReportsDiff } from '@code-pushup/models'; | ||
| import { cleanTestFolder } from '@code-pushup/test-setup'; | ||
| import { executeProcess, readJsonFile } from '@code-pushup/utils'; | ||
| describe('CLI compare', () => { | ||
| const git = simpleGit(); | ||
| beforeEach(async () => { | ||
| if (await git.diff(['--', 'examples/react-todos-app'])) { | ||
| throw new Error( | ||
| 'Unstaged changes found in examples/react-todos-app, please stage or commit them to prevent E2E tests interfering', | ||
| ); | ||
| } | ||
| await cleanTestFolder('tmp/e2e'); | ||
matejchalk marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| await executeProcess({ | ||
| command: 'code-pushup', | ||
| args: ['collect', '--persist.filename=source-report'], | ||
| cwd: 'examples/react-todos-app', | ||
| }); | ||
| await executeProcess({ | ||
| command: 'npx', | ||
| args: ['eslint', '--fix', 'src', '--ext=js,jsx'], | ||
matejchalk marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| cwd: 'examples/react-todos-app', | ||
| }); | ||
| await executeProcess({ | ||
| command: 'code-pushup', | ||
| args: ['collect', '--persist.filename=target-report'], | ||
| cwd: 'examples/react-todos-app', | ||
| }); | ||
| }, 20_000); | ||
| afterEach(async () => { | ||
| await git.checkout(['--', 'examples/react-todos-app']); | ||
| await cleanTestFolder('tmp/e2e'); | ||
| }); | ||
matejchalk marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| it('should compare report.json files and create report-diff.json', async () => { | ||
| await executeProcess({ | ||
| command: 'code-pushup', | ||
| args: [ | ||
| 'compare', | ||
| '--before=../../tmp/e2e/react-todos-app/source-report.json', | ||
| '--after=../../tmp/e2e/react-todos-app/target-report.json', | ||
| ], | ||
| cwd: 'examples/react-todos-app', | ||
| }); | ||
| const reportsDiff = await readJsonFile<ReportsDiff>( | ||
| 'tmp/e2e/react-todos-app/report-diff.json', | ||
| ); | ||
| expect(reportsDiff).toMatchSnapshot({ | ||
| commits: expect.any(Object), | ||
| date: expect.any(String), | ||
| duration: expect.any(Number), | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| import chalk from 'chalk'; | ||
| import { join } from 'node:path'; | ||
| import { CommandModule } from 'yargs'; | ||
| import { compareReportFiles } from '@code-pushup/core'; | ||
| import { PersistConfig } from '@code-pushup/models'; | ||
| import { CLI_NAME } from '../constants'; | ||
| import type { CompareOptions } from '../implementation/compare.model'; | ||
| import { yargsCompareOptionsDefinition } from '../implementation/compare.options'; | ||
| import { ui } from '../implementation/logging'; | ||
| export function yargsCompareCommandObject() { | ||
| const command = 'compare'; | ||
| return { | ||
| command, | ||
| describe: 'Compare 2 report files and create a diff file', | ||
| builder: yargsCompareOptionsDefinition(), | ||
matejchalk marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| handler: async (args: unknown) => { | ||
| ui().logger.log(chalk.bold(CLI_NAME)); | ||
| ui().logger.info(chalk.gray(`Run ${command}...`)); | ||
| const options = args as CompareOptions & { | ||
| persist: Required<PersistConfig>; | ||
| }; | ||
| const { before, after, persist } = options; | ||
| const outputPath = join( | ||
| persist.outputDir, | ||
| `${persist.filename}-diff.json`, | ||
| ); | ||
| await compareReportFiles({ before, after }, outputPath); | ||
| ui().logger.info(`Reports diff written to ${chalk.bold(outputPath)}`); | ||
| }, | ||
| } satisfies CommandModule; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { join } from 'node:path'; | ||
| import { compareReportFiles } from '@code-pushup/core'; | ||
| import { PERSIST_FILENAME, PERSIST_OUTPUT_DIR } from '@code-pushup/models'; | ||
| import { DEFAULT_CLI_CONFIGURATION } from '../../../mocks/constants'; | ||
| import { yargsCli } from '../yargs-cli'; | ||
| import { yargsCompareCommandObject } from './compare-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), | ||
| compareReportFiles: vi.fn(), | ||
| }; | ||
| }); | ||
| describe('compare-command', () => { | ||
| it('should parse input paths from command line and output path from persist config', async () => { | ||
| await yargsCli( | ||
| ['compare', '--before=source-report.json', '--after=target-report.json'], | ||
| { ...DEFAULT_CLI_CONFIGURATION, commands: [yargsCompareCommandObject()] }, | ||
| ).parseAsync(); | ||
| expect(compareReportFiles).toHaveBeenCalledWith< | ||
| Parameters<typeof compareReportFiles> | ||
| >( | ||
| { before: 'source-report.json', after: 'target-report.json' }, | ||
| join(PERSIST_OUTPUT_DIR, `${PERSIST_FILENAME}-diff.json`), | ||
| ); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| import { Diff } from '@code-pushup/utils'; | ||
| export type CompareOptions = Diff<string>; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import { Options } from 'yargs'; | ||
| import type { CompareOptions } from './compare.model'; | ||
| export function yargsCompareOptionsDefinition(): Record< | ||
| keyof CompareOptions, | ||
| Options | ||
| > { | ||
| return { | ||
| before: { | ||
| describe: 'Path to source report.json', | ||
| type: 'string', | ||
| demandOption: true, | ||
matejchalk marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| }, | ||
| after: { | ||
| describe: 'Path to target report.json', | ||
| type: 'string', | ||
| demandOption: true, | ||
| }, | ||
| }; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| import { describe, expect, it } from 'vitest'; | ||
| import { CoreConfig, Format } from '@code-pushup/models'; | ||
| import { CompareOptions } from './implementation/compare.model'; | ||
| import { yargsCompareOptionsDefinition } from './implementation/compare.options'; | ||
| import { | ||
| PersistConfigCliOptions, | ||
| UploadConfigCliOptions, | ||
| @@ -118,4 +120,24 @@ describe('yargsCli', () => { | ||
| }), | ||
| ); | ||
| }); | ||
| it('should parse compare options', async () => { | ||
| const parsedArgv = await yargsCli<GeneralCliOptions & CompareOptions>( | ||
| ['--before=source-report.json', '--after', 'target-report.json'], | ||
| { | ||
| options: { ...options, ...yargsCompareOptionsDefinition() }, | ||
matejchalk marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| }, | ||
| ).parseAsync(); | ||
| expect(parsedArgv.before).toBe('source-report.json'); | ||
| expect(parsedArgv.after).toBe('target-report.json'); | ||
| }); | ||
| it('should error if required compare option is missing', () => { | ||
| expect(() => | ||
| yargsCli<GeneralCliOptions & CompareOptions>([], { | ||
| options: { ...options, ...yargsCompareOptionsDefinition() }, | ||
| noExitProcess: true, | ||
| }).parse(), | ||
| ).toThrow('Missing required arguments: before, after'); | ||
| }); | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.