Skip to content

Commit 3c11de5

Browse files
committed
Sort issues by audit impact
1 parent d32e91c commit 3c11de5

File tree

5 files changed

+205
-11
lines changed

5 files changed

+205
-11
lines changed

__tests__/issues.test.ts

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { issuesMatch } from '../src/issues'
1+
import { getAuditImpactValue, issuesMatch } from '../src/issues'
2+
import type { Report } from '../src/models'
23

34
describe('issues comparison', () => {
45
it('should match issues with exact same metadata', () => {
@@ -163,3 +164,111 @@ describe('issues comparison', () => {
163164
).toBe(true)
164165
})
165166
})
167+
168+
describe('issues sorting', () => {
169+
it('should sum category contributions to calculate audit impact', () => {
170+
expect(
171+
getAuditImpactValue(
172+
{
173+
audit: {
174+
slug: 'react-jsx-key',
175+
title:
176+
'Disallow missing `key` props in iterators/collection literals'
177+
},
178+
plugin: { slug: 'eslint', title: 'ESLint' }
179+
},
180+
{
181+
categories: [
182+
{
183+
title: 'Performance',
184+
// contributes 1%
185+
refs: [
186+
{
187+
type: 'group',
188+
plugin: 'lighthouse',
189+
slug: 'perf',
190+
weight: 99
191+
},
192+
{
193+
type: 'audit',
194+
plugin: 'eslint',
195+
slug: 'react-jsx-key',
196+
weight: 1
197+
}
198+
]
199+
},
200+
{
201+
title: 'Accessibility',
202+
// 0 contribution
203+
refs: [
204+
{
205+
type: 'group',
206+
plugin: 'lighthouse',
207+
slug: 'a11y',
208+
weight: 100
209+
}
210+
]
211+
},
212+
{
213+
title: 'Code quality',
214+
refs: [
215+
{
216+
// group contributes 80%
217+
// audit contributes 10% in group
218+
// => audit contributes 8% in category
219+
type: 'group',
220+
plugin: 'eslint',
221+
slug: 'problems',
222+
weight: 4
223+
},
224+
{
225+
// 0 contribution
226+
type: 'group',
227+
plugin: 'eslint',
228+
slug: 'suggestions',
229+
weight: 1
230+
}
231+
]
232+
}
233+
],
234+
plugins: [
235+
{
236+
slug: 'eslint',
237+
groups: [
238+
{
239+
slug: 'problems',
240+
// contributes 10%
241+
refs: [
242+
...Array.from({ length: 9 }).map((_, i) => ({
243+
slug: `mock-rule-${i}`,
244+
weight: 1
245+
})),
246+
{
247+
slug: 'react-jsx-key',
248+
weight: 1
249+
}
250+
]
251+
},
252+
{
253+
slug: 'suggestions',
254+
// 0 contribution
255+
refs: Array.from({ length: 10 }).map((_, i) => ({
256+
slug: `mock-rule-${10 + i}`,
257+
weight: 1
258+
}))
259+
}
260+
]
261+
},
262+
{
263+
slug: 'lighthouse',
264+
groups: [
265+
{ slug: 'performance', refs: [] },
266+
{ slug: 'a11y', refs: [] }
267+
]
268+
}
269+
]
270+
} as Report
271+
)
272+
).toBe(0.09) // 1% + 8% = 9%
273+
})
274+
})

badges/coverage.svg

Lines changed: 1 addition & 1 deletion
Loading

dist/index.js

Lines changed: 38 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/issues.ts

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ type IssueContext = {
2020
plugin: Pick<PluginMeta, 'slug' | 'title'>
2121
}
2222

23+
type IssueCompareFn = (a: SourceFileIssue, b: SourceFileIssue) => number
24+
2325
export function filterRelevantIssues({
2426
currReport,
2527
prevReport,
@@ -51,11 +53,15 @@ export function filterRelevantIssues({
5153
plugin.audits.flatMap(audit => getAuditIssues(audit, plugin))
5254
)
5355

54-
return issues.filter(
55-
issue =>
56-
isFileChanged(changedFiles, issue.source.file) &&
57-
!prevIssues.some(prevIssue => issuesMatch(prevIssue, issue, changedFiles))
58-
)
56+
return issues
57+
.filter(
58+
issue =>
59+
isFileChanged(changedFiles, issue.source.file) &&
60+
!prevIssues.some(prevIssue =>
61+
issuesMatch(prevIssue, issue, changedFiles)
62+
)
63+
)
64+
.sort(createIssuesSortCompareFn(currReport))
5965
}
6066

6167
function getAuditIssues(
@@ -138,3 +144,47 @@ function adjustedLineSpansMatch(
138144
curr.position.startLine
139145
return prevLineCount === currLineCount - currStartLineOffset
140146
}
147+
148+
function createIssuesSortCompareFn(report: Report): IssueCompareFn {
149+
return (a, b) =>
150+
getAuditImpactValue(b, report) - getAuditImpactValue(a, report)
151+
}
152+
153+
export function getAuditImpactValue(
154+
{ audit, plugin }: IssueContext,
155+
report: Report
156+
): number {
157+
return report.categories
158+
.map((category): number => {
159+
const weights = category.refs.map((ref): number => {
160+
if (ref.plugin !== plugin.slug) {
161+
return 0
162+
}
163+
164+
switch (ref.type) {
165+
case 'audit': {
166+
return ref.slug === audit.slug ? ref.weight : 0
167+
}
168+
169+
case 'group': {
170+
const group = report.plugins
171+
.find(({ slug }) => slug === ref.plugin)
172+
?.groups?.find(({ slug }) => slug === ref.slug)
173+
if (!group?.refs.length) {
174+
return 0
175+
}
176+
const groupRatio =
177+
(group.refs.find(({ slug }) => slug === audit.slug)?.weight ??
178+
0) / group.refs.reduce((acc, { weight }) => acc + weight, 0)
179+
return ref.weight * groupRatio
180+
}
181+
}
182+
})
183+
184+
return (
185+
weights.reduce((acc, weight) => acc + weight, 0) /
186+
category.refs.reduce((acc, { weight }) => acc + weight, 0)
187+
)
188+
})
189+
.reduce((acc, value) => acc + value, 0)
190+
}

0 commit comments

Comments
 (0)