Skip to content

Commit b476ec6

Browse files
authored
CSS Coverage calculation tests (#29)
1 parent 3cbbf63 commit b476ec6

File tree

13 files changed

+846
-133
lines changed

13 files changed

+846
-133
lines changed

src/lib/components/coverage/Coverage.spec.ts

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,21 @@ test.describe('loading example file', () => {
4343
let size_column = header.getByRole('columnheader').nth(1)
4444
await expect.soft(size_column).toHaveAccessibleName('Total size')
4545
await expect.soft(size_column).not.toHaveAttribute('aria-sort')
46-
let coverage_column = header.getByRole('columnheader').nth(2)
46+
let line_count_column = header.getByRole('columnheader').nth(2)
47+
await expect.soft(line_count_column).toHaveAccessibleName('Lines')
48+
await expect.soft(line_count_column).not.toHaveAttribute('aria-sort')
49+
let coverage_column = header.getByRole('columnheader').nth(3)
4750
await expect.soft(coverage_column).toHaveAccessibleName('Coverage')
4851
await expect.soft(coverage_column).not.toHaveAttribute('aria-sort')
4952

5053
// Row contents:
5154
let first_row = table.getByRole('row').nth(1)
52-
await expect.soft(first_row.getByRole('cell').nth(0)).toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/0.BBE7cspC.css')
55+
await expect
56+
.soft(first_row.getByRole('cell').nth(0))
57+
.toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/0.BBE7cspC.css')
5358
await expect.soft(first_row.getByRole('cell').nth(1)).toHaveAccessibleName('38 kB')
54-
await expect.soft(first_row.getByRole('cell').nth(2)).toHaveAccessibleName('72.16%')
59+
await expect.soft(first_row.getByRole('cell').nth(2)).toHaveAccessibleName('2,619')
60+
await expect.soft(first_row.getByRole('cell').nth(3)).toHaveAccessibleName('72.16%')
5561

5662
// Elements in correct state:
5763
await expect.soft(table.getByRole('row').nth(1)).toHaveAttribute('aria-selected', 'true')
@@ -87,19 +93,23 @@ test.describe('loading example file', () => {
8793
await expect.soft(url_header).toHaveAttribute('aria-sort', 'descending')
8894
let first_row = table.getByRole('row').nth(1)
8995
await expect.soft(first_row).toHaveAttribute('aria-selected', 'true')
90-
await expect.soft(first_row.getByRole('cell').nth(0)).toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/Textarea.D3Oc5lUl.css')
96+
await expect
97+
.soft(first_row.getByRole('cell').nth(0))
98+
.toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/Textarea.D3Oc5lUl.css')
9199
await expect.soft(first_row.getByRole('cell').nth(1)).toHaveAccessibleName('238 B')
92-
await expect.soft(first_row.getByRole('cell').nth(2)).toHaveAccessibleName('100%')
100+
await expect.soft(first_row.getByRole('cell').nth(3)).toHaveAccessibleName('100%')
93101
let css_content = await page.getByTestId('pre-css').textContent()
94102
expect.soft(css_content).toContain('textarea.svelte-g7hyhb {')
95103

96104
// Click the same column again to reverse the sort
97105
await url_header.click()
98106
await expect.soft(url_header).toHaveAttribute('aria-sort', 'ascending')
99107
await expect.soft(first_row).toHaveAttribute('aria-selected', 'true')
100-
await expect.soft(first_row.getByRole('cell').nth(0)).toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/0.BBE7cspC.css')
108+
await expect
109+
.soft(first_row.getByRole('cell').nth(0))
110+
.toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/0.BBE7cspC.css')
101111
await expect.soft(first_row.getByRole('cell').nth(1)).toHaveAccessibleName('38 kB')
102-
await expect.soft(first_row.getByRole('cell').nth(2)).toHaveAccessibleName('72.16%')
112+
await expect.soft(first_row.getByRole('cell').nth(3)).toHaveAccessibleName('72.16%')
103113
})
104114

105115
test('sorting by total size', async ({ page }) => {
@@ -109,44 +119,52 @@ test.describe('loading example file', () => {
109119
await expect.soft(size_header).toHaveAttribute('aria-sort', 'descending')
110120
let first_row = table.getByRole('row').nth(1)
111121
await expect.soft(first_row).toHaveAttribute('aria-selected', 'true')
112-
await expect.soft(first_row.getByRole('cell').nth(0)).toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/0.BBE7cspC.css')
122+
await expect
123+
.soft(first_row.getByRole('cell').nth(0))
124+
.toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/0.BBE7cspC.css')
113125
await expect.soft(first_row.getByRole('cell').nth(1)).toHaveAccessibleName('38 kB')
114-
await expect.soft(first_row.getByRole('cell').nth(2)).toHaveAccessibleName('72.16%')
126+
await expect.soft(first_row.getByRole('cell').nth(3)).toHaveAccessibleName('72.16%')
115127
let css_content = await page.getByTestId('pre-css').textContent()
116128
expect.soft(css_content).toContain('.logo.svelte-1jiwtxp {')
117129

118130
// Click the same column again to reverse the sort
119131
await size_header.click()
120132
await expect.soft(size_header).toHaveAttribute('aria-sort', 'ascending')
121133
await expect.soft(first_row).toHaveAttribute('aria-selected', 'true')
122-
await expect.soft(first_row.getByRole('cell').nth(0)).toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/FormattedDate.JHv9Zh3p.css')
134+
await expect
135+
.soft(first_row.getByRole('cell').nth(0))
136+
.toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/FormattedDate.JHv9Zh3p.css')
123137
await expect.soft(first_row.getByRole('cell').nth(1)).toHaveAccessibleName('69 B')
124-
await expect.soft(first_row.getByRole('cell').nth(2)).toHaveAccessibleName('100%')
138+
await expect.soft(first_row.getByRole('cell').nth(3)).toHaveAccessibleName('100%')
125139
css_content = await page.getByTestId('pre-css').textContent()
126140
expect.soft(css_content).toContain('time.svelte-60ldmz {')
127141
})
128142

129143
test('sorting by coverage', async ({ page }) => {
130144
let header = table.getByRole('row').first()
131-
let coverage_header = header.getByRole('columnheader').nth(2)
145+
let coverage_header = header.getByRole('columnheader').nth(3)
132146
await coverage_header.click()
133147
await expect.soft(coverage_header).toHaveAttribute('aria-sort', 'descending')
134148
let first_row = table.getByRole('row').nth(1)
135149
await expect.soft(first_row).toHaveAttribute('aria-selected', 'true')
136-
await expect.soft(first_row.getByRole('cell').nth(0)).toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/23.lTvFD_l9.css')
150+
await expect
151+
.soft(first_row.getByRole('cell').nth(0))
152+
.toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/23.lTvFD_l9.css')
137153
await expect.soft(first_row.getByRole('cell').nth(1)).toHaveAccessibleName('405 B')
138-
await expect.soft(first_row.getByRole('cell').nth(2)).toHaveAccessibleName('100%')
154+
await expect.soft(first_row.getByRole('cell').nth(3)).toHaveAccessibleName('100%')
139155
let css_content = await page.getByTestId('pre-css').textContent()
140156
expect.soft(css_content).toContain('.form.svelte-148ca8f {')
141157

142158
// Click the same column again to reverse the sort
143159
await coverage_header.click()
144160
await expect.soft(coverage_header).toHaveAttribute('aria-sort', 'ascending')
145161
await expect.soft(first_row).toHaveAttribute('aria-selected', 'true')
146-
await expect.soft(first_row.getByRole('cell').nth(0)).toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/25.C4edrrKC.css')
162+
await expect
163+
.soft(first_row.getByRole('cell').nth(0))
164+
.toHaveAccessibleName('https://www.projectwallace.com/_app/immutable/assets/25.C4edrrKC.css')
147165
await expect.soft(first_row.getByRole('cell').nth(1)).toHaveAccessibleName('780 B')
148-
await expect.soft(first_row.getByRole('cell').nth(2)).toHaveAccessibleName('0%')
166+
await expect.soft(first_row.getByRole('cell').nth(3)).toHaveAccessibleName('0%')
149167
css_content = await page.getByTestId('pre-css').textContent()
150168
expect.soft(css_content).toContain('.page.svelte-1b1m7j9 {')
151169
})
152-
})
170+
})

src/lib/components/coverage/Coverage.svelte

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { PaneGroup, Pane, PaneResizer } from 'paneforge'
33
import { format_filesize } from '$lib/format-filesize'
4-
import { format_percentage } from '$lib/format-number'
4+
import { format_number, format_percentage } from '$lib/format-number'
55
import { create_keyboard_list } from '$components/use-keyboard-list.svelte'
66
import Panel from '$components/Panel.svelte'
77
import Meter from '$components/Meter.svelte'
@@ -23,7 +23,12 @@
2323
} = create_keyboard_list()
2424
let selected_index = $state(0)
2525
26-
let calculated = $derived(calculate_coverage(browser_coverage))
26+
function parse_html(html: string) {
27+
let parser = new DOMParser()
28+
return parser.parseFromString(html, 'text/html')
29+
}
30+
31+
let calculated = $derived(calculate_coverage(browser_coverage, parse_html))
2732
2833
let max_lines = $derived.by(() => {
2934
let max = 0
@@ -39,7 +44,7 @@
3944
selected_index = active_index
4045
}
4146
42-
let sort_by = $state<'bytes' | 'coverage' | 'name' | undefined>(undefined)
47+
let sort_by = $state<'bytes' | 'coverage' | 'name' | 'lines' | undefined>(undefined)
4348
let sort_direction = $state<'asc' | 'desc'>('asc')
4449
4550
let sorted_items = $derived.by(() => {
@@ -60,6 +65,9 @@
6065
if (sort_by === 'name') {
6166
return sort_direction === 'asc' ? string_sort(a.url, b.url) : string_sort(b.url, a.url)
6267
}
68+
if (sort_by === 'lines') {
69+
return sort_direction === 'asc' ? a.covered_lines - b.covered_lines : b.covered_lines - a.covered_lines
70+
}
6371
return 0
6472
})
6573
})
@@ -76,7 +84,7 @@
7684
})
7785
</script>
7886

