From 7ec09f051574387b8b25f4190968cda5b8c61615 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexander=20H=C3=A4renstam=27s=20Machine=20Account?=
<322488294+alehar9320-bot@users.noreply.github.com>
Date: Sun, 30 Aug 2026 02:03:08 +0000
Subject: [PATCH 1/3] fix(whats-new): 375 Last-30 lines clear composer (#802)
Narrow-viewport spacing so Last-30 outcome lines clear the composer at 375. Not a restack of #799/#784. Filter rules unchanged.
---
src/__tests__/identity.test.ts | 26 ++++++++++++++++++++++++++
src/pages/whats-new.astro | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 60 insertions(+)
diff --git a/src/__tests__/identity.test.ts b/src/__tests__/identity.test.ts
index c1aed0d6..514ecc31 100644
--- a/src/__tests__/identity.test.ts
+++ b/src/__tests__/identity.test.ts
@@ -2642,4 +2642,30 @@ describe('identity copy', () => {
expect(home).toContain('https://www.linkedin.com/in/alehar/');
expect(home).not.toContain('mailto:');
});
+
+ it('tightens What’s New narrow-viewport spacing so Last-30 lines clear the composer (#802)', () => {
+ const page = readFileSync('src/pages/whats-new.astro', 'utf8');
+ const glance = readFileSync('src/utils/whats-new-glance.ts', 'utf8');
+ expect(page).toContain('@media (max-width: 30em)');
+ expect(page).toMatch(
+ /@media \(max-width: 30em\)[\s\S]*?\.glance\s*\{[\s\S]*?gap:\s*0\.5rem/
+ );
+ expect(page).toMatch(
+ /@media \(max-width: 30em\)[\s\S]*?\.this-week,\s*\.last-30[\s\S]*?padding:\s*0\.6rem 1rem/
+ );
+ expect(page).toMatch(
+ /@media \(max-width: 30em\)[\s\S]*?\.last-30 \.clusters[\s\S]*?gap:\s*0\.45rem/
+ );
+ expect(page).toContain('This week');
+ expect(page).toContain('Last 30 days');
+ expect(page).toContain('What you can see on this site lately.');
+ expect(page).toContain('Full history on GitHub');
+ expect(page).toContain('@media (min-width: 50em)');
+ expect(page).toContain('flex-direction: row');
+ expect(glance).toContain('DROP_PATTERN');
+ expect(glance).toMatch(/\\bengine\\b/);
+ expect(glance).toMatch(/\\bjules\\b/);
+ expect(glance).toContain('WEEK_MS');
+ expect(glance).toContain('MONTH_MS');
+ });
});
diff --git a/src/pages/whats-new.astro b/src/pages/whats-new.astro
index dd1aa23f..3905f69c 100644
--- a/src/pages/whats-new.astro
+++ b/src/pages/whats-new.astro
@@ -223,4 +223,38 @@ const schema = {
outline-offset: 4px;
border-radius: 0.25rem;
}
+
+ /* Narrow phone (375): compress glance rhythm so Last-30 outcome lines clear the docked composer. */
+ @media (max-width: 30em) {
+ .glance {
+ gap: 0.5rem;
+ }
+
+ .this-week,
+ .last-30 {
+ padding: 0.6rem 1rem;
+ }
+
+ .this-week h2,
+ .last-30 h2 {
+ margin-bottom: 0.35rem;
+ }
+
+ .last-30 .clusters {
+ gap: 0.45rem;
+ }
+
+ .group h3 {
+ margin-bottom: 0.25rem;
+ }
+
+ .this-week li,
+ .last-30 li {
+ margin-bottom: 0.15rem;
+ }
+
+ .history {
+ margin-top: 0.25rem;
+ }
+ }
From 769bef3be0d9145c141da75aa0bb14cda978bb2d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alexander=20H=C3=A4renstam=27s=20Machine=20Account?=
<322488294+alehar9320-bot@users.noreply.github.com>
Date: Sun, 30 Aug 2026 02:07:02 +0000
Subject: [PATCH 2/3] style: prettier identity.test.ts (#805)
---
src/__tests__/identity.test.ts | 3922 +++++++++++++++++---------------
1 file changed, 2121 insertions(+), 1801 deletions(-)
diff --git a/src/__tests__/identity.test.ts b/src/__tests__/identity.test.ts
index 514ecc31..7093f689 100644
--- a/src/__tests__/identity.test.ts
+++ b/src/__tests__/identity.test.ts
@@ -1,251 +1,275 @@
-import { existsSync, readFileSync } from 'node:fs';
-import { describe, expect, it } from 'vitest';
+import { existsSync, readFileSync } from "node:fs";
+import { describe, expect, it } from "vitest";
-import { fetchDirectNotFound, isDirectNotFoundPath } from '../direct-not-found';
-import { toVisitorChangelogTitle, toVisitorRelease } from '../utils/visitor-changelog';
+import { fetchDirectNotFound, isDirectNotFoundPath } from "../direct-not-found";
+import {
+ toVisitorChangelogTitle,
+ toVisitorRelease,
+} from "../utils/visitor-changelog";
const files = [
- 'src/pages/biography.astro',
- 'src/pages/work.astro',
- 'src/components/MainHead.astro',
- 'public/llms.txt',
- 'src/pages/api/chat.ts',
+ "src/pages/biography.astro",
+ "src/pages/work.astro",
+ "src/components/MainHead.astro",
+ "public/llms.txt",
+ "src/pages/api/chat.ts",
];
-describe('identity copy', () => {
- const sources = files.map((path) => ({ path, text: readFileSync(path, 'utf8') }));
+describe("identity copy", () => {
+ const sources = files.map((path) => ({
+ path,
+ text: readFileSync(path, "utf8"),
+ }));
- it('does not use Strategic Product Leader in about, biography, meta, llms.txt, or the twin prompt', () => {
+ it("does not use Strategic Product Leader in about, biography, meta, llms.txt, or the twin prompt", () => {
for (const { path, text } of sources) {
- expect(text.toLowerCase(), path).not.toContain('strategic product leader');
- expect(text.toLowerCase(), path).not.toContain('strategic product leadership');
+ expect(text.toLowerCase(), path).not.toContain(
+ "strategic product leader",
+ );
+ expect(text.toLowerCase(), path).not.toContain(
+ "strategic product leadership",
+ );
}
});
- it('uses Product Manager, Developer Experience as the title', () => {
+ it("uses Product Manager, Developer Experience as the title", () => {
for (const { path, text } of sources) {
- expect(text, path).toContain('Product Manager, Developer Experience');
+ expect(text, path).toContain("Product Manager, Developer Experience");
}
});
- it('does not mint unverifiable metrics on Biography, Work, or the twin', () => {
- const work = sources.find((s) => s.path.endsWith('work.astro'))!.text;
- const twin = sources.find((s) => s.path.endsWith('chat.ts'))!.text;
- const bio = sources.find((s) => s.path.endsWith('biography.astro'))!.text;
+ it("does not mint unverifiable metrics on Biography, Work, or the twin", () => {
+ const work = sources.find((s) => s.path.endsWith("work.astro"))!.text;
+ const twin = sources.find((s) => s.path.endsWith("chat.ts"))!.text;
+ const bio = sources.find((s) => s.path.endsWith("biography.astro"))!.text;
for (const { path, text } of sources) {
- expect(text, path).not.toContain('multi-million');
- expect(text, path).not.toContain('several millions');
- expect(text, path).not.toContain('Strategic Product Management');
+ expect(text, path).not.toContain("multi-million");
+ expect(text, path).not.toContain("several millions");
+ expect(text, path).not.toContain("Strategic Product Management");
}
- expect(bio).not.toContain('autonomous industrial AI');
- expect(bio).not.toContain('mailto:');
- expect(bio).toContain('/work/ifs-design-system/');
- expect(bio).toContain('https://www.linkedin.com/in/alehar/');
+ expect(bio).not.toContain("autonomous industrial AI");
+ expect(bio).not.toContain("mailto:");
+ expect(bio).toContain("/work/ifs-design-system/");
+ expect(bio).toContain("https://www.linkedin.com/in/alehar/");
expect(bio).toContain('class="timeline"');
- expect(work).toContain('Product Manager, Developer Experience');
- expect(bio).toContain('Product Manager, Developer Experience at IFS.');
- expect(bio).not.toContain('Explore the professional journey');
- expect(bio).not.toContain('No new numbered');
- expect(bio).not.toContain('only numbered proof');
+ expect(work).toContain("Product Manager, Developer Experience");
+ expect(bio).toContain("Product Manager, Developer Experience at IFS.");
+ expect(bio).not.toContain("Explore the professional journey");
+ expect(bio).not.toContain("No new numbered");
+ expect(bio).not.toContain("only numbered proof");
expect(bio).toContain("Get in touch on{' '}");
- expect(bio.replace(/\s+/g, ' ')).toContain('up to 2x faster delivery');
- expect(bio).toContain('up to 30x ROI');
- expect(twin).toContain('up to 2x faster delivery');
- expect(twin).toContain('up to 30x ROI');
+ expect(bio.replace(/\s+/g, " ")).toContain("up to 2x faster delivery");
+ expect(bio).toContain("up to 30x ROI");
+ expect(twin).toContain("up to 2x faster delivery");
+ expect(twin).toContain("up to 30x ROI");
});
- it('lets the Biography timeline name Zeroheight Design System Awards runner-up', () => {
- const bio = readFileSync('src/pages/biography.astro', 'utf8');
- expect(bio).toContain('Zeroheight Design System Awards runner-up.');
- expect(bio).not.toContain(', Zeroheight runner-up.');
+ it("lets the Biography timeline name Zeroheight Design System Awards runner-up", () => {
+ const bio = readFileSync("src/pages/biography.astro", "utf8");
+ expect(bio).toContain("Zeroheight Design System Awards runner-up.");
+ expect(bio).not.toContain(", Zeroheight runner-up.");
});
- it('keeps spaces around TL;DR strong terms (Astro drops newline-only spaces)', () => {
- const work = sources.find((s) => s.path.endsWith('work.astro'))!.text;
+ it("keeps spaces around TL;DR strong terms (Astro drops newline-only spaces)", () => {
+ const work = sources.find((s) => s.path.endsWith("work.astro"))!.text;
expect(work).toContain("{' '}IFS");
});
- it('keeps the chat FAB off Get in touch on a phone', () => {
- const home = readFileSync('src/pages/index.astro', 'utf8');
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
- const cta = readFileSync('src/components/ContactCTA.astro', 'utf8');
- const global = readFileSync('src/styles/global.css', 'utf8');
- expect(global).toContain('--chat-fab-clearance');
- expect(chat).toContain('bottom: var(--chat-fab-offset)');
- expect(chat).toContain('width: var(--chat-fab-size)');
- expect(home).toContain('padding-block: 1rem var(--chat-fab-clearance)');
- expect(home).toContain('justify-content: flex-start');
- expect(home).toContain('https://www.linkedin.com/in/alehar/');
- expect(home).toContain('Get in touch');
+ it("keeps the chat FAB off Get in touch on a phone", () => {
+ const home = readFileSync("src/pages/index.astro", "utf8");
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
+ const cta = readFileSync("src/components/ContactCTA.astro", "utf8");
+ const global = readFileSync("src/styles/global.css", "utf8");
+ expect(global).toContain("--chat-fab-clearance");
+ expect(chat).toContain("bottom: var(--chat-fab-offset)");
+ expect(chat).toContain("width: var(--chat-fab-size)");
+ expect(home).toContain("padding-block: 1rem var(--chat-fab-clearance)");
+ expect(home).toContain("justify-content: flex-start");
+ expect(home).toContain("https://www.linkedin.com/in/alehar/");
+ expect(home).toContain("Get in touch");
expect(home).toContain('class="cta-hint">LinkedIn
');
- expect(home).not.toContain('LinkedIn · replies from me');
+ expect(home).not.toContain("LinkedIn · replies from me");
expect(home).toContain('class="hero-copy"');
- expect(home).toContain('proof-card');
- expect(home).toContain('Took the design system from first version to IFS Cloud.');
- expect(home).not.toContain('inception');
- expect(home).not.toContain('mailto:');
+ expect(home).toContain("proof-card");
+ expect(home).toContain(
+ "Took the design system from first version to IFS Cloud.",
+ );
+ expect(home).not.toContain("inception");
+ expect(home).not.toContain("mailto:");
expect((home.match(/ {
- const home = readFileSync('src/pages/index.astro', 'utf8');
- expect(home).toContain('Hero title="Product Manager, Developer Experience at IFS"');
+ it("keeps the current DevEx role true and names adjacent hireable pictures in visitor language", () => {
+ const home = readFileSync("src/pages/index.astro", "utf8");
+ expect(home).toContain(
+ 'Hero title="Product Manager, Developer Experience at IFS"',
+ );
expect(home).toContain('class="hero-dek"');
- expect(home).toContain('developer platforms');
- expect(home).toContain('Design systems');
- expect(home).toContain('Industrial AI copilots');
- expect(home).not.toContain('not only DevEx');
- expect(home).toContain('Chalmers software engineering');
- expect(home).not.toContain('MEI');
- expect(home).toContain('Eight years at IFS');
- expect(home).toContain('IFS Design System');
- expect(home).toContain('Design system, copilots, and analytics.');
- expect(home).toContain('https://www.linkedin.com/in/alehar/');
- expect(home).toContain('Get in touch');
- expect(home).not.toContain('mailto:');
+ expect(home).toContain("developer platforms");
+ expect(home).toContain("Design systems");
+ expect(home).toContain("Industrial AI copilots");
+ expect(home).not.toContain("not only DevEx");
+ expect(home).toContain("Chalmers software engineering");
+ expect(home).not.toContain("MEI");
+ expect(home).toContain("Eight years at IFS");
+ expect(home).toContain("IFS Design System");
+ expect(home).toContain("Design system, copilots, and analytics.");
+ expect(home).toContain("https://www.linkedin.com/in/alehar/");
+ expect(home).toContain("Get in touch");
+ expect(home).not.toContain("mailto:");
expect(home).not.toMatch(/\bVP\b/);
- expect(home).not.toContain('Head of');
- expect(home).not.toContain('Director');
+ expect(home).not.toContain("Head of");
+ expect(home).not.toContain("Director");
expect((home.match(/class="proof-card"/g) || []).length).toBe(1);
expect((home.match(/class="hero-dek"/g) || []).length).toBe(1);
});
- it('keeps the chat FAB off Earlier work and the biography timeline on a phone', () => {
- const work = readFileSync('src/pages/work.astro', 'utf8');
- const bio = readFileSync('src/pages/biography.astro', 'utf8');
- expect(work).toContain('padding-bottom: var(--chat-fab-clearance)');
- expect(work).toContain('padding-right: var(--chat-fab-clearance)');
- expect(work).toContain('Earlier work');
- expect(bio).toContain('padding-bottom: var(--chat-fab-clearance)');
- expect(bio).toContain('padding-right: var(--chat-fab-clearance)');
+ it("keeps the chat FAB off Earlier work and the biography timeline on a phone", () => {
+ const work = readFileSync("src/pages/work.astro", "utf8");
+ const bio = readFileSync("src/pages/biography.astro", "utf8");
+ expect(work).toContain("padding-bottom: var(--chat-fab-clearance)");
+ expect(work).toContain("padding-right: var(--chat-fab-clearance)");
+ expect(work).toContain("Earlier work");
+ expect(bio).toContain("padding-bottom: var(--chat-fab-clearance)");
+ expect(bio).toContain("padding-right: var(--chat-fab-clearance)");
expect(bio).toContain('class="timeline"');
- expect(bio).toContain('https://www.linkedin.com/in/alehar/');
- });
-
- it('keeps the chat FAB off the LinkedIn hire CTA on /contact on a phone', () => {
- const contact = readFileSync('src/pages/contact.astro', 'utf8');
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- expect(contact).toContain('padding-bottom: var(--chat-fab-clearance)');
- expect(contact).toContain('padding-right: var(--chat-fab-clearance)');
- expect(contact).toContain('https://www.linkedin.com/in/alehar/');
- expect(contact).toContain('Get in touch');
- expect(contact).not.toContain('mailto:');
- expect(contact).toContain('Product Manager, Developer Experience');
- expect(contact).not.toContain('Open to conversations');
- expect(contact).toContain('LinkedIn · replies from me');
- expect(footer).toContain('padding: 3rem 2rem var(--chat-fab-clearance)');
- });
-
- it('keeps the docked chat FAB off the footer GitHub link at 1280', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- expect(footer).toContain('padding: 2.5rem 5rem var(--chat-fab-clearance)');
- expect(footer).toContain('padding-right: var(--chat-fab-clearance)');
- expect(footer).toContain('https://github.com/alehar9320');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(footer).not.toContain('mailto:');
- });
-
- it('drops Latest Updates from the footer and keeps What’s New', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- expect(footer).not.toContain('Latest Updates');
+ expect(bio).toContain("https://www.linkedin.com/in/alehar/");
+ });
+
+ it("keeps the chat FAB off the LinkedIn hire CTA on /contact on a phone", () => {
+ const contact = readFileSync("src/pages/contact.astro", "utf8");
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ expect(contact).toContain("padding-bottom: var(--chat-fab-clearance)");
+ expect(contact).toContain("padding-right: var(--chat-fab-clearance)");
+ expect(contact).toContain("https://www.linkedin.com/in/alehar/");
+ expect(contact).toContain("Get in touch");
+ expect(contact).not.toContain("mailto:");
+ expect(contact).toContain("Product Manager, Developer Experience");
+ expect(contact).not.toContain("Open to conversations");
+ expect(contact).toContain("LinkedIn · replies from me");
+ expect(footer).toContain("padding: 3rem 2rem var(--chat-fab-clearance)");
+ });
+
+ it("keeps the docked chat FAB off the footer GitHub link at 1280", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ expect(footer).toContain("padding: 2.5rem 5rem var(--chat-fab-clearance)");
+ expect(footer).toContain("padding-right: var(--chat-fab-clearance)");
+ expect(footer).toContain("https://github.com/alehar9320");
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(footer).not.toContain("mailto:");
+ });
+
+ it("drops Latest Updates from the footer and keeps What’s New", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ expect(footer).not.toContain("Latest Updates");
expect(footer).toContain('href="/whats-new/"');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(footer).not.toContain('mailto:');
- expect(existsSync('src/pages/whats-new.astro')).toBe(true);
- const page = readFileSync('src/pages/whats-new.astro', 'utf8');
- expect(page).not.toContain('A public changelog of this site.');
- expect(page).toContain('What you can see on this site lately.');
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(footer).not.toContain("mailto:");
+ expect(existsSync("src/pages/whats-new.astro")).toBe(true);
+ const page = readFileSync("src/pages/whats-new.astro", "utf8");
+ expect(page).not.toContain("A public changelog of this site.");
+ expect(page).toContain("What you can see on this site lately.");
});
it("flattens the home proof affordance and keeps What's next reachable", () => {
- expect(existsSync('src/pages/roadmap.astro')).toBe(true);
- expect(existsSync('src/data/upcoming.ts')).toBe(true);
- const page = readFileSync('src/pages/roadmap.astro', 'utf8');
- const upcoming = readFileSync('src/data/upcoming.ts', 'utf8');
- const home = readFileSync('src/pages/index.astro', 'utf8');
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- const sitemap = readFileSync('src/pages/sitemap.xml.ts', 'utf8');
- const whatsNew = readFileSync('src/pages/whats-new.astro', 'utf8');
+ expect(existsSync("src/pages/roadmap.astro")).toBe(true);
+ expect(existsSync("src/data/upcoming.ts")).toBe(true);
+ const page = readFileSync("src/pages/roadmap.astro", "utf8");
+ const upcoming = readFileSync("src/data/upcoming.ts", "utf8");
+ const home = readFileSync("src/pages/index.astro", "utf8");
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ const sitemap = readFileSync("src/pages/sitemap.xml.ts", "utf8");
+ const whatsNew = readFileSync("src/pages/whats-new.astro", "utf8");
expect(home).toContain('class="proof-card"');
expect(home).toContain('href="/work/ifs-design-system/"');
- expect(home).toContain('Read the case');
+ expect(home).toContain("Read the case");
expect(home).toContain('class="hero-dek"');
- expect(home).toContain('Hero title="Product Manager, Developer Experience at IFS"');
- expect(home).not.toContain('background: var(--gradient-subtle)');
- expect(home).not.toContain('border-radius: 1.5rem');
- expect(home).not.toContain('box-shadow: var(--shadow-sm)');
- expect(home).not.toContain('padding: 1.5rem');
+ expect(home).toContain(
+ 'Hero title="Product Manager, Developer Experience at IFS"',
+ );
+ expect(home).not.toContain("background: var(--gradient-subtle)");
+ expect(home).not.toContain("border-radius: 1.5rem");
+ expect(home).not.toContain("box-shadow: var(--shadow-sm)");
+ expect(home).not.toContain("padding: 1.5rem");
expect(home).not.toContain('href="/roadmap/"');
expect(home).not.toContain('href="/okr/"');
- const nav = readFileSync('src/components/Nav.astro', 'utf8');
+ const nav = readFileSync("src/components/Nav.astro", "utf8");
expect(nav).not.toContain('href="/roadmap/"');
expect(nav).not.toContain('href="/okr/"');
- expect(page).not.toContain('mailto:');
- expect(page).toContain('ContactCTA');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(footer).not.toContain('mailto:');
- expect(upcoming).toContain('export const UPCOMING: readonly string[] = []');
- expect(page).toContain("throw new Error('Roadmap still promises flatten.')");
+ expect(page).not.toContain("mailto:");
+ expect(page).toContain("ContactCTA");
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(footer).not.toContain("mailto:");
+ expect(upcoming).toContain("export const UPCOMING: readonly string[] = []");
+ expect(page).toContain(
+ "throw new Error('Roadmap still promises flatten.')",
+ );
expect(page).not.toContain("Astro.redirect('/')");
expect(page).toContain('Hero title="What\'s next"');
expect(page).toContain(
- "What's next on this site. Product Manager, Developer Experience at IFS — DevEx, design systems, and Industrial AI from work already published here."
+ "What's next on this site. Product Manager, Developer Experience at IFS — DevEx, design systems, and Industrial AI from work already published here.",
);
- expect(footer).not.toContain('showUpcoming');
+ expect(footer).not.toContain("showUpcoming");
expect(footer).toContain('href="/whats-new/"');
expect(footer).toContain('href="/this-site/"');
- expect(footer).toContain('This site');
+ expect(footer).toContain("This site");
expect(footer).toContain('href="/roadmap/"');
expect(footer).toContain("What's next");
expect(footer).toContain('href="/okr/"');
- expect(footer).toContain('Site success');
- expect(footer).not.toContain("What's New{' · '}Upcoming");
+ expect(footer).toContain("Site success");
+ expect(footer).not.toContain(
+ "What's New{' · '}Upcoming",
+ );
expect(sitemap).toContain("'/this-site/'");
expect(sitemap).toContain("'/roadmap/'");
expect(sitemap).toContain("'/okr/'");
- expect(sitemap).not.toContain('UPCOMING.length > 0');
- expect(whatsNew).toContain('What you can see on this site lately.');
+ expect(sitemap).not.toContain("UPCOMING.length > 0");
+ expect(whatsNew).toContain("What you can see on this site lately.");
});
- it('ships /okr/ site-success scoreboard from Nick H2 numbers', () => {
- expect(existsSync('src/pages/okr.astro')).toBe(true);
- expect(existsSync('src/data/okr-h2-2026.ts')).toBe(true);
- const page = readFileSync('src/pages/okr.astro', 'utf8');
- const data = readFileSync('src/data/okr-h2-2026.ts', 'utf8');
- const home = readFileSync('src/pages/index.astro', 'utf8');
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
+ it("ships /okr/ site-success scoreboard from Nick H2 numbers", () => {
+ expect(existsSync("src/pages/okr.astro")).toBe(true);
+ expect(existsSync("src/data/okr-h2-2026.ts")).toBe(true);
+ const page = readFileSync("src/pages/okr.astro", "utf8");
+ const data = readFileSync("src/data/okr-h2-2026.ts", "utf8");
+ const home = readFileSync("src/pages/index.astro", "utf8");
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
expect(page).toContain('Hero title="Site success"');
- expect(page).toContain('Site success | Product Manager, Developer Experience at IFS');
expect(page).toContain(
- 'How this site is measured through the end of 2026. Progress against real baselines.'
+ "Site success | Product Manager, Developer Experience at IFS",
);
expect(page).toContain(
- 'How this site is measured through the end of 2026. Progress against baselines — not a career scoreboard.'
- );
- expect(page).toContain('ContactCTA');
- expect(page).toContain('--chat-fab-clearance');
- expect(data).toContain('297');
- expect(data).toContain('400');
- expect(data).toContain('82.5');
- expect(data).toContain('70');
- expect(data).toContain('0');
- expect(data).toContain('5');
- expect(data).toContain('25');
- expect(data).toContain('LinkedIn');
- expect(page).toContain('Baseline');
- expect(page).toContain('Current');
- expect(page).toContain('Target');
- expect(page).toContain('H2 2026 · baselines frozen 29 Aug 2026 · current refreshed weekly');
- expect(page).not.toContain('mailto:');
- expect(page).not.toContain('Open-to');
- expect(page).not.toContain('career OKR');
+ "How this site is measured through the end of 2026. Progress against real baselines.",
+ );
+ expect(page).toContain(
+ "How this site is measured through the end of 2026. Progress against baselines — not a career scoreboard.",
+ );
+ expect(page).toContain("ContactCTA");
+ expect(page).toContain("--chat-fab-clearance");
+ expect(data).toContain("297");
+ expect(data).toContain("400");
+ expect(data).toContain("82.5");
+ expect(data).toContain("70");
+ expect(data).toContain("0");
+ expect(data).toContain("5");
+ expect(data).toContain("25");
+ expect(data).toContain("LinkedIn");
+ expect(page).toContain("Baseline");
+ expect(page).toContain("Current");
+ expect(page).toContain("Target");
+ expect(page).toContain(
+ "H2 2026 · baselines frozen 29 Aug 2026 · current refreshed weekly",
+ );
+ expect(page).not.toContain("mailto:");
+ expect(page).not.toContain("Open-to");
+ expect(page).not.toContain("career OKR");
expect(data).toContain("baselineLabel: '297'");
expect(data).toContain("currentLabel: '297'");
expect(data).toContain("targetLabel: '400'");
@@ -254,1150 +278,1314 @@ describe('identity copy', () => {
expect(data).toContain("baselineLabel: '0'");
expect(data).toContain("targetLabel: '5'");
expect(data).toContain("baselineLabel: '25'");
- expect(data).toContain('median ≥ 8 / week through Dec');
- expect(data).toContain('Hire tracking in PostHog — live by 30 Sep 2026');
- expect(data).toContain('Hire path stays LinkedIn only');
+ expect(data).toContain("median ≥ 8 / week through Dec");
+ expect(data).toContain("Hire tracking in PostHog — live by 30 Sep 2026");
+ expect(data).toContain("Hire path stays LinkedIn only");
expect(footer).toContain('href="/okr/"');
- expect(footer).toContain('Site success');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
+ expect(footer).toContain("Site success");
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
expect(home).not.toContain('href="/roadmap/"');
expect(home).not.toContain('href="/okr/"');
});
- it('ships /this-site/ as the twin explainer and keeps Upcoming omitted from that page', () => {
- expect(existsSync('src/pages/this-site.astro')).toBe(true);
- const page = readFileSync('src/pages/this-site.astro', 'utf8').replace(/\s+/g, ' ');
- const home = readFileSync('src/pages/index.astro', 'utf8');
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- const whatsNew = readFileSync('src/pages/whats-new.astro', 'utf8');
+ it("ships /this-site/ as the twin explainer and keeps Upcoming omitted from that page", () => {
+ expect(existsSync("src/pages/this-site.astro")).toBe(true);
+ const page = readFileSync("src/pages/this-site.astro", "utf8").replace(
+ /\s+/g,
+ " ",
+ );
+ const home = readFileSync("src/pages/index.astro", "utf8");
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ const whatsNew = readFileSync("src/pages/whats-new.astro", "utf8");
expect(page).toContain('Hero title="This site"');
- expect(page).toContain('Product Manager, Developer Experience at IFS');
- expect(page).toContain('digital twin');
- expect(page).toContain('AI with his context');
- expect(page).toContain('It can get things wrong.');
- expect(page).toContain('It is not him.');
- expect(page).toContain('ContactCTA');
- expect(page).not.toContain('mailto:');
- expect(page).not.toContain('/roadmap/');
- expect(page).not.toContain('Upcoming');
+ expect(page).toContain("Product Manager, Developer Experience at IFS");
+ expect(page).toContain("digital twin");
+ expect(page).toContain("AI with his context");
+ expect(page).toContain("It can get things wrong.");
+ expect(page).toContain("It is not him.");
+ expect(page).toContain("ContactCTA");
+ expect(page).not.toContain("mailto:");
+ expect(page).not.toContain("/roadmap/");
+ expect(page).not.toContain("Upcoming");
expect(page).not.toMatch(/\\bVP\\b/);
expect(footer).toContain('href="/this-site/"');
expect(footer).toContain('href="/whats-new/"');
- expect(footer).not.toContain('showUpcoming');
+ expect(footer).not.toContain("showUpcoming");
expect(home).toContain('class="hero-dek"');
- expect(home).toContain('Hero title="Product Manager, Developer Experience at IFS"');
- expect(whatsNew).toContain('What you can see on this site lately.');
+ expect(home).toContain(
+ 'Hero title="Product Manager, Developer Experience at IFS"',
+ );
+ expect(whatsNew).toContain("What you can see on this site lately.");
expect(whatsNew).not.toContain('href="/this-site/"');
});
- it('puts a middot inside the hidden footer visit-stats span', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- const cta = readFileSync('src/components/ContactCTA.astro', 'utf8');
+ it("puts a middot inside the hidden footer visit-stats span", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ const cta = readFileSync("src/components/ContactCTA.astro", "utf8");
expect(footer).toContain('class="colophon"');
expect(footer).toContain("What's New");
- expect(footer).toContain('data-visit-stats');
- expect(footer).toContain('hidden');
- expect(footer).not.toContain('showUpcoming');
+ expect(footer).toContain("data-visit-stats");
+ expect(footer).toContain("hidden");
+ expect(footer).not.toContain("showUpcoming");
expect(footer).toContain("What's New");
// Space-middot-space as Astro text node so paint is What's New · N.
expect(footer).toContain("{' · '}");
- expect(footer.slice(footer.indexOf('data-visit-stats'))).toContain("{' · '}");
+ expect(footer.slice(footer.indexOf("data-visit-stats"))).toContain(
+ "{' · '}",
+ );
// Fail the prior bug: newline/indent then middot (leading space collapsed in paint).
expect(footer).not.toMatch(/>\s*\n\s+·\s/);
- expect(cta).toContain('LinkedIn · replies from me');
+ expect(cta).toContain("LinkedIn · replies from me");
});
- it('keeps Footer .visit-stats display:inline only when not [hidden]', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- expect(footer).toContain('.visit-stats:not([hidden])');
- expect(footer).toContain('display: inline');
+ it("keeps Footer .visit-stats display:inline only when not [hidden]", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ expect(footer).toContain(".visit-stats:not([hidden])");
+ expect(footer).toContain("display: inline");
// Bare .visit-stats { display: inline } overrides UA [hidden]{display:none}.
expect(footer).not.toMatch(/\.visit-stats\s*\{\s*display:\s*inline/);
expect(footer).toContain("{' · '}");
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
});
- it('leads the colophon visit trigger with unique visitors and names PostHog', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- expect(footer).toContain('formatColophonVisits');
- expect(footer).toContain('formatColophonVisitsTitle');
+ it("leads the colophon visit trigger with unique visitors and names PostHog", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ expect(footer).toContain("formatColophonVisits");
+ expect(footer).toContain("formatColophonVisitsTitle");
expect(footer).toContain("trigger.addEventListener('focus'");
- expect(footer).toContain('shouldShowVisitCount(row.uniqueVisitors)');
- expect(footer).not.toContain('formatPageviewCount(data.pageviews)');
- expect(footer).not.toContain('white-space: nowrap');
- expect(footer).toContain('.visit-stats:not([hidden])');
+ expect(footer).toContain("shouldShowVisitCount(row.uniqueVisitors)");
+ expect(footer).not.toContain("formatPageviewCount(data.pageviews)");
+ expect(footer).not.toContain("white-space: nowrap");
+ expect(footer).toContain(".visit-stats:not([hidden])");
expect(footer).toContain("{' · '}");
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(footer).not.toContain('mailto:');
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(footer).not.toContain("mailto:");
expect(footer).toContain('class="colophon"');
expect(footer.match(/class="group"/g)?.length).toBe(1);
});
- it('drops Report an issue from the footer and keeps LinkedIn hire', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- expect(footer).not.toContain('Report an issue');
+ it("drops Report an issue from the footer and keeps LinkedIn hire", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ expect(footer).not.toContain("Report an issue");
expect(footer).not.toMatch(/github\.com\/[^\s"']+\/issues/);
- expect(footer).not.toContain('Latest Updates');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(footer).not.toContain('mailto:');
- expect(footer).not.toContain('https://github.com/alehar9320/astro-cloudflare-personal-website');
- expect(footer).toContain('https://github.com/alehar9320');
- });
-
- it('drops agentic engineering from the footer and keeps LinkedIn hire', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- expect(footer).not.toContain('agentic engineering');
- expect(footer).not.toContain('Built and run with');
- expect(footer).not.toContain('Latest Updates');
- expect(footer).not.toContain('Report an issue');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(footer).not.toContain('mailto:');
- });
-
- it('drops Source from the footer and keeps LinkedIn hire', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- expect(footer).not.toContain('>Source');
- expect(footer).not.toContain('source-link');
- expect(footer).not.toContain('View source code for this site');
- expect(footer).not.toContain('https://github.com/alehar9320/astro-cloudflare-personal-website');
- expect(footer).toContain('https://github.com/alehar9320');
- expect(footer).not.toContain('Latest Updates');
- expect(footer).not.toContain('Report an issue');
- expect(footer).not.toContain('agentic engineering');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(footer).not.toContain('mailto:');
- });
-
- it('drops Instagram and Facebook from the footer and keeps LinkedIn hire', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- expect(footer).not.toContain('instagram.com');
- expect(footer).not.toContain('facebook.com');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(footer).toContain('https://github.com/alehar9320');
- expect(footer).not.toContain('mailto:');
- expect(footer).not.toContain('Latest Updates');
- expect(footer).not.toContain('Report an issue');
- expect(footer).not.toContain('agentic engineering');
- expect(footer).not.toContain('>Source');
- expect(footer).not.toContain('source-link');
- });
-
- it('drops Designed & Developed from the footer and keeps LinkedIn hire', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- const nav = readFileSync('src/components/Nav.astro', 'utf8');
- expect(footer).not.toContain('Designed & Developed');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(footer).toContain('https://github.com/alehar9320');
- expect(footer).not.toContain('mailto:');
- expect(footer).toContain('Stockholm');
- expect(footer).toContain('Sweden');
+ expect(footer).not.toContain("Latest Updates");
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(footer).not.toContain("mailto:");
+ expect(footer).not.toContain(
+ "https://github.com/alehar9320/astro-cloudflare-personal-website",
+ );
+ expect(footer).toContain("https://github.com/alehar9320");
+ });
+
+ it("drops agentic engineering from the footer and keeps LinkedIn hire", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ expect(footer).not.toContain("agentic engineering");
+ expect(footer).not.toContain("Built and run with");
+ expect(footer).not.toContain("Latest Updates");
+ expect(footer).not.toContain("Report an issue");
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(footer).not.toContain("mailto:");
+ });
+
+ it("drops Source from the footer and keeps LinkedIn hire", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ expect(footer).not.toContain(">Source");
+ expect(footer).not.toContain("source-link");
+ expect(footer).not.toContain("View source code for this site");
+ expect(footer).not.toContain(
+ "https://github.com/alehar9320/astro-cloudflare-personal-website",
+ );
+ expect(footer).toContain("https://github.com/alehar9320");
+ expect(footer).not.toContain("Latest Updates");
+ expect(footer).not.toContain("Report an issue");
+ expect(footer).not.toContain("agentic engineering");
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(footer).not.toContain("mailto:");
+ });
+
+ it("drops Instagram and Facebook from the footer and keeps LinkedIn hire", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ expect(footer).not.toContain("instagram.com");
+ expect(footer).not.toContain("facebook.com");
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(footer).toContain("https://github.com/alehar9320");
+ expect(footer).not.toContain("mailto:");
+ expect(footer).not.toContain("Latest Updates");
+ expect(footer).not.toContain("Report an issue");
+ expect(footer).not.toContain("agentic engineering");
+ expect(footer).not.toContain(">Source");
+ expect(footer).not.toContain("source-link");
+ });
+
+ it("drops Designed & Developed from the footer and keeps LinkedIn hire", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ const nav = readFileSync("src/components/Nav.astro", "utf8");
+ expect(footer).not.toContain("Designed & Developed");
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(footer).toContain("https://github.com/alehar9320");
+ expect(footer).not.toContain("mailto:");
+ expect(footer).toContain("Stockholm");
+ expect(footer).toContain("Sweden");
expect(footer).not.toContain('href="https://astro.build/"');
- expect(footer).not.toContain('instagram.com');
- expect(footer).not.toContain('facebook.com');
- expect(nav).not.toContain('instagram.com');
- expect(nav).not.toContain('facebook.com');
- expect(footer).not.toContain('Latest Updates');
- expect(footer).not.toContain('Report an issue');
- expect(footer).not.toContain('agentic engineering');
- expect(footer).not.toContain('>Source');
- expect(footer).not.toContain('source-link');
- });
-
- it('drops with Astro from the footer and keeps LinkedIn hire', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- const nav = readFileSync('src/components/Nav.astro', 'utf8');
- expect(footer).not.toContain('with Astro');
+ expect(footer).not.toContain("instagram.com");
+ expect(footer).not.toContain("facebook.com");
+ expect(nav).not.toContain("instagram.com");
+ expect(nav).not.toContain("facebook.com");
+ expect(footer).not.toContain("Latest Updates");
+ expect(footer).not.toContain("Report an issue");
+ expect(footer).not.toContain("agentic engineering");
+ expect(footer).not.toContain(">Source");
+ expect(footer).not.toContain("source-link");
+ });
+
+ it("drops with Astro from the footer and keeps LinkedIn hire", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ const nav = readFileSync("src/components/Nav.astro", "utf8");
+ expect(footer).not.toContain("with Astro");
expect(footer).not.toContain('href="https://astro.build/"');
- expect(footer).not.toContain('>Astro');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(footer).toContain('https://github.com/alehar9320');
- expect(footer).not.toContain('mailto:');
- expect(footer).toContain('Stockholm');
- expect(footer).toContain('Sweden');
- expect(footer).not.toContain('instagram.com');
- expect(footer).not.toContain('facebook.com');
- expect(nav).not.toContain('instagram.com');
- expect(nav).not.toContain('facebook.com');
- expect(footer).not.toContain('Designed & Developed');
- expect(footer).not.toContain('Latest Updates');
- expect(footer).not.toContain('Report an issue');
- expect(footer).not.toContain('agentic engineering');
- expect(footer).not.toContain('>Source');
- expect(footer).not.toContain('source-link');
- });
-
- it('drops pin and flag emoji from the footer and keeps LinkedIn hire', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- const nav = readFileSync('src/components/Nav.astro', 'utf8');
- expect(footer).not.toContain('📍');
- expect(footer).not.toContain('🇸🇪');
- expect(footer).toContain('Stockholm');
- expect(footer).toContain('Sweden');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(footer).not.toContain('mailto:');
- expect(nav).not.toContain('instagram.com');
- expect(nav).not.toContain('facebook.com');
- expect(footer).not.toContain('instagram.com');
- expect(footer).not.toContain('facebook.com');
- expect(footer).not.toContain('with Astro');
- expect(footer).not.toContain('Designed & Developed');
- expect(footer).not.toContain('Latest Updates');
- expect(footer).not.toContain('Report an issue');
- expect(footer).not.toContain('agentic engineering');
- expect(footer).not.toContain('>Source');
- expect(footer).not.toContain('source-link');
- });
-
- it('opens footer socials with noopener so the site window stays', () => {
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- const socials = footer.slice(footer.indexOf('class="socials"'), footer.indexOf(''));
- expect(socials).toContain('https://www.linkedin.com/in/alehar/');
- expect(socials).toContain('https://blog.ifs.com/author/alexander-harenstam/');
- expect(socials).toContain('https://github.com/alehar9320');
+ expect(footer).not.toContain(">Astro");
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(footer).toContain("https://github.com/alehar9320");
+ expect(footer).not.toContain("mailto:");
+ expect(footer).toContain("Stockholm");
+ expect(footer).toContain("Sweden");
+ expect(footer).not.toContain("instagram.com");
+ expect(footer).not.toContain("facebook.com");
+ expect(nav).not.toContain("instagram.com");
+ expect(nav).not.toContain("facebook.com");
+ expect(footer).not.toContain("Designed & Developed");
+ expect(footer).not.toContain("Latest Updates");
+ expect(footer).not.toContain("Report an issue");
+ expect(footer).not.toContain("agentic engineering");
+ expect(footer).not.toContain(">Source");
+ expect(footer).not.toContain("source-link");
+ });
+
+ it("drops pin and flag emoji from the footer and keeps LinkedIn hire", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ const nav = readFileSync("src/components/Nav.astro", "utf8");
+ expect(footer).not.toContain("📍");
+ expect(footer).not.toContain("🇸🇪");
+ expect(footer).toContain("Stockholm");
+ expect(footer).toContain("Sweden");
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(footer).not.toContain("mailto:");
+ expect(nav).not.toContain("instagram.com");
+ expect(nav).not.toContain("facebook.com");
+ expect(footer).not.toContain("instagram.com");
+ expect(footer).not.toContain("facebook.com");
+ expect(footer).not.toContain("with Astro");
+ expect(footer).not.toContain("Designed & Developed");
+ expect(footer).not.toContain("Latest Updates");
+ expect(footer).not.toContain("Report an issue");
+ expect(footer).not.toContain("agentic engineering");
+ expect(footer).not.toContain(">Source");
+ expect(footer).not.toContain("source-link");
+ });
+
+ it("opens footer socials with noopener so the site window stays", () => {
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ const socials = footer.slice(
+ footer.indexOf('class="socials"'),
+ footer.indexOf(""),
+ );
+ expect(socials).toContain("https://www.linkedin.com/in/alehar/");
+ expect(socials).toContain(
+ "https://blog.ifs.com/author/alexander-harenstam/",
+ );
+ expect(socials).toContain("https://github.com/alehar9320");
expect([...socials.matchAll(/target="_blank"/g)]).toHaveLength(3);
expect([...socials.matchAll(/rel="noopener noreferrer"/g)]).toHaveLength(3);
- expect(socials).not.toContain('journal');
+ expect(socials).not.toContain("journal");
expect(socials).not.toContain("What's New");
- expect(footer).not.toContain('showUpcoming');
+ expect(footer).not.toContain("showUpcoming");
expect(footer).toContain('href="/this-site/"');
expect(footer).toContain('href="/whats-new/"');
expect(footer).toContain('href="/roadmap/"');
expect(footer).toContain("What's next");
expect(footer).toContain('href="/okr/"');
- expect(footer).not.toContain("What's New{' · '}Upcoming");
- expect(footer).not.toContain('mailto:');
- });
-
- it('drops Instagram and Facebook from site socials and keeps LinkedIn hire', () => {
- const nav = readFileSync('src/components/Nav.astro', 'utf8');
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
- expect(nav).not.toContain('instagram.com');
- expect(nav).not.toContain('facebook.com');
- expect(footer).not.toContain('instagram.com');
- expect(footer).not.toContain('facebook.com');
- expect(nav).toContain('https://www.linkedin.com/in/alehar');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(nav).toContain('https://github.com/alehar9320');
- expect(footer).toContain('https://github.com/alehar9320');
- expect(nav).not.toContain('mailto:');
- expect(footer).not.toContain('mailto:');
- expect(nav.toLowerCase()).not.toContain('alexander-harenstam-cv');
- expect(footer.toLowerCase()).not.toContain('alexander-harenstam-cv');
- expect(nav).not.toContain('.pdf');
- expect(footer).not.toContain('.pdf');
- expect(nav).not.toContain('instagram-logo');
- expect(nav).not.toContain('facebook-logo');
- });
-
- it('keeps the header terminal glyph and drops the competing footer rocket', () => {
- const nav = readFileSync('src/components/Nav.astro', 'utf8');
- const footer = readFileSync('src/components/Footer.astro', 'utf8');
+ expect(footer).not.toContain(
+ "What's New{' · '}Upcoming",
+ );
+ expect(footer).not.toContain("mailto:");
+ });
+
+ it("drops Instagram and Facebook from site socials and keeps LinkedIn hire", () => {
+ const nav = readFileSync("src/components/Nav.astro", "utf8");
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
+ expect(nav).not.toContain("instagram.com");
+ expect(nav).not.toContain("facebook.com");
+ expect(footer).not.toContain("instagram.com");
+ expect(footer).not.toContain("facebook.com");
+ expect(nav).toContain("https://www.linkedin.com/in/alehar");
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(nav).toContain("https://github.com/alehar9320");
+ expect(footer).toContain("https://github.com/alehar9320");
+ expect(nav).not.toContain("mailto:");
+ expect(footer).not.toContain("mailto:");
+ expect(nav.toLowerCase()).not.toContain("alexander-harenstam-cv");
+ expect(footer.toLowerCase()).not.toContain("alexander-harenstam-cv");
+ expect(nav).not.toContain(".pdf");
+ expect(footer).not.toContain(".pdf");
+ expect(nav).not.toContain("instagram-logo");
+ expect(nav).not.toContain("facebook-logo");
+ });
+
+ it("keeps the header terminal glyph and drops the competing footer rocket", () => {
+ const nav = readFileSync("src/components/Nav.astro", "utf8");
+ const footer = readFileSync("src/components/Footer.astro", "utf8");
expect(nav).toContain('icon="terminal-window"');
- expect(nav).not.toContain('rocket-launch');
- expect(footer).not.toContain('rocket-launch');
+ expect(nav).not.toContain("rocket-launch");
+ expect(footer).not.toContain("rocket-launch");
expect(footer).not.toContain('icon="rocket');
- expect(footer).not.toContain('M94.1 184.6');
- expect(footer).not.toContain('favicon.svg');
- expect(footer).not.toContain('terminal-window');
+ expect(footer).not.toContain("M94.1 184.6");
+ expect(footer).not.toContain("favicon.svg");
+ expect(footer).not.toContain("terminal-window");
expect(footer).not.toContain('href="https://astro.build/"');
- expect(footer).not.toContain('>Astro');
- expect(footer).toContain('https://www.linkedin.com/in/alehar/');
- expect(footer).not.toContain('mailto:');
+ expect(footer).not.toContain(">Astro");
+ expect(footer).toContain("https://www.linkedin.com/in/alehar/");
+ expect(footer).not.toContain("mailto:");
});
- it('shows a Live / Not live signal on the chat header', () => {
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
- expect(chat).toContain('chat-live-label');
- expect(chat).not.toContain('status-dot');
+ it("shows a Live / Not live signal on the chat header", () => {
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
+ expect(chat).toContain("chat-live-label");
+ expect(chat).not.toContain("status-dot");
expect(chat).toContain("idle: 'Live'");
expect(chat).toContain("error: 'Not live'");
expect(chat).not.toContain("idle: 'Available'");
- expect(chat).not.toContain('.chat-toggle .status-dot');
+ expect(chat).not.toContain(".chat-toggle .status-dot");
});
- it('points structured data at the live site, not harenstam.com', () => {
- const home = readFileSync('src/pages/index.astro', 'utf8');
+ it("points structured data at the live site, not harenstam.com", () => {
+ const home = readFileSync("src/pages/index.astro", "utf8");
expect(home).toContain("'@id': 'https://me.alehar.workers.dev/#person'");
expect(home).toContain("url: 'https://me.alehar.workers.dev/'");
- expect(home).toContain("url: 'https://me.alehar.workers.dev/assets/portrait.png'");
- expect(home).not.toContain('https://harenstam.com/');
- expect(home).toContain('https://www.linkedin.com/in/alehar/');
- expect(home).toContain('Product Manager, Developer Experience');
- expect(home).not.toContain('mailto:');
+ expect(home).toContain(
+ "url: 'https://me.alehar.workers.dev/assets/portrait.png'",
+ );
+ expect(home).not.toContain("https://harenstam.com/");
+ expect(home).toContain("https://www.linkedin.com/in/alehar/");
+ expect(home).toContain("Product Manager, Developer Experience");
+ expect(home).not.toContain("mailto:");
});
- it('points LinkedIn share photos at the live portrait, not a 404', () => {
- const head = readFileSync('src/components/MainHead.astro', 'utf8');
- expect(head).toContain("shareImage = 'https://me.alehar.workers.dev/assets/portrait.png'");
+ it("points LinkedIn share photos at the live portrait, not a 404", () => {
+ const head = readFileSync("src/components/MainHead.astro", "utf8");
+ expect(head).toContain(
+ "shareImage = 'https://me.alehar.workers.dev/assets/portrait.png'",
+ );
expect(head).toContain('property="og:image"');
- expect(head).toContain('content={shareImage}');
- expect(head).not.toContain('https://harenstam.com/assets/portrait.png');
- expect(head).not.toContain('mailto:');
+ expect(head).toContain("content={shareImage}");
+ expect(head).not.toContain("https://harenstam.com/assets/portrait.png");
+ expect(head).not.toContain("mailto:");
});
- it('points share URLs at the live site, not localhost', () => {
- const head = readFileSync('src/components/MainHead.astro', 'utf8');
+ it("points share URLs at the live site, not localhost", () => {
+ const head = readFileSync("src/components/MainHead.astro", "utf8");
expect(head).toContain("liveOrigin = 'https://me.alehar.workers.dev'");
- expect(head).toContain('new URL(Astro.url.pathname, liveOrigin)');
+ expect(head).toContain("new URL(Astro.url.pathname, liveOrigin)");
expect(head).toContain('property="og:url"');
expect(head).toContain('name="twitter:url"');
- expect(head).toContain('content={shareUrl}');
+ expect(head).toContain("content={shareUrl}");
expect(head).toContain('rel="canonical"');
- expect(head).toContain('href={canonicalUrl}');
- expect(head).not.toContain('content={Astro.url}');
- expect(head).not.toContain('localhost');
- expect(head).not.toContain('harenstam.com');
+ expect(head).toContain("href={canonicalUrl}");
+ expect(head).not.toContain("content={Astro.url}");
+ expect(head).not.toContain("localhost");
+ expect(head).not.toContain("harenstam.com");
});
- it('drops Ask about the work from the twin chat input placeholder', () => {
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
+ it("drops Ask about the work from the twin chat input placeholder", () => {
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
expect(chat).not.toContain('placeholder="Ask about the work"');
expect(chat).toContain('placeholder="Message…"');
expect(chat).not.toMatch(/placeholder="(?!Message…)[^"]*"/);
expect(chat).toContain('id="chat-send"');
- expect(chat).toContain('syncSendReadyState');
- expect(chat).toContain('chatSend.disabled = !ready');
- expect(chat).not.toContain('Ask me something');
- expect(chat).not.toContain('chat-toggle-portrait');
- expect(chat).not.toContain('mailto:');
+ expect(chat).toContain("syncSendReadyState");
+ expect(chat).toContain("chatSend.disabled = !ready");
+ expect(chat).not.toContain("Ask me something");
+ expect(chat).not.toContain("chat-toggle-portrait");
+ expect(chat).not.toContain("mailto:");
expect(chat).toContain("idle: 'Live'");
expect(chat).toContain("error: 'Not live'");
});
- it('drops Ask about the work from the twin chat tooltip', () => {
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
- expect(chat).not.toContain('class="status-tooltip">Ask about the work');
- expect(chat).toContain('id="status-tooltip" class="status-tooltip">');
- expect(chat).toContain('statusTooltip.textContent = tooltipLabels[state]');
- expect(chat).not.toContain('statusTooltip.textContent = statusLabels[state]');
+ it("drops Ask about the work from the twin chat tooltip", () => {
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
+ expect(chat).not.toContain(
+ 'class="status-tooltip">Ask about the work',
+ );
+ expect(chat).toContain(
+ 'id="status-tooltip" class="status-tooltip">',
+ );
+ expect(chat).toContain("statusTooltip.textContent = tooltipLabels[state]");
+ expect(chat).not.toContain(
+ "statusTooltip.textContent = statusLabels[state]",
+ );
expect(chat).toContain("idle: ''");
- expect(chat).not.toMatch(/statusTooltip\.textContent\s*=\s*['"]Ask about the work['"]/);
+ expect(chat).not.toMatch(
+ /statusTooltip\.textContent\s*=\s*['"]Ask about the work['"]/,
+ );
expect(chat).not.toContain('placeholder="Ask about the work"');
expect(chat).toContain('placeholder="Message…"');
expect(chat).not.toMatch(/placeholder="(?!Message…)[^"]*"/);
- expect(chat).not.toContain('Ask me something');
- expect(chat).not.toContain('Ask me anything');
- expect(chat).not.toContain('chat-toggle-portrait');
- expect(chat).not.toContain('mailto:');
+ expect(chat).not.toContain("Ask me something");
+ expect(chat).not.toContain("Ask me anything");
+ expect(chat).not.toContain("chat-toggle-portrait");
+ expect(chat).not.toContain("mailto:");
expect(chat).toContain("idle: 'Live'");
expect(chat).toContain("error: 'Not live'");
});
- it('drops Ask about the work from the twin chat welcome', () => {
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
- expect(chat).not.toContain('Ask about the work, DevEx, Industrial AI, or background.');
+ it("drops Ask about the work from the twin chat welcome", () => {
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
+ expect(chat).not.toContain(
+ "Ask about the work, DevEx, Industrial AI, or background.",
+ );
expect(chat).not.toMatch(/\s*Ask about the work/);
- expect(chat).toContain('DevEx, Industrial AI, or background.');
+ expect(chat).toContain("DevEx, Industrial AI, or background.");
expect(chat).toContain("I'm an AI with his context.");
- expect(chat).not.toContain('class="status-tooltip">Ask about the work');
- expect(chat).toContain('id="status-tooltip" class="status-tooltip">');
+ expect(chat).not.toContain(
+ 'class="status-tooltip">Ask about the work',
+ );
+ expect(chat).toContain(
+ 'id="status-tooltip" class="status-tooltip">',
+ );
expect(chat).toContain("idle: ''");
expect(chat).not.toContain('placeholder="Ask about the work"');
expect(chat).toContain('placeholder="Message…"');
expect(chat).not.toMatch(/placeholder="(?!Message…)[^"]*"/);
- expect(chat).not.toContain('Ask me something');
- expect(chat).not.toContain('Ask me anything');
- expect(chat).not.toContain('chat-toggle-portrait');
- expect(chat).not.toContain('mailto:');
+ expect(chat).not.toContain("Ask me something");
+ expect(chat).not.toContain("Ask me anything");
+ expect(chat).not.toContain("chat-toggle-portrait");
+ expect(chat).not.toContain("mailto:");
expect(chat).toContain("idle: 'Live'");
expect(chat).toContain("error: 'Not live'");
});
- it('drops Ask about the work from the twin chat idle announcer', () => {
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
+ it("drops Ask about the work from the twin chat idle announcer", () => {
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
expect(chat).toContain('id="status-announcer"');
- expect(chat).toContain('statusAnnouncer.textContent = statusLabels[state]');
+ expect(chat).toContain("statusAnnouncer.textContent = statusLabels[state]");
expect(chat).not.toContain("idle: 'Live. Ask about the work'");
- expect(chat).not.toContain('Ask about the work');
+ expect(chat).not.toContain("Ask about the work");
expect(chat).toContain("const statusLabels = {\n idle: 'Live',");
- expect(chat).not.toContain('Ask about the work, DevEx, Industrial AI, or background.');
+ expect(chat).not.toContain(
+ "Ask about the work, DevEx, Industrial AI, or background.",
+ );
expect(chat).not.toMatch(/
\s*Ask about the work/);
- expect(chat).toContain('DevEx, Industrial AI, or background.');
+ expect(chat).toContain("DevEx, Industrial AI, or background.");
expect(chat).toContain("I'm an AI with his context.");
- expect(chat).not.toContain('class="status-tooltip">Ask about the work');
- expect(chat).toContain('id="status-tooltip" class="status-tooltip">');
+ expect(chat).not.toContain(
+ 'class="status-tooltip">Ask about the work',
+ );
+ expect(chat).toContain(
+ 'id="status-tooltip" class="status-tooltip">',
+ );
expect(chat).toContain("idle: ''");
expect(chat).not.toContain('placeholder="Ask about the work"');
expect(chat).toContain('placeholder="Message…"');
expect(chat).not.toMatch(/placeholder="(?!Message…)[^"]*"/);
- expect(chat).not.toContain('Ask me something');
- expect(chat).not.toContain('Ask me anything');
- expect(chat).not.toContain('chat-toggle-portrait');
- expect(chat).not.toContain('mailto:');
+ expect(chat).not.toContain("Ask me something");
+ expect(chat).not.toContain("Ask me anything");
+ expect(chat).not.toContain("chat-toggle-portrait");
+ expect(chat).not.toContain("mailto:");
expect(chat).toContain("idle: 'Live'");
expect(chat).toContain("error: 'Not live'");
});
- it('drops Ask Alexander from the twin chat header', () => {
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
+ it("drops Ask Alexander from the twin chat header", () => {
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
expect(chat).toContain('');
- expect(chat).not.toContain('Ask Alexander');
- expect(chat).not.toContain('Ask about the work');
+ expect(chat).not.toContain("Ask Alexander");
+ expect(chat).not.toContain("Ask about the work");
expect(chat).toContain("const statusLabels = {\n idle: 'Live',");
- expect(chat).not.toContain('Ask about the work, DevEx, Industrial AI, or background.');
+ expect(chat).not.toContain(
+ "Ask about the work, DevEx, Industrial AI, or background.",
+ );
expect(chat).not.toMatch(/
\s*Ask about the work/);
- expect(chat).toContain('DevEx, Industrial AI, or background.');
+ expect(chat).toContain("DevEx, Industrial AI, or background.");
expect(chat).toContain("I'm an AI with his context.");
- expect(chat).not.toContain('class="status-tooltip">Ask about the work');
- expect(chat).toContain('id="status-tooltip" class="status-tooltip">');
+ expect(chat).not.toContain(
+ 'class="status-tooltip">Ask about the work',
+ );
+ expect(chat).toContain(
+ 'id="status-tooltip" class="status-tooltip">',
+ );
expect(chat).toContain("idle: ''");
expect(chat).not.toContain('placeholder="Ask about the work"');
expect(chat).toContain('placeholder="Message…"');
expect(chat).not.toMatch(/placeholder="(?!Message…)[^"]*"/);
- expect(chat).not.toContain('Ask me something');
- expect(chat).not.toContain('Ask me anything');
- expect(chat).not.toContain('chat-toggle-portrait');
- expect(chat).not.toContain('mailto:');
+ expect(chat).not.toContain("Ask me something");
+ expect(chat).not.toContain("Ask me anything");
+ expect(chat).not.toContain("chat-toggle-portrait");
+ expect(chat).not.toContain("mailto:");
expect(chat).toContain("idle: 'Live'");
expect(chat).toContain("error: 'Not live'");
});
- it('lets a visitor clear the twin chat and keeps identity in the header, not the FAB', () => {
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
+ it("lets a visitor clear the twin chat and keeps identity in the header, not the FAB", () => {
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
expect(chat).toContain('aria-label="Clear conversation"');
- expect(chat).toContain('clearConversation');
- expect(chat).toContain('chat-header-avatar');
- expect(chat).not.toContain('Ask Alexander');
+ expect(chat).toContain("clearConversation");
+ expect(chat).toContain("chat-header-avatar");
+ expect(chat).not.toContain("Ask Alexander");
expect(chat).toContain('class="chat-header-name"');
- expect(chat).not.toContain('chat-toggle-portrait');
- expect(chat).not.toContain('mailto:');
+ expect(chat).not.toContain("chat-toggle-portrait");
+ expect(chat).not.toContain("mailto:");
expect(chat).toContain("idle: 'Live'");
expect(chat).toContain("error: 'Not live'");
});
- it('puts the Home headshot in the twin and drops the 480 page portrait', () => {
- const home = readFileSync('src/pages/index.astro', 'utf8');
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
+ it("puts the Home headshot in the twin and drops the 480 page portrait", () => {
+ const home = readFileSync("src/pages/index.astro", "utf8");
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
expect(home).not.toContain('class="portrait"');
expect(home).not.toContain('width="480"');
- expect(home).toContain("url: 'https://me.alehar.workers.dev/assets/portrait.png'");
- expect(home).toContain('https://www.linkedin.com/in/alehar/');
- expect(home).toContain('Get in touch');
- expect(home).not.toContain('mailto:');
- expect(chat).toContain('chat-header-avatar');
- expect(chat).toContain('/assets/portrait.png');
+ expect(home).toContain(
+ "url: 'https://me.alehar.workers.dev/assets/portrait.png'",
+ );
+ expect(home).toContain("https://www.linkedin.com/in/alehar/");
+ expect(home).toContain("Get in touch");
+ expect(home).not.toContain("mailto:");
+ expect(chat).toContain("chat-header-avatar");
+ expect(chat).toContain("/assets/portrait.png");
expect(chat).toContain('class="chat-header-avatar"');
expect(chat).toContain('width="40"');
expect(chat).toContain('height="40"');
expect(chat).toContain('alt=""');
- expect(chat).not.toContain('chat-welcome-portrait');
- expect(chat).not.toContain('message-avatar');
- expect(chat).not.toContain('chat-toggle-portrait');
- expect(chat).not.toContain('mailto:');
+ expect(chat).not.toContain("chat-welcome-portrait");
+ expect(chat).not.toContain("message-avatar");
+ expect(chat).not.toContain("chat-toggle-portrait");
+ expect(chat).not.toContain("mailto:");
});
- it('makes the twin the first-view chat with a headshot, follow-ups, and a docked FAB', () => {
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
- const home = readFileSync('src/pages/index.astro', 'utf8');
- expect(chat).not.toContain('chat-welcome-portrait');
- expect(chat).toContain('/assets/portrait.png');
+ it("makes the twin the first-view chat with a headshot, follow-ups, and a docked FAB", () => {
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
+ const home = readFileSync("src/pages/index.astro", "utf8");
+ expect(chat).not.toContain("chat-welcome-portrait");
+ expect(chat).toContain("/assets/portrait.png");
expect(chat).toContain('class="chat-header-avatar"');
expect(chat).toContain('width="40"');
expect(chat).toContain('height="40"');
- expect(chat).toContain('chat-followups');
- expect(chat).toContain('showFollowUps');
- expect(chat).toContain('How do I get in touch on LinkedIn?');
- expect(chat).toContain('is-prominent');
- expect(chat).toContain('is-docked');
- expect(chat).toContain('dockChat');
- expect(chat).not.toContain('mailto:');
- expect(chat).not.toContain('harenstam.com');
- expect(chat).toContain('M7.9 20A9 9 0 1 0 4 16.1L2 22Z');
- expect(chat).not.toContain('chat-toggle-portrait');
- expect(home).toContain('https://www.linkedin.com/in/alehar/');
- expect(home).toContain('Get in touch');
- expect(home).toContain('Hero title="Product Manager, Developer Experience at IFS"');
- expect(home).not.toContain('mailto:');
- expect(chat).toContain('has-prominent-chat');
- expect(chat).toContain('border-radius: 1rem 1rem 0 0');
- expect(chat).toContain('width: min(52rem, calc(100vw - 3rem))');
- expect(chat).toContain('max-height: 36dvh');
- expect(chat).not.toContain('max-width: 36rem');
- expect(chat).not.toContain('height: 62dvh');
- expect(chat).toContain('translateY(100%)');
- expect(chat).not.toContain('min(32rem, calc(50vw - 2rem))');
- expect(chat).not.toContain('top: 5.5rem');
- expect(chat).not.toContain('50vw');
- expect(chat).not.toContain('--prominent-phone-top');
- expect(chat).not.toContain('layoutProminentPhone');
- expect(home).toContain('cta-hint');
- expect(home).toContain('Get in touch');
+ expect(chat).toContain("chat-followups");
+ expect(chat).toContain("showFollowUps");
+ expect(chat).toContain("How do I get in touch on LinkedIn?");
+ expect(chat).toContain("is-prominent");
+ expect(chat).toContain("is-docked");
+ expect(chat).toContain("dockChat");
+ expect(chat).not.toContain("mailto:");
+ expect(chat).not.toContain("harenstam.com");
+ expect(chat).toContain("M7.9 20A9 9 0 1 0 4 16.1L2 22Z");
+ expect(chat).not.toContain("chat-toggle-portrait");
+ expect(home).toContain("https://www.linkedin.com/in/alehar/");
+ expect(home).toContain("Get in touch");
+ expect(home).toContain(
+ 'Hero title="Product Manager, Developer Experience at IFS"',
+ );
+ expect(home).not.toContain("mailto:");
+ expect(chat).toContain("has-prominent-chat");
+ expect(chat).toContain("border-radius: 1rem 1rem 0 0");
+ expect(chat).toContain("width: min(52rem, calc(100vw - 3rem))");
+ expect(chat).toContain("max-height: 36dvh");
+ expect(chat).not.toContain("max-width: 36rem");
+ expect(chat).not.toContain("height: 62dvh");
+ expect(chat).toContain("translateY(100%)");
+ expect(chat).not.toContain("min(32rem, calc(50vw - 2rem))");
+ expect(chat).not.toContain("top: 5.5rem");
+ expect(chat).not.toContain("50vw");
+ expect(chat).not.toContain("--prominent-phone-top");
+ expect(chat).not.toContain("layoutProminentPhone");
+ expect(home).toContain("cta-hint");
+ expect(home).toContain("Get in touch");
expect(home).toContain('class="cta-hint">LinkedIn
');
- expect(home).not.toContain('LinkedIn · replies from me');
- expect(home).toContain('body.has-prominent-chat');
- expect(home).toContain('padding-bottom: 36dvh');
- expect(home).not.toContain('padding-right: min(34rem, 48vw)');
- expect(home).toContain('proof-card');
- expect(home).toContain('Hero title="Product Manager, Developer Experience at IFS"');
- });
-
- it('opens the twin as a bottom sheet with bubbles and one header portrait', () => {
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
- const home = readFileSync('src/pages/index.astro', 'utf8');
- const nav = readFileSync('src/components/Nav.astro', 'utf8');
- expect(chat).toContain('border-radius: 1rem 1rem 0 0');
- expect(chat).toContain('width: min(52rem, calc(100vw - 3rem))');
- expect(chat).toContain('max-height: 36dvh');
- expect(chat).not.toContain('max-width: 36rem');
- expect(chat).not.toContain('height: 62dvh');
- expect(chat).not.toContain('min-height: 55dvh');
- expect(chat).not.toContain('max-height: 70dvh');
- expect(chat).toContain('translateY(100%)');
- expect(chat).toContain('translateY(0)');
- expect(chat).toContain('220ms');
- expect(chat).not.toContain('hsla(0, 0%, 0%, 0.28)');
+ expect(home).not.toContain("LinkedIn · replies from me");
+ expect(home).toContain("body.has-prominent-chat");
+ expect(home).toContain("padding-bottom: 36dvh");
+ expect(home).not.toContain("padding-right: min(34rem, 48vw)");
+ expect(home).toContain("proof-card");
+ expect(home).toContain(
+ 'Hero title="Product Manager, Developer Experience at IFS"',
+ );
+ });
+
+ it("opens the twin as a bottom sheet with bubbles and one header portrait", () => {
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
+ const home = readFileSync("src/pages/index.astro", "utf8");
+ const nav = readFileSync("src/components/Nav.astro", "utf8");
+ expect(chat).toContain("border-radius: 1rem 1rem 0 0");
+ expect(chat).toContain("width: min(52rem, calc(100vw - 3rem))");
+ expect(chat).toContain("max-height: 36dvh");
+ expect(chat).not.toContain("max-width: 36rem");
+ expect(chat).not.toContain("height: 62dvh");
+ expect(chat).not.toContain("min-height: 55dvh");
+ expect(chat).not.toContain("max-height: 70dvh");
+ expect(chat).toContain("translateY(100%)");
+ expect(chat).toContain("translateY(0)");
+ expect(chat).toContain("220ms");
+ expect(chat).not.toContain("hsla(0, 0%, 0%, 0.28)");
expect(chat).not.toContain('id="chat-scrim"');
- expect(chat).toContain('env(safe-area-inset-bottom');
- expect(chat).not.toContain('min(32rem, calc(50vw - 2rem))');
- expect(chat).not.toContain('top: 5.5rem');
- expect(chat).not.toContain('50vw');
- expect(chat).toContain('padding: 0.75rem 1rem');
- expect(chat).toContain('border-radius: 1rem 1rem 4px 1rem');
- expect(chat).toContain('border-radius: 1rem 1rem 1rem 4px');
- expect(chat).toContain('max-width: 80%');
- expect(chat).toContain('gap: 0.5rem');
- expect(chat).toContain('.chat-header {\n padding: 1rem;');
- expect(chat).toContain('.chat-form {\n padding: 1rem;');
- expect(chat).not.toContain('padding: 0.5rem 1rem 1rem');
- expect(chat).toContain('min-height: 44px');
- expect(chat).toContain('border-radius: 0.75rem');
- expect(chat).toContain('width: 2.5rem');
- expect(chat).toContain('chat-header-avatar');
- expect(chat).not.toContain('chat-welcome-portrait');
- expect(chat).not.toContain('message-avatar');
- expect(chat).not.toContain('chat-toggle-portrait');
- expect(chat).not.toContain('status-dot');
+ expect(chat).toContain("env(safe-area-inset-bottom");
+ expect(chat).not.toContain("min(32rem, calc(50vw - 2rem))");
+ expect(chat).not.toContain("top: 5.5rem");
+ expect(chat).not.toContain("50vw");
+ expect(chat).toContain("padding: 0.75rem 1rem");
+ expect(chat).toContain("border-radius: 1rem 1rem 4px 1rem");
+ expect(chat).toContain("border-radius: 1rem 1rem 1rem 4px");
+ expect(chat).toContain("max-width: 80%");
+ expect(chat).toContain("gap: 0.5rem");
+ expect(chat).toContain(".chat-header {\n padding: 1rem;");
+ expect(chat).toContain(".chat-form {\n padding: 1rem;");
+ expect(chat).not.toContain("padding: 0.5rem 1rem 1rem");
+ expect(chat).toContain("min-height: 44px");
+ expect(chat).toContain("border-radius: 0.75rem");
+ expect(chat).toContain("width: 2.5rem");
+ expect(chat).toContain("chat-header-avatar");
+ expect(chat).not.toContain("chat-welcome-portrait");
+ expect(chat).not.toContain("message-avatar");
+ expect(chat).not.toContain("chat-toggle-portrait");
+ expect(chat).not.toContain("status-dot");
expect(chat).toContain('id="chat-clear"');
- expect(chat).toContain('chat-followups');
- expect(chat).toContain('How do I get in touch on LinkedIn?');
- expect(chat).toContain('What did the IFS design system change?');
+ expect(chat).toContain("chat-followups");
+ expect(chat).toContain("How do I get in touch on LinkedIn?");
+ expect(chat).toContain("What did the IFS design system change?");
expect(chat).toContain("I'm an AI with his context.");
- expect(chat).not.toContain('Ask about the work');
- expect(chat).toContain('prefers-reduced-motion: reduce');
- expect(chat).toContain('transform: none');
- expect(nav).toContain('hsla(var(--gray-999-basis), 0.9)');
- expect(nav).toContain('backdrop-filter: blur(40px) saturate(140%)');
- expect(home).toContain('https://www.linkedin.com/in/alehar/');
- expect(home).toContain('Get in touch');
- expect(home).toContain('Hero title="Product Manager, Developer Experience at IFS"');
- expect(home).not.toContain('mailto:');
- expect(chat).not.toContain('mailto:');
- expect(chat).not.toContain('data-hire-surface');
- });
-
- it('keeps first paint identity and hire, docks chat until open', () => {
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
- const home = readFileSync('src/pages/index.astro', 'utf8');
- const hero = readFileSync('src/components/Hero.astro', 'utf8');
- expect(chat).toContain('First paint is identity + hire');
- expect(chat).toContain('100dvh only after is-open');
+ expect(chat).not.toContain("Ask about the work");
+ expect(chat).toContain("prefers-reduced-motion: reduce");
+ expect(chat).toContain("transform: none");
+ expect(nav).toContain("hsla(var(--gray-999-basis), 0.9)");
+ expect(nav).toContain("backdrop-filter: blur(40px) saturate(140%)");
+ expect(home).toContain("https://www.linkedin.com/in/alehar/");
+ expect(home).toContain("Get in touch");
+ expect(home).toContain(
+ 'Hero title="Product Manager, Developer Experience at IFS"',
+ );
+ expect(home).not.toContain("mailto:");
+ expect(chat).not.toContain("mailto:");
+ expect(chat).not.toContain("data-hire-surface");
+ });
+
+ it("keeps first paint identity and hire, docks chat until open", () => {
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
+ const home = readFileSync("src/pages/index.astro", "utf8");
+ const hero = readFileSync("src/components/Hero.astro", "utf8");
+ expect(chat).toContain("First paint is identity + hire");
+ expect(chat).toContain("100dvh only after is-open");
expect(chat).toContain("classList.add('is-docked')");
expect(chat).not.toContain("classList.add('is-prominent')");
- expect(chat).toContain('height: 100dvh');
- expect(chat).toContain('.chat-container.is-open:not(.is-prominent)');
- expect(chat).toContain('is-docked');
- expect(chat).not.toContain('mailto:');
- expect(home).toContain('Hero title="Product Manager, Developer Experience at IFS"');
+ expect(chat).toContain("height: 100dvh");
+ expect(chat).toContain(".chat-container.is-open:not(.is-prominent)");
+ expect(chat).toContain("is-docked");
+ expect(chat).not.toContain("mailto:");
+ expect(home).toContain(
+ 'Hero title="Product Manager, Developer Experience at IFS"',
+ );
expect(home).toContain('class="hero-dek"');
expect((home.match(/class="hero-dek"/g) || []).length).toBe(1);
- expect(home).toContain('Design systems, developer platforms, and Industrial AI copilots.');
- expect(home).not.toContain('Also hireable');
- expect(home).not.toContain('not only DevEx');
+ expect(home).toContain(
+ "Design systems, developer platforms, and Industrial AI copilots.",
+ );
+ expect(home).not.toContain("Also hireable");
+ expect(home).not.toContain("not only DevEx");
expect(home).toContain('class="hero-bridge"');
- expect(home.replace(/\s+/g, ' ')).toContain('Eight years at IFS');
- expect(home).toContain('Chalmers software engineering');
- expect(home).toContain('Get in touch');
+ expect(home.replace(/\s+/g, " ")).toContain("Eight years at IFS");
+ expect(home).toContain("Chalmers software engineering");
+ expect(home).toContain("Get in touch");
expect(home).toContain('class="cta-hint">LinkedIn');
- expect(home).not.toContain('LinkedIn · replies from me');
- expect(home).toContain('https://www.linkedin.com/in/alehar/');
- expect(home).toContain('proof-card');
- expect(home).toContain('Zeroheight Design System Awards, runner-up');
- expect(home).not.toContain('mailto:');
+ expect(home).not.toContain("LinkedIn · replies from me");
+ expect(home).toContain("https://www.linkedin.com/in/alehar/");
+ expect(home).toContain("proof-card");
+ expect(home).toContain("Zeroheight Design System Awards, runner-up");
+ expect(home).not.toContain("mailto:");
expect(home).not.toMatch(/\bVP\b/);
- expect(hero).toContain('font-size: var(--text-xl)');
- expect(hero).toContain('font-size: var(--text-5xl)');
+ expect(hero).toContain("font-size: var(--text-xl)");
+ expect(hero).toContain("font-size: var(--text-5xl)");
expect(hero).not.toContain("variant = 'primary'");
});
- it('makes phone talking a full-page chat and keeps desktop stage locks', () => {
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
- const home = readFileSync('src/pages/index.astro', 'utf8');
- expect(chat).toContain('@media (max-width: 49.99em)');
- expect(chat).toContain('Phone talking = full page');
- expect(chat).toContain('height: 100dvh');
- expect(chat).toContain('padding-top: 0');
- expect(chat).toContain('max-height: none');
- expect(chat).toContain('border-radius: 0');
- expect(chat).toContain('background-color: var(--gray-999)');
- expect(chat).toContain('.chat-container.is-prominent .chat-header');
- expect(chat).toContain('display: block');
- expect(chat).toContain(':global(body.has-prominent-chat)');
- expect(chat).toContain(':global(body:has(.chat-container.is-open))');
- expect(chat).toContain('overflow: hidden');
+ it("makes phone talking a full-page chat and keeps desktop stage locks", () => {
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
+ const home = readFileSync("src/pages/index.astro", "utf8");
+ expect(chat).toContain("@media (max-width: 49.99em)");
+ expect(chat).toContain("Phone talking = full page");
+ expect(chat).toContain("height: 100dvh");
+ expect(chat).toContain("padding-top: 0");
+ expect(chat).toContain("max-height: none");
+ expect(chat).toContain("border-radius: 0");
+ expect(chat).toContain("background-color: var(--gray-999)");
+ expect(chat).toContain(".chat-container.is-prominent .chat-header");
+ expect(chat).toContain("display: block");
+ expect(chat).toContain(":global(body.has-prominent-chat)");
+ expect(chat).toContain(":global(body:has(.chat-container.is-open))");
+ expect(chat).toContain("overflow: hidden");
// Desktop locks stay in source for ≥50em / identity keep-list.
- expect(chat).toContain('max-height: 36dvh');
- expect(chat).toContain('width: min(52rem, calc(100vw - 3rem))');
- expect(chat).toContain('border-radius: 1rem 1rem 0 0');
- expect(chat).not.toContain('height: 62dvh');
- expect(chat).not.toContain('Get in touch');
- expect(chat).toContain('/assets/portrait.png');
+ expect(chat).toContain("max-height: 36dvh");
+ expect(chat).toContain("width: min(52rem, calc(100vw - 3rem))");
+ expect(chat).toContain("border-radius: 1rem 1rem 0 0");
+ expect(chat).not.toContain("height: 62dvh");
+ expect(chat).not.toContain("Get in touch");
+ expect(chat).toContain("/assets/portrait.png");
expect((chat.match(/class="chat-header-avatar"/g) || []).length).toBe(1);
- expect(home).toContain('padding-bottom: 36dvh');
- expect(home).toContain('padding-bottom: 0');
- expect(home).toContain('https://www.linkedin.com/in/alehar/');
- expect(home).toContain('Get in touch');
- expect(home).toContain('Hero title="Product Manager, Developer Experience at IFS"');
- expect(home).not.toContain('mailto:');
- });
-
- it('docks the open sheet to a compact bottom bar on scroll, not a FAB or right column', () => {
- const chat = readFileSync('src/components/Chat.astro', 'utf8');
- const home = readFileSync('src/pages/index.astro', 'utf8');
- const nav = readFileSync('src/components/Nav.astro', 'utf8');
+ expect(home).toContain("padding-bottom: 36dvh");
+ expect(home).toContain("padding-bottom: 0");
+ expect(home).toContain("https://www.linkedin.com/in/alehar/");
+ expect(home).toContain("Get in touch");
+ expect(home).toContain(
+ 'Hero title="Product Manager, Developer Experience at IFS"',
+ );
+ expect(home).not.toContain("mailto:");
+ });
+
+ it("docks the open sheet to a compact bottom bar on scroll, not a FAB or right column", () => {
+ const chat = readFileSync("src/components/Chat.astro", "utf8");
+ const home = readFileSync("src/pages/index.astro", "utf8");
+ const nav = readFileSync("src/components/Nav.astro", "utf8");
expect(chat).not.toContain('id="chat-dock"');
expect(chat).not.toContain('class="chat-dock"');
- expect(chat).not.toContain('chat-dock-handle');
- expect(chat).not.toContain('min-height: 3.25rem');
+ expect(chat).not.toContain("chat-dock-handle");
+ expect(chat).not.toContain("min-height: 3.25rem");
expect(chat).not.toContain('id="chat-scrim"');
- expect(chat).toContain('.chat-container.is-docked .chat-toggle');
- expect(chat).toContain('.chat-container.is-docked:not(.is-open):not(.is-prominent)');
+ expect(chat).toContain(".chat-container.is-docked .chat-toggle");
+ expect(chat).toContain(
+ ".chat-container.is-docked:not(.is-open):not(.is-prominent)",
+ );
expect(chat).toContain("chatContainer?.classList.add('is-docked')");
- expect(chat).toContain('setChatExpanded(false)');
- expect(chat).toContain('if (window.scrollY > 160) dockChat()');
- expect(chat).toContain('setChatExpanded(true)');
- expect(chat).toContain('prefers-reduced-motion: reduce');
- expect(chat).toContain('transition: none');
- expect(chat).toContain('transform: none');
+ expect(chat).toContain("setChatExpanded(false)");
+ expect(chat).toContain("if (window.scrollY > 160) dockChat()");
+ expect(chat).toContain("setChatExpanded(true)");
+ expect(chat).toContain("prefers-reduced-motion: reduce");
+ expect(chat).toContain("transition: none");
+ expect(chat).toContain("transform: none");
expect(chat).toContain('