mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-30 21:45:13 +02:00
fix(control-pane): keep a superseded refresh failure off the board
A live refresh that fails after a newer load has already replaced the board no longer describes what is shown, so it is only reported while the data it was meant to replace is still on screen. The time of that data now includes its date, for a pane left open overnight, and it is kept in its own binding instead of on the shared state object.
This commit is contained in:
@@ -438,9 +438,10 @@ function renderControlPaneHtml() {
|
||||
|
||||
// The board keeps the last snapshot on screen, so a failed refresh has to
|
||||
// say that the data is no longer live, and since when.
|
||||
let loadedAt = null;
|
||||
function showRefreshFailure(error) {
|
||||
const since = state.loadedAt
|
||||
? ' The data below is from ' + state.loadedAt.toLocaleTimeString() + '.'
|
||||
const since = loadedAt
|
||||
? ' The data below is from ' + loadedAt.toLocaleString() + '.'
|
||||
: '';
|
||||
showError('#app', 'Live refresh failed.' + since + '\\n' + formatError(error));
|
||||
}
|
||||
@@ -651,7 +652,7 @@ function renderControlPaneHtml() {
|
||||
...action,
|
||||
executable: snapshot.execution.allowActions && action.executable
|
||||
})));
|
||||
state.loadedAt = new Date();
|
||||
loadedAt = new Date();
|
||||
clearError('#app');
|
||||
}
|
||||
|
||||
@@ -692,7 +693,12 @@ function renderControlPaneHtml() {
|
||||
// Live board: refresh on a gentle interval; pause while a prompt/tab is hidden.
|
||||
setInterval(() => {
|
||||
if (document.hidden) return;
|
||||
load().catch(showRefreshFailure);
|
||||
const shownSince = loadedAt;
|
||||
load().catch(error => {
|
||||
// A load that finished in the meantime already replaced the data this
|
||||
// refresh was for, so its failure no longer describes the board.
|
||||
if (loadedAt === shownSince) showRefreshFailure(error);
|
||||
});
|
||||
}, 15000);
|
||||
|
||||
load().catch(error => showError('#app', error));
|
||||
|
||||
@@ -28,7 +28,8 @@ function inlineScript(html) {
|
||||
|
||||
// Runs the page script against a stand-in for the few browser APIs it uses:
|
||||
// elements looked up by selector, fetch, and a setInterval whose callback the
|
||||
// test fires itself.
|
||||
// test fires itself. While `hold` is set, a fetch waits in `pending` until the
|
||||
// test settles it.
|
||||
function openPage(snapshot) {
|
||||
const elements = new Map();
|
||||
const element = selector => {
|
||||
@@ -44,17 +45,23 @@ function openPage(snapshot) {
|
||||
}
|
||||
return elements.get(selector);
|
||||
};
|
||||
const page = { online: true, refresh: null, element };
|
||||
const page = { online: true, hold: false, pending: [], refresh: null, element };
|
||||
vm.runInNewContext(inlineScript(renderControlPaneHtml()), {
|
||||
document: { hidden: false, querySelector: element, querySelectorAll: () => [] },
|
||||
window: { location: { href: 'http://127.0.0.1:8765/' } },
|
||||
URL,
|
||||
Intl,
|
||||
console,
|
||||
fetch: async () => {
|
||||
if (!page.online) throw new TypeError('Failed to fetch');
|
||||
return { ok: true, status: 200, statusText: 'OK', json: async () => snapshot };
|
||||
},
|
||||
fetch: () =>
|
||||
new Promise((resolve, reject) => {
|
||||
const reply = {
|
||||
succeed: () => resolve({ ok: true, status: 200, statusText: 'OK', json: async () => snapshot }),
|
||||
fail: () => reject(new TypeError('Failed to fetch'))
|
||||
};
|
||||
if (page.hold) page.pending.push(reply);
|
||||
else if (page.online) reply.succeed();
|
||||
else reply.fail();
|
||||
}),
|
||||
setInterval: callback => {
|
||||
page.refresh = callback;
|
||||
}
|
||||
@@ -92,6 +99,7 @@ async function runTests() {
|
||||
const box = page.element('#app');
|
||||
assert.strictEqual(box.hidden, false, 'the failure is shown');
|
||||
assert.match(box.textContent, /Live refresh failed\. The data below is from /);
|
||||
assert.ok(box.textContent.includes(String(new Date().getFullYear())), 'the time of the data includes its date');
|
||||
assert.match(box.textContent, /Failed to fetch/);
|
||||
|
||||
page.online = true;
|
||||
@@ -103,6 +111,24 @@ async function runTests() {
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
if (
|
||||
await test('a refresh that fails after a newer load succeeded is not reported', async () => {
|
||||
const page = openPage(snapshot);
|
||||
await settle();
|
||||
|
||||
page.hold = true;
|
||||
page.refresh();
|
||||
page.hold = false;
|
||||
page.refresh();
|
||||
await settle();
|
||||
page.pending[0].fail();
|
||||
await settle();
|
||||
assert.strictEqual(page.element('#app').hidden, true, 'the newer data is not marked as stale');
|
||||
})
|
||||
)
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user