TenantAtlas/apps/website/tests/smoke/interaction.spec.ts
ahmido 94cff6ca03 feat: add MSP and internal IT use-case pages (#402)
Implements website feature branch `407-msp-mittelstand-use-case-pages` into `website-dev`.

Summary:
- add German and English MSP and internal IT use-case landing pages
- expose localized buyer-path teasers from the homepage and platform page
- extend smoke coverage for the new routes, navigation links, metadata, and conservative claim checks
- include the aligned Spec Kit artifacts under `specs/407-msp-mittelstand-use-case-pages`

Validation:
- `cd apps/website && corepack pnpm build`
- `cd apps/website && corepack pnpm test -- tests/smoke/public-routes.spec.ts tests/smoke/interaction.spec.ts`

Follow-up integration path after merge:

`website-dev` -> `dev`.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #402
2026-05-27 22:02:42 +00:00

403 lines
11 KiB
TypeScript

import { expect, test } from '@playwright/test';
import {
expectCoreCapabilitiesVisible,
expectNoHorizontalOverflow,
expectTrustHandoffsAreReal,
expectTrustSurfaceVisible,
} from './smoke-helpers';
test('mobile navigation opens with vendored foundation behavior', async ({
page,
isMobile,
}) => {
test.skip(!isMobile, 'mobile navigation is covered by the mobile project');
await page.goto('/');
await page.getByLabel('Toggle navigation').click();
const mobilePanel = page.locator('#navbar-collapse-with-animation');
await expect(
mobilePanel.getByRole('link', { name: 'Plattform', exact: true })
).toBeVisible();
await expect(
mobilePanel.getByRole('link', { name: 'MSPs', exact: true })
).toHaveAttribute('href', '/use-cases/msp');
await expect(
mobilePanel.getByRole('link', { name: 'Interne IT', exact: true })
).toHaveAttribute('href', '/use-cases/mittelstand');
await expect(
mobilePanel.getByRole('link', { name: 'Vertrauen', exact: true })
).toHaveAttribute('href', '/trust');
await expect(
mobilePanel.getByRole('link', { name: 'Docs', exact: true })
).toHaveAttribute('href', '/welcome-to-docs/');
await expect(
mobilePanel.getByRole('link', { name: 'Walkthrough anfragen' })
).toBeVisible();
await expectNoHorizontalOverflow(page);
});
test('mobile navigation exposes localized trust links', async ({
page,
isMobile,
}) => {
test.skip(!isMobile, 'mobile navigation is covered by the mobile project');
for (const { route, label, href } of [
{ route: '/', label: 'Vertrauen', href: '/trust' },
{ route: '/en/', label: 'Trust', href: '/en/trust' },
] as const) {
await page.goto(route);
await page.getByLabel('Toggle navigation').click();
const mobilePanel = page.locator('#navbar-collapse-with-animation');
await expect(
mobilePanel.getByRole('link', { name: label, exact: true })
).toHaveAttribute('href', href);
}
});
test('mobile navigation exposes localized use-case links', async ({
page,
isMobile,
}) => {
test.skip(!isMobile, 'mobile navigation is covered by the mobile project');
for (const { route, links } of [
{
route: '/',
links: [
{ label: 'MSPs', href: '/use-cases/msp' },
{ label: 'Interne IT', href: '/use-cases/mittelstand' },
],
},
{
route: '/en/',
links: [
{ label: 'MSPs', href: '/en/use-cases/msp' },
{ label: 'Internal IT', href: '/en/use-cases/mittelstand' },
],
},
] as const) {
await page.goto(route);
await page.getByLabel('Toggle navigation').click();
const mobilePanel = page.locator('#navbar-collapse-with-animation');
for (const link of links) {
await expect(
mobilePanel.getByRole('link', { name: link.label, exact: true })
).toHaveAttribute('href', link.href);
}
}
});
test('theme toggle keeps page content visible', async ({ page }) => {
await page.goto('/');
await page.locator('button[aria-label="Dark Theme Toggle"]:visible').click();
await expect(
page.getByRole('heading', {
name: /Microsoft 365 Policies unter Kontrolle bringen/,
})
).toBeVisible();
await page.locator('button[aria-label="Light Theme Toggle"]:visible').click();
await expect(
page.getByRole('heading', {
name: /Microsoft 365 Policies unter Kontrolle bringen/,
})
).toBeVisible();
});
test('homepage mobile first viewport remains readable', async ({
page,
isMobile,
}) => {
test.skip(!isMobile, 'mobile viewport is covered by the mobile project');
await page.goto('/');
const heading = page.getByRole('heading', {
name: /Microsoft 365 Policies unter Kontrolle bringen/i,
});
await expect(heading).toBeVisible();
const box = await heading.boundingBox();
expect(box?.y ?? Number.POSITIVE_INFINITY).toBeLessThan(360);
await expectCoreCapabilitiesVisible(page);
await expectNoHorizontalOverflow(page);
});
test('homepage exposes localized buyer-path teasers', async ({ page }) => {
for (const { route, teaserLinks } of [
{
route: '/',
teaserLinks: [
{ name: 'MSP-Use-Case ansehen', href: '/use-cases/msp' },
{ name: 'Interne IT ansehen', href: '/use-cases/mittelstand' },
],
},
{
route: '/en/',
teaserLinks: [
{ name: 'Explore MSP use case', href: '/en/use-cases/msp' },
{
name: 'Explore internal IT use case',
href: '/en/use-cases/mittelstand',
},
],
},
] as const) {
await page.goto(route);
const main = page.locator('main');
for (const link of teaserLinks) {
await expect(
main.getByRole('link', { name: link.name, exact: true })
).toHaveAttribute('href', link.href);
}
}
});
test('desktop keyboard reaches navigation, CTAs, footer, and contact controls', async ({
page,
isMobile,
}) => {
test.skip(
isMobile,
'desktop keyboard order is covered by the desktop project'
);
await page.goto('/contact');
const reached = new Set<string>();
for (let index = 0; index < 80; index += 1) {
await page.keyboard.press('Tab');
const active = await page.evaluate(() => {
const element = document.activeElement as HTMLElement | null;
return {
id: element?.id || '',
href: element?.getAttribute('href') || '',
text: element?.textContent?.replace(/\s+/g, ' ').trim() || '',
aria: element?.getAttribute('aria-label') || '',
tag: element?.tagName || '',
};
});
for (const value of [
active.id,
active.href,
active.text,
active.aria,
active.tag,
]) {
if (value) {
reached.add(value);
}
}
}
expect([...reached].join('\n')).toContain('/platform');
expect([...reached].join('\n')).toContain('/use-cases/msp');
expect([...reached].join('\n')).toContain('/use-cases/mittelstand');
expect([...reached].join('\n')).toContain('/trust');
expect([...reached].join('\n')).toContain('/welcome-to-docs/');
expect([...reached].join('\n')).toContain('Kontakt');
expect([...reached].join('\n')).toContain('/privacy');
expect([...reached].join('\n')).toContain('hs-firstname-contacts');
expect([...reached].join('\n')).toContain('E-Mail-Entwurf vorbereiten');
});
for (const { route, heading, terms, contactHref } of [
{
route: '/use-cases/msp',
heading: /Microsoft 365 Governance.*MSPs/i,
terms: [/Review Packs/i, /Accepted Risks/i, /PSA/i],
contactHref: '/contact',
},
{
route: '/en/use-cases/msp',
heading: /Microsoft 365 governance.*MSPs/i,
terms: [/review packs/i, /accepted risks/i, /PSA/i],
contactHref: '/en/contact',
},
] as const) {
test(`${route} communicates the MSP buyer path quickly`, async ({ page }) => {
await page.goto(route);
await expect(page.getByRole('heading', { name: heading })).toBeVisible();
for (const term of terms) {
await expect(page.locator('main')).toContainText(term);
}
await expect(
page.locator(`main a[href="${contactHref}"]`).first()
).toBeVisible();
await expectNoHorizontalOverflow(page);
});
}
for (const { route, heading, terms, contactHref } of [
{
route: '/use-cases/mittelstand',
heading: /Kontrolle und Evidence/i,
terms: [/IT Operations/i, /IT-Leitung/i, /Governance-Layer/i],
contactHref: '/contact',
},
{
route: '/en/use-cases/mittelstand',
heading: /Control and evidence/i,
terms: [/IT Operations/i, /IT Leadership/i, /Governance layer/i],
contactHref: '/en/contact',
},
] as const) {
test(`${route} communicates the internal IT buyer path quickly`, async ({
page,
}) => {
await page.goto(route);
await expect(page.getByRole('heading', { name: heading })).toBeVisible();
for (const term of terms) {
await expect(page.locator('main')).toContainText(term);
}
await expect(
page.locator(`main a[href="${contactHref}"]`).first()
).toBeVisible();
await expectNoHorizontalOverflow(page);
});
}
for (const { route, locale } of [
{ route: '/trust', locale: 'de' },
{ route: '/en/trust', locale: 'en' },
] as const) {
test(`${route} remains readable across configured viewports`, async ({
page,
}) => {
await page.goto(route);
await expectTrustSurfaceVisible(page, locale);
await expectTrustHandoffsAreReal(page, locale);
await expectNoHorizontalOverflow(page);
});
}
for (const { route, heading, contactHref } of [
{
route: '/use-cases/msp',
heading: /Microsoft 365 Governance.*MSPs/i,
contactHref: '/contact',
},
{
route: '/use-cases/mittelstand',
heading: /Kontrolle und Evidence/i,
contactHref: '/contact',
},
{
route: '/en/use-cases/msp',
heading: /Microsoft 365 governance.*MSPs/i,
contactHref: '/en/contact',
},
{
route: '/en/use-cases/mittelstand',
heading: /Control and evidence/i,
contactHref: '/en/contact',
},
] as const) {
test(`${route} remains readable across configured viewports`, async ({
page,
}) => {
await page.goto(route);
await expect(page.getByRole('heading', { name: heading })).toBeVisible();
await expect(
page.locator(`main a[href="${contactHref}"]`).first()
).toBeVisible();
await expectNoHorizontalOverflow(page);
});
}
test('reduced motion keeps preview pages understandable', async ({ page }) => {
await page.emulateMedia({ reducedMotion: 'reduce' });
for (const route of [
'/',
'/platform',
'/pricing',
'/use-cases/msp',
'/use-cases/mittelstand',
] as const) {
await page.goto(route);
await expect(page.locator('h1').first()).toBeVisible();
await expect(page.locator('main')).toContainText('Tenantial');
await expectNoHorizontalOverflow(page);
}
});
test.describe('without JavaScript', () => {
test.use({ javaScriptEnabled: false });
test('primary content and links remain usable', async ({ page }) => {
for (const route of [
'/',
'/platform',
'/contact',
'/trust',
'/use-cases/msp',
'/use-cases/mittelstand',
'/en/trust',
'/en/use-cases/msp',
'/en/use-cases/mittelstand',
] as const) {
await page.goto(route);
await expect(page.locator('h1').first()).toBeVisible();
await expect(
page
.getByRole('link', { name: /Kontakt|Contact|Walkthrough|Trust/i })
.first()
).toBeVisible();
await expectNoHorizontalOverflow(page);
}
});
});
test('language picker switches between German default and English routes', async ({
page,
isMobile,
}) => {
test.skip(isMobile, 'desktop language picker is covered by this interaction');
await page.goto('/platform');
await page.getByLabel('Sprache wechseln').click();
await page.getByRole('link', { name: 'English', exact: true }).click();
await expect(page).toHaveURL(/\/en\/platform\/?$/);
await expect(
page.getByRole('heading', {
name: /Policy governance model for Microsoft 365/i,
})
).toBeVisible();
await page.getByLabel('Sprache wechseln').click();
await page.getByRole('link', { name: 'Deutsch', exact: true }).click();
await expect(page).toHaveURL(/\/platform\/?$/);
await expect(
page.getByRole('heading', {
name: /Policy-Governance-Modell für Microsoft 365/i,
})
).toBeVisible();
});