Skip to content

Commit 5f2f633

Browse files
nicoburnsmrego
authored andcommitted
Include WPT scores directly in website
Signed-off-by: Nico Burns <[email protected]>
1 parent 24f9c2d commit 5f2f633

File tree

4 files changed

+308
-1
lines changed

4 files changed

+308
-1
lines changed

.eleventy.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ module.exports = function(eleventyConfig) {
99
eleventyConfig.addPassthroughCopy("CNAME");
1010
eleventyConfig.addPassthroughCopy({"assets/img": "img"});
1111
eleventyConfig.addPassthroughCopy({"assets/svg": "svg"});
12+
eleventyConfig.addPassthroughCopy({"assets/js": "js"});
1213
eleventyConfig.addPassthroughCopy({"node_modules/reveal.js/dist": "reveal.js/dist"});
1314
eleventyConfig.addPassthroughCopy({"node_modules/reveal.js/plugin": "reveal.js/plugin"});
1415
eleventyConfig.addPassthroughCopy({"assets/reveal.js-theme": "reveal.js/dist/theme"});

_data/menu.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"subpages": [
66
{ "title": "Contributing", "url": "/contributing/" },
77
{ "title": "Servo Book", "url": "https://book.servo.org" },
8-
{ "title": "WPT pass rates", "url": "https://wpt.servo.org/" },
8+
{ "title": "WPT pass rates", "url": "/wpt/" },
99
{ "title": "Code of Conduct", "url": "/coc/" }
1010
]
1111
},

assets/js/load-chart.js

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/* global google */
2+
3+
google.charts.load('current', { packages: ['corechart', 'line'] })
4+
google.charts.setOnLoadCallback(setupChart)
5+
6+
const fetchData = fetch('https://wpt.servo.org/scores.json')
7+
const embed = location.search === '?embed'
8+
let dark_mode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
9+
10+
const periodRanges = {
11+
'last month': 1,
12+
'last 3 months': 3,
13+
'last 6 months': 6,
14+
'last year': 12,
15+
'since April 2023': null
16+
}
17+
18+
if (embed) {
19+
document.documentElement.classList.add('embed')
20+
}
21+
22+
function formatDate (date) {
23+
const months = [
24+
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
25+
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
26+
]
27+
const year = date.getFullYear()
28+
const month = months[date.getMonth()]
29+
const day = date.getDate()
30+
return `${month} ${day}, ${year}`
31+
}
32+
33+
function parseDateString (date) {
34+
const [y, m, d] = date.split('-')
35+
return new Date(y, m - 1, d)
36+
}
37+
38+
function toolTip (date, wpt_sha, servo_version, score, engine) {
39+
return `
40+
<b>${formatDate(date)}</b></br>
41+
Score: <b>${score / 10}</b></br>
42+
WPT: ${wpt_sha}</br>
43+
Servo (${engine}): ${servo_version}
44+
`
45+
}
46+
47+
function setupChart () {
48+
const maxDate = new Date()
49+
50+
const options = {
51+
height: 350,
52+
fontSize: 16,
53+
legend: {
54+
position: 'top',
55+
...(dark_mode
56+
? {
57+
textStyle: { color: '#f5f5f5' }
58+
}
59+
: {})
60+
},
61+
hAxis: {
62+
format: 'MMM-YYYY',
63+
viewWindow: {
64+
max: maxDate
65+
},
66+
...(dark_mode
67+
? {
68+
textStyle: { color: '#f5f5f5' }
69+
}
70+
: {})
71+
},
72+
vAxis: {
73+
format: 'percent',
74+
viewWindow: {
75+
min: 0,
76+
max: 1
77+
},
78+
...(dark_mode
79+
? {
80+
textStyle: { color: '#f5f5f5' }
81+
}
82+
: {})
83+
},
84+
explorer: {
85+
actions: ['dragToZoom', 'rightClickToReset'],
86+
axis: 'horizontal',
87+
keepInBounds: true,
88+
maxZoomIn: 4.0
89+
},
90+
tooltip: {
91+
// textStyle has no effect if isHtml is true
92+
isHtml: true,
93+
trigger: 'both'
94+
},
95+
...(dark_mode
96+
? {
97+
backgroundColor: '#121619'
98+
}
99+
: {})
100+
}
101+
102+
const node = document.getElementById('servo-chart')
103+
const area_dropdown = document.getElementById('selected-area')
104+
const period_dropdown = document.getElementById('selected-period')
105+
const chart = new google.visualization.LineChart(node)
106+
const AREA_SCORE_OFFSET = 3
107+
let all_scores
108+
109+
Object.keys(periodRanges).forEach(date => {
110+
const selector = document.createElement('option')
111+
selector.value = date
112+
selector.textContent = date
113+
period_dropdown.appendChild(selector)
114+
})
115+
116+
function update_chart () {
117+
if (!all_scores) throw new Error('scores not loaded')
118+
const chosen_area = area_dropdown.value
119+
const chosen_period = period_dropdown.value
120+
const area_index = all_scores.area_keys.indexOf(chosen_area)
121+
const table = new google.visualization.DataTable()
122+
options.series = []
123+
const monthsToSubtract = periodRanges[chosen_period]
124+
const minDate = monthsToSubtract
125+
? new Date(maxDate.getFullYear(), maxDate.getMonth() - monthsToSubtract, 1)
126+
: null
127+
128+
table.addColumn('date', 'runOn')
129+
130+
options.series.push({ color: dark_mode ? '#CC9933' : '#3366CC' })
131+
table.addColumn('number', 'Servo')
132+
table.addColumn({ type: 'string', role: 'tooltip', p: { html: true } })
133+
134+
for (const scores_for_run of all_scores.scores) {
135+
const area_score = scores_for_run[area_index + AREA_SCORE_OFFSET]
136+
const [date_string, wpt_sha, browser_version] = scores_for_run
137+
const date = parseDateString(date_string)
138+
if (date < minDate) {
139+
continue
140+
}
141+
const row = [
142+
date,
143+
area_score / 1000,
144+
toolTip(date, wpt_sha, browser_version, area_score, 'Servo')
145+
]
146+
table.addRow(row)
147+
}
148+
chart.draw(table, options)
149+
}
150+
151+
function removeChildren (parent) {
152+
while (parent.firstChild) {
153+
parent.removeChild(parent.firstChild)
154+
}
155+
return parent
156+
}
157+
158+
function update_table (scores) {
159+
const score_table = document.getElementById('score-table-body')
160+
removeChildren(score_table)
161+
162+
for (const [idx, area] of scores.area_keys.entries()) {
163+
const recent_score = scores.scores[scores.scores.length - 1]
164+
score_table.insertAdjacentHTML(
165+
'beforeend',
166+
`<tr class="${idx % 2 ? 'odd' : 'even'}">
167+
<td>${scores.focus_areas[area]}</td>
168+
<td class="score">${String(recent_score[idx + AREA_SCORE_OFFSET] / 10).padEnd(4, '.0')}%</td>
169+
</tr>`
170+
)
171+
}
172+
}
173+
174+
fetchData
175+
.then(resp => resp.json())
176+
.then(scores => {
177+
all_scores = scores
178+
if (scores.scores.length < 60) {
179+
options.hAxis.format = 'dd MMM YYYY'
180+
} else {
181+
options.hAxis.format = 'MMM YYYY'
182+
}
183+
184+
for (const area of scores.area_keys) {
185+
const selector = document.createElement('option')
186+
selector.value = area
187+
selector.textContent = scores.focus_areas[area]
188+
area_dropdown.appendChild(selector)
189+
}
190+
191+
update_chart()
192+
193+
area_dropdown.onchange = update_chart
194+
period_dropdown.onchange = update_chart
195+
if (window.matchMedia) {
196+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ({ matches }) => {
197+
dark_mode = matches
198+
if (dark_mode) {
199+
options.legend.textStyle = { color: '#f5f5f5' }
200+
options.hAxis.textStyle = { color: '#f5f5f5' }
201+
options.vAxis.textStyle = { color: '#f5f5f5' }
202+
options.backgroundColor = '#121619'
203+
} else {
204+
options.legend.textStyle = { color: 'black' }
205+
options.hAxis.textStyle = { color: 'black' }
206+
options.vAxis.textStyle = { color: 'black' }
207+
options.backgroundColor = 'white'
208+
}
209+
update_chart()
210+
})
211+
}
212+
area_dropdown.value = scores.area_keys[0]
213+
period_dropdown.value = Object.keys(periodRanges)[4]
214+
update_table(scores)
215+
update_chart()
216+
})
217+
}

