Skip to content

Commit

Permalink
feat(core): fetch portal comparison link if available when comparing …
Browse files Browse the repository at this point in the history
…reports
  • Loading branch information
matejchalk committed Jul 23, 2024
1 parent 76f18be commit a08978e
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 12 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"node": ">=18.16"
},
"dependencies": {
"@code-pushup/portal-client": "^0.8.0",
"@code-pushup/portal-client": "^0.9.0",
"@isaacs/cliui": "^8.0.2",
"@nx/devkit": "17.3.2",
"@poppinss/cliui": "^6.4.0",
Expand Down
13 changes: 9 additions & 4 deletions packages/cli/src/lib/compare/compare-command.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { bold, gray } from 'ansis';
import { CommandModule } from 'yargs';
import { compareReportFiles } from '@code-pushup/core';
import { PersistConfig } from '@code-pushup/models';
import { PersistConfig, UploadConfig } from '@code-pushup/models';
import { ui } from '@code-pushup/utils';
import { CLI_NAME } from '../constants';
import type { CompareOptions } from '../implementation/compare.model';
import { CompareOptions } from '../implementation/compare.model';
import { yargsCompareOptionsDefinition } from '../implementation/compare.options';

export function yargsCompareCommandObject() {
Expand All @@ -19,11 +19,16 @@ export function yargsCompareCommandObject() {

const options = args as CompareOptions & {
persist: Required<PersistConfig>;
upload?: UploadConfig;
};

const { before, after, persist } = options;
const { before, after, persist, upload } = options;

const outputPaths = await compareReportFiles({ before, after }, persist);
const outputPaths = await compareReportFiles(
{ before, after },
persist,
upload,
);

ui().logger.info(
`Reports diff written to ${outputPaths
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/lib/compare/compare-command.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ describe('compare-command', () => {
filename: DEFAULT_PERSIST_FILENAME,
format: DEFAULT_PERSIST_FORMAT,
},
expect.any(Object),
);
});

Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"dependencies": {
"@code-pushup/models": "0.48.0",
"@code-pushup/utils": "0.48.0",
"@code-pushup/portal-client": "^0.8.0",
"@code-pushup/portal-client": "^0.9.0",
"ansis": "^3.3.0"
},
"type": "commonjs",
Expand Down
44 changes: 42 additions & 2 deletions packages/core/src/lib/compare.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import {
PortalOperationError,
getPortalComparisonLink,
} from '@code-pushup/portal-client';
import {
type Format,
type PersistConfig,
Report,
ReportsDiff,
type UploadConfig,
reportSchema,
} from '@code-pushup/models';
import {
Expand All @@ -14,6 +19,7 @@ import {
generateMdReportsDiff,
readJsonFile,
scoreReport,
ui,
} from '@code-pushup/utils';
import { name as packageName, version } from '../../package.json';
import {
Expand All @@ -26,6 +32,7 @@ import {
export async function compareReportFiles(
inputPaths: Diff<string>,
persistConfig: Required<PersistConfig>,
uploadConfig: UploadConfig | undefined,
): Promise<string[]> {
const { outputDir, filename, format } = persistConfig;

Expand All @@ -40,10 +47,15 @@ export async function compareReportFiles(

const reportsDiff = compareReports(reports);

const portalUrl =
uploadConfig && reportsDiff.commits && format.includes('md')
? await fetchPortalComparisonLink(uploadConfig, reportsDiff.commits)
: undefined;

return Promise.all(
format.map(async fmt => {
const outputPath = join(outputDir, `${filename}-diff.${fmt}`);
const content = reportsDiffToFileContent(reportsDiff, fmt);
const content = reportsDiffToFileContent(reportsDiff, fmt, portalUrl);
await ensureDirectoryExists(outputDir);
await writeFile(outputPath, content);
return outputPath;
Expand Down Expand Up @@ -86,11 +98,39 @@ export function compareReports(reports: Diff<Report>): ReportsDiff {
function reportsDiffToFileContent(
reportsDiff: ReportsDiff,
format: Format,
portalUrl: string | undefined,
): string {
switch (format) {
case 'json':
return JSON.stringify(reportsDiff, null, 2);
case 'md':
return generateMdReportsDiff(reportsDiff);
return generateMdReportsDiff(reportsDiff, portalUrl ?? undefined);
}
}

async function fetchPortalComparisonLink(
uploadConfig: UploadConfig,
commits: NonNullable<ReportsDiff['commits']>,
): Promise<string | undefined> {
const { server, apiKey, organization, project } = uploadConfig;
try {
return await getPortalComparisonLink({
server,
apiKey,
parameters: {
organization,
project,
before: commits.before.hash,
after: commits.after.hash,
},
});
} catch (error) {
if (error instanceof PortalOperationError) {
ui().logger.warning(
`Failed to fetch portal comparison link - ${error.message}`,
);
return undefined;
}
throw error;
}
}
119 changes: 119 additions & 0 deletions packages/core/src/lib/compare.unit.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { vol } from 'memfs';
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { getPortalComparisonLink } from '@code-pushup/portal-client';
import { Commit, Report, reportsDiffSchema } from '@code-pushup/models';
import {
COMMIT_ALT_MOCK,
Expand All @@ -13,6 +15,11 @@ import { Diff, fileExists, readJsonFile } from '@code-pushup/utils';
import { compareReportFiles, compareReports } from './compare';

describe('compareReportFiles', () => {
const commitShas = {
before: MINIMAL_REPORT_MOCK.commit!.hash,
after: REPORT_MOCK.commit!.hash,
};

beforeEach(() => {
vol.fromJSON(
{
Expand All @@ -30,6 +37,7 @@ describe('compareReportFiles', () => {
after: join(MEMFS_VOLUME, 'target-report.json'),
},
{ outputDir: MEMFS_VOLUME, filename: 'report', format: ['json'] },
undefined,
);

const reportsDiffPromise = readJsonFile(
Expand All @@ -48,6 +56,7 @@ describe('compareReportFiles', () => {
after: join(MEMFS_VOLUME, 'target-report.json'),
},
{ outputDir: MEMFS_VOLUME, filename: 'report', format: ['json', 'md'] },
undefined,
);

await expect(
Expand All @@ -57,6 +66,116 @@ describe('compareReportFiles', () => {
fileExists(join(MEMFS_VOLUME, 'report-diff.md')),
).resolves.toBeTruthy();
});

it('should include portal link (fetched using upload config) in Markdown file', async () => {
await compareReportFiles(
{
before: join(MEMFS_VOLUME, 'source-report.json'),
after: join(MEMFS_VOLUME, 'target-report.json'),
},
{ outputDir: MEMFS_VOLUME, filename: 'report', format: ['json', 'md'] },
{
server: 'https://api.code-pushup.dev/graphql',
apiKey: 'cp_XXXXX',
organization: 'dunder-mifflin',
project: 'website',
},
);

await expect(
readFile(join(MEMFS_VOLUME, 'report-diff.md'), 'utf8'),
).resolves.toContain(
`[🕵️ See full comparison in Code PushUp portal 🔍](https://code-pushup.example.com/portal/dunder-mifflin/website/comparison/${commitShas.before}/${commitShas.after})`,
);

expect(getPortalComparisonLink).toHaveBeenCalledWith<
Parameters<typeof getPortalComparisonLink>
>({
server: 'https://api.code-pushup.dev/graphql',
apiKey: 'cp_XXXXX',
parameters: {
organization: 'dunder-mifflin',
project: 'website',
before: commitShas.before,
after: commitShas.after,
},
});
});

it('should not include portal link in Markdown if upload config is missing', async () => {
await compareReportFiles(
{
before: join(MEMFS_VOLUME, 'source-report.json'),
after: join(MEMFS_VOLUME, 'target-report.json'),
},
{ outputDir: MEMFS_VOLUME, filename: 'report', format: ['json', 'md'] },
undefined,
);

await expect(
readFile(join(MEMFS_VOLUME, 'report-diff.md'), 'utf8'),
).resolves.not.toContain(
'[🕵️ See full comparison in Code PushUp portal 🔍]',
);

expect(getPortalComparisonLink).not.toHaveBeenCalled();
});

it('should not include portal link in Markdown if report has no associated commits', async () => {
vol.fromJSON(
{
'source-report.json': JSON.stringify({
...MINIMAL_REPORT_MOCK,
commit: null,
} satisfies Report),
'target-report.json': JSON.stringify(REPORT_MOCK),
},
MEMFS_VOLUME,
);
await compareReportFiles(
{
before: join(MEMFS_VOLUME, 'source-report.json'),
after: join(MEMFS_VOLUME, 'target-report.json'),
},
{ outputDir: MEMFS_VOLUME, filename: 'report', format: ['json', 'md'] },
{
server: 'https://api.code-pushup.dev/graphql',
apiKey: 'cp_XXXXX',
organization: 'dunder-mifflin',
project: 'website',
},
);

await expect(
readFile(join(MEMFS_VOLUME, 'report-diff.md'), 'utf8'),
).resolves.not.toContain(
'[🕵️ See full comparison in Code PushUp portal 🔍]',
);

expect(getPortalComparisonLink).not.toHaveBeenCalled();
});

it('should not fetch portal link if Markdown not included in formats', async () => {
await compareReportFiles(
{
before: join(MEMFS_VOLUME, 'source-report.json'),
after: join(MEMFS_VOLUME, 'target-report.json'),
},
{ outputDir: MEMFS_VOLUME, filename: 'report', format: ['json'] },
{
server: 'https://api.code-pushup.dev/graphql',
apiKey: 'cp_XXXXX',
organization: 'dunder-mifflin',
project: 'website',
},
);

expect(getPortalComparisonLink).not.toHaveBeenCalled();

await expect(
fileExists(join(MEMFS_VOLUME, 'report-diff.md')),
).resolves.toBeFalsy();
});
});

describe('compareReports', () => {
Expand Down
5 changes: 5 additions & 0 deletions testing/test-setup/src/lib/portal-client.mock.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { vi } from 'vitest';
import type {
PortalComparisonLinkArgs,
PortalUploadArgs,
ReportFragment,
} from '@code-pushup/portal-client';
Expand All @@ -15,5 +16,9 @@ vi.mock('@code-pushup/portal-client', async () => {
url: `https://code-pushup.example.com/portal/${data.organization}/${data.project}/commit/${data.commit}`,
}),
),
getPortalComparisonLink: vi.fn(
async ({ parameters }: PortalComparisonLinkArgs) =>
`https://code-pushup.example.com/portal/${parameters.organization}/${parameters.project}/comparison/${parameters.before}/${parameters.after}`,
),
};
});

0 comments on commit a08978e

Please sign in to comment.