feat(ui): auth-gate the landing, add Help page and Home nav
- Landing shows the tool tiles only when signed in and a sign-in card
otherwise; the login callback now returns to /.
- New public /help page: intro plus a per-tool Carbon accordion.
- Nav restructured to Home, {services}, divider, Help, Sign In/Out in
both the header and the side nav; the header right keeps only the
identity chip. Side-nav logo enlarged, border removed, spacing
equalised. Sign-out modal no longer flashes before it is defined.
- Per-deploy asset versioning (?v=) with immutable asset caching and
no-store HTML; help step lists show their numbers again.
- Tests updated and extended (48 total).
This commit is contained in:
+84
-21
@@ -112,7 +112,7 @@ async function login(base) {
|
||||
assert.equal(loginRes.status, 302);
|
||||
const callbackRes = await fetch(`${base}/auth/callback?sessionId=s1`, manual);
|
||||
assert.equal(callbackRes.status, 302);
|
||||
assert.equal(callbackRes.headers.get('location'), '/fingerprint');
|
||||
assert.equal(callbackRes.headers.get('location'), '/');
|
||||
const session = cookieValue(callbackRes, 'session');
|
||||
const profile = cookieValue(callbackRes, 'user_profile');
|
||||
assert.ok(session, 'session cookie set');
|
||||
@@ -121,14 +121,19 @@ async function login(base) {
|
||||
return `session=${session}; user_profile=${profile}`;
|
||||
}
|
||||
|
||||
test('landing page links both tools', async () => {
|
||||
test('signed-out landing prompts sign-in; signed-in landing links both tools', async () => {
|
||||
const { base, stop } = await bootApp();
|
||||
try {
|
||||
const res = await fetch(`${base}/`);
|
||||
const html = await res.text();
|
||||
assert.equal(res.status, 200);
|
||||
assert.match(html, /href="\/cropper"/);
|
||||
assert.match(html, /href="\/fingerprint"/);
|
||||
const anon = await (await fetch(`${base}/`)).text();
|
||||
// no tools until authenticated — a call to action instead
|
||||
assert.match(anon, /href="\/auth\/login">Sign in with YOUreka</);
|
||||
assert.ok(!anon.includes('href="/cropper"'), 'no cropper tile when signed out');
|
||||
assert.ok(!anon.includes('href="/fingerprint"'), 'no fingerprint tile when signed out');
|
||||
|
||||
const cookies = await login(base);
|
||||
const authed = await (await fetch(`${base}/`, { headers: { cookie: cookies } })).text();
|
||||
assert.match(authed, /href="\/cropper"/);
|
||||
assert.match(authed, /href="\/fingerprint"/);
|
||||
} finally {
|
||||
await stop();
|
||||
}
|
||||
@@ -350,28 +355,86 @@ test('landing header is server-rendered for the session', async () => {
|
||||
const anon = await (await fetch(`${base}/`)).text();
|
||||
assert.match(anon, /href="\/auth\/login">Sign in</);
|
||||
assert.ok(!anon.includes('<!--app-header-->'), 'header marker is replaced');
|
||||
// tools live in the header nav and in the mobile side nav
|
||||
assert.match(anon, /cds-header-nav-item href="\/cropper"/);
|
||||
assert.match(anon, /cds-header-nav-item href="\/fingerprint"/);
|
||||
assert.match(anon, /cds-side-nav-link href="\/cropper"/);
|
||||
assert.match(anon, /cds-side-nav-divider/);
|
||||
// side nav items carry Carbon icons in the title-icon slot
|
||||
assert.match(anon, /cds-side-nav-link href="\/cropper"[^>]*><svg slot="title-icon"/);
|
||||
assert.match(anon, /cds-side-nav-link href="\/fingerprint"[^>]*><svg slot="title-icon"/);
|
||||
assert.match(anon, /cds-side-nav-link href="\/auth\/logout"[^>]*><svg slot="title-icon"/);
|
||||
assert.match(anon, /cds-side-nav-link href="\/auth\/logout"/);
|
||||
// signed out: no service links, but Help and Sign in are always there
|
||||
assert.ok(!anon.includes('cds-header-nav-item href="/cropper"'), 'no cropper in header nav when signed out');
|
||||
assert.ok(!anon.includes('cds-header-nav-item href="/fingerprint"'), 'no fingerprint in header nav when signed out');
|
||||
assert.match(anon, /cds-header-nav-item href="\/help"/);
|
||||
assert.match(anon, /cds-side-nav-link href="\/help"/);
|
||||
// the divider only groups the services, so it is absent when signed out
|
||||
assert.ok(!anon.includes('cds-side-nav-divider'), 'no side nav divider without services');
|
||||
assert.ok(!anon.includes('header-nav-divider'), 'no header divider without services');
|
||||
// no identity or sign-out when signed out
|
||||
assert.ok(!anon.includes('user-chip'), 'no user chip when signed out');
|
||||
assert.ok(!anon.includes('data-signout'), 'no sign-out when signed out');
|
||||
assert.ok(!anon.includes('signout-modal'), 'no sign-out modal when signed out');
|
||||
assert.match(anon, /youreka-logo\.png/);
|
||||
assert.match(anon, /header-menu-button/);
|
||||
// favicon is the Youreka logo; sign-out is confirmed via a Carbon modal
|
||||
// the side nav brand is just the logo — no repeated "YOUreka Tools" text
|
||||
assert.match(anon, /<div class="side-nav-brand">\s*<img[^>]*>\s*<\/div>/);
|
||||
// favicon is the Youreka logo
|
||||
assert.match(anon, /rel="icon"[^>]*youreka-favicon\.png/);
|
||||
assert.match(anon, /data-signout/);
|
||||
assert.match(anon, /cds-modal id="signout-modal"/);
|
||||
assert.match(anon, /id="signout-confirm"/);
|
||||
|
||||
const cookies = await login(base);
|
||||
const authed = await (await fetch(`${base}/`, { headers: { cookie: cookies } })).text();
|
||||
assert.match(authed, /Ada/);
|
||||
assert.match(authed, /user-chip/);
|
||||
assert.match(authed, /href="\/auth\/logout"[^>]*>Sign out</);
|
||||
// signed in: services live in the header nav and in the mobile side nav
|
||||
assert.match(authed, /cds-header-nav-item href="\/cropper"/);
|
||||
assert.match(authed, /cds-header-nav-item href="\/fingerprint"/);
|
||||
assert.match(authed, /cds-header-nav-item href="\/help"/);
|
||||
assert.match(authed, /cds-header-nav-item href="\/auth\/logout" data-signout/);
|
||||
assert.match(authed, /cds-side-nav-link href="\/cropper"/);
|
||||
assert.match(authed, /cds-side-nav-link href="\/help"/);
|
||||
assert.match(authed, /cds-side-nav-divider/);
|
||||
assert.match(authed, /header-nav-divider/);
|
||||
// side nav items carry Carbon icons in the title-icon slot
|
||||
assert.match(authed, /cds-side-nav-link href="\/cropper"[^>]*><svg slot="title-icon"/);
|
||||
assert.match(authed, /cds-side-nav-link href="\/fingerprint"[^>]*><svg slot="title-icon"/);
|
||||
assert.match(authed, /cds-side-nav-link href="\/help"[^>]*><svg slot="title-icon"/);
|
||||
assert.match(authed, /cds-side-nav-link href="\/auth\/logout"[^>]*><svg slot="title-icon"/);
|
||||
// sign-out is confirmed via a Carbon modal
|
||||
assert.match(authed, /data-signout/);
|
||||
assert.match(authed, /cds-modal id="signout-modal"/);
|
||||
assert.match(authed, /id="signout-confirm"/);
|
||||
} finally {
|
||||
await stop();
|
||||
}
|
||||
});
|
||||
|
||||
test('help page is public and documents each service', async () => {
|
||||
const { base, stop } = await bootApp();
|
||||
try {
|
||||
const res = await fetch(`${base}/help`);
|
||||
assert.equal(res.status, 200);
|
||||
const html = await res.text();
|
||||
assert.ok(!html.includes('<!--app-header-->'), 'header marker is replaced');
|
||||
assert.match(html, /cds-accordion-item title="Image Cropper"/);
|
||||
assert.match(html, /cds-accordion-item title="Fingerprint Agent"/);
|
||||
assert.match(html, /accordion\.min\.js/);
|
||||
assert.match(html, /cds-header-nav-item href="\/help" aria-current="page"/);
|
||||
assert.match(html, /cds-side-nav-link href="\/help" aria-current="page"/);
|
||||
} finally {
|
||||
await stop();
|
||||
}
|
||||
});
|
||||
|
||||
test('asset URLs are versioned per deployment; HTML is never cached', async () => {
|
||||
const { base, stop } = await bootApp();
|
||||
try {
|
||||
const res = await fetch(`${base}/`);
|
||||
const html = await res.text();
|
||||
assert.equal(res.headers.get('cache-control'), 'no-store');
|
||||
assert.match(html, /\/assets\/style\.css\?v=[a-z0-9]+/);
|
||||
assert.match(html, /\/assets\/youreka-favicon\.png\?v=[a-z0-9]+/);
|
||||
|
||||
// versioned assets may be cached hard...
|
||||
const css = await fetch(`${base}/assets/style.css?v=x`);
|
||||
assert.equal(css.status, 200);
|
||||
assert.match(css.headers.get('cache-control'), /immutable/);
|
||||
// ...but the raw .html templates must not be
|
||||
const tpl = await fetch(`${base}/assets/help.html`);
|
||||
assert.equal(tpl.headers.get('cache-control'), 'no-store');
|
||||
} finally {
|
||||
await stop();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user