Skip to content

Commit

Permalink
feat(models): create reportsDiff schema
Browse files Browse the repository at this point in the history
  • Loading branch information
matejchalk committed Mar 11, 2024
1 parent 09a7ab0 commit 75dc8aa
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 11 deletions.
25 changes: 14 additions & 11 deletions packages/models/src/lib/audit-output.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { z } from 'zod';
import { nonnegativeIntSchema, slugSchema } from './implementation/schemas';
import {
nonnegativeIntSchema,
scoreSchema,
slugSchema,
} from './implementation/schemas';
import { errorItems, hasDuplicateStrings } from './implementation/utils';
import { issueSchema } from './issue';

export const auditValueSchema =
nonnegativeIntSchema.describe('Raw numeric value');
export const auditDisplayValueSchema = z
.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" })
.optional();

export const auditDetailsSchema = z.object(
{
issues: z.array(issueSchema, { description: 'List of findings' }),
Expand All @@ -14,16 +24,9 @@ export type AuditDetails = z.infer<typeof auditDetailsSchema>;
export const auditOutputSchema = z.object(
{
slug: slugSchema.describe('Reference to audit'),
displayValue: z
.string({ description: "Formatted value (e.g. '0.9 s', '2.1 MB')" })
.optional(),
value: nonnegativeIntSchema.describe('Raw numeric value'),
score: z
.number({
description: 'Value between 0 and 1',
})
.min(0)
.max(1),
displayValue: auditDisplayValueSchema,
value: auditValueSchema,
score: scoreSchema,
details: auditDetailsSchema.optional(),
},
{ description: 'Audit information' },
Expand Down
8 changes: 8 additions & 0 deletions packages/models/src/lib/implementation/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ export const titleSchema = z
.string({ description: 'Descriptive name' })
.max(MAX_TITLE_LENGTH);

/** Schema for score of audit, category or group */
export const scoreSchema = z
.number({
description: 'Value between 0 and 1',
})
.min(0)
.max(1);

/**
* Used for categories, plugins and audits
* @param options
Expand Down
98 changes: 98 additions & 0 deletions packages/models/src/lib/reports-diff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { type ZodTypeAny, z } from 'zod';
import {
auditDisplayValueSchema,
auditOutputSchema,
auditValueSchema,
} from './audit-output';
import { commitSchema } from './commit';
import {
executionMetaSchema,
packageVersionSchema,
scoreSchema,
slugSchema,
titleSchema,
} from './implementation/schemas';
import { pluginMetaSchema } from './plugin-config';

function makeComparisonSchema<T extends ZodTypeAny>(schema: T) {
return z.object({
before: schema,
after: schema,
});
}

const scorableMetaSchema = z.object({ slug: slugSchema, title: titleSchema });
const scorableWithPluginMetaSchema = scorableMetaSchema.merge(
z.object({
plugin: pluginMetaSchema.pick({ slug: true, title: true }),
}),
);

const scorableDiffSchema = scorableMetaSchema.merge(
z.object({
scores: makeComparisonSchema(scoreSchema).merge(
z.object({ diff: z.number().min(-1).max(1) }),
),
}),
);
const scorableWithPluginDiffSchema = scorableDiffSchema.merge(
scorableWithPluginMetaSchema,
);

const categoryDiffSchema = scorableDiffSchema;
const groupDiffSchema = scorableWithPluginDiffSchema;
const auditDiffSchema = scorableWithPluginDiffSchema.merge(
z.object({
values: makeComparisonSchema(auditValueSchema).merge(
z.object({ diff: z.number().int() }),
),
displayValues: makeComparisonSchema(auditDisplayValueSchema),
}),
);

const categoryResultSchema = scorableMetaSchema.merge(
z.object({ score: scoreSchema }),
);
const groupResultSchema = scorableWithPluginMetaSchema.merge(
z.object({ score: scoreSchema }),
);
const auditResultSchema = scorableWithPluginMetaSchema.merge(
auditOutputSchema.pick({ score: true, value: true, displayValue: true }),
);

export const reportsDiffSchema = z
.object({
commits: makeComparisonSchema(commitSchema).nullable(),
categories: z.object({
changed: z.array(categoryDiffSchema),
unchanged: z.array(categoryResultSchema),
added: z.array(categoryResultSchema),
removed: z.array(categoryResultSchema),
}),
groups: z.object({
changed: z.array(groupDiffSchema),
unchanged: z.array(groupResultSchema),
added: z.array(groupResultSchema),
removed: z.array(groupResultSchema),
}),
audits: z.object({
changed: z.array(auditDiffSchema),
unchanged: z.array(auditResultSchema),
added: z.array(auditResultSchema),
removed: z.array(auditResultSchema),
}),
})
.merge(
packageVersionSchema({
versionDescription: 'NPM version of the CLI',
required: true,
}),
)
.merge(
executionMetaSchema({
descriptionDate: 'Start date and time of the compare run',
descriptionDuration: 'Duration of the compare run in ms',
}),
);

export type ReportsDiff = z.infer<typeof reportsDiffSchema>;
95 changes: 95 additions & 0 deletions packages/models/src/lib/reports-diff.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { type ReportsDiff, reportsDiffSchema } from './reports-diff';

describe('reportsDiffSchema', () => {
it('should parse valid reports diff', () => {
expect(() =>
reportsDiffSchema.parse({
commits: {
before: {
hash: 'abcdef0123456789abcdef0123456789abcdef01',
message: 'Do stuff',
author: 'John Doe',
date: new Date('2023-03-07T23:00:00+01:00'),
},
after: {
hash: '0123456789abcdef0123456789abcdef01234567',
message: 'Fix stuff',
author: 'Jane Doe',
date: new Date(),
},
},
date: new Date().toISOString(),
duration: 42,
packageName: '@code-pushup/core',
version: '1.2.3',
categories: {
changed: [
{
slug: 'perf',
title: 'Performance',
scores: { before: 0.7, after: 0.66, diff: -0.04 },
},
],
unchanged: [{ slug: 'a11y', title: 'Accessibility', score: 1 }],
added: [],
removed: [],
},
groups: {
changed: [],
unchanged: [],
added: [],
removed: [
{
slug: 'problems',
title: 'Problems',
plugin: { slug: 'eslint', title: 'ESLint' },
score: 0.8,
},
],
},
audits: {
changed: [
{
slug: 'lcp',
title: 'Largest Contentful Paint',
plugin: { slug: 'lighthouse', title: 'Lighthouse' },
scores: {
before: 0.9,
after: 0.7,
diff: -0.2,
},
values: {
before: 1810,
after: 1920,
diff: 110,
},
displayValues: {
before: '1.8 s',
after: '1.9 s',
},
},
],
unchanged: [
{
slug: 'image-alt',
title: 'Image elements have `[alt]` attributes',
plugin: { slug: 'lighthouse', title: 'Lighthouse' },
score: 1,
value: 0,
},
],
added: [
{
slug: 'document-title',
title: 'Document has a `<title>` element',
plugin: { slug: 'lighthouse', title: 'Lighthouse' },
score: 1,
value: 0,
},
],
removed: [],
},
} satisfies ReportsDiff),
).not.toThrow();
});
});

0 comments on commit 75dc8aa

Please sign in to comment.