Gate authenticated homepage rendering until the public Hop home swap completes, with failure fallback and regression tests
Hop-State: A_06FNBF34SYZE98C5FTCSNQ8 Hop-Proposal: R_06FNBF2AQPTF8B2YVMQ8NJG Hop-Task: T_06FNBEGE901NTR8R2RAPC70 Hop-Attempt: AT_06FNBEGE90FVP4AP4KQ3E40
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import {readFile} from 'node:fs/promises';
|
||||
import test from 'node:test';
|
||||
import vm from 'node:vm';
|
||||
|
||||
const source = await readFile(new URL('./hop-native.js', import.meta.url), 'utf8');
|
||||
|
||||
function page({search = '', signedIn = false, fetchResult} = {}) {
|
||||
const dataset = {};
|
||||
const publicHomeLink = {
|
||||
classList: {toggle() {}},
|
||||
href: 'https://hop.test/',
|
||||
};
|
||||
let currentMainReplaced = false;
|
||||
const currentMain = {
|
||||
replaceWith() {
|
||||
currentMainReplaced = true;
|
||||
},
|
||||
};
|
||||
const publicMain = {
|
||||
parentElement: {querySelectorAll: () => []},
|
||||
};
|
||||
const document = {
|
||||
documentElement: {dataset},
|
||||
title: 'Dashboard',
|
||||
addEventListener() {},
|
||||
querySelector(selector) {
|
||||
if (selector === 'meta[name="hop-signed-user-id"]') return signedIn ? {} : null;
|
||||
if (selector === '[data-hop-public-home-link]') return signedIn ? publicHomeLink : null;
|
||||
if (selector === '[role="main"]') return currentMain;
|
||||
return null;
|
||||
},
|
||||
querySelectorAll() {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
const context = {
|
||||
console: {error() {}},
|
||||
document,
|
||||
DOMParser: class {
|
||||
parseFromString() {
|
||||
return {
|
||||
title: 'Hop',
|
||||
querySelector: (selector) => selector === '#hop-main' ? publicMain : null,
|
||||
};
|
||||
}
|
||||
},
|
||||
fetch: fetchResult || (() => Promise.resolve({ok: true, text: () => Promise.resolve('<main id="hop-main"></main>')})),
|
||||
MutationObserver: class { observe() {} },
|
||||
URL,
|
||||
URLSearchParams,
|
||||
window: {
|
||||
location: {href: `https://hop.test/${search}`, origin: 'https://hop.test', pathname: '/', search},
|
||||
requestAnimationFrame() {},
|
||||
},
|
||||
};
|
||||
vm.runInNewContext(source, context);
|
||||
return {dataset, get currentMainReplaced() { return currentMainReplaced; }};
|
||||
}
|
||||
|
||||
async function settle() {
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
}
|
||||
|
||||
test('keeps the authenticated dashboard hidden until the public homepage is ready', async () => {
|
||||
let finishFetch;
|
||||
const fetchResult = () => new Promise((resolve) => { finishFetch = resolve; });
|
||||
const result = page({search: '?hop=home', signedIn: true, fetchResult});
|
||||
|
||||
assert.equal(result.dataset.hopPublicHome, 'loading');
|
||||
assert.equal(result.dataset.hopNative, undefined);
|
||||
assert.equal(result.currentMainReplaced, false);
|
||||
|
||||
finishFetch({ok: true, text: () => Promise.resolve('<main id="hop-main"></main>')});
|
||||
await settle();
|
||||
|
||||
assert.equal(result.dataset.hopPublicHome, 'rendered');
|
||||
assert.equal(result.dataset.hopNative, 'true');
|
||||
assert.equal(result.currentMainReplaced, true);
|
||||
});
|
||||
|
||||
test('reveals the existing page when the public homepage request fails', async () => {
|
||||
const result = page({
|
||||
search: '?hop=home',
|
||||
signedIn: true,
|
||||
fetchResult: () => Promise.reject(new Error('offline')),
|
||||
});
|
||||
|
||||
assert.equal(result.dataset.hopNative, undefined);
|
||||
await settle();
|
||||
|
||||
assert.equal(result.dataset.hopPublicHome, 'failed');
|
||||
assert.equal(result.dataset.hopNative, 'true');
|
||||
assert.equal(result.currentMainReplaced, false);
|
||||
});
|
||||
|
||||
test('reveals ordinary pages immediately', () => {
|
||||
const result = page({signedIn: true});
|
||||
assert.equal(result.dataset.hopNative, 'true');
|
||||
});
|
||||
Reference in New Issue
Block a user