Skip to content

Commit

Permalink
feat(utils): add audits sorting for reports (#302)
Browse files Browse the repository at this point in the history
Added audits sorting for both reports, md and stdout, added sorting of
audits at the categories and groups.
Closes #210
  • Loading branch information
MishaSeredenkoPushBased committed Nov 23, 2023
1 parent ee21143 commit 10ee12e
Show file tree
Hide file tree
Showing 7 changed files with 497 additions and 281 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,71 +6,71 @@ exports[`report-to-stdout > should contain all sections when using the fixture r
ESLint audits
● Disallow assignment operators in conditional expressions 0
● Disallow reassigning \`const\` variables 0
● Disallow the use of \`debugger\` 0
● Disallow invalid regular expression strings in \`RegExp\` 0
constructors
● Disallow the use of undeclared variables unless mentioned in 0
\`/*global */\` comments
● Disallow loops with a body that allows only one iteration 0
● Disallow negating the left operand of relational operators 0
● Disallow use of optional chaining in contexts where the 0
\`undefined\` value is not allowed
● Disallow unused variables 1 warning
● Require calls to \`isNaN()\` when checking for \`NaN\` 0
● Enforce comparing \`typeof\` expressions against valid strings 0
● Require braces around arrow function bodies 1 warning
● Enforce camelcase naming convention 0
● Enforce consistent brace style for all control statements 0
● Require the use of \`===\` and \`!==\` 1 warning
● Enforce a maximum number of lines of code in a function 1 warning
● Enforce a maximum number of lines per file 0
● Disallow missing props validation in a React component definition 6 warnings
● Disallow variable declarations from shadowing variables declared 3 warnings
in the outer scope
● Require \`let\` or \`const\` instead of \`var\` 0
● Require or disallow method and property shorthand syntax for 3 warnings
object literals
● Require using arrow functions for callbacks 0
● Require \`const\` declarations for variables that are never 1 warning
reassigned after declared
● Disallow using Object.assign with an object literal as the first 0
argument and prefer the use of object spread instead
● Require or disallow \\"Yoda\\" conditions 0
● Disallow missing \`key\` props in iterators/collection literals 1 warning
● Disallow missing props validation in a React component definition 6 warnings
● Disallow missing React when using JSX 0
● enforces the Rules of Hooks 0
● verifies the list of dependencies for Hooks like useEffect and 2 warnings
similar
● Disallow missing displayName in a React component definition 0
● Disallow missing \`key\` props in iterators/collection literals 1 warning
● Disallow unused variables 1 warning
● Enforce a maximum number of lines of code in a function 1 warning
● Require \`const\` declarations for variables that are never 1 warning
reassigned after declared
● Require braces around arrow function bodies 1 warning
● Require the use of \`===\` and \`!==\` 1 warning
● Disallow \`target=\\"_blank\\"\` attribute without \`rel=\\"noreferrer\\"\` 0
● Disallow assignment operators in conditional expressions 0
● Disallow comments from being inserted as text nodes 0
● Disallow direct mutation of this.state 0
● Disallow duplicate properties in JSX 0
● Disallow \`target=\\"_blank\\"\` attribute without \`rel=\\"noreferrer\\"\` 0
● Disallow undeclared variables in JSX 0
● Disallow React to be incorrectly marked as unused 0
● Disallow variables used in JSX to be incorrectly marked as unused 0
● Disallow invalid regular expression strings in \`RegExp\` 0
constructors
● Disallow loops with a body that allows only one iteration 0
● Disallow missing displayName in a React component definition 0
● Disallow missing React when using JSX 0
● Disallow negating the left operand of relational operators 0
● Disallow passing of children as props 0
● Disallow when a DOM element is using both children and 0
dangerouslySetInnerHTML
● Disallow React to be incorrectly marked as unused 0
● Disallow reassigning \`const\` variables 0
● Disallow the use of \`debugger\` 0
● Disallow the use of undeclared variables unless mentioned in 0
\`/*global */\` comments
● Disallow undeclared variables in JSX 0
● Disallow unescaped HTML entities from appearing in markup 0
● Disallow usage of deprecated methods 0
● Disallow direct mutation of this.state 0
● Disallow usage of findDOMNode 0
● Disallow usage of isMounted 0
● Disallow usage of the return value of ReactDOM.render 0
● Disallow using string references 0
● Disallow unescaped HTML entities from appearing in markup 0
● Disallow usage of unknown DOM property 0
● Disallow use of optional chaining in contexts where the 0
\`undefined\` value is not allowed
● Disallow using Object.assign with an object literal as the first 0
argument and prefer the use of object spread instead
● Disallow using string references 0
● Disallow variables used in JSX to be incorrectly marked as unused 0
● Disallow when a DOM element is using both children and 0
dangerouslySetInnerHTML
● Enforce a maximum number of lines per file 0
● Enforce camelcase naming convention 0
● Enforce comparing \`typeof\` expressions against valid strings 0
● Enforce consistent brace style for all control statements 0
● Enforce ES5 or ES6 class for returning value in render function 0
● enforces the Rules of Hooks 0
● Require \`let\` or \`const\` instead of \`var\` 0
● Require calls to \`isNaN()\` when checking for \`NaN\` 0
● Require or disallow \\"Yoda\\" conditions 0
● Require using arrow functions for callbacks 0
Lighthouse audits
● First Contentful Paint 1.2 s
● Largest Contentful Paint 1.5 s
● Total Blocking Time 0 ms
● Cumulative Layout Shift 0
● Speed Index 1.2 s
● Cumulative Layout Shift 0
● Total Blocking Time 0 ms
Categories
Expand Down
134 changes: 67 additions & 67 deletions packages/utils/src/lib/report-to-md.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
AuditReport,
CategoryConfig,
CategoryRef,
Issue,
PluginReport,
} from '@code-pushup/models';
import { CommitData } from './git';
import {
Expand All @@ -24,15 +24,24 @@ import {
detailsTableHeaders,
formatDuration,
formatReportScore,
getAuditByRef,
getGroupWithAudits,
getPluginNameFromSlug,
getRoundScoreMarker,
getSeverityIcon,
getSquaredScoreMarker,
pluginMetaTableHeaders,
reportHeadlineText,
reportMetaTableHeaders,
reportOverviewTableHeaders,
sortAudits,
sortCategoryAudits,
} from './report';
import { EnrichedScoredAuditGroup, ScoredReport } from './scoring';
import {
EnrichedScoredAuditGroupWithAudits,
ScoredReport,
WeighedAuditReport,
} from './scoring';
import { slugify } from './transformation';

export function reportToMd(
Expand Down Expand Up @@ -87,14 +96,37 @@ function reportToCategoriesSection(report: ScoredReport): string {
)} Score: ${style(formatReportScore(category.score))}`;
const categoryDocs = getDocsAndDescription(category);

const refs = category.refs.reduce((acc, ref) => {
if (ref.type === 'group') {
acc += groupRefItemToCategorySection(ref.slug, ref.plugin, plugins);
} else {
acc += auditRefItemToCategorySection(ref.slug, ref.plugin, plugins);
}
return acc;
}, '');
const auditsAndGroups = category.refs.reduce(
(
acc: {
audits: WeighedAuditReport[];
groups: EnrichedScoredAuditGroupWithAudits[];
},
ref: CategoryRef,
) => ({
...acc,
...(ref.type === 'group'
? {
groups: [
...acc.groups,
getGroupWithAudits(ref.slug, ref.plugin, plugins),
],
}
: {
audits: [...acc.audits, getAuditByRef(ref, plugins)],
}),
}),
{ groups: [], audits: [] },
);

const audits = auditsAndGroups.audits
.sort(sortCategoryAudits)
.map(audit => auditItemToCategorySection(audit, plugins))
.join(NEW_LINE);

const groups = auditsAndGroups.groups
.map(group => groupItemToCategorySection(group, plugins))
.join('');

return (
acc +
Expand All @@ -105,73 +137,43 @@ function reportToCategoriesSection(report: ScoredReport): string {
categoryDocs +
categoryScore +
NEW_LINE +
groups +
NEW_LINE +
refs
audits
);
}, '');

return h2('🏷 Categories') + NEW_LINE + categoryDetails;
}

