playwright-reborn 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +26 -9
- package/dist/fixture.js +4 -3
- package/dist/fixture.js.map +1 -1
- package/dist/format.js +15 -2
- package/dist/format.js.map +1 -1
- package/dist/options.js +4 -3
- package/dist/options.js.map +1 -1
- package/dist/paths.js +19 -11
- package/dist/paths.js.map +1 -1
- package/dist/render.js +43 -25
- package/dist/render.js.map +1 -1
- package/dist/serialize.d.ts +1 -0
- package/dist/serialize.js +20 -8
- package/dist/serialize.js.map +1 -1
- package/dist/status.js +4 -4
- package/dist/status.js.map +1 -1
- package/dist/ui/report.css +432 -288
- package/dist/ui/report.js +301 -335
- package/package.json +4 -3
package/dist/ui/report.js
CHANGED
|
@@ -17,20 +17,22 @@
|
|
|
17
17
|
var TABS = ['overview', 'tests', 'screenshots'];
|
|
18
18
|
if (data.showLogs !== false) TABS.push('logs');
|
|
19
19
|
if (data.showFiles !== false) TABS.push('files');
|
|
20
|
+
var testIndex = new Map(data.tests.map(function (test) { return [test.id, test]; }));
|
|
20
21
|
var route = readRoute();
|
|
21
22
|
var state = {
|
|
22
23
|
tab: route.tab,
|
|
23
24
|
filter: 'all',
|
|
24
25
|
query: '',
|
|
25
|
-
selectedId: route.id
|
|
26
|
+
selectedId: testIndex.has(route.id) ? route.id : initialFailure(),
|
|
26
27
|
attempt: 0,
|
|
27
28
|
showApi: false,
|
|
28
29
|
testPane: 'steps',
|
|
29
30
|
listLimit: 200,
|
|
31
|
+
timelineLimit: 200,
|
|
30
32
|
shotFilter: 'all',
|
|
31
33
|
logQuery: '',
|
|
32
|
-
tag:
|
|
33
|
-
projectFilter:
|
|
34
|
+
tag: null,
|
|
35
|
+
projectFilter: null,
|
|
34
36
|
overview: data.overview === 'timeline' ? 'timeline' : 'chart',
|
|
35
37
|
order: 'grouped',
|
|
36
38
|
band: 'all',
|
|
@@ -45,18 +47,22 @@
|
|
|
45
47
|
var findTimer = 0;
|
|
46
48
|
|
|
47
49
|
app.innerHTML = [
|
|
48
|
-
'<div class="atmosphere" aria-hidden="true"><canvas class="bands"></canvas><div class="grain"></div><div class="veil"></div></div>',
|
|
49
50
|
'<div class="shell">',
|
|
50
51
|
'<aside class="rail">',
|
|
51
|
-
'<div class="
|
|
52
|
-
'<span class="
|
|
52
|
+
'<div class="sidebar-brand">' + icon('brand') + '<span>Reborn<span class="brand-caption">Test reports</span></span></div>',
|
|
53
|
+
'<div class="project-card">' + icon('folder') + '<div><span class="project-caption">Workspace</span><strong id="workspace-name"></strong></div></div>',
|
|
54
|
+
'<p class="nav-caption">Run workspace</p>',
|
|
55
|
+
'<div class="tabbar" role="tablist" aria-label="Report" aria-orientation="vertical">',
|
|
56
|
+
|
|
53
57
|
TABS.map(function (id) {
|
|
54
|
-
return tabButton(id, id.charAt(0).toUpperCase() + id.slice(1));
|
|
58
|
+
return tabButton(id, id === 'overview' ? 'Dashboard' : id.charAt(0).toUpperCase() + id.slice(1));
|
|
55
59
|
}).join(''),
|
|
56
60
|
'</div>',
|
|
57
|
-
'<p class="
|
|
61
|
+
'<div class="sidebar-bottom"><p class="nav-caption">Current run</p><p class="sidebar-state run-state" id="sidebar-state"></p><p class="sidebar-note">Completed <span id="sidebar-time"></span></p><div class="keyboard-hint"><kbd>/</kbd><span>Find a test</span></div></div>',
|
|
58
62
|
'</aside>',
|
|
59
63
|
'<div class="stage">',
|
|
64
|
+
'<header class="workspace-bar"><div class="breadcrumb"><span id="breadcrumb-company"></span><span class="breadcrumb-separator">/</span><strong id="page-name">Dashboard</strong></div><button class="theme-toggle" id="theme-toggle" type="button" role="switch" aria-checked="false" aria-label="Dark mode">' + icon('moon') + '<span>Dark mode</span><span class="theme-switch" aria-hidden="true"></span></button></header>',
|
|
65
|
+
'<div class="report-content">',
|
|
60
66
|
'<div id="panel-overview" role="tabpanel"></div>',
|
|
61
67
|
'<div id="panel-tests" role="tabpanel" hidden></div>',
|
|
62
68
|
'<div id="panel-screenshots" role="tabpanel" hidden></div>',
|
|
@@ -65,11 +71,16 @@
|
|
|
65
71
|
'<p class="foot" id="foot"></p>',
|
|
66
72
|
'</div>',
|
|
67
73
|
'</div>',
|
|
74
|
+
'</div>',
|
|
68
75
|
].join('');
|
|
69
76
|
|
|
70
77
|
document.body.dataset.accent = accentName();
|
|
78
|
+
applyTheme(readTheme());
|
|
71
79
|
document.title = companyName() + (data.summary ? ' · ' + data.summary : '');
|
|
72
|
-
|
|
80
|
+
document.getElementById('workspace-name').textContent = companyName();
|
|
81
|
+
document.getElementById('breadcrumb-company').textContent = companyName();
|
|
82
|
+
document.getElementById('sidebar-time').textContent = formatWhen(data.generatedAt);
|
|
83
|
+
setRunState(document.getElementById('sidebar-state'));
|
|
73
84
|
var footBits = [];
|
|
74
85
|
if (data.showCredit === true) footBits.push(el('span', null, ['Open source by World of Z']));
|
|
75
86
|
footBits.push(el('span', null, [formatWhen(data.generatedAt)]));
|
|
@@ -81,87 +92,41 @@
|
|
|
81
92
|
['3to5', '3 to 5 minutes'],
|
|
82
93
|
['over5', 'Over 5 minutes'],
|
|
83
94
|
];
|
|
84
|
-
var CHART_COLORS = ['var(--
|
|
95
|
+
var CHART_COLORS = ['var(--heading)', 'var(--chart-2)', 'var(--chart-3)', 'var(--chart-4)'];
|
|
85
96
|
|
|
97
|
+
syncTabOrientation();
|
|
98
|
+
window.addEventListener('resize', syncTabOrientation);
|
|
86
99
|
paintOverview();
|
|
87
100
|
showTab(state.tab, true);
|
|
88
101
|
bind();
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
canvas.width = Math.max(1, Math.floor(rect.width * ratio));
|
|
113
|
-
canvas.height = Math.max(1, Math.floor(rect.height * ratio));
|
|
114
|
-
}
|
|
115
|
-
var rgb = accentParts();
|
|
116
|
-
function draw(now) {
|
|
117
|
-
var w = canvas.width;
|
|
118
|
-
var h = canvas.height;
|
|
119
|
-
var t = reduce ? 8 : now / 1000;
|
|
120
|
-
ctx.clearRect(0, 0, w, h);
|
|
121
|
-
var wash = ctx.createLinearGradient(0, 0, 0, h);
|
|
122
|
-
wash.addColorStop(0, '#123d24');
|
|
123
|
-
wash.addColorStop(0.42, '#0a2416');
|
|
124
|
-
wash.addColorStop(1, '#050806');
|
|
125
|
-
ctx.fillStyle = wash;
|
|
126
|
-
ctx.fillRect(0, 0, w, h);
|
|
127
|
-
columns.forEach(function (column) {
|
|
128
|
-
var sway = Math.sin(t * column.speed + column.phase) * 0.02;
|
|
129
|
-
var breath = 0.4 + 0.6 * (0.5 + 0.5 * Math.sin(t * column.speed * 1.4 + column.phase));
|
|
130
|
-
var x = (column.at + sway) * w;
|
|
131
|
-
var spread = Math.max(10, column.width * w * (0.55 + breath * 0.35));
|
|
132
|
-
var gradient = ctx.createLinearGradient(x - spread, 0, x + spread, 0);
|
|
133
|
-
var alpha = 0.28 + 0.62 * breath;
|
|
134
|
-
gradient.addColorStop(0, 'rgba(' + rgb + ', 0)');
|
|
135
|
-
gradient.addColorStop(0.42, 'rgba(' + rgb + ', ' + (alpha * 0.45).toFixed(3) + ')');
|
|
136
|
-
gradient.addColorStop(0.5, 'rgba(' + rgb + ', ' + alpha.toFixed(3) + ')');
|
|
137
|
-
gradient.addColorStop(0.58, 'rgba(' + rgb + ', ' + (alpha * 0.45).toFixed(3) + ')');
|
|
138
|
-
gradient.addColorStop(1, 'rgba(' + rgb + ', 0)');
|
|
139
|
-
ctx.fillStyle = gradient;
|
|
140
|
-
ctx.fillRect(x - spread, 0, spread * 2, h * 0.92);
|
|
141
|
-
});
|
|
142
|
-
if (!reduce) requestAnimationFrame(draw);
|
|
143
|
-
}
|
|
144
|
-
resize();
|
|
145
|
-
window.addEventListener('resize', resize);
|
|
146
|
-
requestAnimationFrame(function (now) {
|
|
147
|
-
if (!canvas.width) resize();
|
|
148
|
-
draw(now);
|
|
149
|
-
});
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
function icon(name) {
|
|
105
|
+
var paths = {
|
|
106
|
+
brand: '<path d="m8 5-5 7 5 7m8-14 5 7-5 7m-8-7 3 3 5-6"/>',
|
|
107
|
+
overview: '<rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/>',
|
|
108
|
+
tests: '<path d="m9 11 2 2 4-4"/><rect x="5" y="4" width="14" height="17" rx="2"/><path d="M9 4V2h6v2"/>',
|
|
109
|
+
screenshots: '<rect x="3" y="3" width="18" height="18" rx="3"/><circle cx="8" cy="8" r="1.5"/><path d="m3 17 6-6 4 4 3-3 5 5"/>',
|
|
110
|
+
logs: '<path d="m5 6 4 4-4 4m7 3h7"/><rect x="2" y="2" width="20" height="20" rx="3"/>',
|
|
111
|
+
files: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8zm0 0v6h6M8 13h8m-8 4h5"/>',
|
|
112
|
+
folder: '<path d="M3 7V5a2 2 0 0 1 2-2h5l2 3h7a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z"/>',
|
|
113
|
+
search: '<circle cx="10.5" cy="10.5" r="6.5"/><path d="m16 16 5 5"/>',
|
|
114
|
+
clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
|
|
115
|
+
check: '<circle cx="12" cy="12" r="9"/><path d="m8 12 3 3 5-6"/>',
|
|
116
|
+
alert: '<path d="m12 3 10 18H2Z M12 9v4m0 3v1"/>',
|
|
117
|
+
moon: '<path d="M21 13a9 9 0 0 1-10-10 9 9 0 1 0 10 10Z"/>',
|
|
118
|
+
arrow: '<path d="M5 12h14m-5-5 5 5-5 5"/>',
|
|
119
|
+
};
|
|
120
|
+
return '<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' + (paths[name] || paths.tests) + '</svg>';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function syncTabOrientation() {
|
|
124
|
+
document.querySelector('.tabbar').setAttribute('aria-orientation', window.innerWidth <= 800 ? 'horizontal' : 'vertical');
|
|
150
125
|
}
|
|
151
126
|
|
|
152
|
-
function
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
var lastY = window.scrollY;
|
|
156
|
-
window.addEventListener('scroll', function () {
|
|
157
|
-
var y = window.scrollY;
|
|
158
|
-
var down = y > lastY + 6;
|
|
159
|
-
var up = y < lastY - 6;
|
|
160
|
-
if (y < 24) rail.classList.remove('is-hidden');
|
|
161
|
-
else if (down) rail.classList.add('is-hidden');
|
|
162
|
-
else if (up) rail.classList.remove('is-hidden');
|
|
163
|
-
lastY = y;
|
|
164
|
-
}, { passive: true });
|
|
127
|
+
function setRunState(node) {
|
|
128
|
+
node.dataset.status = data.status;
|
|
129
|
+
node.textContent = { passed: 'Run passed', failed: 'Run failed', timedout: 'Run timed out', interrupted: 'Run interrupted' }[data.status] || 'Run finished';
|
|
165
130
|
}
|
|
166
131
|
|
|
167
132
|
function companyName() {
|
|
@@ -174,27 +139,32 @@
|
|
|
174
139
|
return allowed[data.accent] ? data.accent : 'green';
|
|
175
140
|
}
|
|
176
141
|
|
|
177
|
-
function
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
142
|
+
function readTheme() {
|
|
143
|
+
try {
|
|
144
|
+
var saved = localStorage.getItem('reborn-theme');
|
|
145
|
+
if (saved === 'light' || saved === 'dark') return saved;
|
|
146
|
+
} catch (error) { /* Storage may be unavailable for local reports. */ }
|
|
147
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function applyTheme(theme) {
|
|
151
|
+
document.body.dataset.theme = theme;
|
|
152
|
+
document.getElementById('theme-toggle').setAttribute('aria-checked', theme === 'dark' ? 'true' : 'false');
|
|
185
153
|
}
|
|
186
154
|
|
|
187
155
|
function tabButton(id, label) {
|
|
188
|
-
|
|
156
|
+
var count = id === 'tests' ? data.tests.length : null;
|
|
157
|
+
return '<button id="tab-' + id + '" class="tab" role="tab" type="button" data-tab="' + id + '" aria-selected="false" aria-controls="panel-' + id + '">' + icon(id) + '<span>' + label + '</span>' + (count === null ? '' : '<span class="nav-count" aria-hidden="true">' + count + '</span>') + '</button>';
|
|
189
158
|
}
|
|
190
159
|
|
|
191
160
|
function readRoute() {
|
|
192
|
-
var hash =
|
|
193
|
-
try {
|
|
194
|
-
|
|
161
|
+
var hash = location.hash.slice(1);
|
|
162
|
+
try {
|
|
163
|
+
if (hash.indexOf('tests/') === 0) return { tab: 'tests', id: decodeURIComponent(hash.slice(6)) };
|
|
164
|
+
hash = decodeURIComponent(hash);
|
|
165
|
+
} catch (error) { return { tab: 'overview' }; }
|
|
195
166
|
if (TABS.indexOf(hash) !== -1) return { tab: hash };
|
|
196
|
-
if (
|
|
197
|
-
if (data.tests.some(function (test) { return test.id === hash; })) return { tab: 'tests', id: hash };
|
|
167
|
+
if (testIndex.has(hash)) return { tab: 'tests', id: hash };
|
|
198
168
|
return { tab: 'overview' };
|
|
199
169
|
}
|
|
200
170
|
|
|
@@ -206,13 +176,16 @@
|
|
|
206
176
|
function showTab(tab, quiet) {
|
|
207
177
|
if (TABS.indexOf(tab) === -1) tab = 'overview';
|
|
208
178
|
state.tab = tab;
|
|
179
|
+
document.getElementById('page-name').textContent = tab === 'overview' ? 'Dashboard' : tab.charAt(0).toUpperCase() + tab.slice(1);
|
|
209
180
|
document.querySelectorAll('[role="tab"]').forEach(function (button) {
|
|
210
181
|
button.setAttribute('aria-selected', button.dataset.tab === tab ? 'true' : 'false');
|
|
182
|
+
button.tabIndex = button.dataset.tab === tab ? 0 : -1;
|
|
211
183
|
});
|
|
212
184
|
TABS.forEach(function (name) {
|
|
213
185
|
var panel = document.getElementById('panel-' + name);
|
|
214
186
|
var on = name === tab;
|
|
215
187
|
panel.hidden = !on;
|
|
188
|
+
panel.setAttribute('aria-labelledby', 'tab-' + name);
|
|
216
189
|
panel.classList.toggle('panel', true);
|
|
217
190
|
if (on && !quiet && !reduce) {
|
|
218
191
|
panel.classList.remove('panel-enter');
|
|
@@ -224,68 +197,111 @@
|
|
|
224
197
|
if (tab === 'screenshots') ensureShots();
|
|
225
198
|
if (tab === 'logs') ensureLogs();
|
|
226
199
|
if (tab === 'files') ensureFiles();
|
|
227
|
-
writeHash();
|
|
228
|
-
|
|
200
|
+
writeHash(!quiet);
|
|
201
|
+
|
|
229
202
|
}
|
|
230
203
|
|
|
231
|
-
function writeHash() {
|
|
204
|
+
function writeHash(push) {
|
|
232
205
|
var next = state.tab === 'overview' ? 'overview' : state.tab;
|
|
233
206
|
if (state.tab === 'tests' && state.selectedId) next = 'tests/' + encodeURIComponent(state.selectedId);
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
var glow = document.getElementById('tab-glow');
|
|
240
|
-
var active = document.querySelector('.tab[aria-selected="true"]');
|
|
241
|
-
if (!bar || !glow || !active) return;
|
|
242
|
-
var barBox = bar.getBoundingClientRect();
|
|
243
|
-
var box = active.getBoundingClientRect();
|
|
244
|
-
glow.style.width = box.width + 'px';
|
|
245
|
-
glow.style.transform = 'translateX(' + (box.left - barBox.left - 4) + 'px)';
|
|
207
|
+
if (location.hash === '#' + next) return;
|
|
208
|
+
if (window.history && history.replaceState) {
|
|
209
|
+
var url = location.protocol === 'about:' ? location.href.split('#')[0] + '#' + next : '#' + next;
|
|
210
|
+
try { history[push ? 'pushState' : 'replaceState'](null, '', url); } catch (error) { /* Some artifact viewers block history updates. */ }
|
|
211
|
+
}
|
|
246
212
|
}
|
|
247
213
|
|
|
248
214
|
function paintOverview() {
|
|
249
215
|
var host = document.getElementById('panel-overview');
|
|
250
216
|
host.innerHTML = [
|
|
251
217
|
'<header class="hero">',
|
|
252
|
-
'<div><
|
|
253
|
-
'<p class="
|
|
218
|
+
'<div><p class="dashboard-kicker">Run overview</p><h1 class="word">Dashboard</h1><p class="product" id="product"></p><p class="summary" id="summary"></p></div>',
|
|
219
|
+
'<p class="run-state" id="run-state"></p>',
|
|
254
220
|
'</header>',
|
|
221
|
+
'<p class="meta" id="meta"></p>',
|
|
255
222
|
'<div id="errors"></div>',
|
|
256
|
-
'<
|
|
257
|
-
'<
|
|
258
|
-
'<div class="
|
|
259
|
-
'<
|
|
260
|
-
'<div class="legend" id="legend"></div>',
|
|
261
|
-
'<div class="
|
|
262
|
-
'<
|
|
263
|
-
'<div class="views" role="group" aria-label="Overview shape">',
|
|
264
|
-
'<
|
|
265
|
-
'<button type="button" class="view" data-overview="timeline">Timeline</button>',
|
|
266
|
-
'</div>',
|
|
267
|
-
'</div>',
|
|
268
|
-
'<p class="section-note" id="range-note"></p>',
|
|
269
|
-
'<div class="charts" id="duration"></div>',
|
|
223
|
+
'<div class="metric-grid" id="metrics"></div>',
|
|
224
|
+
'<div class="dashboard-grid">',
|
|
225
|
+
'<section class="dashboard-card result-card"><div class="card-heading"><h2>Test results</h2><span class="card-detail">This run</span></div>',
|
|
226
|
+
'<div class="result-body"><div class="result-ring" id="result-ring"><div><strong id="pass-percent"></strong><span>Passed</span></div></div>',
|
|
227
|
+
'<div class="legend" id="legend"></div></div><p class="card-note" id="result-note"></p></section>',
|
|
228
|
+
'<section class="dashboard-card console"><div class="console-body">',
|
|
229
|
+
'<div class="range-head"><h2 class="section-label" id="range-label">Duration</h2>',
|
|
230
|
+
'<div class="views" role="group" aria-label="Overview shape"><button type="button" class="view" data-overview="chart">Chart</button><button type="button" class="view" data-overview="timeline">Timeline</button></div></div>',
|
|
231
|
+
'<p class="section-note" id="range-note"></p><div class="charts" id="duration"></div>',
|
|
270
232
|
'<div class="strip-window" id="strip-window"><div class="strip-scroll"><div class="strip" id="strip" role="group" aria-label="Tests by time"></div></div></div>',
|
|
233
|
+
'</div></section>',
|
|
234
|
+
'<section class="dashboard-card attention-card"><div class="card-heading"><h2>Needs attention</h2><button type="button" class="dashboard-link" data-go-tests>View all tests ' + icon('arrow') + '</button></div><div id="attention-list"></div></section>',
|
|
235
|
+
'<section class="dashboard-card environment-card"><div class="card-heading"><h2>Run details</h2>' + icon('folder') + '</div><dl class="run-details" id="run-details"></dl><dl class="info" id="info"></dl></section>',
|
|
271
236
|
'</div>',
|
|
272
|
-
'</section>',
|
|
273
237
|
].join('');
|
|
274
|
-
var word = document.getElementById('word');
|
|
275
|
-
word.textContent = companyName();
|
|
276
|
-
var product = document.getElementById('product');
|
|
277
238
|
var subtitle = typeof data.productSubtitle === 'string' ? data.productSubtitle.trim() : 'Test Automation';
|
|
278
|
-
|
|
279
|
-
|
|
239
|
+
document.getElementById('product').textContent = subtitle;
|
|
240
|
+
document.getElementById('product').hidden = !subtitle;
|
|
280
241
|
document.getElementById('summary').textContent = data.summary || '';
|
|
281
|
-
|
|
242
|
+
fill(document.getElementById('meta'), metaBits().map(function (bit) { return el('span', null, [bit]); }));
|
|
243
|
+
setRunState(document.getElementById('run-state'));
|
|
244
|
+
paintDashboard();
|
|
282
245
|
paintErrors();
|
|
283
246
|
paintInfo();
|
|
284
247
|
paintCounts(document.getElementById('legend'), false);
|
|
285
248
|
paintDuration();
|
|
286
249
|
paintStrip();
|
|
287
250
|
applyOverview();
|
|
288
|
-
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function paintDashboard() {
|
|
254
|
+
var counts = data.counts || {};
|
|
255
|
+
var total = data.tests.length;
|
|
256
|
+
var passed = counts.passed || 0;
|
|
257
|
+
var percent = total ? Math.round(passed / total * 100) : 0;
|
|
258
|
+
var problems = data.tests.filter(isProblem);
|
|
259
|
+
var metrics = [
|
|
260
|
+
['tests', 'Total tests', String(total), 'Across ' + uniqueValues(function (test) { return [test.file]; }).length + ' test files', ''],
|
|
261
|
+
['check', 'Pass rate', total ? percent + '%' : '—', passed + ' of ' + total + ' tests passed', 'positive'],
|
|
262
|
+
['clock', 'Run duration', formatDuration(data.duration || 0), 'Wall time including retries', ''],
|
|
263
|
+
['alert', 'Needs attention', String(problems.length), 'Failed, flaky or interrupted', problems.length ? 'warning' : 'positive'],
|
|
264
|
+
];
|
|
265
|
+
fill(document.getElementById('metrics'), metrics.map(function (metric) {
|
|
266
|
+
var node = el('div', { class: 'metric ' + metric[4] }, [
|
|
267
|
+
el('div', { class: 'metric-label' }, [metric[1]]),
|
|
268
|
+
el('strong', { class: 'metric-value' }, [metric[2]]),
|
|
269
|
+
el('span', { class: 'metric-note' }, [metric[3]]),
|
|
270
|
+
]);
|
|
271
|
+
node.querySelector('.metric-label').insertAdjacentHTML('beforeend', icon(metric[0]));
|
|
272
|
+
return node;
|
|
273
|
+
}));
|
|
274
|
+
document.getElementById('pass-percent').textContent = total ? percent + '%' : '—';
|
|
275
|
+
var colors = { passed: 'var(--pass)', failed: 'var(--fail)', timedOut: 'var(--fail)', flaky: 'var(--flaky)', skipped: 'var(--skip)', interrupted: 'var(--timeout)' };
|
|
276
|
+
var offset = 0;
|
|
277
|
+
var stops = [];
|
|
278
|
+
Object.keys(colors).forEach(function (status) {
|
|
279
|
+
var amount = counts[status] || 0;
|
|
280
|
+
if (!amount || !total) return;
|
|
281
|
+
var next = offset + amount / total * 100;
|
|
282
|
+
stops.push(colors[status] + ' ' + offset + '% ' + next + '%');
|
|
283
|
+
offset = next;
|
|
284
|
+
});
|
|
285
|
+
document.getElementById('result-ring').style.background = stops.length ? 'conic-gradient(' + stops.join(',') + ')' : 'var(--hairline)';
|
|
286
|
+
document.getElementById('result-note').textContent = 'Select a result to explore its tests. Pass rate includes skipped tests in the total.';
|
|
287
|
+
var attention = problems.slice(0, 5).map(function (test) {
|
|
288
|
+
return el('button', { type: 'button', class: 'attention-row open-test', 'data-id': test.id, 'data-attempt': String(failedAttempt(test)) }, [
|
|
289
|
+
el('span', { class: 'attention-status sw', 'data-status': test.status }, [statusLabel(test)]),
|
|
290
|
+
el('span', { class: 'attention-test' }, [el('strong', null, [test.title]), el('span', null, [test.file])]),
|
|
291
|
+
el('span', { class: 'attention-time' }, [formatDuration(test.duration)]),
|
|
292
|
+
]);
|
|
293
|
+
});
|
|
294
|
+
if (!attention.length) attention.push(el('p', { class: 'empty' }, [total ? 'No tests need attention. Your run is looking good.' : 'No tests ran. Run your test suite to see results here.']));
|
|
295
|
+
if (problems.length > 5) attention.push(el('p', { class: 'card-note' }, ['Showing 5 of ' + problems.length + ' tests. View all tests to explore the rest.']));
|
|
296
|
+
fill(document.getElementById('attention-list'), attention);
|
|
297
|
+
var rows = [['Started', formatWhen(data.startTime)], ['Workers', String(data.workers || 0)], ['Screenshots', { off: 'Off', failure: 'On failure', last: 'Last step', steps: 'Every step' }[data.screenshots] || 'Off']];
|
|
298
|
+
if (data.showProjects === true && (data.projects || []).length) rows.push(['Projects', data.projects.join(', ')]);
|
|
299
|
+
if (data.shard) rows.push(['Shard', data.shard]);
|
|
300
|
+
var customLabels = (data.info || []).map(function (row) { return String(row.label).toLowerCase(); });
|
|
301
|
+
rows = rows.filter(function (row) { return customLabels.indexOf(row[0].toLowerCase()) === -1; });
|
|
302
|
+
fill(document.getElementById('run-details'), rows.map(function (row) {
|
|
303
|
+
return el('div', null, [el('dt', null, [row[0]]), el('dd', null, [row[1]])]);
|
|
304
|
+
}));
|
|
289
305
|
}
|
|
290
306
|
|
|
291
307
|
function paintInfo() {
|
|
@@ -361,7 +377,7 @@
|
|
|
361
377
|
empty.setAttribute('cx', '110');
|
|
362
378
|
empty.setAttribute('cy', '110');
|
|
363
379
|
empty.setAttribute('r', '96');
|
|
364
|
-
empty.setAttribute('fill', '
|
|
380
|
+
empty.setAttribute('fill', 'var(--hairline)');
|
|
365
381
|
svg.appendChild(empty);
|
|
366
382
|
return svg;
|
|
367
383
|
}
|
|
@@ -479,7 +495,7 @@
|
|
|
479
495
|
function paintCounts(host, withFind) {
|
|
480
496
|
var counts = data.counts || {};
|
|
481
497
|
var filters = [
|
|
482
|
-
['all', '
|
|
498
|
+
['all', 'tests', counts.total || 0],
|
|
483
499
|
['passed', 'passed', counts.passed || 0],
|
|
484
500
|
['failed', 'failed', counts.failed || 0],
|
|
485
501
|
['flaky', 'flaky', counts.flaky || 0],
|
|
@@ -490,22 +506,9 @@
|
|
|
490
506
|
var buttons = filters.map(function (filter) {
|
|
491
507
|
return el('button', { class: 'count', type: 'button', 'data-status': filter[0], 'aria-pressed': 'false' }, [
|
|
492
508
|
el('span', { class: 'n' }, [String(filter[2])]),
|
|
493
|
-
filter[1],
|
|
509
|
+
' ' + filter[1],
|
|
494
510
|
]);
|
|
495
511
|
});
|
|
496
|
-
if (host.id === 'legend') {
|
|
497
|
-
buttons.unshift(el('div', { class: 'count count-static' }, [
|
|
498
|
-
el('span', { class: 'n' }, [formatDuration(data.duration || 0)]),
|
|
499
|
-
'total',
|
|
500
|
-
]));
|
|
501
|
-
buttons.forEach(function (button) {
|
|
502
|
-
if (button.classList.contains('count-static')) return;
|
|
503
|
-
var node = button.querySelector('.n');
|
|
504
|
-
var value = Number(node.textContent);
|
|
505
|
-
node.textContent = '0';
|
|
506
|
-
countUp(node, value, function (next) { return String(Math.round(next)); });
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
512
|
var children = buttons;
|
|
510
513
|
if (withFind) {
|
|
511
514
|
children = buttons.concat([el('div', { class: 'find-wrap' }, [
|
|
@@ -517,7 +520,7 @@
|
|
|
517
520
|
}
|
|
518
521
|
|
|
519
522
|
function syncPressed() {
|
|
520
|
-
document.querySelectorAll('.count').forEach(function (button) {
|
|
523
|
+
document.querySelectorAll('.count[data-status]').forEach(function (button) {
|
|
521
524
|
button.setAttribute('aria-pressed', button.dataset.status === state.filter ? 'true' : 'false');
|
|
522
525
|
});
|
|
523
526
|
}
|
|
@@ -538,7 +541,7 @@
|
|
|
538
541
|
function paintStrip() {
|
|
539
542
|
var strip = document.getElementById('strip');
|
|
540
543
|
if (!data.tests.length) {
|
|
541
|
-
fill(strip, []);
|
|
544
|
+
fill(strip, [el('p', { class: 'empty' }, ['No tests ran.'])]);
|
|
542
545
|
return;
|
|
543
546
|
}
|
|
544
547
|
var max = data.tests.reduce(function (hi, test) { return Math.max(hi, test.duration || 0); }, 1);
|
|
@@ -547,7 +550,7 @@
|
|
|
547
550
|
});
|
|
548
551
|
strip.className = 'run-list';
|
|
549
552
|
strip.style.height = '';
|
|
550
|
-
|
|
553
|
+
var nodes = ordered.slice(0, state.timelineLimit).map(function (test) {
|
|
551
554
|
var fillBar = el('span', { class: 'run-fill', 'data-status': test.status });
|
|
552
555
|
fillBar.style.width = Math.max(2, Math.round(((test.duration || 0) / max) * 100)) + '%';
|
|
553
556
|
return el('button', {
|
|
@@ -562,51 +565,10 @@
|
|
|
562
565
|
el('span', { class: 'run-track' }, [fillBar]),
|
|
563
566
|
el('span', { class: 'run-time' }, [formatDuration(test.duration)]),
|
|
564
567
|
]);
|
|
565
|
-
}));
|
|
566
|
-
markStrip();
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
function paintCanvasStrip(host) {
|
|
570
|
-
var canvas = el('canvas', { class: 'strip-canvas', 'aria-label': 'Tests by duration' });
|
|
571
|
-
fill(host, [canvas]);
|
|
572
|
-
var hits = [];
|
|
573
|
-
function draw() {
|
|
574
|
-
var width = host.clientWidth || 600;
|
|
575
|
-
var height = 52;
|
|
576
|
-
var ratio = window.devicePixelRatio || 1;
|
|
577
|
-
canvas.width = Math.floor(width * ratio);
|
|
578
|
-
canvas.height = Math.floor(height * ratio);
|
|
579
|
-
canvas.style.width = width + 'px';
|
|
580
|
-
canvas.style.height = height + 'px';
|
|
581
|
-
var ctx = canvas.getContext('2d');
|
|
582
|
-
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
|
|
583
|
-
ctx.clearRect(0, 0, width, height);
|
|
584
|
-
var total = data.tests.reduce(function (sum, test) { return sum + Math.max(test.duration || 0, 1); }, 0);
|
|
585
|
-
var x = 0;
|
|
586
|
-
hits = [];
|
|
587
|
-
data.tests.forEach(function (test) {
|
|
588
|
-
var w = Math.max(2, (Math.max(test.duration || 0, 1) / total) * width);
|
|
589
|
-
ctx.fillStyle = canvasColor(test.status);
|
|
590
|
-
ctx.fillRect(x, 8, Math.max(1, w - 2), 36);
|
|
591
|
-
hits.push({ x: x, w: w, id: test.id });
|
|
592
|
-
x += w;
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
draw();
|
|
596
|
-
window.addEventListener('resize', draw);
|
|
597
|
-
canvas.addEventListener('mousemove', function (event) {
|
|
598
|
-
var rect = canvas.getBoundingClientRect();
|
|
599
|
-
var x = event.clientX - rect.left;
|
|
600
|
-
var hit = hits.find(function (item) { return x >= item.x && x < item.x + item.w; });
|
|
601
|
-
if (hit) showNow(hit.id);
|
|
602
|
-
});
|
|
603
|
-
canvas.addEventListener('mouseleave', function () { showNow(state.selectedId); });
|
|
604
|
-
canvas.addEventListener('click', function (event) {
|
|
605
|
-
var rect = canvas.getBoundingClientRect();
|
|
606
|
-
var x = event.clientX - rect.left;
|
|
607
|
-
var hit = hits.find(function (item) { return x >= item.x && x < item.x + item.w; });
|
|
608
|
-
if (hit) choose(hit.id, true);
|
|
609
568
|
});
|
|
569
|
+
if (ordered.length > state.timelineLimit) nodes.push(el('button', { class: 'more', type: 'button', id: 'show-more-timeline' }, ['Show more tests']));
|
|
570
|
+
fill(strip, nodes);
|
|
571
|
+
markStrip();
|
|
610
572
|
}
|
|
611
573
|
|
|
612
574
|
function canvasColor(status) {
|
|
@@ -617,7 +579,7 @@
|
|
|
617
579
|
}
|
|
618
580
|
|
|
619
581
|
function markStrip() {
|
|
620
|
-
var visible =
|
|
582
|
+
var visible = Object.create(null);
|
|
621
583
|
filteredTests().forEach(function (test) { visible[test.id] = true; });
|
|
622
584
|
document.querySelectorAll('.run-row').forEach(function (segment) {
|
|
623
585
|
segment.classList.toggle('is-dim', !visible[segment.dataset.id]);
|
|
@@ -637,7 +599,7 @@
|
|
|
637
599
|
}
|
|
638
600
|
|
|
639
601
|
function uniqueValues(pick) {
|
|
640
|
-
var seen =
|
|
602
|
+
var seen = Object.create(null);
|
|
641
603
|
data.tests.forEach(function (test) {
|
|
642
604
|
pick(test).forEach(function (value) {
|
|
643
605
|
if (value) seen[value] = true;
|
|
@@ -646,33 +608,25 @@
|
|
|
646
608
|
return Object.keys(seen).sort();
|
|
647
609
|
}
|
|
648
610
|
|
|
611
|
+
function selectControl(id, label, choices, current) {
|
|
612
|
+
var select = el('select', { id: id, class: 'filter-select' }, choices.map(function (choice) {
|
|
613
|
+
return el('option', { value: choice[0] }, [choice[1]]);
|
|
614
|
+
}));
|
|
615
|
+
select.value = current || '';
|
|
616
|
+
return el('label', { class: 'filter-control', for: id }, [el('span', null, [label]), select]);
|
|
617
|
+
}
|
|
618
|
+
|
|
649
619
|
function paintTags() {
|
|
650
620
|
var host = document.getElementById('tags');
|
|
651
621
|
if (!host) return;
|
|
652
622
|
var tags = uniqueValues(function (test) { return (test.tags || []).filter(keptTag); });
|
|
653
623
|
var projects = uniqueValues(function (test) { return test.project ? [test.project] : []; });
|
|
654
|
-
var nodes = [];
|
|
655
|
-
if (tags.length) {
|
|
656
|
-
nodes.push(el('span', { class: 'order-label' }, ['Tags']));
|
|
657
|
-
nodes.push(chip('tag-btn', 'all', 'All', state.tag));
|
|
658
|
-
tags.forEach(function (tag) { nodes.push(chip('tag-btn', tag, tag, state.tag)); });
|
|
659
|
-
}
|
|
624
|
+
var nodes = [selectControl('tag-select', 'Tags', [['', 'All tags']].concat(tags.map(function (tag) { return [tag, tag]; })), state.tag)];
|
|
660
625
|
if (data.showProjectFilter === true && projects.length > 1) {
|
|
661
|
-
nodes.push(
|
|
662
|
-
nodes.push(chip('project-btn', 'all', 'All', state.projectFilter));
|
|
663
|
-
projects.forEach(function (project) { nodes.push(chip('project-btn', project, project, state.projectFilter)); });
|
|
626
|
+
nodes.push(selectControl('project-select', 'Project', [['', 'All projects']].concat(projects.map(function (project) { return [project, project]; })), state.projectFilter));
|
|
664
627
|
}
|
|
665
|
-
host.hidden = nodes.length === 0;
|
|
666
628
|
fill(host, nodes);
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
function chip(className, value, label, current) {
|
|
670
|
-
return el('button', {
|
|
671
|
-
class: className,
|
|
672
|
-
type: 'button',
|
|
673
|
-
'data-value': value,
|
|
674
|
-
'aria-pressed': current === value ? 'true' : 'false',
|
|
675
|
-
}, [label]);
|
|
629
|
+
if (!tags.length) document.getElementById('tag-select').disabled = true;
|
|
676
630
|
}
|
|
677
631
|
|
|
678
632
|
function sortTests(tests) {
|
|
@@ -692,17 +646,10 @@
|
|
|
692
646
|
function paintOrder() {
|
|
693
647
|
var host = document.getElementById('order');
|
|
694
648
|
if (!host) return;
|
|
695
|
-
var orders = [['grouped', 'Grouped'], ['slowest', 'Slowest'], ['fastest', 'Fastest'], ['started', '
|
|
696
|
-
var nodes = [
|
|
697
|
-
return el('button', {
|
|
698
|
-
class: 'order-btn',
|
|
699
|
-
type: 'button',
|
|
700
|
-
'data-order': order[0],
|
|
701
|
-
'aria-pressed': state.order === order[0] ? 'true' : 'false',
|
|
702
|
-
}, [order[1]]);
|
|
703
|
-
}));
|
|
649
|
+
var orders = [['grouped', 'Grouped'], ['slowest', 'Slowest first'], ['fastest', 'Fastest first'], ['started', 'Start time']];
|
|
650
|
+
var nodes = [selectControl('sort-select', 'Sort', orders, state.order)];
|
|
704
651
|
if (state.band !== 'all') {
|
|
705
|
-
nodes.push(el('button', { class: 'band-clear', type: 'button', 'aria-
|
|
652
|
+
nodes.push(el('button', { class: 'band-clear', type: 'button', 'aria-label': 'Clear duration filter: ' + bandLabel(state.band) }, [bandLabel(state.band), el('span', { 'aria-hidden': 'true' }, [' ×'])]));
|
|
706
653
|
}
|
|
707
654
|
fill(host, nodes);
|
|
708
655
|
}
|
|
@@ -711,7 +658,7 @@
|
|
|
711
658
|
if (built.tests) return;
|
|
712
659
|
built.tests = true;
|
|
713
660
|
var host = document.getElementById('panel-tests');
|
|
714
|
-
host.innerHTML = '<div class="legend" id="test-tools"></div><div class="tags" id="tags"></div><div class="order" id="order"></div><div class="split"><div id="tests"></div><section class="detail" id="detail"></section></div>';
|
|
661
|
+
host.innerHTML = '<div class="page-heading"><h2>Tests</h2><p>Explore results, retries and the steps behind every test.</p></div><div class="legend" id="test-tools"></div><div class="filter-toolbar"><div class="tags" id="tags"></div><div class="order" id="order"></div></div><div class="list-summary" id="list-summary"></div><div class="split"><div id="tests"></div><section class="detail" id="detail"></section></div>';
|
|
715
662
|
paintCounts(document.getElementById('test-tools'), true);
|
|
716
663
|
paintTags();
|
|
717
664
|
paintOrder();
|
|
@@ -724,8 +671,13 @@
|
|
|
724
671
|
var host = document.getElementById('tests');
|
|
725
672
|
if (!host) return;
|
|
726
673
|
var tests = filteredTests();
|
|
674
|
+
var summary = document.getElementById('list-summary');
|
|
675
|
+
if (summary) fill(summary, [
|
|
676
|
+
el('span', { role: 'status', 'aria-live': 'polite' }, [tests.length + ' of ' + data.tests.length + ' tests']),
|
|
677
|
+
hasFilters() ? el('button', { type: 'button', class: 'reset-filters' }, ['Clear filters']) : null,
|
|
678
|
+
]);
|
|
727
679
|
if (!tests.length) {
|
|
728
|
-
fill(host, [el('p', { class: 'empty' }, [data.tests.length ? 'No tests match.' : 'No tests ran.'])]);
|
|
680
|
+
fill(host, [el('p', { class: 'empty' }, [data.tests.length ? 'No tests match. Clear filters or try another search.' : 'No tests ran.'])]);
|
|
729
681
|
return;
|
|
730
682
|
}
|
|
731
683
|
if (state.order !== 'grouped') {
|
|
@@ -733,20 +685,20 @@
|
|
|
733
685
|
var shown = sorted.slice(0, state.listLimit);
|
|
734
686
|
var flat = shown.map(function (test) { return rowButton(test, true); });
|
|
735
687
|
if (shown.length < sorted.length) {
|
|
736
|
-
flat.push(el('button', { class: 'more', type: 'button', id: 'show-rest' }, ['Show
|
|
688
|
+
flat.push(el('button', { class: 'more', type: 'button', id: 'show-rest' }, ['Show more tests']));
|
|
737
689
|
}
|
|
738
690
|
fill(host, flat);
|
|
739
691
|
return;
|
|
740
692
|
}
|
|
741
693
|
var groups = [];
|
|
742
|
-
var problemIds =
|
|
694
|
+
var problemIds = Object.create(null);
|
|
743
695
|
var problems = tests.filter(isProblem);
|
|
744
696
|
if (state.filter === 'all' && !state.query && problems.length && problems.length !== tests.length) {
|
|
745
697
|
problems.forEach(function (test) { problemIds[test.id] = true; });
|
|
746
698
|
groups.push({ name: 'Problems', tests: problems });
|
|
747
699
|
}
|
|
748
700
|
var byFile = [];
|
|
749
|
-
var index =
|
|
701
|
+
var index = Object.create(null);
|
|
750
702
|
tests.forEach(function (test) {
|
|
751
703
|
if (problemIds[test.id]) return;
|
|
752
704
|
if (index[test.file] == null) {
|
|
@@ -771,7 +723,7 @@
|
|
|
771
723
|
return el('div', { class: 'group' }, [el('p', { class: 'file-name' }, [group.name])].concat(rows));
|
|
772
724
|
}).filter(Boolean);
|
|
773
725
|
if (truncated) {
|
|
774
|
-
nodes.push(el('button', { class: 'more', type: 'button', id: 'show-rest' }, ['Show
|
|
726
|
+
nodes.push(el('button', { class: 'more', type: 'button', id: 'show-rest' }, ['Show more tests']));
|
|
775
727
|
}
|
|
776
728
|
fill(host, nodes);
|
|
777
729
|
}
|
|
@@ -805,7 +757,7 @@
|
|
|
805
757
|
['logs', 'Logs'],
|
|
806
758
|
['shots', 'Screenshots'],
|
|
807
759
|
];
|
|
808
|
-
var tabs = el('div', { class: 'subtabs', role: '
|
|
760
|
+
var tabs = el('div', { class: 'subtabs', role: 'group', 'aria-label': 'Test details' }, panes.map(function (pane) {
|
|
809
761
|
return el('button', {
|
|
810
762
|
class: 'subtab',
|
|
811
763
|
type: 'button',
|
|
@@ -816,6 +768,7 @@
|
|
|
816
768
|
var body = [headerBlock(test), tabs];
|
|
817
769
|
if (test.attempts.length > 1) body.push(attemptTabs(test));
|
|
818
770
|
if (attempt) body.push(paneBody(test, attempt));
|
|
771
|
+
else body.push(el('p', { class: 'empty block' }, ['This test did not run.']));
|
|
819
772
|
fill(host, [el('div', { class: 'detail-body' }, body)]);
|
|
820
773
|
}
|
|
821
774
|
|
|
@@ -831,7 +784,7 @@
|
|
|
831
784
|
if (test.group && test.group.length) bits.push(el('p', { class: 'kicker' }, [test.group.join(' / ')]));
|
|
832
785
|
bits.push(el('h2', null, [test.title]));
|
|
833
786
|
var where = [el('span', null, [test.file + ':' + test.line])];
|
|
834
|
-
if (test.project) where.push(el('span', null, [test.project]));
|
|
787
|
+
if (data.showProjects === true && test.project) where.push(el('span', null, [test.project]));
|
|
835
788
|
(test.tags || []).forEach(function (tag) { where.push(el('span', null, [tag])); });
|
|
836
789
|
bits.push(el('p', { class: 'where' }, where));
|
|
837
790
|
bits.push(el('p', { class: 'status-line' }, [
|
|
@@ -859,7 +812,8 @@
|
|
|
859
812
|
|
|
860
813
|
function errorBlock(attempt) {
|
|
861
814
|
if (!attempt.errors || !attempt.errors.length) return null;
|
|
862
|
-
var
|
|
815
|
+
var images = (attempt.attachments || []).filter(function (item) { return item.kind === 'image' && safeSrc(item.path); });
|
|
816
|
+
var image = images.find(function (item) { return item.role === 'failure'; }) || images[images.length - 1];
|
|
863
817
|
var messages = attempt.errors.map(function (error) {
|
|
864
818
|
var nodes = [el('pre', null, [error.message || 'The test failed.'])];
|
|
865
819
|
if (error.location) nodes.push(el('p', { class: 'where-error' }, [error.location]));
|
|
@@ -949,8 +903,9 @@
|
|
|
949
903
|
var src = safeSrc(file.path);
|
|
950
904
|
var copy = el('div', { class: 'tile-copy' }, [
|
|
951
905
|
el('p', { class: 'tile-title' }, [test.title]),
|
|
906
|
+
el('p', { class: 'tile-meta' }, ['Attempt ' + (index + 1) + ' · ' + attemptLabel(attempt.status)]),
|
|
952
907
|
el('p', { class: 'tile-meta' }, [file.stepTitle || file.name || 'Screenshot']),
|
|
953
|
-
el('button', { class: 'open-test', type: 'button', 'data-id': test.id, 'data-attempt': String(index) }, ['Open test']),
|
|
908
|
+
el('button', { class: 'open-test', type: 'button', 'data-id': test.id, 'data-attempt': String(index), 'data-open-pane': 'shots' }, ['Open test']),
|
|
954
909
|
]);
|
|
955
910
|
var tile = el('div', { class: 'tile', 'data-role': file.role || '', 'data-status': test.status }, [
|
|
956
911
|
el('button', { class: 'shot', type: 'button', 'data-src': src, 'data-alt': file.name || test.title }, [
|
|
@@ -1014,7 +969,7 @@
|
|
|
1014
969
|
];
|
|
1015
970
|
if (attempt.stdout) nodes.push(el('div', { class: 'well' }, [el('p', null, ['Output']), el('pre', null, [attempt.stdout])]));
|
|
1016
971
|
if (attempt.stderr) nodes.push(el('div', { class: 'well' }, [el('p', null, ['Error output']), el('pre', null, [attempt.stderr])]));
|
|
1017
|
-
nodes.push(el('button', { class: 'open-test', type: 'button', 'data-id': test.id, 'data-attempt': String(index) }, ['Open test']));
|
|
972
|
+
nodes.push(el('button', { class: 'open-test', type: 'button', 'data-id': test.id, 'data-attempt': String(index), 'data-open-pane': 'logs' }, ['Open test']));
|
|
1018
973
|
var card = el('article', { class: 'log-card', 'data-blob': blob }, nodes);
|
|
1019
974
|
cards.push(card);
|
|
1020
975
|
});
|
|
@@ -1022,15 +977,21 @@
|
|
|
1022
977
|
fill(host, [
|
|
1023
978
|
el('h2', null, ['Logs']),
|
|
1024
979
|
el('input', { id: 'log-find', class: 'find', type: 'search', placeholder: 'Find in logs', 'aria-label': 'Find in logs' }),
|
|
1025
|
-
el('
|
|
980
|
+
el('p', { class: 'empty', id: 'log-empty', hidden: cards.length > 0 }, ['No logs in this run.']),
|
|
981
|
+
el('div', { id: 'log-list' }, cards),
|
|
1026
982
|
]);
|
|
1027
983
|
}
|
|
1028
984
|
|
|
1029
985
|
function applyLogFilter() {
|
|
1030
986
|
var query = state.logQuery.trim().toLowerCase();
|
|
987
|
+
var visible = 0;
|
|
1031
988
|
document.querySelectorAll('.log-card').forEach(function (card) {
|
|
1032
989
|
card.hidden = Boolean(query) && card.dataset.blob.indexOf(query) === -1;
|
|
990
|
+
if (!card.hidden) visible += 1;
|
|
1033
991
|
});
|
|
992
|
+
var empty = document.getElementById('log-empty');
|
|
993
|
+
empty.hidden = visible > 0;
|
|
994
|
+
empty.textContent = query ? 'No logs match. Try another search.' : 'No logs in this run.';
|
|
1034
995
|
}
|
|
1035
996
|
|
|
1036
997
|
function ensureFiles() {
|
|
@@ -1067,10 +1028,41 @@
|
|
|
1067
1028
|
}
|
|
1068
1029
|
|
|
1069
1030
|
function bind() {
|
|
1070
|
-
window.addEventListener('
|
|
1031
|
+
window.addEventListener('hashchange', function () {
|
|
1032
|
+
var next = readRoute();
|
|
1033
|
+
if (next.tab === 'tests' && next.id && testById(next.id)) {
|
|
1034
|
+
resetFilters();
|
|
1035
|
+
state.selectedId = next.id;
|
|
1036
|
+
state.attempt = failedAttempt(testById(next.id));
|
|
1037
|
+
state.testPane = defaultPane(testById(next.id));
|
|
1038
|
+
if (built.tests) { paintTags(); paintOrder(); paintList(); paintDetail(); syncPressed(); }
|
|
1039
|
+
}
|
|
1040
|
+
showTab(next.tab, true);
|
|
1041
|
+
});
|
|
1042
|
+
document.querySelector('.skip').addEventListener('click', function (event) {
|
|
1043
|
+
event.preventDefault();
|
|
1044
|
+
showTab('tests');
|
|
1045
|
+
document.getElementById('find').focus();
|
|
1046
|
+
});
|
|
1071
1047
|
app.addEventListener('click', function (event) {
|
|
1072
1048
|
var target = event.target instanceof Element ? event.target : null;
|
|
1073
1049
|
if (!target) return;
|
|
1050
|
+
if (target.closest('.reset-filters')) {
|
|
1051
|
+
resetFilters(); keepSelectionInFilter(); paintTags(); paintOrder(); paintDuration(); paintList(); paintDetail(); syncPressed();
|
|
1052
|
+
document.getElementById('find').focus();
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
if (target.closest('#theme-toggle')) {
|
|
1056
|
+
var theme = document.body.dataset.theme === 'dark' ? 'light' : 'dark';
|
|
1057
|
+
applyTheme(theme);
|
|
1058
|
+
try { localStorage.setItem('reborn-theme', theme); } catch (error) { /* Theme still works without storage. */ }
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
if (target.closest('[data-go-tests]')) {
|
|
1062
|
+
showTab('tests');
|
|
1063
|
+
document.getElementById('find').focus();
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1074
1066
|
var tab = target.closest('.tab');
|
|
1075
1067
|
if (tab) {
|
|
1076
1068
|
showTab(tab.dataset.tab);
|
|
@@ -1084,7 +1076,7 @@
|
|
|
1084
1076
|
}
|
|
1085
1077
|
var opener = target.closest('.open-test');
|
|
1086
1078
|
if (opener) {
|
|
1087
|
-
openTest(opener.dataset.id, Number(opener.dataset.attempt || 0));
|
|
1079
|
+
openTest(opener.dataset.id, Number(opener.dataset.attempt || 0), opener.dataset.openPane);
|
|
1088
1080
|
return;
|
|
1089
1081
|
}
|
|
1090
1082
|
var view = target.closest('.view');
|
|
@@ -1119,35 +1111,6 @@
|
|
|
1119
1111
|
paintDetail();
|
|
1120
1112
|
return;
|
|
1121
1113
|
}
|
|
1122
|
-
var tag = target.closest('.tag-btn');
|
|
1123
|
-
if (tag) {
|
|
1124
|
-
state.tag = tag.dataset.value || 'all';
|
|
1125
|
-
state.listLimit = 200;
|
|
1126
|
-
keepSelectionInFilter();
|
|
1127
|
-
paintTags();
|
|
1128
|
-
paintList();
|
|
1129
|
-
paintDetail();
|
|
1130
|
-
return;
|
|
1131
|
-
}
|
|
1132
|
-
var projectButton = target.closest('.project-btn');
|
|
1133
|
-
if (projectButton) {
|
|
1134
|
-
state.projectFilter = projectButton.dataset.value || 'all';
|
|
1135
|
-
state.listLimit = 200;
|
|
1136
|
-
keepSelectionInFilter();
|
|
1137
|
-
paintTags();
|
|
1138
|
-
paintList();
|
|
1139
|
-
paintDetail();
|
|
1140
|
-
return;
|
|
1141
|
-
}
|
|
1142
|
-
var order = target.closest('.order-btn');
|
|
1143
|
-
if (order) {
|
|
1144
|
-
state.order = order.dataset.order || 'grouped';
|
|
1145
|
-
keepSelectionInFilter();
|
|
1146
|
-
paintOrder();
|
|
1147
|
-
paintList();
|
|
1148
|
-
paintDetail();
|
|
1149
|
-
return;
|
|
1150
|
-
}
|
|
1151
1114
|
var count = target.closest('.count');
|
|
1152
1115
|
if (count && !count.classList.contains('shot-filter') && !count.classList.contains('count-static')) {
|
|
1153
1116
|
state.filter = count.dataset.status || 'all';
|
|
@@ -1159,7 +1122,6 @@
|
|
|
1159
1122
|
paintList();
|
|
1160
1123
|
paintDetail();
|
|
1161
1124
|
markStrip();
|
|
1162
|
-
showNow(state.selectedId);
|
|
1163
1125
|
}
|
|
1164
1126
|
return;
|
|
1165
1127
|
}
|
|
@@ -1177,12 +1139,14 @@
|
|
|
1177
1139
|
if (pane) {
|
|
1178
1140
|
state.testPane = pane.dataset.pane || 'steps';
|
|
1179
1141
|
paintDetail();
|
|
1142
|
+
document.querySelector('.subtab[data-pane="' + state.testPane + '"]').focus();
|
|
1180
1143
|
return;
|
|
1181
1144
|
}
|
|
1182
1145
|
var attempt = target.closest('.attempt');
|
|
1183
1146
|
if (attempt) {
|
|
1184
1147
|
state.attempt = Number(attempt.dataset.index || 0);
|
|
1185
1148
|
paintDetail();
|
|
1149
|
+
document.querySelector('.attempt[data-index="' + state.attempt + '"]').focus();
|
|
1186
1150
|
return;
|
|
1187
1151
|
}
|
|
1188
1152
|
if (target.closest('#api-toggle')) {
|
|
@@ -1192,15 +1156,36 @@
|
|
|
1192
1156
|
if (toggle) toggle.focus();
|
|
1193
1157
|
return;
|
|
1194
1158
|
}
|
|
1159
|
+
if (target.closest('#show-more-timeline')) {
|
|
1160
|
+
state.timelineLimit += 200; paintStrip();
|
|
1161
|
+
var moreTimeline = document.getElementById('show-more-timeline');
|
|
1162
|
+
if (moreTimeline) moreTimeline.focus();
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1195
1165
|
if (target.closest('#show-rest')) {
|
|
1196
|
-
state.listLimit
|
|
1166
|
+
state.listLimit += 200;
|
|
1197
1167
|
paintList();
|
|
1168
|
+
var more = document.getElementById('show-rest');
|
|
1169
|
+
if (more) more.focus();
|
|
1198
1170
|
return;
|
|
1199
1171
|
}
|
|
1200
1172
|
var shot = target.closest('.shot');
|
|
1201
1173
|
if (shot) openShot(shot.dataset.src, shot.dataset.alt || 'Screenshot');
|
|
1202
1174
|
});
|
|
1203
1175
|
|
|
1176
|
+
app.addEventListener('change', function (event) {
|
|
1177
|
+
var control = event.target;
|
|
1178
|
+
if (!control || ['tag-select', 'project-select', 'sort-select'].indexOf(control.id) === -1) return;
|
|
1179
|
+
if (control.id === 'tag-select') state.tag = control.value || null;
|
|
1180
|
+
if (control.id === 'project-select') state.projectFilter = control.value || null;
|
|
1181
|
+
if (control.id === 'sort-select') state.order = control.value || 'grouped';
|
|
1182
|
+
state.listLimit = 200;
|
|
1183
|
+
keepSelectionInFilter();
|
|
1184
|
+
paintList();
|
|
1185
|
+
paintDetail();
|
|
1186
|
+
markStrip();
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1204
1189
|
app.addEventListener('input', function (event) {
|
|
1205
1190
|
if (!event.target) return;
|
|
1206
1191
|
if (event.target.id === 'find') {
|
|
@@ -1213,7 +1198,6 @@
|
|
|
1213
1198
|
paintList();
|
|
1214
1199
|
paintDetail();
|
|
1215
1200
|
markStrip();
|
|
1216
|
-
showNow(state.selectedId);
|
|
1217
1201
|
}, 80);
|
|
1218
1202
|
}
|
|
1219
1203
|
if (event.target.id === 'log-find') {
|
|
@@ -1222,21 +1206,13 @@
|
|
|
1222
1206
|
}
|
|
1223
1207
|
});
|
|
1224
1208
|
|
|
1225
|
-
app.addEventListener('mouseover', function (event) {
|
|
1226
|
-
var segment = event.target && event.target.closest ? event.target.closest('.run-row') : null;
|
|
1227
|
-
if (segment) showNow(segment.dataset.id);
|
|
1228
|
-
});
|
|
1229
|
-
app.addEventListener('mouseout', function (event) {
|
|
1230
|
-
var segment = event.target && event.target.closest ? event.target.closest('.run-row') : null;
|
|
1231
|
-
if (!segment) return;
|
|
1232
|
-
var next = event.relatedTarget && event.relatedTarget.closest ? event.relatedTarget.closest('.run-row') : null;
|
|
1233
|
-
if (!next) showNow(state.selectedId);
|
|
1234
|
-
});
|
|
1235
|
-
|
|
1236
1209
|
document.addEventListener('keydown', function (event) {
|
|
1237
|
-
|
|
1210
|
+
if (event.defaultPrevented || event.isComposing || event.metaKey || event.ctrlKey || event.altKey || document.querySelector('dialog[open]')) return;
|
|
1211
|
+
if (event.target instanceof HTMLSelectElement) return;
|
|
1212
|
+
var typing = event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement || event.target instanceof HTMLSelectElement || event.target.isContentEditable;
|
|
1238
1213
|
if (typing) {
|
|
1239
1214
|
if (event.key === 'Escape') {
|
|
1215
|
+
window.clearTimeout(findTimer);
|
|
1240
1216
|
event.target.value = '';
|
|
1241
1217
|
if (event.target.id === 'find') {
|
|
1242
1218
|
state.query = '';
|
|
@@ -1244,7 +1220,6 @@
|
|
|
1244
1220
|
paintList();
|
|
1245
1221
|
paintDetail();
|
|
1246
1222
|
markStrip();
|
|
1247
|
-
showNow(state.selectedId);
|
|
1248
1223
|
}
|
|
1249
1224
|
if (event.target.id === 'log-find') {
|
|
1250
1225
|
state.logQuery = '';
|
|
@@ -1254,7 +1229,8 @@
|
|
|
1254
1229
|
return;
|
|
1255
1230
|
}
|
|
1256
1231
|
if (event.key >= '1' && event.key <= '5') {
|
|
1257
|
-
|
|
1232
|
+
var destination = TABS[Number(event.key) - 1];
|
|
1233
|
+
if (destination) showTab(destination);
|
|
1258
1234
|
return;
|
|
1259
1235
|
}
|
|
1260
1236
|
if (event.key === '/') {
|
|
@@ -1264,19 +1240,19 @@
|
|
|
1264
1240
|
if (find) find.focus();
|
|
1265
1241
|
return;
|
|
1266
1242
|
}
|
|
1267
|
-
if ((event.key === 'ArrowRight' || event.key === 'ArrowLeft' || event.key === 'Home' || event.key === 'End') && event.target.closest && event.target.closest('.tabbar')) {
|
|
1243
|
+
if ((event.key === 'ArrowRight' || event.key === 'ArrowLeft' || event.key === 'ArrowDown' || event.key === 'ArrowUp' || event.key === 'Home' || event.key === 'End') && event.target.closest && event.target.closest('.tabbar')) {
|
|
1268
1244
|
event.preventDefault();
|
|
1269
1245
|
var index = TABS.indexOf(state.tab);
|
|
1270
1246
|
if (event.key === 'Home') index = 0;
|
|
1271
1247
|
else if (event.key === 'End') index = TABS.length - 1;
|
|
1272
|
-
else if (event.key === 'ArrowRight') index = (index + 1) % TABS.length;
|
|
1248
|
+
else if (event.key === 'ArrowRight' || event.key === 'ArrowDown') index = (index + 1) % TABS.length;
|
|
1273
1249
|
else index = (index - 1 + TABS.length) % TABS.length;
|
|
1274
1250
|
showTab(TABS[index]);
|
|
1275
1251
|
var next = document.querySelector('.tab[data-tab="' + TABS[index] + '"]');
|
|
1276
1252
|
if (next) next.focus();
|
|
1277
1253
|
return;
|
|
1278
1254
|
}
|
|
1279
|
-
if (state.tab !== 'tests' || (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')) return;
|
|
1255
|
+
if (state.tab !== 'tests' || !event.target.closest || !event.target.closest('#tests') || (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')) return;
|
|
1280
1256
|
var rows = Array.prototype.slice.call(document.querySelectorAll('#tests .row'));
|
|
1281
1257
|
if (!rows.length) return;
|
|
1282
1258
|
event.preventDefault();
|
|
@@ -1291,21 +1267,15 @@
|
|
|
1291
1267
|
|
|
1292
1268
|
function choose(id, resetFilter) {
|
|
1293
1269
|
if (!id || !testById(id)) return;
|
|
1294
|
-
if (resetFilter)
|
|
1295
|
-
state.filter = 'all';
|
|
1296
|
-
state.query = '';
|
|
1297
|
-
state.band = 'all';
|
|
1298
|
-
state.tag = 'all';
|
|
1299
|
-
state.projectFilter = 'all';
|
|
1300
|
-
var find = document.getElementById('find');
|
|
1301
|
-
if (find) find.value = '';
|
|
1302
|
-
}
|
|
1270
|
+
if (resetFilter) resetFilters();
|
|
1303
1271
|
if (state.selectedId !== id) {
|
|
1304
1272
|
var test = testById(id);
|
|
1305
1273
|
state.selectedId = id;
|
|
1306
1274
|
state.attempt = failedAttempt(test);
|
|
1307
1275
|
state.testPane = defaultPane(test);
|
|
1308
1276
|
}
|
|
1277
|
+
var position = filteredTests().findIndex(function (test) { return test.id === id; });
|
|
1278
|
+
state.listLimit = Math.max(state.listLimit, position + 1);
|
|
1309
1279
|
showTab('tests');
|
|
1310
1280
|
if (built.tests) {
|
|
1311
1281
|
syncPressed();
|
|
@@ -1316,37 +1286,46 @@
|
|
|
1316
1286
|
markStrip();
|
|
1317
1287
|
}
|
|
1318
1288
|
paintDuration();
|
|
1319
|
-
showNow(state.selectedId);
|
|
1320
1289
|
var row = document.querySelector('#tests .row[data-id="' + cssEscape(id) + '"]');
|
|
1290
|
+
if (row) row.focus({ preventScroll: true });
|
|
1321
1291
|
if (row && row.scrollIntoView) row.scrollIntoView({ block: 'nearest', behavior: reduce ? 'auto' : 'smooth' });
|
|
1322
1292
|
}
|
|
1323
1293
|
|
|
1294
|
+
function hasFilters() {
|
|
1295
|
+
return state.filter !== 'all' || state.query.trim() || state.band !== 'all' || state.tag !== null || state.projectFilter !== null;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
function resetFilters() {
|
|
1299
|
+
window.clearTimeout(findTimer);
|
|
1300
|
+
state.filter = 'all'; state.query = ''; state.band = 'all'; state.tag = null; state.projectFilter = null;
|
|
1301
|
+
state.listLimit = 200;
|
|
1302
|
+
var find = document.getElementById('find');
|
|
1303
|
+
if (find) find.value = '';
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1324
1306
|
function keepSelectionInFilter() {
|
|
1325
1307
|
var tests = filteredTests();
|
|
1308
|
+
markStrip();
|
|
1326
1309
|
if (tests.some(function (test) { return test.id === state.selectedId; })) return;
|
|
1327
1310
|
var next = tests[0];
|
|
1328
1311
|
if (!next) {
|
|
1329
1312
|
state.selectedId = null;
|
|
1313
|
+
writeHash();
|
|
1330
1314
|
return;
|
|
1331
1315
|
}
|
|
1332
1316
|
state.selectedId = next.id;
|
|
1333
1317
|
state.attempt = failedAttempt(next);
|
|
1334
1318
|
state.testPane = defaultPane(next);
|
|
1319
|
+
writeHash();
|
|
1335
1320
|
}
|
|
1336
1321
|
|
|
1337
|
-
function openTest(id, attempt) {
|
|
1322
|
+
function openTest(id, attempt, pane) {
|
|
1338
1323
|
var test = testById(id);
|
|
1339
1324
|
if (!test) return;
|
|
1340
|
-
|
|
1341
|
-
state.query = '';
|
|
1342
|
-
state.band = 'all';
|
|
1343
|
-
state.tag = 'all';
|
|
1344
|
-
state.projectFilter = 'all';
|
|
1345
|
-
var find = document.getElementById('find');
|
|
1346
|
-
if (find) find.value = '';
|
|
1325
|
+
resetFilters();
|
|
1347
1326
|
state.selectedId = id;
|
|
1348
1327
|
state.attempt = attempt;
|
|
1349
|
-
state.testPane = 'shots';
|
|
1328
|
+
state.testPane = pane === 'logs' ? 'logs' : 'shots';
|
|
1350
1329
|
showTab('tests');
|
|
1351
1330
|
if (built.tests) {
|
|
1352
1331
|
paintTags();
|
|
@@ -1355,43 +1334,30 @@
|
|
|
1355
1334
|
paintDetail();
|
|
1356
1335
|
markStrip();
|
|
1357
1336
|
}
|
|
1337
|
+
syncPressed();
|
|
1358
1338
|
paintDuration();
|
|
1339
|
+
document.getElementById('detail').scrollIntoView({ block: 'nearest' });
|
|
1359
1340
|
}
|
|
1360
1341
|
|
|
1361
1342
|
function openShot(src, alt) {
|
|
1362
1343
|
var safe = safeSrc(src);
|
|
1363
1344
|
if (!safe) return;
|
|
1364
1345
|
var close = el('button', { class: 'close-shot', type: 'button' }, ['Close']);
|
|
1365
|
-
var
|
|
1346
|
+
var returnFocus = document.activeElement;
|
|
1347
|
+
var dialog = el('dialog', { 'aria-label': alt || 'Screenshot' }, [close, el('img', { src: safe, alt: alt })]);
|
|
1366
1348
|
close.addEventListener('click', function () { dialog.close(); });
|
|
1367
1349
|
dialog.addEventListener('click', function (event) { if (event.target === dialog) dialog.close(); });
|
|
1368
|
-
dialog.addEventListener('close', function () { dialog.remove(); });
|
|
1350
|
+
dialog.addEventListener('close', function () { dialog.remove(); if (returnFocus && returnFocus.isConnected) returnFocus.focus(); });
|
|
1369
1351
|
document.body.appendChild(dialog);
|
|
1370
1352
|
dialog.showModal();
|
|
1371
1353
|
}
|
|
1372
1354
|
|
|
1373
|
-
function showNow(id) {
|
|
1374
|
-
var host = document.getElementById('now');
|
|
1375
|
-
if (!host) return;
|
|
1376
|
-
var test = id ? testById(id) : null;
|
|
1377
|
-
if (!test) {
|
|
1378
|
-
fill(host, [data.tests.length ? 'Hover a test, or click one.' : 'No tests ran.']);
|
|
1379
|
-
return;
|
|
1380
|
-
}
|
|
1381
|
-
fill(host, [
|
|
1382
|
-
el('span', { class: 'sw', 'data-status': test.status }, [statusLabel(test)]),
|
|
1383
|
-
el('span', { class: 'name' }, [test.title]),
|
|
1384
|
-
el('span', null, [formatDuration(test.duration)]),
|
|
1385
|
-
el('span', { class: 'now-file' }, [test.file]),
|
|
1386
|
-
]);
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
1355
|
function filteredTests() {
|
|
1390
1356
|
var query = state.query.trim().toLowerCase();
|
|
1391
1357
|
return data.tests.filter(function (test) {
|
|
1392
1358
|
if (state.filter !== 'all' && test.status !== state.filter) return false;
|
|
1393
|
-
if (state.tag !==
|
|
1394
|
-
if (state.projectFilter !==
|
|
1359
|
+
if (state.tag !== null && (test.tags || []).indexOf(state.tag) === -1) return false;
|
|
1360
|
+
if (state.projectFilter !== null && test.project !== state.projectFilter) return false;
|
|
1395
1361
|
if (state.band !== 'all' && (test.status === 'skipped' || durationBand(test.duration) !== state.band)) return false;
|
|
1396
1362
|
if (!query) return true;
|
|
1397
1363
|
var blob = [test.title, test.file, test.project].concat(test.group || [], test.tags || []).join(' ').toLowerCase();
|
|
@@ -1405,7 +1371,7 @@
|
|
|
1405
1371
|
}
|
|
1406
1372
|
|
|
1407
1373
|
function testById(id) {
|
|
1408
|
-
return
|
|
1374
|
+
return testIndex.get(id) || null;
|
|
1409
1375
|
}
|
|
1410
1376
|
|
|
1411
1377
|
function failedAttempt(test) {
|
|
@@ -1419,7 +1385,7 @@
|
|
|
1419
1385
|
|
|
1420
1386
|
function defaultPane(test) {
|
|
1421
1387
|
if (!test) return 'steps';
|
|
1422
|
-
if (test
|
|
1388
|
+
if (isProblem(test)) return 'error';
|
|
1423
1389
|
return 'steps';
|
|
1424
1390
|
}
|
|
1425
1391
|
|