fix: preserve journey geometry and mailto placement
This commit is contained in:
@@ -242,12 +242,13 @@ const assertSemanticHtml = (html, inputs, contract) => {
|
||||
for (const copy of requiredVisibleCopy) {
|
||||
if (!semanticText.includes(copy)) throw new Error(`Missing visible semantic evidence: ${copy}`);
|
||||
}
|
||||
const mailLinks = [...appRoot.querySelectorAll('a[href^="mailto:"]')]
|
||||
.filter((node) => isVisibleSemanticNode(node, appRoot));
|
||||
if (mailLinks.length !== 2 || mailLinks.some((link) =>
|
||||
const mailLinks = [...document.querySelectorAll('a[href^="mailto:"]')];
|
||||
const startMailLinks = [...document.querySelectorAll('#start a[href^="mailto:"]')];
|
||||
const contactMailLinks = [...document.querySelectorAll('#contact a[href^="mailto:"]')];
|
||||
if (mailLinks.length !== 2 || startMailLinks.length !== 1 || contactMailLinks.length !== 1 || mailLinks.some((link) =>
|
||||
link.getAttribute('href') !== `mailto:${inputs.content.contact.email}` || link.textContent !== 'Email Luca'
|
||||
)) {
|
||||
throw new Error('Visible semantic mailto links must be exactly the hero and contact Email Luca actions.');
|
||||
throw new Error('Mailto links must be exactly one Email Luca action beneath each hero and contact section.');
|
||||
}
|
||||
|
||||
const moduleScript = document.querySelector(`script[type="module"][src="/${contract.entryModule}"]`);
|
||||
|
||||
@@ -13,7 +13,7 @@ defineProps<{ groups: readonly ToolkitGroup[] }>();
|
||||
<ul class="toolkit-items">
|
||||
<li v-for="technology in group.technologies" :key="technology.name" class="toolkit-item" data-testid="toolkit-technology">
|
||||
<BrandIcon v-if="brandIconAvailable(technology.name)" :label="technology.name" />
|
||||
<span>{{ technology.name }}</span>
|
||||
<span :class="{ 'toolkit-item__label--text-only': !brandIconAvailable(technology.name) }">{{ technology.name }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
@@ -21,12 +21,14 @@
|
||||
max-inline-size: 40ch;
|
||||
color: var(--text-muted);
|
||||
font-size: var(--step-0);
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.hero-journey__availability {
|
||||
max-inline-size: 52ch;
|
||||
color: var(--text-muted);
|
||||
font-size: var(--step--1);
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.hero-journey__actions {
|
||||
@@ -36,6 +38,10 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hero-journey__actions .hero-journey__action {
|
||||
margin-block-start: 0;
|
||||
}
|
||||
|
||||
.hero-journey__action,
|
||||
.project-link,
|
||||
.contact-link,
|
||||
@@ -584,6 +590,10 @@
|
||||
font-size: var(--step--1);
|
||||
}
|
||||
|
||||
.toolkit-item__label--text-only {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.brand-icon {
|
||||
inline-size: 1.05rem;
|
||||
block-size: 1.05rem;
|
||||
|
||||
@@ -79,17 +79,14 @@
|
||||
display: grid;
|
||||
min-block-size: calc(100svh - var(--site-navigation-block-size));
|
||||
align-items: start;
|
||||
padding: clamp(2rem, 8vh, 6rem) 4vw 16vh;
|
||||
padding: clamp(3.5rem, 12vh, 8rem) 4vw 16vh;
|
||||
scroll-margin-block-start: var(--site-navigation-block-size);
|
||||
}
|
||||
|
||||
.hero-journey__content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
min-inline-size: 0;
|
||||
inline-size: min(38rem, 50vw);
|
||||
gap: 1rem;
|
||||
inline-size: min(34rem, 39vw);
|
||||
gap: 0.75rem;
|
||||
padding: clamp(1.4rem, 2.2vw, 2.1rem);
|
||||
border: 1px solid var(--line);
|
||||
border-inline-start: 3px solid var(--cyan);
|
||||
|
||||
@@ -102,6 +102,20 @@ test('exposes the complete semantic portfolio from the generated document', asyn
|
||||
await expect(page.getByRole('link', { name: 'Explore work' })).toHaveAttribute('href', '#selected-builds');
|
||||
await expect(page.getByRole('link', { name: 'Email Luca' })).toHaveCount(2);
|
||||
await expect(page.locator('[data-featured-operation]')).toContainText('without data loss');
|
||||
expect(await page.locator('[data-testid="toolkit-technology"]', { hasText: 'DNS' }).locator('span').evaluate(
|
||||
(label) => getComputedStyle(label).gridColumn,
|
||||
)).toBe('1 / -1');
|
||||
expect(await page.locator('.hero-journey').evaluate((hero) => {
|
||||
const content = hero.querySelector('.hero-journey__content')!;
|
||||
const heroStyle = getComputedStyle(hero);
|
||||
const contentStyle = getComputedStyle(content);
|
||||
return {
|
||||
preservesPadding: Math.abs(Number.parseFloat(heroStyle.paddingTop) - innerHeight * 0.12) < 0.01,
|
||||
preservesWidth: Math.abs(Number.parseFloat(contentStyle.width) - Math.min(544, innerWidth * 0.39)) < 0.1,
|
||||
position: contentStyle.position,
|
||||
zIndex: contentStyle.zIndex,
|
||||
};
|
||||
})).toEqual({ preservesPadding: true, preservesWidth: true, position: 'static', zIndex: 'auto' });
|
||||
expect(
|
||||
await page.locator('h1, h2, h3').evaluateAll((headings) => {
|
||||
const levels = headings.map((heading) =>
|
||||
|
||||
@@ -131,6 +131,16 @@ describe('artifact verification', () => {
|
||||
name: 'a mailto link with an address that differs from public content',
|
||||
mutate: (html: string) => html.replace('href="mailto:luca.pislaru@gmail.com">Email Luca</a>', 'href="mailto:wrong@example.test">Email Luca</a>'),
|
||||
},
|
||||
{
|
||||
name: 'a hidden mailto link outside the app root',
|
||||
mutate: (html: string) => html.replace('</body>', '<a hidden href="mailto:luca.pislaru@gmail.com">Email Luca</a></body>'),
|
||||
},
|
||||
{
|
||||
name: 'two mailto links placed beneath contact instead of one per section',
|
||||
mutate: (html: string) => html
|
||||
.replace('href="mailto:luca.pislaru@gmail.com">Email Luca</a>', 'href="#selected-builds">Email Luca</a>')
|
||||
.replace('</footer>', '<a href="mailto:luca.pislaru@gmail.com">Email Luca</a></footer>'),
|
||||
},
|
||||
])('rejects $name', async ({ mutate }) => {
|
||||
const { directory, html, inputs, contract } = await writeValidArtifacts();
|
||||
await writeFile(join(directory, 'index.html'), mutate(html));
|
||||
|
||||
@@ -171,6 +171,7 @@ describe("semantic section components", () => {
|
||||
|
||||
expect(tile.text()).toBe("DNS");
|
||||
expect(tile.find("svg").exists()).toBe(false);
|
||||
expect(tile.get("span").classes()).toContain("toolkit-item__label--text-only");
|
||||
});
|
||||
|
||||
it("renders a visible mailto contact and safe verified social links", () => {
|
||||
|
||||
Reference in New Issue
Block a user