wpt.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
layout: default.html
3+
title: WPT Scores
4+
---
5+
<style>
6+
.odd {
7+
background-color: #f0efef;
8+
}
9+
10+
#selected-area {
11+
padding: 10px;
12+
}
13+
14+
#selected-period {
15+
padding: 10px;
16+
}
17+
18+
#score-table {
19+
width: 100%;
20+
margin-top: 30px;
21+
}
22+
23+
#score-table th {
24+
border-bottom: 1px solid black;
25+
text-transform: uppercase;
26+
}
27+
28+
#score-table th, td {
29+
padding: 5px 10px;
30+
}
31+
32+
#score-table th:nth-child(1) {
33+
text-align: left;
34+
}
35+
36+
#score-table-body .score {
37+
text-align: right;
38+
}
39+
40+
#score-explanation {
41+
padding: 10px;
42+
margin-top: 20px;
43+
margin-bottom: 40px;
44+
font-size: 1rem;
45+
}
46+
47+
.chart-filter-bar {
48+
display: flex;
49+
flex-wrap: wrap;
50+
column-gap: 40px;
51+
row-gap: 20px;
52+
}
53+
54+
.chart-filter {
55+
display: flex;
56+
align-items: center;
57+
gap: 20px;
58+
}
59+
</style>
60+
<div class="inner-container wpt-score-page">
61+
<h1>{{ title }}</h1>
62+
<br>
63+
<div class="chart-filter-bar">
64+
<div class="chart-filter">
65+
<label for="selected-area">Test suite:</label>
66+
<select id="selected-area" name="selected-area"></select>
67+
</div>
68+
<div class="chart-filter">
69+
<label for="selected-period">period:</label>
70+
<select id="selected-period" name="selected-period"></select>
71+
</div>
72+
</div>
73+
<div id="servo-chart"></div>
74+
<table id="score-table">
75+
<thead id="score-table-header"><tr><th>Test Suite</th><th>Score</th></tr></thead>
76+
<tbody id="score-table-body"></tbody>
77+
</table>
78+
<div id="score-explanation">
79+
Scores are calculated as percentages of total <b>enabled</b>
80+
tests within the suite that pass. A passing test with no
81+
subtests gets a score of 1 while a test with subtests gets a
82+
score between 0 and 1 representing the fraction of passing
83+
subtests within that test. This is different from the
84+
percentages on wpt.fyi which is calculated by giving equal
85+
weight to both top-level tests and subtests.
86+
</div>
87+
</div>
88+
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
89+
<script type="text/javascript" src="{{ '/js/load-chart.js' | url }}"></script>

0 commit comments

Comments
 (0)