Skip to content

Commit 0261567

Browse files
committed
chore: SVG badge, responsive design, data util
1 parent 5e055d5 commit 0261567

File tree

7 files changed

+132
-53
lines changed

7 files changed

+132
-53
lines changed

e2e-report/app/badge/route.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { ImageResponse } from 'next/og'
2+
import testData from '@/utils/data'
3+
4+
export const dynamic = 'force-static'
5+
6+
const labelStyle = {
7+
background: 'linear-gradient(#2e51ed, #316bf4)',
8+
color: 'white',
9+
}
10+
11+
const bgStyles = {
12+
ok: { background: 'linear-gradient(to bottom, #22c55e, #86efac)' },
13+
warning: { background: 'linear-gradient(to bottom, #ca8a04, #fef08a)' },
14+
error: { background: 'linear-gradient(to bottom, #dc2626, #f87171)', color: 'white' },
15+
}
16+
17+
// Generate an SVG badge with test status and target Next.js version
18+
export async function GET(request) {
19+
const valueStyle =
20+
bgStyles[testData.failed === 0 ? 'ok' : testData.unknownFailuresCount > 0 ? 'error' : 'warning']
21+
22+
const badge = (
23+
<Badge
24+
label="Next.js runtime v5"
25+
labelStyle={labelStyle}
26+
value={testData.nextVersion}
27+
valueStyle={valueStyle}
28+
/>
29+
)
30+
return new ImageResponse(badge, {
31+
width: 400,
32+
height: 60,
33+
})
34+
}
35+
36+
function Badge({ label, labelStyle, value, valueStyle }) {
37+
return (
38+
<div tw="flex items-center w-full h-full text-[28px] rounded-md overflow-hidden bg-transparent">
39+
<span tw="items-center h-full p-3.5" style={labelStyle}>
40+
{label}
41+
</span>
42+
<span tw="items-center h-full flex-grow justify-center" style={valueStyle}>
43+
{value}
44+
</span>
45+
</div>
46+
)
47+
}

e2e-report/app/globals.css

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
}
2020

2121
.table.issues-table :where(td:nth-last-child(-n + 4)) {
22-
@apply text-center;
22+
@apply text-center p-1 md:p-4;
2323
}
2424

2525
.table.issues-table :where(td:nth-last-child(-n + 3)) {
@@ -30,4 +30,4 @@
3030
@apply font-bold;
3131
@apply text-3xl;
3232
}
33-
}
33+
}

e2e-report/app/page.js

+20-28
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,40 @@ import Image from 'next/image'
22
import Table from '@/components/table'
33
import ComponentSwitcher from '@/components/switcher'
44
import StatsRow from '@/components/stats'
5-
6-
import testData from '@/data/test-results.json'
5+
import testData from '@/utils/data'
76

87
export default function Home() {
9-
const tableComponents = createTableComponents(testData)
8+
// User can switch between two test suite tables: one with all non-empty suites,
9+
// and another showing only suites with failed tests (and the failed cases in them)
10+
const tableComponents = {
11+
'All suites': <Table suites={testData.nonEmptySuites} />,
12+
'Failed tests only': <Table suites={testData.suitesWithIssues} />,
13+
}
1014

1115
return (
12-
<div className="flex flex-col w-full items-center font-primary">
16+
<div className="flex flex-col w-full items-center font-primary overflow-x-hidden">
1317
<Header />
1418
<StatsRow testData={testData} />
15-
<div className="max-w-5xl w-full p-8">
19+
<div className="max-w-5xl w-full py-4 px-0 md:p-8">
1620
<ComponentSwitcher components={tableComponents} />
1721
</div>
1822
</div>
1923
)
2024
}
2125

