Skip to content

Commit

Permalink
feat(plugin-lighthouse): add only filters logic for categories (#515)
Browse files Browse the repository at this point in the history
  • Loading branch information
BioPhoton committed Feb 29, 2024
1 parent b38620e commit 3cb8fa4
Show file tree
Hide file tree
Showing 12 changed files with 441 additions and 209 deletions.
9 changes: 5 additions & 4 deletions examples/plugins/src/lighthouse/src/lighthouse.plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import {
} from '@code-pushup/models';
import {
ensureDirectoryExists,
filterAuditsBySlug,
filterGroupsByAuditSlug,
filterItemRefsBy,
toArray,
} from '@code-pushup/utils';
import {
Expand Down Expand Up @@ -74,8 +73,10 @@ export async function create(options: PluginOptions) {
onlyCategories: ['performance'],
headless,
}),
audits: filterAuditsBySlug(audits, onlyAudits),
groups: filterGroupsByAuditSlug([categoryCorePerfGroup], onlyAudits),
audits: audits.filter(({ slug }) => onlyAudits.includes(slug)),
groups: filterItemRefsBy([categoryCorePerfGroup], ({ slug }) =>
onlyAudits.includes(slug),
),
} satisfies PluginConfig;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,12 @@
import { expect } from 'vitest';
import {
auditSchema,
groupSchema,
pluginConfigSchema,
} from '@code-pushup/models';
import { AUDITS, GROUPS } from './constants';
import { pluginConfigSchema } from '@code-pushup/models';
import { lighthousePlugin } from './lighthouse-plugin';

describe('lighthousePlugin', () => {
it('should create valid plugin config', () => {
const pluginConfig = lighthousePlugin({
url: 'https://code-pushup-portal.com',
});
const pluginConfig = lighthousePlugin('https://code-pushup-portal.com');
expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow();
expect(pluginConfig.audits).toHaveLength(168);
expect(pluginConfig.groups).toHaveLength(5);
});
});

describe('generated-constants', () => {
it.each(AUDITS.map(a => [a.slug, a]))(
'should parse audit "%s" correctly',
(_, audit) => {
expect(() => auditSchema.parse(audit)).not.toThrow();
expect(audit.description).toEqual(expect.any(String));
},
);

it.each(GROUPS.map(a => [a.slug, a]))(
'should parse group "%s" correctly',
(_, group) => {
expect(() => groupSchema.parse(group)).not.toThrow();
},
);
});
42 changes: 15 additions & 27 deletions packages/plugin-lighthouse/src/lib/lighthouse-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,28 @@
import { Audit, AuditOutputs, Group, PluginConfig } from '@code-pushup/models';
import {
filterAuditsBySlug,
filterGroupsByAuditSlug,
} from '@code-pushup/utils';
type Config as LighthouseConfig,
type CliFlags as LighthouseFlags,
} from 'lighthouse';
import { PluginConfig } from '@code-pushup/models';
import { AUDITS, GROUPS, LIGHTHOUSE_PLUGIN_SLUG } from './constants';
import { validateOnlyAudits } from './utils';

export type LighthousePluginOptions = {
url: string;
outputPath?: string;
onlyAudits?: string | string[];
verbose?: boolean;
headless?: boolean;
userDataDir?: string;
};
import { filterAuditsAndGroupsByOnlyOptions } from './utils';