79-
{#snippet sorted_th(name: 'bytes' | 'coverage' | 'name', label: string)}
87+
{#snippet sorted_th(name: 'bytes' | 'coverage' | 'name' | 'lines', label: string)}
8088
{@const sort_by_attr = sort_by === name ? (sort_direction === 'asc' ? 'ascending' : 'descending') : undefined}
8189
<th scope="col" aria-sort={sort_by_attr}>
8290
<button
@@ -103,18 +111,22 @@
103111
<div>
104112
<dt>Coverage</dt>
105113
<dd>{format_percentage(calculated.coverage_ratio)}</dd>
114+
<dd>{format_percentage(calculated.line_coverage)} of lines</dd>
106115
</div>
107116
<div>
108-
<dt>Total size</dt>
117+
<dt>Total</dt>
109118
<dd>{format_filesize(calculated.used_bytes + calculated.unused_bytes)}</dd>
119+
<dd>{format_number(calculated.total_lines)} lines</dd>
110120
</div>
111121
<div>
112122
<dt>Used</dt>
113123
<dd>{format_filesize(calculated.used_bytes)}</dd>
124+
<dd>{format_number(calculated.covered_lines)} lines</dd>
114125
</div>
115126
<div>
116127
<dt>Unused</dt>
117128
<dd>{format_filesize(calculated.unused_bytes)}</dd>
129+
<dd>{format_number(calculated.uncovered_lines)} lines</dd>
118130
</div>
119131
</div>
120132
</Panel>
@@ -123,14 +135,15 @@
123135
<h2 class="sr-only">Coverage per stylesheet</h2>
124136
<div class="devtools" data-empty={calculated.coverage_per_stylesheet.length === 0 ? 'true' : 'false'}>
125137
{#if calculated.coverage_per_stylesheet.length > 0}
126-
<PaneGroup direction="horizontal">
138+
<PaneGroup direction="horizontal" autoSaveId="css-coverage">
127139
<Pane defaultSize={50} minSize={20}>
128140
<Table>
129141
<caption class="sr-only">Coverage per origin</caption>
130142
<thead>
131143
<tr>
132144
{@render sorted_th('name', 'URL')}
133145
{@render sorted_th('bytes', 'Total size')}
146+
{@render sorted_th('lines', 'Lines')}
134147
{@render sorted_th('coverage', 'Coverage')}
135148
<th scope="col">Coverage visualized</th>
136149
</tr>
@@ -139,12 +152,13 @@
139152
<tbody use:root={{ onchange }} style:--meter-height="0.5rem">
140153
{#each sorted_items as item_index, index}
141154
{@const stylesheet = calculated.coverage_per_stylesheet[item_index]}
142-
{@const { url, total_bytes, coverage_ratio } = stylesheet}
143-
<tr use:item={{ value: index.toString() }} aria-selected={selected_index === index ? 'true': 'false'}>
155+
{@const { url, total_bytes, total_lines, coverage_ratio, covered_lines } = stylesheet}
156+
<tr use:item={{ value: index.toString() }} aria-selected={selected_index === index ? 'true' : 'false'}>
144157
<td class="url">
145158
{url}
146159
</td>
147160
<td class="numeric">{format_filesize(total_bytes)}</td>
161+
<td class="numeric">{format_number(total_lines)}</td>
148162
<td class="numeric">{format_percentage(coverage_ratio)}</td>
149163
<td>
150164
<div style:width={(stylesheet.total_lines / max_lines) * 100 + '%'}>
@@ -223,6 +237,11 @@
223237
flex-direction: row;
224238
justify-content: space-between;
225239
}
240+
241+
& > div {
242+
display: grid;
243+
row-gap: var(--space-2);
244+
}
226245
}
227246
228247
.css-slide {
@@ -231,15 +250,16 @@
231250
border-inline-start: 1px solid var(--fg-450);
232251
}
233252
234-
dt {
253+
dt,
254+
dd:last-of-type {
235255
text-transform: uppercase;
236256
color: var(--fg-300);
237257
font-weight: var(--font-bold);
238258
font-size: var(--size-sm);
239259
}
240260
241-
dd {
242-
font-size: var(--size-5xl);
261+
dd:first-of-type {
262+
font-size: var(--size-4xl);
243263
line-height: var(--leading-none);
244264
font-weight: var(--font-ultrabold);
245265
color: var(--fg-0);
@@ -263,7 +283,13 @@
263283
}
264284
265285
tr {
266-
--meter-bg: var(--bg-500);
286+
--meter-bg: repeating-linear-gradient(
287+
-45deg,
288+
var(--error-400),
289+
var(--error-400) 3px,
290+
var(--error-200) 3px,
291+
var(--error-200) 6px
292+
);
267293
}
268294
269295
.sort-button {

0 commit comments

Comments
 (0)