22-
// User can switch between two test suite tables: one with all non-empty suites,
23-
// and another showing only suites with failed tests (and the failed cases in them)
24-
function createTableComponents(testData) {
25-
testData.results.forEach((suite) => {
26-
suite.failedKnown =
27-
suite.testCases?.filter((t) => t.status === 'failed' && t.reason).length || 0
28-
suite.failedUnknown = suite.failed - suite.failedKnown
29-
})
30-
const nonEmptySuites = testData.results.filter((suite) => suite.testCases?.length > 0)
31-
const suitesWithIssues = nonEmptySuites.filter((suite) => suite.failed > 0)
32-
suitesWithIssues.forEach((suite) => {
33-
suite.testCases = suite.testCases.filter((t) => t.status === 'failed')
34-
})
35-
36-
return {
37-
'All suites': <Table suites={nonEmptySuites} />,
38-
'Failed tests only': <Table suites={suitesWithIssues} />,
39-
}
40-
}
41-
4226
function Header() {
4327
return (
44-
<div className="flex w-full items-center gap-4 bg-primary text-base-100 p-4">
45-
<Image alt="netlify logo" src="/logo.svg" width={97} height={40} />
46-
<span className="text-lg font-bold uppercase">Next.js E2E Tests on Netlify Runtime v5</span>
28+
<div className="flex w-full items-center gap-4 bg-primary text-base-100 p-2 md:p-4 justify-center">
29+
<Image
30+
alt="netlify logo"
31+
src="/logo.svg"
32+
width={97}
33+
height={40}
34+
className="hidden md:block"
35+
/>
36+
<span className="md:text-lg font-bold uppercase">
37+
Next.js E2E Tests on Netlify Runtime v5
38+
</span>
4739
</div>
4840
)
4941
}

e2e-report/components/stats.js