export function lighthousePlugin(
options: LighthousePluginOptions,
url: string,
flags?: Partial<LighthouseFlags>,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
config?: Partial<LighthouseConfig>,
): PluginConfig {
const { onlyAudits = [] } = options;

validateOnlyAudits(AUDITS, onlyAudits);
const audits: Audit[] = filterAuditsBySlug(AUDITS, onlyAudits);
const groups: Group[] = filterGroupsByAuditSlug(GROUPS, onlyAudits);

const { audits, groups } = filterAuditsAndGroupsByOnlyOptions(
AUDITS,
GROUPS,
flags,
);
return {
slug: LIGHTHOUSE_PLUGIN_SLUG,
title: 'Lighthouse',
icon: 'lighthouse',
audits,
groups,
runner: (): AuditOutputs =>
audits.map(audit => ({
...audit,
score: 0,
value: 0,
})),
runner: () => audits.map(({ slug }) => ({ slug, value: 0, score: 0 })),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,15 @@ import { lighthousePlugin } from './lighthouse-plugin';

describe('lighthousePlugin-config-object', () => {
it('should create valid plugin config', () => {
const pluginConfig = lighthousePlugin({
url: 'https://code-pushup-portal.com',
});
const pluginConfig = lighthousePlugin('https://code-pushup-portal.com');
expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow();
expect(pluginConfig.audits.length).toBeGreaterThan(0);
expect(pluginConfig.groups?.length).toBeGreaterThan(0);
});

it('should filter audits by onlyAudits string "first-contentful-paint"', () => {
const pluginConfig = lighthousePlugin({
url: 'https://code-pushup-portal.com',
onlyAudits: 'first-contentful-paint',
const pluginConfig = lighthousePlugin('https://code-pushup-portal.com', {
onlyAudits: ['first-contentful-paint'],
});

expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow();
Expand All @@ -33,9 +30,8 @@ describe('lighthousePlugin-config-object', () => {
});

it('should filter groups by onlyAudits string "first-contentful-paint"', () => {
const pluginConfig = lighthousePlugin({
url: 'https://code-pushup-portal.com',
onlyAudits: 'first-contentful-paint',
const pluginConfig = lighthousePlugin('https://code-pushup-portal.com', {
onlyAudits: ['first-contentful-paint'],
});

expect(() => pluginConfigSchema.parse(pluginConfig)).not.toThrow();
Expand Down
75 changes: 68 additions & 7 deletions packages/plugin-lighthouse/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type { CliFlags } from 'lighthouse';
import { Audit } from '@code-pushup/models';
import { objectToCliArgs, toArray } from '@code-pushup/utils';
import type { CliFlags as LighthouseFlags } from 'lighthouse';
import { Audit, Group } from '@code-pushup/models';
import { filterItemRefsBy, objectToCliArgs, toArray } from '@code-pushup/utils';
import { LIGHTHOUSE_REPORT_NAME } from './constants';

type RefinedLighthouseOption = {
url: CliFlags['_'];
chromeFlags?: Record<CliFlags['chromeFlags'][number], string>;
url: LighthouseFlags['_'];
chromeFlags?: Record<LighthouseFlags['chromeFlags'][number], string>;
};
export type LighthouseCliOptions = RefinedLighthouseOption &
Partial<Omit<CliFlags, keyof RefinedLighthouseOption>>;
Partial<Omit<LighthouseFlags, keyof RefinedLighthouseOption>>;

export function getLighthouseCliArguments(
options: LighthouseCliOptions,
Expand Down Expand Up @@ -59,7 +59,7 @@ export class AuditsNotImplementedError extends Error {
export function validateOnlyAudits(
audits: Audit[],
onlyAudits: string | string[],
): audits is Audit[] {
): boolean {
const missingAudtis = toArray(onlyAudits).filter(
slug => !audits.some(audit => audit.slug === slug),
);
Expand All @@ -68,3 +68,64 @@ export function validateOnlyAudits(
}
return true;
}

export class CategoriesNotImplementedError extends Error {
constructor(categorySlugs: string[]) {
super(`categories: "${categorySlugs.join(', ')}" not implemented`);
}
}

export function validateOnlyCategories(
groups: Group[],
onlyCategories: string | string[],
): boolean {
const missingCategories = toArray(onlyCategories).filter(slug =>
groups.every(group => group.slug !== slug),
);
if (missingCategories.length > 0) {
throw new CategoriesNotImplementedError(missingCategories);
}
return true;
}

export function filterAuditsAndGroupsByOnlyOptions(
audits: Audit[],
groups: Group[],
options?: Pick<LighthouseFlags, 'onlyAudits' | 'onlyCategories'>,
): {
audits: Audit[];
groups: Group[];
} {
const { onlyAudits, onlyCategories } = options ?? {};

// category wins over audits
if (onlyCategories && onlyCategories.length > 0) {
validateOnlyCategories(groups, onlyCategories);

const categorieSlugs = new Set(onlyCategories);
const filteredGroups: Group[] = groups.filter(({ slug }) =>
categorieSlugs.has(slug),
);
const auditSlugsFromRemainingGroups = new Set(
filteredGroups.flatMap(({ refs }) => refs.map(({ slug }) => slug)),
);
return {
audits: audits.filter(({ slug }) =>
auditSlugsFromRemainingGroups.has(slug),
),
groups: filteredGroups,
};
} else if (onlyAudits && onlyAudits.length > 0) {
validateOnlyAudits(audits, onlyAudits);
const auditSlugs = new Set(onlyAudits);
return {
audits: audits.filter(({ slug }) => auditSlugs.has(slug)),
groups: filterItemRefsBy(groups, ({ slug }) => auditSlugs.has(slug)),
};
}
// return unchanged
return {
audits,
groups,
};
}
Loading

0 comments on commit 3cb8fa4

Please sign in to comment.