From e709709ab3bdec917bdd14e7c690a2ce0443f935 Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Tue, 2 Sep 2025 11:10:44 -0400 Subject: [PATCH 01/15] feat: adjust scoreTarget handling --- package-lock.json | 8 ++++---- package.json | 2 +- packages/ci/package.json | 2 +- packages/ci/src/lib/portal/transform.ts | 4 +--- packages/ci/src/lib/portal/transform.unit.test.ts | 2 -- packages/core/package.json | 2 +- packages/core/src/lib/implementation/report-to-gql.ts | 4 +--- 7 files changed, 9 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 53ab456d8..13e3212ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "license": "MIT", "dependencies": { - "@code-pushup/portal-client": "^0.15.0", + "@code-pushup/portal-client": "^0.16.0", "@isaacs/cliui": "^8.0.2", "@nx/devkit": "21.4.1", "@poppinss/cliui": "6.4.1", @@ -2319,9 +2319,9 @@ } }, "node_modules/@code-pushup/portal-client": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@code-pushup/portal-client/-/portal-client-0.15.0.tgz", - "integrity": "sha512-y3ZPaNdd1zK4lYu70S0sDC1Z5Qd1NIQWECt/8O4r0A6UTrQLYht+pgNFDFuCDvHnQrLkwmR7ib8xPbJJ74W+3w==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@code-pushup/portal-client/-/portal-client-0.16.0.tgz", + "integrity": "sha512-JlMRcTKkJygVfLS+IWQxDRDnvF64p4q+QDLIXzQPep6X99C1OH3MnA9jbfGAOew/3xqOILCrifn0y54fyRs8Qg==", "license": "MIT", "dependencies": { "graphql": "^16.6.0", diff --git a/package.json b/package.json index ba7596460..2d82efb62 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "node": ">=22.14" }, "dependencies": { - "@code-pushup/portal-client": "^0.15.0", + "@code-pushup/portal-client": "^0.16.0", "@isaacs/cliui": "^8.0.2", "@nx/devkit": "21.4.1", "@poppinss/cliui": "6.4.1", diff --git a/packages/ci/package.json b/packages/ci/package.json index ac33cbe3b..f3898e802 100644 --- a/packages/ci/package.json +++ b/packages/ci/package.json @@ -27,7 +27,7 @@ "type": "module", "dependencies": { "@code-pushup/models": "0.77.0", - "@code-pushup/portal-client": "^0.15.0", + "@code-pushup/portal-client": "^0.16.0", "@code-pushup/utils": "0.77.0", "glob": "^11.0.1", "simple-git": "^3.20.0", diff --git a/packages/ci/src/lib/portal/transform.ts b/packages/ci/src/lib/portal/transform.ts index 1f33862f1..1c2d7bf1d 100644 --- a/packages/ci/src/lib/portal/transform.ts +++ b/packages/ci/src/lib/portal/transform.ts @@ -62,6 +62,7 @@ function transformGQLCategory(category: CategoryFragment): CategoryConfig { slug: category.slug, title: category.title, ...(category.description && { description: category.description }), + ...(category.scoreTarget && { scoreTarget: category.scoreTarget }), refs: category.refs.map( ({ target, weight }): CategoryRef => ({ type: lowercase(target.__typename), @@ -70,9 +71,6 @@ function transformGQLCategory(category: CategoryFragment): CategoryConfig { weight, }), ), - // TODO: Portal API migration - convert isBinary to scoreTarget for backward compatibility - // Remove this conversion when Portal API supports scoreTarget (#713) - ...(category.isBinary && { scoreTarget: 1 }), }; } diff --git a/packages/ci/src/lib/portal/transform.unit.test.ts b/packages/ci/src/lib/portal/transform.unit.test.ts index 07212b147..5ae819fca 100644 --- a/packages/ci/src/lib/portal/transform.unit.test.ts +++ b/packages/ci/src/lib/portal/transform.unit.test.ts @@ -25,7 +25,6 @@ describe('transformGQLReport', () => { { slug: 'code-style', title: 'Code style', - isBinary: false, score: 0.5, refs: [ { @@ -41,7 +40,6 @@ describe('transformGQLReport', () => { { slug: 'bundle-size', title: 'Bundle size', - isBinary: false, score: 0.75, refs: [ { diff --git a/packages/core/package.json b/packages/core/package.json index 3daa7548d..5ca9fd5f6 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -44,7 +44,7 @@ "ansis": "^3.3.0" }, "peerDependencies": { - "@code-pushup/portal-client": "^0.15.0" + "@code-pushup/portal-client": "^0.16.0" }, "peerDependenciesMeta": { "@code-pushup/portal-client": { diff --git a/packages/core/src/lib/implementation/report-to-gql.ts b/packages/core/src/lib/implementation/report-to-gql.ts index 6c0dab792..36b882064 100644 --- a/packages/core/src/lib/implementation/report-to-gql.ts +++ b/packages/core/src/lib/implementation/report-to-gql.ts @@ -199,9 +199,7 @@ function categoryToGQL(category: CategoryConfig): PortalCategory { slug: category.slug, title: category.title, description: category.description, - // TODO: Portal API migration - convert scoreTarget to isBinary for backward compatibility - // Remove this conversion when Portal API supports scoreTarget (#713) - isBinary: category.scoreTarget === 1, + ...(category.scoreTarget && { scoreTarget: category.scoreTarget }), refs: category.refs.map(ref => ({ plugin: ref.plugin, type: categoryRefTypeToGQL(ref.type), From ee208fbee900e54a1774d3427ed19533fea9f689 Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Tue, 2 Sep 2025 14:57:51 -0400 Subject: [PATCH 02/15] feat(plugin-eslint): add scoreTargets to plugin options and tests --- packages/plugin-eslint/src/lib/config.ts | 6 ++- .../plugin-eslint/src/lib/eslint-plugin.ts | 7 ++- .../src/lib/eslint-plugin.unit.test.ts | 50 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 packages/plugin-eslint/src/lib/eslint-plugin.unit.test.ts diff --git a/packages/plugin-eslint/src/lib/config.ts b/packages/plugin-eslint/src/lib/config.ts index 6a539b502..e49e14990 100644 --- a/packages/plugin-eslint/src/lib/config.ts +++ b/packages/plugin-eslint/src/lib/config.ts @@ -1,5 +1,8 @@ import { z } from 'zod'; -import { pluginArtifactOptionsSchema } from '@code-pushup/models'; +import { + pluginArtifactOptionsSchema, + pluginScoreTargetsSchema, +} from '@code-pushup/models'; import { toArray } from '@code-pushup/utils'; const patternsSchema = z @@ -63,5 +66,6 @@ export type CustomGroup = z.infer; export const eslintPluginOptionsSchema = z.object({ groups: z.array(customGroupSchema).optional(), artifacts: pluginArtifactOptionsSchema.optional(), + scoreTargets: pluginScoreTargetsSchema, }); export type ESLintPluginOptions = z.infer; diff --git a/packages/plugin-eslint/src/lib/eslint-plugin.ts b/packages/plugin-eslint/src/lib/eslint-plugin.ts index 49f6ea228..2c5617b41 100644 --- a/packages/plugin-eslint/src/lib/eslint-plugin.ts +++ b/packages/plugin-eslint/src/lib/eslint-plugin.ts @@ -40,7 +40,11 @@ export async function eslintPlugin( schemaType: 'ESLint plugin config', }); - const { groups: customGroups, artifacts } = options + const { + groups: customGroups, + artifacts, + scoreTargets, + } = options ? parseSchema(eslintPluginOptionsSchema, options, { schemaType: 'ESLint plugin options', }) @@ -69,5 +73,6 @@ export async function eslintPlugin( targets, ...(artifacts ? { artifacts } : {}), }), + ...(scoreTargets && { scoreTargets }), }; } diff --git a/packages/plugin-eslint/src/lib/eslint-plugin.unit.test.ts b/packages/plugin-eslint/src/lib/eslint-plugin.unit.test.ts new file mode 100644 index 000000000..f583a3f4d --- /dev/null +++ b/packages/plugin-eslint/src/lib/eslint-plugin.unit.test.ts @@ -0,0 +1,50 @@ +import { pluginConfigSchema } from '@code-pushup/models'; +import { eslintPlugin } from './eslint-plugin.js'; + +vi.mock('./meta/index.js', () => ({ + listAuditsAndGroups: vi.fn().mockResolvedValue({ + audits: [ + { slug: 'type-safety', title: 'Type Safety' }, + { slug: 'no-empty', title: 'Disallow empty block statements' }, + ], + groups: [], + }), +})); + +describe('eslintPlugin', () => { + it('should pass scoreTargets to PluginConfig when provided', async () => { + const pluginConfig = await eslintPlugin( + { + eslintrc: 'eslint.config.js', + patterns: ['src/**/*.js'], + }, + { scoreTargets: 0.8 }, + ); + + expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); + expect(pluginConfig.scoreTargets).toBe(0.8); + }); + + it('should pass object scoreTargets to PluginConfig', async () => { + const scoreTargets = { 'type-safety': 0.9 }; + const pluginConfig = await eslintPlugin( + { + eslintrc: 'eslint.config.js', + patterns: ['src/**/*.js'], + }, + { scoreTargets }, + ); + + expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); + expect(pluginConfig.scoreTargets).toStrictEqual(scoreTargets); + }); + + it('should not have scoreTargets when not provided', async () => { + const pluginConfig = await eslintPlugin({ + eslintrc: 'eslint.config.js', + patterns: ['src/**/*.js'], + }); + + expect(pluginConfig.scoreTargets).toBeUndefined(); + }); +}); From 810cfac5c0a002e71371c3b26a50f86e519ba585 Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Tue, 2 Sep 2025 12:40:11 -0400 Subject: [PATCH 03/15] feat(plugin-jsdocs): add scoreTargets to plugin options and tests --- packages/plugin-jsdocs/src/lib/config.ts | 2 + .../plugin-jsdocs/src/lib/config.unit.test.ts | 11 ++++ .../plugin-jsdocs/src/lib/jsdocs-plugin.ts | 2 + .../src/lib/jsdocs-plugin.unit.test.ts | 50 ++++++++++++++++++- 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/packages/plugin-jsdocs/src/lib/config.ts b/packages/plugin-jsdocs/src/lib/config.ts index cd139c50c..d746bf922 100644 --- a/packages/plugin-jsdocs/src/lib/config.ts +++ b/packages/plugin-jsdocs/src/lib/config.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; +import { pluginScoreTargetsSchema } from '@code-pushup/models'; const patternsSchema = z .union([z.string(), z.array(z.string()).min(1)]) @@ -19,6 +20,7 @@ const jsDocsTargetObjectSchema = z 'List of audit slugs to evaluate. When specified, only these audits will be evaluated.', ), patterns: patternsSchema, + scoreTargets: pluginScoreTargetsSchema, }) .refine(data => !(data.skipAudits && data.onlyAudits), { message: "You can't define 'skipAudits' and 'onlyAudits' simultaneously", diff --git a/packages/plugin-jsdocs/src/lib/config.unit.test.ts b/packages/plugin-jsdocs/src/lib/config.unit.test.ts index d77dd19ff..6f90ac5d5 100644 --- a/packages/plugin-jsdocs/src/lib/config.unit.test.ts +++ b/packages/plugin-jsdocs/src/lib/config.unit.test.ts @@ -142,4 +142,15 @@ describe('JsDocsPlugin Configuration', () => { ).toThrow('Invalid input'); }); }); + + describe('scoreTargets', () => { + it('should accept valid score targets', () => { + expect(() => + jsDocsPluginConfigSchema.parse({ + patterns: ['src/**/*.ts'], + scoreTargets: { 'functions-coverage': 0.9 }, + }), + ).not.toThrow(); + }); + }); }); diff --git a/packages/plugin-jsdocs/src/lib/jsdocs-plugin.ts b/packages/plugin-jsdocs/src/lib/jsdocs-plugin.ts index 694d0c135..8de4c3410 100644 --- a/packages/plugin-jsdocs/src/lib/jsdocs-plugin.ts +++ b/packages/plugin-jsdocs/src/lib/jsdocs-plugin.ts @@ -32,6 +32,7 @@ export const PLUGIN_DOCS_URL = */ export function jsDocsPlugin(config: JsDocsPluginConfig): PluginConfig { const jsDocsConfig = jsDocsPluginConfigSchema.parse(config); + const scoreTargets = jsDocsConfig.scoreTargets; return { slug: PLUGIN_SLUG, @@ -42,5 +43,6 @@ export function jsDocsPlugin(config: JsDocsPluginConfig): PluginConfig { groups: filterGroupsByOnlyAudits(groups, jsDocsConfig), audits: filterAuditsByPluginConfig(jsDocsConfig), runner: createRunnerFunction(jsDocsConfig), + ...(scoreTargets && { scoreTargets }), }; } diff --git a/packages/plugin-jsdocs/src/lib/jsdocs-plugin.unit.test.ts b/packages/plugin-jsdocs/src/lib/jsdocs-plugin.unit.test.ts index e6ec0b74f..8774ff32e 100644 --- a/packages/plugin-jsdocs/src/lib/jsdocs-plugin.unit.test.ts +++ b/packages/plugin-jsdocs/src/lib/jsdocs-plugin.unit.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; +import { pluginConfigSchema } from '@code-pushup/models'; import { PLUGIN_SLUG, groups } from './constants.js'; import { PLUGIN_DESCRIPTION, @@ -13,8 +14,24 @@ import { } from './utils.js'; vi.mock('./utils.js', () => ({ - filterAuditsByPluginConfig: vi.fn().mockReturnValue(['mockAudit']), - filterGroupsByOnlyAudits: vi.fn().mockReturnValue(['mockGroup']), + filterAuditsByPluginConfig: vi.fn().mockReturnValue([ + { + slug: 'mock-audit', + title: 'Mock Audit', + }, + ]), + filterGroupsByOnlyAudits: vi.fn().mockReturnValue([ + { + slug: 'mock-group', + title: 'Mock Group', + refs: [ + { + slug: 'mock-audit', + weight: 1, + }, + ], + }, + ]), })); vi.mock('./runner/runner.js', () => ({ @@ -68,4 +85,33 @@ describe('jsDocsPlugin', () => { expect(createRunnerFunction).toHaveBeenCalledWith(config); }); + + it('should pass scoreTargets to PluginConfig when provided', () => { + const pluginConfig = jsDocsPlugin({ + patterns: ['src/**/*.ts'], + scoreTargets: 0.9, + }); + + expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); + expect(pluginConfig.scoreTargets).toBe(0.9); + }); + + it('should pass object scoreTargets to PluginConfig', () => { + const scoreTargets = { 'functions-coverage': 0.9 }; + const pluginConfig = jsDocsPlugin({ + patterns: ['src/**/*.ts'], + scoreTargets, + }); + + expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); + expect(pluginConfig.scoreTargets).toStrictEqual(scoreTargets); + }); + + it('should not have scoreTargets when not provided', () => { + expect( + jsDocsPlugin({ + patterns: ['src/**/*.ts'], + }).scoreTargets, + ).toBeUndefined(); + }); }); From 79928d3400838093ebf4e1465286e734b45cb1d9 Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Tue, 2 Sep 2025 12:47:59 -0400 Subject: [PATCH 04/15] feat(plugin-lighthouse): add scoreTargets to plugin options and tests --- .../src/lib/lighthouse-plugin.ts | 10 ++++++-- .../src/lib/lighthouse-plugin.unit.test.ts | 25 +++++++++++++++++++ packages/plugin-lighthouse/src/lib/types.ts | 2 ++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/plugin-lighthouse/src/lib/lighthouse-plugin.ts b/packages/plugin-lighthouse/src/lib/lighthouse-plugin.ts index cb5184a81..14ecb1176 100644 --- a/packages/plugin-lighthouse/src/lib/lighthouse-plugin.ts +++ b/packages/plugin-lighthouse/src/lib/lighthouse-plugin.ts @@ -10,8 +10,13 @@ export function lighthousePlugin( urls: LighthouseUrls, flags?: LighthouseOptions, ): PluginConfig { - const { skipAudits, onlyAudits, onlyCategories, ...unparsedFlags } = - normalizeFlags(flags ?? {}); + const { + skipAudits, + onlyAudits, + onlyCategories, + scoreTargets, + ...unparsedFlags + } = normalizeFlags(flags ?? {}); const { urls: normalizedUrls, context } = normalizeUrlInput(urls); @@ -40,5 +45,6 @@ export function lighthousePlugin( ...unparsedFlags, }), context, + ...(scoreTargets && { scoreTargets }), }; } diff --git a/packages/plugin-lighthouse/src/lib/lighthouse-plugin.unit.test.ts b/packages/plugin-lighthouse/src/lib/lighthouse-plugin.unit.test.ts index 8ea1735b1..89afc748e 100644 --- a/packages/plugin-lighthouse/src/lib/lighthouse-plugin.unit.test.ts +++ b/packages/plugin-lighthouse/src/lib/lighthouse-plugin.unit.test.ts @@ -120,4 +120,29 @@ describe('lighthousePlugin-config-object', () => { }), ); }); + + it('should pass scoreTargets to PluginConfig when provided', () => { + const pluginConfig = lighthousePlugin('https://code-pushup-portal.com', { + scoreTargets: 0.8, + }); + + expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); + expect(pluginConfig.scoreTargets).toBe(0.8); + }); + + it('should pass object scoreTargets to PluginConfig', () => { + const scoreTargets = { 'first-contentful-paint': 0.9 }; + const pluginConfig = lighthousePlugin('https://code-pushup-portal.com', { + scoreTargets, + }); + + expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); + expect(pluginConfig.scoreTargets).toStrictEqual(scoreTargets); + }); + + it('should not have scoreTargets when not provided', () => { + expect( + lighthousePlugin('https://code-pushup-portal.com').scoreTargets, + ).toBeUndefined(); + }); }); diff --git a/packages/plugin-lighthouse/src/lib/types.ts b/packages/plugin-lighthouse/src/lib/types.ts index 2933287ab..3e1b2bb6a 100644 --- a/packages/plugin-lighthouse/src/lib/types.ts +++ b/packages/plugin-lighthouse/src/lib/types.ts @@ -1,4 +1,5 @@ import type { CliFlags } from 'lighthouse'; +import type { PluginScoreTargets } from '@code-pushup/models'; import type { ExcludeNullableProps } from '@code-pushup/utils'; import type { LIGHTHOUSE_GROUP_SLUGS } from './constants.js'; @@ -26,6 +27,7 @@ export type LighthouseOptions = ExcludeNullableProps< onlyGroups?: string | string[]; onlyAudits?: string | string[]; skipAudits?: string | string[]; + scoreTargets?: PluginScoreTargets; }; export type LighthouseGroupSlug = (typeof LIGHTHOUSE_GROUP_SLUGS)[number]; From d15e63c866f8882eb910d6c99f9837d98cfa3563 Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Tue, 2 Sep 2025 13:02:02 -0400 Subject: [PATCH 05/15] feat(plugin-js-packages): add scoreTargets to plugin options and tests --- packages/plugin-js-packages/src/lib/config.ts | 9 +++-- .../src/lib/config.unit.test.ts | 9 +++++ .../src/lib/js-packages-plugin.ts | 10 ++++-- .../src/lib/js-packages-plugin.unit.test.ts | 34 ++++++++++++++++++- 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/packages/plugin-js-packages/src/lib/config.ts b/packages/plugin-js-packages/src/lib/config.ts index 37f79b687..62f086d9b 100644 --- a/packages/plugin-js-packages/src/lib/config.ts +++ b/packages/plugin-js-packages/src/lib/config.ts @@ -1,5 +1,9 @@ import { z } from 'zod'; -import { type IssueSeverity, issueSeveritySchema } from '@code-pushup/models'; +import { + type IssueSeverity, + issueSeveritySchema, + pluginScoreTargetsSchema, +} from '@code-pushup/models'; import { defaultAuditLevelMapping } from './constants.js'; export const dependencyGroups = ['prod', 'dev', 'optional'] as const; @@ -74,6 +78,7 @@ export const jsPackagesPluginConfigSchema = z.object({ 'Mapping of audit levels to issue severity. Custom mapping or overrides may be entered manually, otherwise has a default preset.', ), packageJsonPath: packageJsonPathSchema, + scoreTargets: pluginScoreTargetsSchema, }); export type JSPackagesPluginConfig = z.input< @@ -81,5 +86,5 @@ export type JSPackagesPluginConfig = z.input< >; export type FinalJSPackagesPluginConfig = Required< - z.infer + Omit, 'scoreTargets'> >; diff --git a/packages/plugin-js-packages/src/lib/config.unit.test.ts b/packages/plugin-js-packages/src/lib/config.unit.test.ts index 0b80823af..9ea26f3b3 100644 --- a/packages/plugin-js-packages/src/lib/config.unit.test.ts +++ b/packages/plugin-js-packages/src/lib/config.unit.test.ts @@ -88,4 +88,13 @@ describe('fillAuditLevelMapping', () => { info: 'info', }); }); + + it('should accept valid score targets', () => { + expect(() => + jsPackagesPluginConfigSchema.parse({ + packageManager: 'npm', + scoreTargets: { 'npm-outdated-dev': 0.9 }, + }), + ).not.toThrow(); + }); }); diff --git a/packages/plugin-js-packages/src/lib/js-packages-plugin.ts b/packages/plugin-js-packages/src/lib/js-packages-plugin.ts index 2744f2b27..8fe2491cd 100644 --- a/packages/plugin-js-packages/src/lib/js-packages-plugin.ts +++ b/packages/plugin-js-packages/src/lib/js-packages-plugin.ts @@ -34,8 +34,13 @@ import { normalizeConfig } from './utils.js'; export async function jsPackagesPlugin( config?: JSPackagesPluginConfig, ): Promise { - const { packageManager, checks, depGroups, ...jsPackagesPluginConfigRest } = - await normalizeConfig(config); + const { + packageManager, + checks, + depGroups, + scoreTargets, + ...jsPackagesPluginConfigRest + } = await normalizeConfig(config); const runnerScriptPath = path.join( fileURLToPath(path.dirname(import.meta.url)), @@ -64,6 +69,7 @@ export async function jsPackagesPlugin( packageManager: packageManager.slug, dependencyGroups: depGroups, }), + ...(scoreTargets && { scoreTargets }), }; } diff --git a/packages/plugin-js-packages/src/lib/js-packages-plugin.unit.test.ts b/packages/plugin-js-packages/src/lib/js-packages-plugin.unit.test.ts index 7bb2a682a..d5058aaab 100644 --- a/packages/plugin-js-packages/src/lib/js-packages-plugin.unit.test.ts +++ b/packages/plugin-js-packages/src/lib/js-packages-plugin.unit.test.ts @@ -1,6 +1,11 @@ import { vol } from 'memfs'; import { describe, expect, it } from 'vitest'; -import type { Group, PluginConfig, RunnerConfig } from '@code-pushup/models'; +import { + type Group, + type PluginConfig, + type RunnerConfig, + pluginConfigSchema, +} from '@code-pushup/models'; import { MEMFS_VOLUME } from '@code-pushup/test-utils'; import { jsPackagesPlugin } from './js-packages-plugin.js'; @@ -179,4 +184,31 @@ describe('jsPackagesPlugin', () => { }), ); }); + + it('should pass scoreTargets to PluginConfig when provided', async () => { + const pluginConfig = await jsPackagesPlugin({ + packageManager: 'npm', + scoreTargets: 0.8, + }); + + expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); + expect(pluginConfig.scoreTargets).toBe(0.8); + }); + + it('should pass object scoreTargets to PluginConfig', async () => { + const scoreTargets = { 'npm-outdated-dev': 0.9 }; + const pluginConfig = await jsPackagesPlugin({ + packageManager: 'npm', + scoreTargets, + }); + + expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); + expect(pluginConfig.scoreTargets).toStrictEqual(scoreTargets); + }); + + it('should not have scoreTargets when not provided', async () => { + const pluginConfig = await jsPackagesPlugin({ packageManager: 'npm' }); + + expect(pluginConfig.scoreTargets).toBeUndefined(); + }); }); From 9a5390cbce1f220c9a674a7b2c1a316fcd51a447 Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Tue, 2 Sep 2025 13:09:11 -0400 Subject: [PATCH 06/15] feat(plugin-typescript): add scoreTargets to plugin options and tests --- packages/plugin-typescript/src/lib/schema.ts | 2 ++ .../src/lib/schema.unit.test.ts | 9 +++++++ .../src/lib/typescript-plugin.ts | 9 ++++--- .../src/lib/typescript-plugin.unit.test.ts | 25 +++++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/packages/plugin-typescript/src/lib/schema.ts b/packages/plugin-typescript/src/lib/schema.ts index ceb9c8959..ac4b9da48 100644 --- a/packages/plugin-typescript/src/lib/schema.ts +++ b/packages/plugin-typescript/src/lib/schema.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; +import { pluginScoreTargetsSchema } from '@code-pushup/models'; import { AUDITS, DEFAULT_TS_CONFIG } from './constants.js'; import type { AuditSlug } from './types.js'; @@ -15,6 +16,7 @@ export const typescriptPluginConfigSchema = z.object({ .array(z.enum(auditSlugs)) .describe('Filters TypeScript compiler errors by diagnostic codes') .optional(), + scoreTargets: pluginScoreTargetsSchema, }); export type TypescriptPluginOptions = z.input< diff --git a/packages/plugin-typescript/src/lib/schema.unit.test.ts b/packages/plugin-typescript/src/lib/schema.unit.test.ts index c16f8fed9..dea7114cc 100644 --- a/packages/plugin-typescript/src/lib/schema.unit.test.ts +++ b/packages/plugin-typescript/src/lib/schema.unit.test.ts @@ -70,4 +70,13 @@ describe('typescriptPluginConfigSchema', () => { String.raw`Invalid option: expected one of \"syntax-errors\"|\"semantic-errors\"|`, ); }); + + it('should accept valid score targets', () => { + expect(() => + typescriptPluginConfigSchema.parse({ + tsconfig, + scoreTargets: { 'no-implicit-any-errors': 0.9 }, + }), + ).not.toThrow(); + }); }); diff --git a/packages/plugin-typescript/src/lib/typescript-plugin.ts b/packages/plugin-typescript/src/lib/typescript-plugin.ts index 588ce4d60..80c95f21a 100644 --- a/packages/plugin-typescript/src/lib/typescript-plugin.ts +++ b/packages/plugin-typescript/src/lib/typescript-plugin.ts @@ -17,9 +17,11 @@ const packageJson = createRequire(import.meta.url)( export async function typescriptPlugin( options?: TypescriptPluginOptions, ): Promise { - const { tsconfig = DEFAULT_TS_CONFIG, onlyAudits } = parseOptions( - options ?? {}, - ); + const { + tsconfig = DEFAULT_TS_CONFIG, + onlyAudits, + scoreTargets, + } = parseOptions(options ?? {}); const filteredAudits = getAudits({ onlyAudits }); const filteredGroups = getGroups({ onlyAudits }); @@ -40,6 +42,7 @@ export async function typescriptPlugin( tsconfig, expectedAudits: filteredAudits, }), + ...(scoreTargets && { scoreTargets }), }; } diff --git a/packages/plugin-typescript/src/lib/typescript-plugin.unit.test.ts b/packages/plugin-typescript/src/lib/typescript-plugin.unit.test.ts index 838298b7d..02393f5bd 100644 --- a/packages/plugin-typescript/src/lib/typescript-plugin.unit.test.ts +++ b/packages/plugin-typescript/src/lib/typescript-plugin.unit.test.ts @@ -37,4 +37,29 @@ describe('typescriptPlugin-config-object', () => { } as unknown as TypescriptPluginOptions), ).rejects.toThrow(/invalid_type/); }); + + it('should pass scoreTargets to PluginConfig when provided', async () => { + const pluginConfig = await typescriptPlugin({ + scoreTargets: 0.8, + }); + + expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); + expect(pluginConfig.scoreTargets).toBe(0.8); + }); + + it('should pass object scoreTargets to PluginConfig', async () => { + const scoreTargets = { 'no-implicit-any-errors': 0.9 }; + const pluginConfig = await typescriptPlugin({ + scoreTargets, + }); + + expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); + expect(pluginConfig.scoreTargets).toStrictEqual(scoreTargets); + }); + + it('should not have scoreTargets when not provided', async () => { + const pluginConfig = await typescriptPlugin(); + + expect(pluginConfig.scoreTargets).toBeUndefined(); + }); }); From 12853149d9e65849de771911dbf2b7d08a581717 Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Tue, 2 Sep 2025 13:19:00 -0400 Subject: [PATCH 07/15] docs(cli): reference PluginScoreTargets --- packages/models/docs/models-reference.md | 40 +++++++++++++++--------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/packages/models/docs/models-reference.md b/packages/models/docs/models-reference.md index c2aa5bac8..9965cc111 100644 --- a/packages/models/docs/models-reference.md +++ b/packages/models/docs/models-reference.md @@ -1290,21 +1290,21 @@ _(\*) Required._ _Object containing the following properties:_ -| Property | Description | Type | -| :---------------- | :--------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- | -| `packageName` | NPM package name | `string` | -| `version` | NPM version of the package | `string` | -| **`title`** (\*) | Descriptive name | `string` (_max length: 256_) | -| `description` | Description (markdown) | `string` (_max length: 65536_) | -| `docsUrl` | Plugin documentation site | `string` (_url_) (_optional_) _or_ `''` | -| `isSkipped` | | `boolean` | -| **`slug`** (\*) | Unique plugin slug within core config | [Slug](#slug) | -| **`icon`** (\*) | Icon from VSCode Material Icons extension | [MaterialIcon](#materialicon) | -| **`runner`** (\*) | | [RunnerConfig](#runnerconfig) _or_ [RunnerFunction](#runnerfunction) | -| **`audits`** (\*) | List of audits maintained in a plugin | _Array of at least 1 [Audit](#audit) items_ | -| `groups` | List of groups | _Array of [Group](#group) items_ | -| `scoreTargets` | Score targets that trigger a perfect score. Number for all audits or record { slug: target } for specific audits | `number` (_≥0, ≤1_) (_optional_) _or_ _Object with dynamic keys of type_ `string` _and values of type_ `number` (_≥0, ≤1_) | -| `context` | Plugin-specific context data for helpers | [PluginContext](#plugincontext) | +| Property | Description | Type | +| :---------------- | :--------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------- | +| `packageName` | NPM package name | `string` | +| `version` | NPM version of the package | `string` | +| **`title`** (\*) | Descriptive name | `string` (_max length: 256_) | +| `description` | Description (markdown) | `string` (_max length: 65536_) | +| `docsUrl` | Plugin documentation site | `string` (_url_) (_optional_) _or_ `''` | +| `isSkipped` | | `boolean` | +| **`slug`** (\*) | Unique plugin slug within core config | [Slug](#slug) | +| **`icon`** (\*) | Icon from VSCode Material Icons extension | [MaterialIcon](#materialicon) | +| **`runner`** (\*) | | [RunnerConfig](#runnerconfig) _or_ [RunnerFunction](#runnerfunction) | +| **`audits`** (\*) | List of audits maintained in a plugin | _Array of at least 1 [Audit](#audit) items_ | +| `groups` | List of groups | _Array of [Group](#group) items_ | +| `scoreTargets` | Score targets that trigger a perfect score. Number for all audits or record { slug: target } for specific audits | [PluginScoreTargets](#pluginscoretargets) | +| `context` | Plugin-specific context data for helpers | [PluginContext](#plugincontext) | _(\*) Required._ @@ -1356,6 +1356,16 @@ _Object containing the following properties:_ _(\*) Required._ +## PluginScoreTargets + +Score targets that trigger a perfect score. Number for all audits or record { slug: target } for specific audits + +_Union of the following possible types:_ + +- `number` (_≥0, ≤1_) (_optional_) +- _Object with dynamic keys of type_ `string` _and values of type_ `number` (_≥0, ≤1_) + (_optional_) + ## Report Collect output data From c245ccb816d9277a32a579cfdb01fcaef1e22971 Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Tue, 2 Sep 2025 13:19:21 -0400 Subject: [PATCH 08/15] feat: add pluginScoreTargetsSchema to exports --- packages/models/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index 7e64d5403..370d64e7c 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -86,6 +86,7 @@ export { pluginConfigSchema, pluginContextSchema, pluginMetaSchema, + pluginScoreTargetsSchema, type PluginConfig, type PluginContext, type PluginMeta, From ea9f6fbb16763216848d0c453fa07fe567f0e127 Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Tue, 2 Sep 2025 13:20:30 -0400 Subject: [PATCH 09/15] fix(models): update variable name in tests --- packages/models/src/lib/plugin-config.unit.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/models/src/lib/plugin-config.unit.test.ts b/packages/models/src/lib/plugin-config.unit.test.ts index 69ccaa9ff..3c6fe2d74 100644 --- a/packages/models/src/lib/plugin-config.unit.test.ts +++ b/packages/models/src/lib/plugin-config.unit.test.ts @@ -40,7 +40,7 @@ describe('pluginConfigSchema', () => { ).not.toThrow(); }); - it('should accept a valid plugin configuration with a score target', () => { + it('should accept a valid plugin configuration with score targets', () => { expect(() => pluginConfigSchema.parse({ slug: 'lighthouse', @@ -60,7 +60,7 @@ describe('pluginConfigSchema', () => { displayValue: '180 ms', }, ], - scoreTarget: { 'total-blocking-time': 0.9 }, + scoreTargets: { 'total-blocking-time': 0.9 }, audits: [ { slug: 'first-contentful-paint', title: 'First Contentful Paint' }, { slug: 'total-blocking-time', title: 'Total Blocking Time' }, From f7dec24fca50fdb364d31fe2939eb5a9df88833b Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Tue, 2 Sep 2025 13:59:03 -0400 Subject: [PATCH 10/15] feat(plugin-coverage): replace perfectScoreThreshold with scoreTargets --- packages/plugin-coverage/README.md | 2 +- packages/plugin-coverage/src/lib/config.ts | 10 +--- .../src/lib/config.unit.test.ts | 24 ++++++++-- .../src/lib/coverage-plugin.ts | 3 ++ .../src/lib/coverage-plugin.unit.test.ts | 28 ++++++++++- .../plugin-coverage/src/lib/runner/index.ts | 13 +---- .../src/lib/runner/runner.int.test.ts | 5 +- packages/plugin-coverage/src/lib/utils.ts | 16 ------- .../src/lib/utils.unit.test.ts | 47 ------------------- 9 files changed, 57 insertions(+), 91 deletions(-) delete mode 100644 packages/plugin-coverage/src/lib/utils.unit.test.ts diff --git a/packages/plugin-coverage/README.md b/packages/plugin-coverage/README.md index dec029c3a..cfa6fd5e6 100644 --- a/packages/plugin-coverage/README.md +++ b/packages/plugin-coverage/README.md @@ -128,7 +128,7 @@ The plugin accepts the following parameters: - For a single project, providing paths to results as strings is enough. - If you have a monorepo, both path to results (`resultsPath`) and path from the root to project the results belong to (`pathToProject`) need to be provided for the LCOV format. For Nx monorepos, you can use our helper function `getNxCoveragePaths` to get the path information automatically. - (optional) `coverageToolCommand`: If you wish to run your coverage tool to generate the results first, you may define it here. -- (optional) `perfectScoreThreshold`: If your coverage goal is not 100%, you may define it here in range 0-1. Any score above the defined threshold will be given the perfect score. The value will stay unaffected. +- (optional) `scoreTargets`: If your coverage goal is not 100%, you may define it here in range 0-1. Any score above the defined threshold will be given the perfect score. The value will stay unaffected. ### Audits and group diff --git a/packages/plugin-coverage/src/lib/config.ts b/packages/plugin-coverage/src/lib/config.ts index 4db726ed0..daf51c182 100644 --- a/packages/plugin-coverage/src/lib/config.ts +++ b/packages/plugin-coverage/src/lib/config.ts @@ -1,4 +1,5 @@ import { z } from 'zod'; +import { pluginScoreTargetsSchema } from '@code-pushup/models'; export const coverageTypeSchema = z.enum(['function', 'branch', 'line']); export type CoverageType = z.infer; @@ -50,14 +51,7 @@ export const coveragePluginConfigSchema = z.object({ .describe( 'Path to all code coverage report files. Only LCOV format is supported for now.', ), - perfectScoreThreshold: z - .number() - .gt(0) - .max(1) - .describe( - 'Score will be 1 (perfect) for this coverage and above. Score range is 0 - 1.', - ) - .optional(), + scoreTargets: pluginScoreTargetsSchema, }); export type CoveragePluginConfig = z.input; export type FinalCoveragePluginConfig = z.infer< diff --git a/packages/plugin-coverage/src/lib/config.unit.test.ts b/packages/plugin-coverage/src/lib/config.unit.test.ts index 517f8231a..765ab447e 100644 --- a/packages/plugin-coverage/src/lib/config.unit.test.ts +++ b/packages/plugin-coverage/src/lib/config.unit.test.ts @@ -20,7 +20,7 @@ describe('coveragePluginConfigSchema', () => { command: 'npx nx run-many', args: ['-t', 'test', '--coverage'], }, - perfectScoreThreshold: 0.85, + scoreTargets: 0.85, } satisfies CoveragePluginConfig), ).not.toThrow(); }); @@ -33,6 +33,24 @@ describe('coveragePluginConfigSchema', () => { ).not.toThrow(); }); + it('accepts number scoreTargets', () => { + expect(() => + coveragePluginConfigSchema.parse({ + reports: ['coverage/cli/lcov.info'], + scoreTargets: 0.8, + } satisfies CoveragePluginConfig), + ).not.toThrow(); + }); + + it('accepts object scoreTargets', () => { + expect(() => + coveragePluginConfigSchema.parse({ + reports: ['coverage/cli/lcov.info'], + scoreTargets: { 'function-coverage': 0.9 }, + } satisfies CoveragePluginConfig), + ).not.toThrow(); + }); + it('replaces undefined coverage with all available types', () => { const config = { reports: ['coverage/cli/lcov.info'], @@ -86,12 +104,12 @@ describe('coveragePluginConfigSchema', () => { ).toThrow('invalid_type'); }); - it('throws for invalid score threshold', () => { + it('throws for invalid score targets', () => { expect(() => coveragePluginConfigSchema.parse({ coverageTypes: ['line'], reports: ['coverage/cli/lcov.info'], - perfectScoreThreshold: 1.1, + scoreTargets: 1.1, } satisfies CoveragePluginConfig), ).toThrow('too_big'); }); diff --git a/packages/plugin-coverage/src/lib/coverage-plugin.ts b/packages/plugin-coverage/src/lib/coverage-plugin.ts index 72e037d0b..2727f3f73 100644 --- a/packages/plugin-coverage/src/lib/coverage-plugin.ts +++ b/packages/plugin-coverage/src/lib/coverage-plugin.ts @@ -65,6 +65,8 @@ export async function coveragePlugin( '../../package.json', ) as typeof import('../../package.json'); + const scoreTargets = coverageConfig.scoreTargets; + return { slug: 'coverage', title: 'Code coverage', @@ -76,5 +78,6 @@ export async function coveragePlugin( audits, groups: [group], runner: await createRunnerConfig(runnerScriptPath, coverageConfig), + ...(scoreTargets && { scoreTargets }), }; } diff --git a/packages/plugin-coverage/src/lib/coverage-plugin.unit.test.ts b/packages/plugin-coverage/src/lib/coverage-plugin.unit.test.ts index cfa60083d..949960aad 100644 --- a/packages/plugin-coverage/src/lib/coverage-plugin.unit.test.ts +++ b/packages/plugin-coverage/src/lib/coverage-plugin.unit.test.ts @@ -1,6 +1,6 @@ import path from 'node:path'; import { describe, expect, it } from 'vitest'; -import type { RunnerConfig } from '@code-pushup/models'; +import { type RunnerConfig, pluginConfigSchema } from '@code-pushup/models'; import { coveragePlugin } from './coverage-plugin.js'; vi.mock('./runner/index.ts', () => ({ @@ -81,4 +81,30 @@ describe('coveragePlugin', () => { }), ); }); + + it('should pass scoreTargets to PluginConfig when provided', async () => { + const pluginConfig = await coveragePlugin({ + reports: [LCOV_PATH], + scoreTargets: 0.8, + }); + + expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); + expect(pluginConfig.scoreTargets).toBe(0.8); + }); + + it('should pass object scoreTargets to PluginConfig', async () => { + const scoreTargets = { 'function-coverage': 0.9, 'line-coverage': 0.8 }; + const pluginConfig = await coveragePlugin({ + reports: [LCOV_PATH], + scoreTargets, + }); + + expect(pluginConfig.scoreTargets).toStrictEqual(scoreTargets); + }); + + it('should not have scoreTargets when not provided', async () => { + const pluginConfig = await coveragePlugin({ reports: [LCOV_PATH] }); + + expect(pluginConfig.scoreTargets).toBeUndefined(); + }); }); diff --git a/packages/plugin-coverage/src/lib/runner/index.ts b/packages/plugin-coverage/src/lib/runner/index.ts index 6f28d9940..f22ed5476 100644 --- a/packages/plugin-coverage/src/lib/runner/index.ts +++ b/packages/plugin-coverage/src/lib/runner/index.ts @@ -1,11 +1,7 @@ import { bold } from 'ansis'; import { writeFile } from 'node:fs/promises'; import path from 'node:path'; -import type { - AuditOutputs, - RunnerConfig, - RunnerFilesPaths, -} from '@code-pushup/models'; +import type { RunnerConfig, RunnerFilesPaths } from '@code-pushup/models'; import { ProcessError, createRunnerFiles, @@ -17,7 +13,6 @@ import { ui, } from '@code-pushup/utils'; import type { FinalCoveragePluginConfig } from '../config.js'; -import { applyMaxScoreAboveThreshold } from '../utils.js'; import { lcovResultsToAuditOutputs } from './lcov/lcov-runner.js'; export async function executeRunner({ @@ -68,8 +63,6 @@ export async function createRunnerConfig( JSON.stringify(config), ); - const threshold = config.perfectScoreThreshold; - return { command: 'node', args: [ @@ -78,9 +71,5 @@ export async function createRunnerConfig( ], configFile: runnerConfigPath, outputFile: runnerOutputPath, - ...(threshold != null && { - outputTransform: outputs => - applyMaxScoreAboveThreshold(outputs as AuditOutputs, threshold), - }), }; } diff --git a/packages/plugin-coverage/src/lib/runner/runner.int.test.ts b/packages/plugin-coverage/src/lib/runner/runner.int.test.ts index 17dcda01e..91c2579b7 100644 --- a/packages/plugin-coverage/src/lib/runner/runner.int.test.ts +++ b/packages/plugin-coverage/src/lib/runner/runner.int.test.ts @@ -11,7 +11,7 @@ describe('createRunnerConfig', () => { const runnerConfig = await createRunnerConfig('executeRunner.ts', { reports: ['coverage/lcov.info'], coverageTypes: ['branch'], - perfectScoreThreshold: 85, + scoreTargets: 0.85, continueOnCommandFail: true, }); expect(runnerConfig).toStrictEqual({ @@ -21,7 +21,6 @@ describe('createRunnerConfig', () => { expect.stringContaining('plugin-config.json'), expect.stringContaining('runner-output.json'), ], - outputTransform: expect.any(Function), outputFile: expect.stringContaining('runner-output.json'), configFile: expect.stringContaining('plugin-config.json'), }); @@ -32,7 +31,7 @@ describe('createRunnerConfig', () => { coverageTypes: ['line'], reports: ['coverage/lcov.info'], coverageToolCommand: { command: 'npm', args: ['run', 'test'] }, - perfectScoreThreshold: 85, + scoreTargets: 0.85, continueOnCommandFail: true, }; diff --git a/packages/plugin-coverage/src/lib/utils.ts b/packages/plugin-coverage/src/lib/utils.ts index 371f36a63..79d7badac 100644 --- a/packages/plugin-coverage/src/lib/utils.ts +++ b/packages/plugin-coverage/src/lib/utils.ts @@ -1,4 +1,3 @@ -import type { AuditOutputs } from '@code-pushup/models'; import type { CoverageType } from './config.js'; export const coverageDescription: Record = { @@ -8,21 +7,6 @@ export const coverageDescription: Record = { function: 'Measures how many functions were called in at least one test.', }; -/** - * Since more code coverage does not necessarily mean better score, this optional override allows for defining custom coverage goals. - * @param outputs original results - * @param threshold threshold above which the score is to be 1 - * @returns Outputs with overriden score (not value) to 1 if it reached a defined threshold. - */ -export function applyMaxScoreAboveThreshold( - outputs: AuditOutputs, - threshold: number, -): AuditOutputs { - return outputs.map(output => - output.score >= threshold ? { ...output, score: 1 } : output, - ); -} - export const coverageTypeWeightMapper: Record = { /* eslint-disable @typescript-eslint/no-magic-numbers */ function: 6, diff --git a/packages/plugin-coverage/src/lib/utils.unit.test.ts b/packages/plugin-coverage/src/lib/utils.unit.test.ts deleted file mode 100644 index d1ccedc1b..000000000 --- a/packages/plugin-coverage/src/lib/utils.unit.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import type { AuditOutput } from '@code-pushup/models'; -import { applyMaxScoreAboveThreshold } from './utils.js'; - -describe('applyMaxScoreAboveThreshold', () => { - it('should transform score above threshold to maximum', () => { - expect( - applyMaxScoreAboveThreshold( - [ - { - slug: 'branch-coverage', - value: 75, - score: 0.75, - }, - ], - 0.7, - ), - ).toEqual([ - { - slug: 'branch-coverage', - value: 75, - score: 1, - }, - ]); - }); - - it('should leave score below threshold untouched', () => { - expect( - applyMaxScoreAboveThreshold( - [ - { - slug: 'line-coverage', - value: 60, - score: 0.6, - }, - ], - 0.7, - ), - ).toEqual([ - { - slug: 'line-coverage', - value: 60, - score: 0.6, - }, - ]); - }); -}); From 9a3c1bcca039cd99944d09bd597f503ee6b2907c Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Tue, 2 Sep 2025 17:00:40 -0400 Subject: [PATCH 11/15] test: add snapshot test for scoreTargets effect on category scores --- .../generate-md-report.unit.test.ts.snap | 99 +++++++++++++ .../reports/generate-md-report.unit.test.ts | 137 ++++++++++++++++++ 2 files changed, 236 insertions(+) diff --git a/packages/utils/src/lib/reports/__snapshots__/generate-md-report.unit.test.ts.snap b/packages/utils/src/lib/reports/__snapshots__/generate-md-report.unit.test.ts.snap index 29b525112..ef537b024 100644 --- a/packages/utils/src/lib/reports/__snapshots__/generate-md-report.unit.test.ts.snap +++ b/packages/utils/src/lib/reports/__snapshots__/generate-md-report.unit.test.ts.snap @@ -176,6 +176,105 @@ Made with ❤ by [Code PushUp](https://github.com/code-pushup/cli#readme) " `; +exports[`generateMdReport > should show scoreTargets effect on category scores (before vs after) 1`] = ` +{ + "withScoreTargets": "# Code PushUp Report + +| 🏷 Category | ⭐ Score | 🛡 Audits | +| :------------------------------ | :-------: | :-------: | +| [Code Coverage](#code-coverage) | 🟢 **97** | 3 | + +## 🏷 Categories + +### Code Coverage + +🟢 Score: **97** + +- 🟢 Code Coverage (_Code coverage_) + - 🟨 [Line Coverage](#line-coverage-code-coverage) - **70%** + - 🟩 [Function Coverage](#function-coverage-code-coverage) - **100%** + - 🟩 [Branch Coverage](#branch-coverage-code-coverage) - **80%** + +## 🛡️ Audits + +### Function Coverage (Code coverage) + +🟩 **100%** (score: 100) + +### Line Coverage (Code coverage) + +🟨 **70%** (score: 70) + +### Branch Coverage (Code coverage) + +🟩 **80%** (score: 100) + +## About + +Report was created by [Code PushUp](https://github.com/code-pushup/cli#readme) on Wed, Jan 1, 2025, 12:00 AM UTC. + +| Plugin | Audits | Version | Duration | +| :------------ | :----: | :------: | -------: | +| Code coverage | 3 | \`v1.0.0\` | 5.00 s | + +| Commit | Version | Duration | Plugins | Categories | Audits | +| :----------------------------------------------------------- | :------: | -------: | :-----: | :--------: | :----: | +| ci: update action (535b8e9e557336618a764f3fa45609d224a62837) | \`v1.0.0\` | 4.20 s | 1 | 1 | 3 | + +--- + +Made with ❤ by [Code PushUp](https://github.com/code-pushup/cli#readme) +", + "withoutScoreTargets": "# Code PushUp Report + +| 🏷 Category | ⭐ Score | 🛡 Audits | +| :------------------------------ | :-------: | :-------: | +| [Code Coverage](#code-coverage) | 🟢 **91** | 3 | + +## 🏷 Categories + +### Code Coverage + +🟢 Score: **91** + +- 🟢 Code Coverage (_Code coverage_) + - 🟨 [Line Coverage](#line-coverage-code-coverage) - **70%** + - 🟨 [Branch Coverage](#branch-coverage-code-coverage) - **80%** + - 🟩 [Function Coverage](#function-coverage-code-coverage) - **100%** + +## 🛡️ Audits + +### Function Coverage (Code coverage) + +🟩 **100%** (score: 100) + +### Line Coverage (Code coverage) + +🟨 **70%** (score: 70) + +### Branch Coverage (Code coverage) + +🟨 **80%** (score: 80) + +## About + +Report was created by [Code PushUp](https://github.com/code-pushup/cli#readme) on Wed, Jan 1, 2025, 12:00 AM UTC. + +| Plugin | Audits | Version | Duration | +| :------------ | :----: | :------: | -------: | +| Code coverage | 3 | \`v1.0.0\` | 5.00 s | + +| Commit | Version | Duration | Plugins | Categories | Audits | +| :----------------------------------------------------------- | :------: | -------: | :-----: | :--------: | :----: | +| ci: update action (535b8e9e557336618a764f3fa45609d224a62837) | \`v1.0.0\` | 4.20 s | 1 | 1 | 3 | + +--- + +Made with ❤ by [Code PushUp](https://github.com/code-pushup/cli#readme) +", +} +`; + exports[`tableSection > should render complete section 1`] = ` "| Phase | % of LCP | Timing | | :----------: | :------: | :----: | diff --git a/packages/utils/src/lib/reports/generate-md-report.unit.test.ts b/packages/utils/src/lib/reports/generate-md-report.unit.test.ts index 91f47f7c9..bd994085d 100644 --- a/packages/utils/src/lib/reports/generate-md-report.unit.test.ts +++ b/packages/utils/src/lib/reports/generate-md-report.unit.test.ts @@ -916,4 +916,141 @@ module.exports = { }), ).toMatchSnapshot(); }); + + it('should show scoreTargets effect on category scores (before vs after)', () => { + const basePluginData: ScoredReport['plugins'][number] = { + date: '2025-08-02T09:00:00.000Z', + duration: 5000, + version: 'v1.0.0', + icon: 'folder-coverage-open', + packageName: '@code-pushup/coverage-plugin', + slug: 'coverage', + title: 'Code coverage', + audits: [ + { + slug: 'function-coverage', + title: 'Function Coverage', + score: 1, + value: 100, + displayValue: '100%', + }, + { + slug: 'line-coverage', + title: 'Line Coverage', + score: 0.7, + value: 70, + displayValue: '70%', + }, + { + slug: 'branch-coverage', + title: 'Branch Coverage', + score: 0.8, + value: 80, + displayValue: '80%', + }, + ], + groups: [ + { + slug: 'coverage', + title: 'Code Coverage', + score: 0.91, // (1*6 + 0.7*1 + 0.8*3) / 10 = 9.1/10 + refs: [ + { slug: 'function-coverage', weight: 6 }, + { slug: 'line-coverage', weight: 1 }, + { slug: 'branch-coverage', weight: 3 }, + ], + }, + ], + }; + + const reportWithoutTargets: ScoredReport = { + ...baseScoredReport, + plugins: [basePluginData], + categories: [ + { + title: 'Code Coverage', + slug: 'code-coverage', + score: 0.91, + refs: [ + { + type: 'group', + plugin: 'coverage', + slug: 'coverage', + weight: 1, + }, + ], + }, + ], + }; + + const reportWithTargets: ScoredReport = { + ...baseScoredReport, + plugins: [ + { + ...basePluginData, + audits: [ + { + slug: 'function-coverage', + title: 'Function Coverage', + score: 1, + value: 100, + displayValue: '100%', + scoreTarget: 0.75, + }, + { + slug: 'line-coverage', + title: 'Line Coverage', + score: 0.7, + value: 70, + displayValue: '70%', + scoreTarget: 0.75, + }, + { + slug: 'branch-coverage', + title: 'Branch Coverage', + score: 1, // 0.8 >= 0.75 + value: 80, + displayValue: '80%', + scoreTarget: 0.75, + }, + ], + groups: [ + { + slug: 'coverage', + title: 'Code Coverage', + score: 0.97, // (1*6 + 0.7*1 + 1*3) / 10 = 9.7/10 + refs: [ + { slug: 'function-coverage', weight: 6 }, + { slug: 'line-coverage', weight: 1 }, + { slug: 'branch-coverage', weight: 3 }, + ], + }, + ], + }, + ], + categories: [ + { + title: 'Code Coverage', + slug: 'code-coverage', + score: 0.97, + refs: [ + { + type: 'group', + plugin: 'coverage', + slug: 'coverage', + weight: 1, + }, + ], + }, + ], + }; + + const withoutTargetsReport = generateMdReport(reportWithoutTargets); + const withTargetsReport = generateMdReport(reportWithTargets); + + expect({ + withoutScoreTargets: withoutTargetsReport, + withScoreTargets: withTargetsReport, + }).toMatchSnapshot(); + }); }); From e1d0005dd254cb64aec369f6d47e2ec815e03e31 Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Tue, 2 Sep 2025 18:11:51 -0400 Subject: [PATCH 12/15] test(cli): replace mock implementation with spy --- .../src/lib/eslint-plugin.unit.test.ts | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/plugin-eslint/src/lib/eslint-plugin.unit.test.ts b/packages/plugin-eslint/src/lib/eslint-plugin.unit.test.ts index f583a3f4d..963074f5a 100644 --- a/packages/plugin-eslint/src/lib/eslint-plugin.unit.test.ts +++ b/packages/plugin-eslint/src/lib/eslint-plugin.unit.test.ts @@ -1,17 +1,20 @@ import { pluginConfigSchema } from '@code-pushup/models'; import { eslintPlugin } from './eslint-plugin.js'; - -vi.mock('./meta/index.js', () => ({ - listAuditsAndGroups: vi.fn().mockResolvedValue({ - audits: [ - { slug: 'type-safety', title: 'Type Safety' }, - { slug: 'no-empty', title: 'Disallow empty block statements' }, - ], - groups: [], - }), -})); +import * as metaModule from './meta/index.js'; describe('eslintPlugin', () => { + const listAuditsAndGroupsSpy = vi.spyOn(metaModule, 'listAuditsAndGroups'); + + beforeAll(() => { + listAuditsAndGroupsSpy.mockResolvedValue({ + audits: [ + { slug: 'type-safety', title: 'Type Safety' }, + { slug: 'no-empty', title: 'Disallow empty block statements' }, + ], + groups: [], + }); + }); + it('should pass scoreTargets to PluginConfig when provided', async () => { const pluginConfig = await eslintPlugin( { From 4ab9088488f531290068efd966af5dc06543564a Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Wed, 3 Sep 2025 10:16:59 -0400 Subject: [PATCH 13/15] fix: include scoreTarget only if it has value --- packages/ci/src/lib/portal/transform.ts | 2 +- packages/core/src/lib/implementation/report-to-gql.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ci/src/lib/portal/transform.ts b/packages/ci/src/lib/portal/transform.ts index 1c2d7bf1d..e93300a30 100644 --- a/packages/ci/src/lib/portal/transform.ts +++ b/packages/ci/src/lib/portal/transform.ts @@ -62,7 +62,7 @@ function transformGQLCategory(category: CategoryFragment): CategoryConfig { slug: category.slug, title: category.title, ...(category.description && { description: category.description }), - ...(category.scoreTarget && { scoreTarget: category.scoreTarget }), + ...(category.scoreTarget != null && { scoreTarget: category.scoreTarget }), refs: category.refs.map( ({ target, weight }): CategoryRef => ({ type: lowercase(target.__typename), diff --git a/packages/core/src/lib/implementation/report-to-gql.ts b/packages/core/src/lib/implementation/report-to-gql.ts index 36b882064..f70bc248d 100644 --- a/packages/core/src/lib/implementation/report-to-gql.ts +++ b/packages/core/src/lib/implementation/report-to-gql.ts @@ -199,7 +199,7 @@ function categoryToGQL(category: CategoryConfig): PortalCategory { slug: category.slug, title: category.title, description: category.description, - ...(category.scoreTarget && { scoreTarget: category.scoreTarget }), + ...(category.scoreTarget != null && { scoreTarget: category.scoreTarget }), refs: category.refs.map(ref => ({ plugin: ref.plugin, type: categoryRefTypeToGQL(ref.type), From 934ac1a1f80f2f6eabc985253e5d7f306d63979d Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Wed, 3 Sep 2025 10:22:46 -0400 Subject: [PATCH 14/15] test: remove redundant test cases --- .../src/lib/coverage-plugin.unit.test.ts | 17 +----------- .../src/lib/eslint-plugin.unit.test.ts | 27 +------------------ .../src/lib/js-packages-plugin.unit.test.ts | 16 ----------- .../src/lib/jsdocs-plugin.unit.test.ts | 18 ------------- .../src/lib/lighthouse-plugin.unit.test.ts | 15 ----------- .../src/lib/typescript-plugin.unit.test.ts | 15 ----------- 6 files changed, 2 insertions(+), 106 deletions(-) diff --git a/packages/plugin-coverage/src/lib/coverage-plugin.unit.test.ts b/packages/plugin-coverage/src/lib/coverage-plugin.unit.test.ts index 949960aad..6967a58f1 100644 --- a/packages/plugin-coverage/src/lib/coverage-plugin.unit.test.ts +++ b/packages/plugin-coverage/src/lib/coverage-plugin.unit.test.ts @@ -83,28 +83,13 @@ describe('coveragePlugin', () => { }); it('should pass scoreTargets to PluginConfig when provided', async () => { - const pluginConfig = await coveragePlugin({ - reports: [LCOV_PATH], - scoreTargets: 0.8, - }); - - expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); - expect(pluginConfig.scoreTargets).toBe(0.8); - }); - - it('should pass object scoreTargets to PluginConfig', async () => { const scoreTargets = { 'function-coverage': 0.9, 'line-coverage': 0.8 }; const pluginConfig = await coveragePlugin({ reports: [LCOV_PATH], scoreTargets, }); + expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); expect(pluginConfig.scoreTargets).toStrictEqual(scoreTargets); }); - - it('should not have scoreTargets when not provided', async () => { - const pluginConfig = await coveragePlugin({ reports: [LCOV_PATH] }); - - expect(pluginConfig.scoreTargets).toBeUndefined(); - }); }); diff --git a/packages/plugin-eslint/src/lib/eslint-plugin.unit.test.ts b/packages/plugin-eslint/src/lib/eslint-plugin.unit.test.ts index 963074f5a..4d8386dfd 100644 --- a/packages/plugin-eslint/src/lib/eslint-plugin.unit.test.ts +++ b/packages/plugin-eslint/src/lib/eslint-plugin.unit.test.ts @@ -7,28 +7,12 @@ describe('eslintPlugin', () => { beforeAll(() => { listAuditsAndGroupsSpy.mockResolvedValue({ - audits: [ - { slug: 'type-safety', title: 'Type Safety' }, - { slug: 'no-empty', title: 'Disallow empty block statements' }, - ], + audits: [{ slug: 'type-safety', title: 'Type Safety' }], groups: [], }); }); it('should pass scoreTargets to PluginConfig when provided', async () => { - const pluginConfig = await eslintPlugin( - { - eslintrc: 'eslint.config.js', - patterns: ['src/**/*.js'], - }, - { scoreTargets: 0.8 }, - ); - - expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); - expect(pluginConfig.scoreTargets).toBe(0.8); - }); - - it('should pass object scoreTargets to PluginConfig', async () => { const scoreTargets = { 'type-safety': 0.9 }; const pluginConfig = await eslintPlugin( { @@ -41,13 +25,4 @@ describe('eslintPlugin', () => { expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); expect(pluginConfig.scoreTargets).toStrictEqual(scoreTargets); }); - - it('should not have scoreTargets when not provided', async () => { - const pluginConfig = await eslintPlugin({ - eslintrc: 'eslint.config.js', - patterns: ['src/**/*.js'], - }); - - expect(pluginConfig.scoreTargets).toBeUndefined(); - }); }); diff --git a/packages/plugin-js-packages/src/lib/js-packages-plugin.unit.test.ts b/packages/plugin-js-packages/src/lib/js-packages-plugin.unit.test.ts index d5058aaab..2b8b90dcb 100644 --- a/packages/plugin-js-packages/src/lib/js-packages-plugin.unit.test.ts +++ b/packages/plugin-js-packages/src/lib/js-packages-plugin.unit.test.ts @@ -186,16 +186,6 @@ describe('jsPackagesPlugin', () => { }); it('should pass scoreTargets to PluginConfig when provided', async () => { - const pluginConfig = await jsPackagesPlugin({ - packageManager: 'npm', - scoreTargets: 0.8, - }); - - expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); - expect(pluginConfig.scoreTargets).toBe(0.8); - }); - - it('should pass object scoreTargets to PluginConfig', async () => { const scoreTargets = { 'npm-outdated-dev': 0.9 }; const pluginConfig = await jsPackagesPlugin({ packageManager: 'npm', @@ -205,10 +195,4 @@ describe('jsPackagesPlugin', () => { expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); expect(pluginConfig.scoreTargets).toStrictEqual(scoreTargets); }); - - it('should not have scoreTargets when not provided', async () => { - const pluginConfig = await jsPackagesPlugin({ packageManager: 'npm' }); - - expect(pluginConfig.scoreTargets).toBeUndefined(); - }); }); diff --git a/packages/plugin-jsdocs/src/lib/jsdocs-plugin.unit.test.ts b/packages/plugin-jsdocs/src/lib/jsdocs-plugin.unit.test.ts index 8774ff32e..19b6f9b0f 100644 --- a/packages/plugin-jsdocs/src/lib/jsdocs-plugin.unit.test.ts +++ b/packages/plugin-jsdocs/src/lib/jsdocs-plugin.unit.test.ts @@ -87,16 +87,6 @@ describe('jsDocsPlugin', () => { }); it('should pass scoreTargets to PluginConfig when provided', () => { - const pluginConfig = jsDocsPlugin({ - patterns: ['src/**/*.ts'], - scoreTargets: 0.9, - }); - - expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); - expect(pluginConfig.scoreTargets).toBe(0.9); - }); - - it('should pass object scoreTargets to PluginConfig', () => { const scoreTargets = { 'functions-coverage': 0.9 }; const pluginConfig = jsDocsPlugin({ patterns: ['src/**/*.ts'], @@ -106,12 +96,4 @@ describe('jsDocsPlugin', () => { expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); expect(pluginConfig.scoreTargets).toStrictEqual(scoreTargets); }); - - it('should not have scoreTargets when not provided', () => { - expect( - jsDocsPlugin({ - patterns: ['src/**/*.ts'], - }).scoreTargets, - ).toBeUndefined(); - }); }); diff --git a/packages/plugin-lighthouse/src/lib/lighthouse-plugin.unit.test.ts b/packages/plugin-lighthouse/src/lib/lighthouse-plugin.unit.test.ts index 89afc748e..753630bf8 100644 --- a/packages/plugin-lighthouse/src/lib/lighthouse-plugin.unit.test.ts +++ b/packages/plugin-lighthouse/src/lib/lighthouse-plugin.unit.test.ts @@ -122,15 +122,6 @@ describe('lighthousePlugin-config-object', () => { }); it('should pass scoreTargets to PluginConfig when provided', () => { - const pluginConfig = lighthousePlugin('https://code-pushup-portal.com', { - scoreTargets: 0.8, - }); - - expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); - expect(pluginConfig.scoreTargets).toBe(0.8); - }); - - it('should pass object scoreTargets to PluginConfig', () => { const scoreTargets = { 'first-contentful-paint': 0.9 }; const pluginConfig = lighthousePlugin('https://code-pushup-portal.com', { scoreTargets, @@ -139,10 +130,4 @@ describe('lighthousePlugin-config-object', () => { expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); expect(pluginConfig.scoreTargets).toStrictEqual(scoreTargets); }); - - it('should not have scoreTargets when not provided', () => { - expect( - lighthousePlugin('https://code-pushup-portal.com').scoreTargets, - ).toBeUndefined(); - }); }); diff --git a/packages/plugin-typescript/src/lib/typescript-plugin.unit.test.ts b/packages/plugin-typescript/src/lib/typescript-plugin.unit.test.ts index 02393f5bd..f3425389a 100644 --- a/packages/plugin-typescript/src/lib/typescript-plugin.unit.test.ts +++ b/packages/plugin-typescript/src/lib/typescript-plugin.unit.test.ts @@ -39,15 +39,6 @@ describe('typescriptPlugin-config-object', () => { }); it('should pass scoreTargets to PluginConfig when provided', async () => { - const pluginConfig = await typescriptPlugin({ - scoreTargets: 0.8, - }); - - expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); - expect(pluginConfig.scoreTargets).toBe(0.8); - }); - - it('should pass object scoreTargets to PluginConfig', async () => { const scoreTargets = { 'no-implicit-any-errors': 0.9 }; const pluginConfig = await typescriptPlugin({ scoreTargets, @@ -56,10 +47,4 @@ describe('typescriptPlugin-config-object', () => { expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow(); expect(pluginConfig.scoreTargets).toStrictEqual(scoreTargets); }); - - it('should not have scoreTargets when not provided', async () => { - const pluginConfig = await typescriptPlugin(); - - expect(pluginConfig.scoreTargets).toBeUndefined(); - }); }); From 4afb131d1a37f5acdfa2dae872fc2e96c3617780 Mon Sep 17 00:00:00 2001 From: hanna-skryl Date: Wed, 3 Sep 2025 12:00:54 -0400 Subject: [PATCH 15/15] test: remove scoreTargets effect test case --- .../generate-md-report.unit.test.ts.snap | 99 ------------- .../reports/generate-md-report.unit.test.ts | 137 ------------------ 2 files changed, 236 deletions(-) diff --git a/packages/utils/src/lib/reports/__snapshots__/generate-md-report.unit.test.ts.snap b/packages/utils/src/lib/reports/__snapshots__/generate-md-report.unit.test.ts.snap index ef537b024..29b525112 100644 --- a/packages/utils/src/lib/reports/__snapshots__/generate-md-report.unit.test.ts.snap +++ b/packages/utils/src/lib/reports/__snapshots__/generate-md-report.unit.test.ts.snap @@ -176,105 +176,6 @@ Made with ❤ by [Code PushUp](https://github.com/code-pushup/cli#readme) " `; -exports[`generateMdReport > should show scoreTargets effect on category scores (before vs after) 1`] = ` -{ - "withScoreTargets": "# Code PushUp Report - -| 🏷 Category | ⭐ Score | 🛡 Audits | -| :------------------------------ | :-------: | :-------: | -| [Code Coverage](#code-coverage) | 🟢 **97** | 3 | - -## 🏷 Categories - -### Code Coverage - -🟢 Score: **97** - -- 🟢 Code Coverage (_Code coverage_) - - 🟨 [Line Coverage](#line-coverage-code-coverage) - **70%** - - 🟩 [Function Coverage](#function-coverage-code-coverage) - **100%** - - 🟩 [Branch Coverage](#branch-coverage-code-coverage) - **80%** - -## 🛡️ Audits - -### Function Coverage (Code coverage) - -🟩 **100%** (score: 100) - -### Line Coverage (Code coverage) - -🟨 **70%** (score: 70) - -### Branch Coverage (Code coverage) - -🟩 **80%** (score: 100) - -## About - -Report was created by [Code PushUp](https://github.com/code-pushup/cli#readme) on Wed, Jan 1, 2025, 12:00 AM UTC. - -| Plugin | Audits | Version | Duration | -| :------------ | :----: | :------: | -------: | -| Code coverage | 3 | \`v1.0.0\` | 5.00 s | - -| Commit | Version | Duration | Plugins | Categories | Audits | -| :----------------------------------------------------------- | :------: | -------: | :-----: | :--------: | :----: | -| ci: update action (535b8e9e557336618a764f3fa45609d224a62837) | \`v1.0.0\` | 4.20 s | 1 | 1 | 3 | - ---- - -Made with ❤ by [Code PushUp](https://github.com/code-pushup/cli#readme) -", - "withoutScoreTargets": "# Code PushUp Report - -| 🏷 Category | ⭐ Score | 🛡 Audits | -| :------------------------------ | :-------: | :-------: | -| [Code Coverage](#code-coverage) | 🟢 **91** | 3 | - -## 🏷 Categories - -### Code Coverage - -🟢 Score: **91** - -- 🟢 Code Coverage (_Code coverage_) - - 🟨 [Line Coverage](#line-coverage-code-coverage) - **70%** - - 🟨 [Branch Coverage](#branch-coverage-code-coverage) - **80%** - - 🟩 [Function Coverage](#function-coverage-code-coverage) - **100%** - -## 🛡️ Audits - -### Function Coverage (Code coverage) - -🟩 **100%** (score: 100) - -### Line Coverage (Code coverage) - -🟨 **70%** (score: 70) - -### Branch Coverage (Code coverage) - -🟨 **80%** (score: 80) - -## About - -Report was created by [Code PushUp](https://github.com/code-pushup/cli#readme) on Wed, Jan 1, 2025, 12:00 AM UTC. - -| Plugin | Audits | Version | Duration | -| :------------ | :----: | :------: | -------: | -| Code coverage | 3 | \`v1.0.0\` | 5.00 s | - -| Commit | Version | Duration | Plugins | Categories | Audits | -| :----------------------------------------------------------- | :------: | -------: | :-----: | :--------: | :----: | -| ci: update action (535b8e9e557336618a764f3fa45609d224a62837) | \`v1.0.0\` | 4.20 s | 1 | 1 | 3 | - ---- - -Made with ❤ by [Code PushUp](https://github.com/code-pushup/cli#readme) -", -} -`; - exports[`tableSection > should render complete section 1`] = ` "| Phase | % of LCP | Timing | | :----------: | :------: | :----: | diff --git a/packages/utils/src/lib/reports/generate-md-report.unit.test.ts b/packages/utils/src/lib/reports/generate-md-report.unit.test.ts index bd994085d..91f47f7c9 100644 --- a/packages/utils/src/lib/reports/generate-md-report.unit.test.ts +++ b/packages/utils/src/lib/reports/generate-md-report.unit.test.ts @@ -916,141 +916,4 @@ module.exports = { }), ).toMatchSnapshot(); }); - - it('should show scoreTargets effect on category scores (before vs after)', () => { - const basePluginData: ScoredReport['plugins'][number] = { - date: '2025-08-02T09:00:00.000Z', - duration: 5000, - version: 'v1.0.0', - icon: 'folder-coverage-open', - packageName: '@code-pushup/coverage-plugin', - slug: 'coverage', - title: 'Code coverage', - audits: [ - { - slug: 'function-coverage', - title: 'Function Coverage', - score: 1, - value: 100, - displayValue: '100%', - }, - { - slug: 'line-coverage', - title: 'Line Coverage', - score: 0.7, - value: 70, - displayValue: '70%', - }, - { - slug: 'branch-coverage', - title: 'Branch Coverage', - score: 0.8, - value: 80, - displayValue: '80%', - }, - ], - groups: [ - { - slug: 'coverage', - title: 'Code Coverage', - score: 0.91, // (1*6 + 0.7*1 + 0.8*3) / 10 = 9.1/10 - refs: [ - { slug: 'function-coverage', weight: 6 }, - { slug: 'line-coverage', weight: 1 }, - { slug: 'branch-coverage', weight: 3 }, - ], - }, - ], - }; - - const reportWithoutTargets: ScoredReport = { - ...baseScoredReport, - plugins: [basePluginData], - categories: [ - { - title: 'Code Coverage', - slug: 'code-coverage', - score: 0.91, - refs: [ - { - type: 'group', - plugin: 'coverage', - slug: 'coverage', - weight: 1, - }, - ], - }, - ], - }; - - const reportWithTargets: ScoredReport = { - ...baseScoredReport, - plugins: [ - { - ...basePluginData, - audits: [ - { - slug: 'function-coverage', - title: 'Function Coverage', - score: 1, - value: 100, - displayValue: '100%', - scoreTarget: 0.75, - }, - { - slug: 'line-coverage', - title: 'Line Coverage', - score: 0.7, - value: 70, - displayValue: '70%', - scoreTarget: 0.75, - }, - { - slug: 'branch-coverage', - title: 'Branch Coverage', - score: 1, // 0.8 >= 0.75 - value: 80, - displayValue: '80%', - scoreTarget: 0.75, - }, - ], - groups: [ - { - slug: 'coverage', - title: 'Code Coverage', - score: 0.97, // (1*6 + 0.7*1 + 1*3) / 10 = 9.7/10 - refs: [ - { slug: 'function-coverage', weight: 6 }, - { slug: 'line-coverage', weight: 1 }, - { slug: 'branch-coverage', weight: 3 }, - ], - }, - ], - }, - ], - categories: [ - { - title: 'Code Coverage', - slug: 'code-coverage', - score: 0.97, - refs: [ - { - type: 'group', - plugin: 'coverage', - slug: 'coverage', - weight: 1, - }, - ], - }, - ], - }; - - const withoutTargetsReport = generateMdReport(reportWithoutTargets); - const withTargetsReport = generateMdReport(reportWithTargets); - - expect({ - withoutScoreTargets: withoutTargetsReport, - withScoreTargets: withTargetsReport, - }).toMatchSnapshot(); - }); });