Skip to content

Commit 296df7d

Browse files
authored
feat(cli): add --persist.filename cli option (#187)
1 parent a32f433 commit 296df7d

31 files changed

+404
-216
lines changed

e2e/cli-e2e/mocks/fs.mock.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { mkdirSync, rmSync, writeFileSync } from 'fs';
22
import { join } from 'path';
3+
import { ensureDirectoryExists } from '@code-pushup/utils';
34

5+
// @TODO move into testing library
46
export function cleanFolder<T extends object>(
57
dirName = 'tmp',
68
content?: { [key in keyof T]: string },
@@ -13,7 +15,7 @@ export function cleanFolder<T extends object>(
1315
}
1416
}
1517
}
16-
18+
// @TODO move into testing library
1719
export function cleanFolderPutGitKeep<T extends object>(
1820
dirName = 'tmp',
1921
content?: { [key in keyof T]: string },
@@ -27,3 +29,12 @@ export function cleanFolderPutGitKeep<T extends object>(
2729
}
2830
}
2931
}
32+
33+
export function setupFolder(dirName = 'tmp', content?: Record<string, string>) {
34+
ensureDirectoryExists(dirName);
35+
if (content) {
36+
for (const fileName in content) {
37+
writeFileSync(join(dirName, fileName), content[fileName]);
38+
}
39+
}
40+
}

e2e/cli-e2e/mocks/utils.ts

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// @TODO move logic into testing library
2+
import { join } from 'path';
3+
import {
4+
CliArgsObject,
5+
ProcessConfig,
6+
executeProcess,
7+
objectToCliArgs,
8+
} from '@code-pushup/utils';
9+
10+
export const extensions = ['js', 'mjs', 'ts'] as const;
11+
export type Extension = (typeof extensions)[number];
12+
13+
export const configFile = (ext: Extension = 'ts') =>
14+
join(process.cwd(), `e2e/cli-e2e/mocks/code-pushup.config.${ext}`);
15+
16+
export const execCli = (
17+
command: string,
18+
argObj: Partial<CliArgsObject>,
19+
processOptions?: Omit<ProcessConfig, 'args' | 'command' | 'observer'>,
20+
) =>
21+
executeProcess({
22+
command: 'npx',
23+
args: [
24+
'./dist/packages/cli',
25+
command,
26+
...objectToCliArgs({
27+
verbose: true,
28+
progress: false,
29+
config: configFile(),
30+
...argObj,
31+
}),
32+
],
33+
...processOptions,
34+
});