function auditRefItemToCategorySection(
refSlug: string,
refPlugin: string,
function auditItemToCategorySection(
audit: WeighedAuditReport,
plugins: ScoredReport['plugins'],
): string {
const plugin = plugins.find(({ slug }) => slug === refPlugin) as PluginReport;
const pluginAudit = plugin?.audits.find(({ slug }) => slug === refSlug);

if (!pluginAudit) {
throwIsNotPresentError(`Audit ${refSlug}`, plugin?.slug);
}

const pluginTitle = getPluginNameFromSlug(audit.plugin, plugins);
const auditTitle = link(
`#${slugify(pluginAudit.title)}-${slugify(plugin.title)}`,
pluginAudit?.title,
`#${slugify(audit.title)}-${slugify(pluginTitle)}`,
audit?.title,
);

return (
li(
`${getSquaredScoreMarker(pluginAudit.score)} ${auditTitle} (_${
plugin.title
}_) - ${getAuditResult(pluginAudit)}`,
) + NEW_LINE
return li(
`${getSquaredScoreMarker(
audit.score,
)} ${auditTitle} (_${pluginTitle}_) - ${getAuditResult(audit)}`,
);
}

function groupRefItemToCategorySection(
refSlug: string,
refPlugin: string,
function groupItemToCategorySection(
group: EnrichedScoredAuditGroupWithAudits,
plugins: ScoredReport['plugins'],
): string {
const plugin = plugins.find(({ slug }) => slug === refPlugin) as PluginReport;
const group = plugin?.groups?.find(
({ slug }) => slug === refSlug,
) as EnrichedScoredAuditGroup;
const pluginTitle = getPluginNameFromSlug(group.plugin, plugins);
const groupScore = Number(formatReportScore(group?.score || 0));

if (!group) {
throwIsNotPresentError(`Group ${refSlug}`, plugin?.slug);
}

const groupTitle = li(
`${getRoundScoreMarker(groupScore)} ${group.title} (_${plugin.title}_)`,
`${getRoundScoreMarker(groupScore)} ${group.title} (_${pluginTitle}_)`,
);
const foundAudits = group.refs.reduce<AuditReport[]>((acc, ref) => {
const audit = plugin?.audits.find(
({ slug: auditSlugInPluginAudits }) =>
auditSlugInPluginAudits === ref.slug,
);
if (audit) {
return [...acc, audit];
}

return acc;
}, []);

const groupAudits = foundAudits.reduce((acc, audit) => {
const groupAudits = group.audits.reduce((acc, audit) => {
const auditTitle = link(
`#${slugify(audit.title)}-${slugify(plugin.title)}`,
`#${slugify(audit.title)}-${slugify(pluginTitle)}`,
audit?.title,
);
acc += ` ${li(
Expand All @@ -187,9 +189,12 @@ function groupRefItemToCategorySection(
}

function reportToAuditsSection(report: ScoredReport): string {
const auditsData = report.plugins.reduce((acc, plugin) => {
const audits = plugin.audits.reduce((acc, audit) => {
const auditTitle = `${audit.title} (${plugin.title})`;
const auditsSection = report.plugins.reduce((acc, plugin) => {
const auditsData = plugin.audits.sort(sortAudits).reduce((acc, audit) => {
const auditTitle = `${audit.title} (${getPluginNameFromSlug(
audit.plugin,
report.plugins,
)})`;
const detailsTitle = `${getSquaredScoreMarker(
audit.score,
)} ${getAuditResult(audit, true)} (score: ${formatReportScore(
Expand Down Expand Up @@ -243,11 +248,10 @@ function reportToAuditsSection(report: ScoredReport): string {

return acc;
}, '');

return acc + audits;
return acc + auditsData;
}, '');

return h2('🛡️ Audits') + NEW_LINE + NEW_LINE + auditsData;
return h2('🛡️ Audits') + NEW_LINE + NEW_LINE + auditsSection;
}

function reportToAboutSection(
Expand Down Expand Up @@ -325,7 +329,3 @@ function getAuditResult(audit: AuditReport, isHtml = false): string {
? `<b>${displayValue || value}</b>`
: style(String(displayValue || value));
}

function throwIsNotPresentError(itemName: string, presentPlace: string): never {
throw new Error(`${itemName} is not present in ${presentPlace}`);
}
3 changes: 2 additions & 1 deletion packages/utils/src/lib/report-to-stdout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
formatReportScore,
reportHeadlineText,
reportRawOverviewTableHeaders,
sortAudits,
} from './report';
import { ScoredReport } from './scoring';

Expand Down Expand Up @@ -73,7 +74,7 @@ function reportToDetailSection(report: ScoredReport): string {

const ui = cliui({ width: 80 });

audits.forEach(({ score, title, displayValue, value }) => {
audits.sort(sortAudits).forEach(({ score, title, displayValue, value }) => {
ui.div(
{
text: withColor({ score, text: '●' }),
Expand Down
Loading

0 comments on commit 10ee12e

Please sign in to comment.