+2-9
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,6 @@ export default function StatsRow({ testData }) {
44
const testsRun = testData.passed + testData.failed
55
const passRate = ((testData.passed / testsRun) * 100).toFixed(1)
66

7-
const allFailures = testData.results
8-
.flatMap((suite) => {
9-
return suite.testCases?.filter((t) => t.status === 'failed')
10-
})
11-
.filter(Boolean)
12-
const knownFailuesCount = allFailures.filter((t) => !!t.reason).length
13-
147
return (
158
<div className="stats stats-vertical lg:stats-horizontal border-b rounded w-full">
169
<div className="stat">
@@ -40,7 +33,7 @@ export default function StatsRow({ testData }) {
4033
<div className="size-6">
4134
<InfoIcon />
4235
</div>
43-
<span>{knownFailuesCount}</span>
36+
<span>{testData.knownFailuresCount}</span>
4437
</div>
4538
<div className="stat-desc">mapped to GitHub issues</div>
4639
</div>
@@ -51,7 +44,7 @@ export default function StatsRow({ testData }) {
5144
<div className="size-6">
5245
<ErrorIcon />
5346
</div>
54-
<span>{allFailures.length - knownFailuesCount}</span>
47+
<span>{testData.unknownFailuresCount}</span>
5548
</div>
5649
<div className="stat-desc">not mapped to issues</div>
5750
</div>

e2e-report/components/switcher.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default function ComponentSwitcher({ components, defaultLabel }) {
1010

1111
return (
1212
<div className="flex flex-col gap-4">
13-
<div className="flex justify-end">
13+
<div className="flex justify-center md:justify-end">
1414
<ToggleButtonGroup
1515
labels={labels}
1616
selectedLabel={selectedLabel}
@@ -28,7 +28,7 @@ function ToggleButtonGroup({ labels, selectedLabel, setSelectedLabel }) {
2828
{labels.map((label) => {
2929
return (
3030
<input
31-
className="join-item btn btn-sm rounded"
31+
className="join-item btn text-base md:btn-sm rounded"
3232
name="optionsGroup"
3333
key={label}
3434
type="radio"

e2e-report/components/table.js

+17-12
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ import { GithubIcon } from './icons'
33

44
// Show test suites and non-passing cases per each
55
export default function Table({ suites }) {
6-
const countColumns = ['Pass', 'Fail', 'Known fail', 'Skip']
6+
const countColumns = ['Pass', 'Fail', 'Known', 'Skip']
77

88
return (
99
<table className="table issues-table text-base w-full">
1010
<thead>
1111
<tr>
12-
<th>Test suite name</th>
12+
<th className="p-2 md:p-4">Test suite name</th>
1313
{countColumns.map((col) => {
1414
return (
15-
<th className="w-16 p-2 text-center" key={col}>
15+
<th className="md:w-16 p-0 md:p-2 text-center" key={col}>
1616
{col}
1717
</th>
1818
)
@@ -32,9 +32,11 @@ function TestSuiteRow({ suite, idx }) {
3232
const badgeClasses = 'badge font-bold rounded '
3333
return (
3434
<>
35-
<tr key={'tr-' + idx}>
36-
<td>
37-
<Link href={suite.file}>{suite.name}</Link>
35+
<tr key={'tr-' + idx} className="text-sm md:text-base">
36+
<td className="p-2 md:p-4">
37+
<Link href={suite.file} className="text-sm md:text-base">
38+
{suite.name}
39+
</Link>
3840
</td>
3941
<td>{suite.passed}</td>
4042
<td>
@@ -76,8 +78,8 @@ function IssueRow({ test }) {
7678

7779
return (
7880
<tr>
79-
<td colSpan={5} className="border bg-base-200/[.4] pl-6">
80-
<div key={test.name} className="flex flex-col gap-1 text-neutral">
81+
<td colSpan={5} className="border bg-base-200/[.4] md:pl-6">
82+
<div key={test.name} className="flex flex-col gap-1 text-neutral text-left">
8183
<div className="flex gap-2 items-center py-1">
8284
{test.status == 'failed' ? (
8385
!!test.reason ? (
@@ -88,15 +90,18 @@ function IssueRow({ test }) {
8890
) : (
8991
<div className="badge badge-accent rounded">Skipped</div>
9092
)}
91-
<span className="flex gap-1 opacity-90">
92-
<span className="font-bold">Test:</span>
93-
{shorten(test.name)}
93+
<span className="opacity-90 text-sm md:text-base">
94+
<span className="font-bold">Test:</span> {shorten(test.name)}
9495
</span>
9596
</div>
9697
{!!test.reason && (
9798
<div className="flex justify-start text-neutral font-bold opacity-90">
9899
{test.link ? (
99-
<Link href={test.link} target="_blank" className="flex gap-1 items-center">
100+
<Link
101+
href={test.link}
102+
target="_blank"
103+
className="flex gap-1 items-center text-sm md:text-base"
104+
>
100105
<GithubIcon className="w-4" />
101106
<span>{shorten(test.reason)}</span>
102107
</Link>

e2e-report/utils/data.js

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import fileData from '@/data/test-results.json'
2+
3+
const nonEmptySuites = fileData.results.filter((suite) => suite.testCases?.length > 0)
4+
nonEmptySuites.forEach((suite) => {
5+
const actualFailed = suite.testCases?.filter((t) => t.status === 'failed') || []
6+
if (actualFailed.length !== suite.failed) {
7+
console.warn(
8+
`In suite "${suite.name}", failed value is ${suite.failed} but count of actual failed cases found is ${actualFailed.length}`,
9+
)
10+
suite.failed = actualFailed.length
11+
}
12+
13+
suite.failedKnown = actualFailed.filter((t) => !!t.reason).length || 0
14+
suite.failedUnknown = suite.failed - suite.failedKnown
15+
})
16+
17+
const suitesWithIssues = nonEmptySuites.filter((suite) => suite.failed > 0)
18+
suitesWithIssues.forEach((suite) => {
19+
suite.testCases = suite.testCases.filter((t) => t.status === 'failed')
20+
})
21+
22+
const allFailures = fileData.results
23+
.flatMap((suite) => {
24+
return suite.testCases?.filter((t) => t.status === 'failed')
25+
})
26+
.filter(Boolean)
27+
const knownFailuresCount = allFailures.filter((t) => !!t.reason).length
28+
const unknownFailuresCount = allFailures.length - knownFailuresCount
29+
30+
const testData = {
31+
passed: fileData.passed,
32+
failed: fileData.failed,
33+
skipped: fileData.skipped,
34+
nextVersion: fileData.nextVersion,
35+
testDate: fileData.testDate,
36+
nonEmptySuites,
37+
suitesWithIssues,
38+
knownFailuresCount,
39+
unknownFailuresCount,
40+
}
41+
42+
export default testData

0 commit comments

Comments
 (0)