e2e/cli-e2e/tests/__snapshots__/help.spec.ts.snap

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Options:
1919
--config Path the the config file, e.g. code-pushup.config.j
2020
s [string] [default: \\"code-pushup.config.js\\"]
2121
--persist.outputDir Directory for the produced reports [string]
22+
--persist.filename Filename for the produced reports. [string]
2223
--persist.format Format of the report output. e.g. \`md\`, \`json\`, \`st
2324
dout\` [array]
2425
--upload.organization Organization slug from portal [string]

e2e/cli-e2e/tests/print-config.spec.ts

+13-31
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,21 @@
11
import { join } from 'path';
22
import { expect } from 'vitest';
3-
import {
4-
CliArgsObject,
5-
executeProcess,
6-
objectToCliArgs,
7-
} from '@code-pushup/utils';
3+
import { CliArgsObject } from '@code-pushup/utils';
4+
import { configFile, execCli, extensions } from '../mocks/utils';
85

9-
const extensions = ['js', 'mjs', 'ts'] as const;
10-
type Extension = (typeof extensions)[number];
11-
12-
const configFile = (ext: Extension) =>
13-
join(process.cwd(), `e2e/cli-e2e/mocks/code-pushup.config.${ext}`);
14-
15-
const execCli = (argObj: Partial<CliArgsObject>) =>
16-
executeProcess({
17-
command: 'npx',
18-
args: [
19-
'./dist/packages/cli',
20-
'print-config',
21-
...objectToCliArgs({
22-
verbose: true,
23-
...argObj,
24-
}),
25-
],
26-
});
6+
const execCliPrintConfig = (argObj: Partial<CliArgsObject>) =>
7+
execCli('print-config', argObj);
278

289
describe('print-config', () => {
2910
it.each(extensions)('should load .%s config file', async ext => {
30-
const { code, stderr, stdout } = await execCli({ config: configFile(ext) });
11+
const { code, stderr, stdout } = await execCliPrintConfig({
12+
config: configFile(ext),
13+
});
3114
expect(code).toBe(0);
3215
expect(stderr).toBe('');
3316
const args = JSON.parse(stdout);
3417
expect(args).toEqual({
35-
progress: true,
18+
progress: false,
3619
verbose: true,
3720
config: expect.stringContaining(`code-pushup.config.${ext}`),
3821
upload: {
@@ -51,14 +34,14 @@ describe('print-config', () => {
5134
});
5235

5336
it('should load .ts config file and merge cli arguments', async () => {
54-
const { code, stderr, stdout } = await execCli({
55-
config: configFile('ts'),
37+
const { code, stderr, stdout } = await execCliPrintConfig({
38+
'persist.filename': 'my-report',
5639
});
5740
expect(code).toBe(0);
5841
expect(stderr).toBe('');
5942
const args = JSON.parse(stdout);
6043
expect(args).toEqual({
61-
progress: true,
44+
progress: false,
6245
verbose: true,
6346
config: expect.stringContaining(`code-pushup.config.ts`),
6447
upload: {
@@ -69,16 +52,15 @@ describe('print-config', () => {
6952
},
7053
persist: {
7154
outputDir: join('tmp', 'ts'),
72-
filename: 'report',
55+
filename: 'my-report',
7356
},
7457
plugins: expect.any(Array),
7558
categories: expect.any(Array),
7659
});
7760
});
7861

7962
it('should parse persist.format from arguments', async () => {
80-
const { code, stderr, stdout } = await execCli({
81-
config: configFile('ts'),
63+
const { code, stderr, stdout } = await execCliPrintConfig({
8264
'persist.format': ['md', 'json', 'stdout'],
8365
});
8466
expect(code).toBe(0);

packages/cli/src/lib/autorun/command-object.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ vi.mock('@code-pushup/portal-client', async () => {
3232
),
3333
};
3434
});
35-
3635
const baseArgs = [
3736
'autorun',
3837
...objectToCliArgs({
@@ -70,6 +69,7 @@ describe('autorun-command-object', () => {
7069
...baseArgs,
7170
...objectToCliArgs({
7271
'persist.format': 'md',
72+
'persist.filename': 'my-report',
7373
'upload.apiKey': 'some-other-api-key',
7474
'upload.server': 'https://other-example.com/api',
7575
}),

packages/cli/src/lib/collect/command-object.spec.ts

+5-7
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@ import { dirname, join } from 'path';
22
import { fileURLToPath } from 'url';
33
import { CollectAndPersistReportsOptions } from '@code-pushup/core';
44
import { objectToCliArgs } from '@code-pushup/utils';
5-
import {
6-
cleanFolderPutGitKeep,
7-
mockConsole,
8-
unmockConsole,
9-
} from '../../../test';
5+
import { mockConsole, unmockConsole } from '../../../test';
106
import { DEFAULT_CLI_CONFIGURATION } from '../../../test/constants';
117
import { yargsCli } from '../yargs-cli';
128
import { yargsCollectCommandObject } from './command-object';
139

10+
const getFilename = () => 'report';
1411
const baseArgs = [
1512
...objectToCliArgs({
1613
progress: false,
@@ -36,21 +33,22 @@ describe('collect-command-object', () => {
3633

3734
beforeEach(() => {
3835
logs = [];
39-
cleanFolderPutGitKeep();
4036
mockConsole((...args: unknown[]) => {
4137
logs.push(...args);
4238
});
4339
});
4440
afterEach(() => {
45-
cleanFolderPutGitKeep();
41+
logs = [];
4642
unmockConsole();
4743
});
4844

4945
it('should override config with CLI arguments', async () => {
46+
const filename = getFilename();
5047
const args = [
5148
...baseArgs,
5249
...objectToCliArgs({
5350
'persist.format': 'md',
51+
'persist.filename': filename,
5452
}),
5553
];
5654
const parsedArgv = (await cli(

packages/cli/src/lib/implementation/core-config-options.ts

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ export function yargsCoreConfigOptionsDefinition(): Record<ArgNames, Options> {
99
describe: 'Directory for the produced reports',
1010
type: 'string',
1111
},
12+
'persist.filename': {
13+
describe: 'Filename for the produced reports.',
14+
type: 'string',
15+
},
1216
'persist.format': {
1317
describe: 'Format of the report output. e.g. `md`, `json`, `stdout`',
1418
type: 'array',

packages/cli/src/lib/implementation/model.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export type GeneralCliOptions = GlobalOptions;
55

66
export type CoreConfigCliOptions = {
77
'persist.outputDir': string;
8+
'persist.filename': string;
89
'persist.format': Format | string;
910
'upload.organization': string;
1011
'upload.project': string;

packages/cli/src/lib/print-config/command-object.spec.ts

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ describe('print-config-command-object', () => {
2929
it('should print existing config', async () => {
3030
const parsedArgv = await cli(baseArgs).parseAsync();
3131
expect(parsedArgv.persist.outputDir).toBe('tmp');
32+
expect(parsedArgv.persist.filename).toBe('report');
3233
expect(parsedArgv.persist.format).toBeUndefined();
3334
expect(parsedArgv.upload?.organization).toBe('code-pushup');
3435
expect(parsedArgv.upload?.project).toBe('cli');
@@ -43,6 +44,7 @@ describe('print-config-command-object', () => {
4344
const overrides = {
4445
'persist.outputDir': 'custom/output/dir',
4546
'persist.format': ['md'],
47+
'persist.filename': 'custom-report-filename',
4648
'upload.organization': 'custom-org',
4749
'upload.project': 'custom-project',
4850
'upload.apiKey': 'custom-api-key',
@@ -52,6 +54,7 @@ describe('print-config-command-object', () => {
5254

5355
const parsedArgv = await cli(args).parseAsync();
5456
expect(parsedArgv.persist.outputDir).toBe(overrides['persist.outputDir']);
57+
expect(parsedArgv.persist.filename).toBe('custom-report-filename');
5558
expect(parsedArgv.persist.format).toEqual(overrides['persist.format']);
5659
expect(parsedArgv.upload?.organization).toEqual(
5760
overrides['upload.organization'],

packages/cli/src/lib/upload/command-object.spec.ts

+17-12
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
import { UploadOptions } from '@code-pushup/core';
1010
import { report } from '@code-pushup/models/testing';
1111
import { CliArgsObject, objectToCliArgs } from '@code-pushup/utils';
12-
import { cleanFolderPutGitKeep } from '../../../test';
12+
import { setupFolder } from '../../../test';
1313
import { DEFAULT_CLI_CONFIGURATION } from '../../../test/constants';
1414
import { yargsCli } from '../yargs-cli';
1515
import { yargsUploadCommandObject } from './command-object';
@@ -26,7 +26,9 @@ vi.mock('@code-pushup/portal-client', async () => {
2626
),
2727
};
2828
});
29+
const dummyReport = report();
2930

31+
// @TODO move into test library
3032
const baseArgs = [
3133
'upload',
3234
...objectToCliArgs({
@@ -47,22 +49,15 @@ const cli = (args: string[]) =>
4749
commands: [yargsUploadCommandObject()],
4850
});
4951

50-
const reportFile = (format: 'json' | 'md' = 'json') => 'report.' + format;
51-
const dummyReport = report();
52-
5352
describe('upload-command-object', () => {
5453
beforeEach(async () => {
5554
vi.clearAllMocks();
56-
cleanFolderPutGitKeep('tmp', {
57-
[reportFile()]: JSON.stringify(dummyReport),
58-
});
59-
});
60-
61-
afterEach(async () => {
62-
cleanFolderPutGitKeep('tmp');
6355
});
6456

6557
it('should override config with CLI arguments', async () => {
58+
setupFolder('tmp', {
59+
['report.json']: JSON.stringify(dummyReport),
60+
});
6661
const args = [
6762
...baseArgs,
6863
...objectToCliArgs<CliArgsObject>({
@@ -82,7 +77,17 @@ describe('upload-command-object', () => {
8277
});
8378

8479
it('should call portal-client function with correct parameters', async () => {
85-
await cli(baseArgs).parseAsync();
80+
const reportFileName = 'my-report';
81+
setupFolder('tmp', {
82+
[reportFileName + '.json']: JSON.stringify(dummyReport),
83+
});
84+
const args = [
85+
...baseArgs,
86+
...objectToCliArgs<CliArgsObject>({
87+
'persist.filename': reportFileName,
88+
}),
89+
];
90+
await cli(args).parseAsync();
8691
expect(uploadToPortal).toHaveBeenCalledWith({
8792
apiKey: 'dummy-api-key',
8893
server: 'https://example.com/api',

packages/cli/test/fs.mock.ts

+4-18
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,12 @@
1-
import { mkdirSync, rmSync, writeFileSync } from 'fs';
1+
import { writeFileSync } from 'fs';
22
import { join } from 'path';
3+
import { ensureDirectoryExists } from '@code-pushup/utils';
34

4-
export function cleanFolder<T extends object>(
5+
export async function setupFolder<T extends object>(
56
dirName = 'tmp',
67
content?: { [key in keyof T]: string },
78
) {
8-
rmSync(dirName, { recursive: true, force: true });
9-
mkdirSync(dirName, { recursive: true });
10-
if (content) {
11-
for (const fileName in content) {
12-
writeFileSync(join(dirName, fileName), content[fileName]);
13-
}
14-
}
15-
}
16-
17-
export function cleanFolderPutGitKeep<T extends object>(
18-
dirName = 'tmp',
19-
content?: { [key in keyof T]: string },
20-
) {
21-
rmSync(dirName, { recursive: true, force: true });
22-
mkdirSync(dirName, { recursive: true });
23-
writeFileSync(join(dirName, '.gitkeep'), '');
9+
await ensureDirectoryExists(dirName);
2410
if (content) {
2511
for (const fileName in content) {
2612
writeFileSync(join(dirName, fileName), content[fileName]);
File renamed without changes.

packages/core/src/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export {
2-
logPersistedResults,
32
persistReport,
43
PersistError,
54
PersistDirError,

packages/core/src/lib/collect-and-persist.spec.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ vi.mock('@code-pushup/portal-client', async () => {
2222
});
2323

2424
const outputDir = 'tmp';
25-
const reportPath = (path = outputDir, format: 'json' | 'md' = 'json') =>
26-
join(path, 'report.' + format);
25+
const reportPath = (format: 'json' | 'md' = 'json') =>
26+
join(outputDir, `report.${format}`);
2727

2828
describe('collectAndPersistReports', () => {
2929
beforeEach(async () => {
@@ -34,9 +34,10 @@ describe('collectAndPersistReports', () => {
3434
});
3535

3636
it('should work', async () => {
37+
const cfg = minimalConfig(outputDir);
3738
await collectAndPersistReports({
3839
...DEFAULT_TESTING_CLI_OPTIONS,
39-
...minimalConfig(outputDir),
40+
...cfg,
4041
});
4142
const result = JSON.parse(readFileSync(reportPath()).toString()) as Report;
4243
expect(result.plugins[0]?.audits[0]?.slug).toBe('audit-1');

packages/core/src/lib/implementation/persist.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
FOOTER_PREFIX,
1515
README_LINK,
1616
} from '@code-pushup/utils';
17-
import { mockConsole, unmockConsole } from '../../../test/console.mock';
17+
import { mockConsole, unmockConsole } from '../../../test';
1818
import { logPersistedResults, persistReport } from './persist';
1919

2020
// Mock file system API's

0 commit comments

Comments
 (0)