Implements website feature branch `410-public-docs-ia`. Target branch: `website-dev`. Validation: - `corepack pnpm --filter @tenantatlas/website build` - Playwright smoke coverage for public routes and docs interactions - Static claim scans for `apps/website/src`, `apps/website/public`, and `apps/website/dist` Follow-up integration path after merge: `website-dev` -> `dev`. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #412
947 lines
27 KiB
TypeScript
947 lines
27 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', '/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);
|
|
}
|
|
}
|
|
});
|
|
|
|
for (const { route, heading, topicHrefs, publicHrefs } of [
|
|
{
|
|
route: '/docs/',
|
|
heading: /Tenantial Dokumentation/i,
|
|
topicHrefs: [
|
|
'/docs/getting-started/',
|
|
'/docs/microsoft-365-provider/',
|
|
'/docs/permissions-data-access/',
|
|
'/docs/data-processing-trust/',
|
|
'/docs/policy-evidence/',
|
|
'/docs/drift-detection/',
|
|
'/docs/backups-versioning-recovery/',
|
|
'/docs/findings-exceptions-accepted-risk/',
|
|
'/docs/review-packs-decisions/',
|
|
'/docs/evaluation-pilot/',
|
|
'/docs/known-limitations/',
|
|
'/docs/faq/',
|
|
],
|
|
publicHrefs: ['/platform', '/trust', '/evaluierung', '/platform/review-packs'],
|
|
},
|
|
{
|
|
route: '/en/docs/',
|
|
heading: /Tenantial documentation/i,
|
|
topicHrefs: [
|
|
'/en/docs/getting-started/',
|
|
'/en/docs/microsoft-365-provider/',
|
|
'/en/docs/permissions-data-access/',
|
|
'/en/docs/data-processing-trust/',
|
|
'/en/docs/policy-evidence/',
|
|
'/en/docs/drift-detection/',
|
|
'/en/docs/backups-versioning-recovery/',
|
|
'/en/docs/findings-exceptions-accepted-risk/',
|
|
'/en/docs/review-packs-decisions/',
|
|
'/en/docs/evaluation-pilot/',
|
|
'/en/docs/known-limitations/',
|
|
'/en/docs/faq/',
|
|
],
|
|
publicHrefs: [
|
|
'/en/platform',
|
|
'/en/trust',
|
|
'/en/evaluation',
|
|
'/en/platform/review-packs',
|
|
],
|
|
},
|
|
] as const) {
|
|
test(`${route} exposes the full docs hub and real public handoffs`, async ({
|
|
page,
|
|
}) => {
|
|
await page.goto(route);
|
|
|
|
await expect(
|
|
page.locator('main').getByRole('heading', { level: 1, name: heading })
|
|
).toBeVisible();
|
|
|
|
for (const href of topicHrefs) {
|
|
await expect(page.locator(`main a[href="${href}"]`).first()).toBeVisible();
|
|
}
|
|
|
|
for (const href of publicHrefs) {
|
|
await expect(page.locator(`main a[href="${href}"]`).first()).toBeVisible();
|
|
}
|
|
|
|
await expectNoHorizontalOverflow(page);
|
|
});
|
|
}
|
|
|
|
for (const { route, heading, terms, relatedHref } of [
|
|
{
|
|
route: '/docs/getting-started/',
|
|
heading: /Erste Schritte/i,
|
|
terms: [/Demo|Pilot/i, /Governance/i, /Credentials|Secrets/i],
|
|
relatedHref: '/docs/evaluation-pilot/',
|
|
},
|
|
{
|
|
route: '/en/docs/getting-started/',
|
|
heading: /Getting Started/i,
|
|
terms: [/demo|pilot/i, /governance/i, /credentials|secrets/i],
|
|
relatedHref: '/en/docs/evaluation-pilot/',
|
|
},
|
|
] as const) {
|
|
test(`${route} makes the onboarding path readable`, async ({ page }) => {
|
|
await page.goto(route);
|
|
|
|
await expect(
|
|
page.locator('main').getByRole('heading', { level: 1, name: heading })
|
|
).toBeVisible();
|
|
|
|
for (const term of terms) {
|
|
await expect(page.locator('main')).toContainText(term);
|
|
}
|
|
|
|
await expect(page.locator(`main a[href="${relatedHref}"]`).first()).toBeVisible();
|
|
await expectNoHorizontalOverflow(page);
|
|
});
|
|
}
|
|
|
|
for (const { route, heading, terms, relatedHref } of [
|
|
{
|
|
route: '/docs/permissions-data-access/',
|
|
heading: /Berechtigungen|Datenzugriff/i,
|
|
terms: [/Les[t]?ender Zugriff|Least Privilege/i, /Rollen|Freigabe/i],
|
|
relatedHref: '/docs/data-processing-trust/',
|
|
},
|
|
{
|
|
route: '/docs/data-processing-trust/',
|
|
heading: /Datenverarbeitung|Trust/i,
|
|
terms: [/DPA|TOM|Subprozessoren/i, /Statussprache|Handoffs/i],
|
|
relatedHref: '/trust',
|
|
},
|
|
{
|
|
route: '/docs/review-packs-decisions/',
|
|
heading: /Review Packs|Entscheidungen/i,
|
|
terms: [/Executive Summary|Decision Summary/i, /Evidence|Accepted Risks/i],
|
|
relatedHref: '/platform/review-packs',
|
|
},
|
|
{
|
|
route: '/docs/evaluation-pilot/',
|
|
heading: /Evaluierung|Pilot/i,
|
|
terms: [/Pilot/i, /Trust|Berechtigung/i],
|
|
relatedHref: '/evaluierung',
|
|
},
|
|
{
|
|
route: '/docs/known-limitations/',
|
|
heading: /Bekannte Grenzen|Limitations/i,
|
|
terms: [/Microsoft 365/i, /autonome Korrektur|Compliance/i],
|
|
relatedHref: '/trust',
|
|
},
|
|
{
|
|
route: '/docs/faq/',
|
|
heading: /FAQ/i,
|
|
terms: [/Microsoft 365/i, /Trust|Pilot/i],
|
|
relatedHref: '/contact',
|
|
},
|
|
{
|
|
route: '/en/docs/permissions-data-access/',
|
|
heading: /Permissions|Data Access/i,
|
|
terms: [/read-oriented|least privilege/i, /roles|approval/i],
|
|
relatedHref: '/en/docs/data-processing-trust/',
|
|
},
|
|
{
|
|
route: '/en/docs/data-processing-trust/',
|
|
heading: /Data Processing|Trust/i,
|
|
terms: [/DPA|TOM|subprocessors/i, /status|handoff/i],
|
|
relatedHref: '/en/trust',
|
|
},
|
|
{
|
|
route: '/en/docs/review-packs-decisions/',
|
|
heading: /Review Packs|Decisions/i,
|
|
terms: [/executive summary|decision summary/i, /evidence|accepted risks/i],
|
|
relatedHref: '/en/platform/review-packs',
|
|
},
|
|
{
|
|
route: '/en/docs/evaluation-pilot/',
|
|
heading: /Evaluation|Pilot/i,
|
|
terms: [/pilot/i, /trust|permission/i],
|
|
relatedHref: '/en/evaluation',
|
|
},
|
|
{
|
|
route: '/en/docs/known-limitations/',
|
|
heading: /Known Limitations/i,
|
|
terms: [/Microsoft 365/i, /autonomous correction|compliance/i],
|
|
relatedHref: '/en/trust',
|
|
},
|
|
{
|
|
route: '/en/docs/faq/',
|
|
heading: /FAQ/i,
|
|
terms: [/Microsoft 365/i, /trust|pilot/i],
|
|
relatedHref: '/en/contact',
|
|
},
|
|
] as const) {
|
|
test(`${route} answers reviewer-facing questions conservatively`, async ({
|
|
page,
|
|
}) => {
|
|
await page.goto(route);
|
|
|
|
await expect(
|
|
page.locator('main').getByRole('heading', { level: 1, name: heading })
|
|
).toBeVisible();
|
|
|
|
for (const term of terms) {
|
|
await expect(page.locator('main')).toContainText(term);
|
|
}
|
|
|
|
await expect(page.locator(`main a[href="${relatedHref}"]`).first()).toBeVisible();
|
|
await expectNoHorizontalOverflow(page);
|
|
});
|
|
}
|
|
|
|
for (const { route, heading, terms, relatedHref } of [
|
|
{
|
|
route: '/docs/microsoft-365-provider/',
|
|
heading: /Microsoft 365 Provider/i,
|
|
terms: [/Microsoft 365/i, /weitere Policy-Domänen|Zukunft/i],
|
|
relatedHref: '/docs/evaluation-pilot/',
|
|
},
|
|
{
|
|
route: '/docs/policy-evidence/',
|
|
heading: /Policy Evidence|Evidence/i,
|
|
terms: [/Evidence/i, /Findings|Review/i],
|
|
relatedHref: '/platform/review-packs',
|
|
},
|
|
{
|
|
route: '/docs/drift-detection/',
|
|
heading: /Drift Detection|Drift/i,
|
|
terms: [/bekannten Zuständen|Veränderung/i, /Review|Priorisierung/i],
|
|
relatedHref: '/platform',
|
|
},
|
|
{
|
|
route: '/docs/backups-versioning-recovery/',
|
|
heading: /Backups|Versionierung|Recovery/i,
|
|
terms: [/Versionierung|bekannte Zustände/i, /Recovery/i],
|
|
relatedHref: '/docs/evaluation-pilot/',
|
|
},
|
|
{
|
|
route: '/docs/findings-exceptions-accepted-risk/',
|
|
heading: /Findings|Ausnahmen|Accepted Risk/i,
|
|
terms: [/Accepted Risk|Ausnahmen/i, /Evidence|Management|Audit/i],
|
|
relatedHref: '/platform/review-packs',
|
|
},
|
|
{
|
|
route: '/en/docs/microsoft-365-provider/',
|
|
heading: /Microsoft 365 Provider/i,
|
|
terms: [/Microsoft 365/i, /additional policy domains|future/i],
|
|
relatedHref: '/en/docs/evaluation-pilot/',
|
|
},
|
|
{
|
|
route: '/en/docs/policy-evidence/',
|
|
heading: /Policy Evidence|Evidence/i,
|
|
terms: [/evidence/i, /findings|review/i],
|
|
relatedHref: '/en/platform/review-packs',
|
|
},
|
|
{
|
|
route: '/en/docs/drift-detection/',
|
|
heading: /Drift Detection|Drift/i,
|
|
terms: [/known states|readable change/i, /review|prioritization/i],
|
|
relatedHref: '/en/platform',
|
|
},
|
|
{
|
|
route: '/en/docs/backups-versioning-recovery/',
|
|
heading: /Backups|Versioning|Recovery/i,
|
|
terms: [/versioning|known states/i, /recovery/i],
|
|
relatedHref: '/en/docs/evaluation-pilot/',
|
|
},
|
|
{
|
|
route: '/en/docs/findings-exceptions-accepted-risk/',
|
|
heading: /Findings|Exceptions|Accepted Risk/i,
|
|
terms: [/accepted risk|exceptions/i, /evidence|management|audit/i],
|
|
relatedHref: '/en/platform/review-packs',
|
|
},
|
|
] as const) {
|
|
test(`${route} explains technical governance concepts in public language`, async ({
|
|
page,
|
|
}) => {
|
|
await page.goto(route);
|
|
|
|
await expect(
|
|
page.locator('main').getByRole('heading', { level: 1, name: heading })
|
|
).toBeVisible();
|
|
|
|
for (const term of terms) {
|
|
await expect(page.locator('main')).toContainText(term);
|
|
}
|
|
|
|
await expect(page.locator(`main a[href="${relatedHref}"]`).first()).toBeVisible();
|
|
await expectNoHorizontalOverflow(page);
|
|
});
|
|
}
|
|
|
|
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('/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);
|
|
});
|
|
}
|
|
|
|
for (const { route, heading, sectionHeadings, trustHref, contactHref } of [
|
|
{
|
|
route: '/platform/review-packs',
|
|
heading: /Review Packs.*Screenshot-Governance/i,
|
|
sectionHeadings: [
|
|
/Technische Wahrheit allein reicht nicht/i,
|
|
/Von Policy-Zustand zur Review-Entscheidung/i,
|
|
/Was in einem Review Pack zusammenkommt/i,
|
|
/Evidence erklärt mehr als Screenshots/i,
|
|
/Decision Summaries machen Entscheidungen lesbar/i,
|
|
/Customer-safe Review statt interner Rohdetails/i,
|
|
/Warum dieselbe Story für MSPs und Enterprise IT funktioniert/i,
|
|
/Roh-Export versus Review-Story/i,
|
|
],
|
|
trustHref: '/trust',
|
|
contactHref: '/contact',
|
|
},
|
|
{
|
|
route: '/en/platform/review-packs',
|
|
heading: /Review packs.*screenshot governance/i,
|
|
sectionHeadings: [
|
|
/Technical truth alone is not enough/i,
|
|
/From policy state to a review decision/i,
|
|
/What comes together in a review pack/i,
|
|
/Evidence should explain more than screenshots/i,
|
|
/Decision summaries make decisions readable/i,
|
|
/Customer-safe review instead of internal raw detail/i,
|
|
/Why the same story works for MSPs and enterprise IT/i,
|
|
/Raw export versus review story/i,
|
|
],
|
|
trustHref: '/en/trust',
|
|
contactHref: '/en/contact',
|
|
},
|
|
] as const) {
|
|
test(`${route} communicates the review-pack decision story quickly`, async ({
|
|
page,
|
|
}) => {
|
|
await page.goto(route);
|
|
|
|
await expect(page.getByRole('heading', { name: heading })).toBeVisible();
|
|
|
|
for (const sectionHeading of sectionHeadings) {
|
|
await expect(
|
|
page.getByRole('heading', { name: sectionHeading })
|
|
).toBeVisible();
|
|
}
|
|
|
|
await expect(page.locator('main')).toContainText(
|
|
/Accepted Risks|accepted risks/
|
|
);
|
|
await expect(page.locator('main')).toContainText(
|
|
/Decision Summary|decision summary/i
|
|
);
|
|
await expect(page.locator('main')).toContainText(/Roh-Export|Raw export/);
|
|
await expect(page.locator('main')).toContainText(/MSP/i);
|
|
await expect(page.locator('main')).toContainText(/Enterprise IT/i);
|
|
await expect(
|
|
page.locator(`main a[href="${trustHref}"]`).first()
|
|
).toBeVisible();
|
|
await expect(
|
|
page.locator(`main a[href="${contactHref}"]`).first()
|
|
).toBeVisible();
|
|
await expectNoHorizontalOverflow(page);
|
|
});
|
|
}
|
|
|
|
for (const { route, linkName, href, targetHeading } of [
|
|
{
|
|
route: '/',
|
|
linkName: 'Review-Pack-Story ansehen',
|
|
href: '/platform/review-packs',
|
|
targetHeading: /Review Packs.*Screenshot-Governance/i,
|
|
},
|
|
{
|
|
route: '/platform',
|
|
linkName: 'Review Packs verstehen',
|
|
href: '/platform/review-packs',
|
|
targetHeading: /Review Packs.*Screenshot-Governance/i,
|
|
},
|
|
{
|
|
route: '/use-cases/msp',
|
|
linkName: 'Review-Pack-Story für Kunden zeigen',
|
|
href: '/platform/review-packs',
|
|
targetHeading: /Review Packs.*Screenshot-Governance/i,
|
|
},
|
|
{
|
|
route: '/use-cases/mittelstand',
|
|
linkName: 'Review-Pack-Story für Reviews',
|
|
href: '/platform/review-packs',
|
|
targetHeading: /Review Packs.*Screenshot-Governance/i,
|
|
},
|
|
{
|
|
route: '/en/',
|
|
linkName: 'Explore review-pack story',
|
|
href: '/en/platform/review-packs',
|
|
targetHeading: /Review packs.*screenshot governance/i,
|
|
},
|
|
{
|
|
route: '/en/platform',
|
|
linkName: 'See review-pack story',
|
|
href: '/en/platform/review-packs',
|
|
targetHeading: /Review packs.*screenshot governance/i,
|
|
},
|
|
{
|
|
route: '/en/use-cases/msp',
|
|
linkName: 'Show the review-pack story',
|
|
href: '/en/platform/review-packs',
|
|
targetHeading: /Review packs.*screenshot governance/i,
|
|
},
|
|
{
|
|
route: '/en/use-cases/mittelstand',
|
|
linkName: 'Review-pack story for reviews',
|
|
href: '/en/platform/review-packs',
|
|
targetHeading: /Review packs.*screenshot governance/i,
|
|
},
|
|
] as const) {
|
|
test(`${route} clicks through to the review-pack page`, async ({ page }) => {
|
|
await page.goto(route);
|
|
|
|
await page
|
|
.getByRole('link', { name: linkName, exact: true })
|
|
.first()
|
|
.click();
|
|
|
|
await expect(page).toHaveURL(new RegExp(`${href}/?$`));
|
|
await expect(
|
|
page.getByRole('heading', { name: targetHeading })
|
|
).toBeVisible();
|
|
await expectNoHorizontalOverflow(page);
|
|
});
|
|
}
|
|
|
|
for (const { route, linkName, href, targetHeading } of [
|
|
{
|
|
route: '/',
|
|
linkName: 'Evaluierung & Pilot ansehen',
|
|
href: '/evaluierung',
|
|
targetHeading:
|
|
/Tenantial sicher evaluieren und schneller zum Pilot kommen/i,
|
|
},
|
|
{
|
|
route: '/platform',
|
|
linkName: 'Evaluierung & Rollout ansehen',
|
|
href: '/evaluierung',
|
|
targetHeading:
|
|
/Tenantial sicher evaluieren und schneller zum Pilot kommen/i,
|
|
},
|
|
{
|
|
route: '/platform/review-packs',
|
|
linkName: 'Evaluierungspfad ansehen',
|
|
href: '/evaluierung',
|
|
targetHeading:
|
|
/Tenantial sicher evaluieren und schneller zum Pilot kommen/i,
|
|
},
|
|
{
|
|
route: '/trust',
|
|
linkName: 'Evaluierung & Procurement ansehen',
|
|
href: '/evaluierung',
|
|
targetHeading:
|
|
/Tenantial sicher evaluieren und schneller zum Pilot kommen/i,
|
|
},
|
|
{
|
|
route: '/use-cases/msp',
|
|
linkName: 'Evaluierung & Pilot planen',
|
|
href: '/evaluierung',
|
|
targetHeading:
|
|
/Tenantial sicher evaluieren und schneller zum Pilot kommen/i,
|
|
},
|
|
{
|
|
route: '/en/',
|
|
linkName: 'See evaluation and pilot',
|
|
href: '/en/evaluation',
|
|
targetHeading: /Evaluate Tenantial and get to a focused pilot faster/i,
|
|
},
|
|
{
|
|
route: '/en/platform',
|
|
linkName: 'See evaluation and rollout',
|
|
href: '/en/evaluation',
|
|
targetHeading: /Evaluate Tenantial and get to a focused pilot faster/i,
|
|
},
|
|
{
|
|
route: '/en/platform/review-packs',
|
|
linkName: 'See the evaluation path',
|
|
href: '/en/evaluation',
|
|
targetHeading: /Evaluate Tenantial and get to a focused pilot faster/i,
|
|
},
|
|
{
|
|
route: '/en/trust',
|
|
linkName: 'See evaluation and procurement',
|
|
href: '/en/evaluation',
|
|
targetHeading: /Evaluate Tenantial and get to a focused pilot faster/i,
|
|
},
|
|
{
|
|
route: '/en/use-cases/msp',
|
|
linkName: 'Plan evaluation and pilot',
|
|
href: '/en/evaluation',
|
|
targetHeading: /Evaluate Tenantial and get to a focused pilot faster/i,
|
|
},
|
|
] as const) {
|
|
test(`${route} clicks through to the evaluation page`, async ({ page }) => {
|
|
await page.goto(route);
|
|
|
|
await page
|
|
.getByRole('link', { name: linkName, exact: true })
|
|
.first()
|
|
.click();
|
|
|
|
await expect(page).toHaveURL(new RegExp(`${href}/?$`));
|
|
await expect(
|
|
page.getByRole('heading', { name: targetHeading })
|
|
).toBeVisible();
|
|
await expectNoHorizontalOverflow(page);
|
|
});
|
|
}
|
|
|
|
for (const { route, heading, contactHref } of [
|
|
{
|
|
route: '/platform/review-packs',
|
|
heading: /Review Packs.*Screenshot-Governance/i,
|
|
contactHref: '/contact',
|
|
},
|
|
{
|
|
route: '/en/platform/review-packs',
|
|
heading: /Review packs.*screenshot governance/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);
|
|
});
|
|
}
|
|
|
|
for (const { route, heading, contactHref } of [
|
|
{
|
|
route: '/evaluierung',
|
|
heading: /Tenantial sicher evaluieren und schneller zum Pilot kommen/i,
|
|
contactHref: '/contact',
|
|
},
|
|
{
|
|
route: '/en/evaluation',
|
|
heading: /Evaluate Tenantial and get to a focused pilot faster/i,
|
|
contactHref: '/en/contact',
|
|
},
|
|
] as const) {
|
|
test(`${route} keeps the evaluation page actionable and readable`, 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 page.locator(`main a[href="${contactHref}"]`).first().click();
|
|
await expect(page).toHaveURL(new RegExp(`${contactHref}/?$`));
|
|
await expectNoHorizontalOverflow(page);
|
|
});
|
|
}
|
|
|
|
test('reduced motion keeps preview pages understandable', async ({ page }) => {
|
|
await page.emulateMedia({ reducedMotion: 'reduce' });
|
|
|
|
for (const route of [
|
|
'/',
|
|
'/platform',
|
|
'/platform/review-packs',
|
|
'/evaluierung',
|
|
'/pricing',
|
|
'/use-cases/msp',
|
|
'/use-cases/mittelstand',
|
|
'/en/platform/review-packs',
|
|
'/en/evaluation',
|
|
] 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',
|
|
'/platform/review-packs',
|
|
'/evaluierung',
|
|
'/contact',
|
|
'/trust',
|
|
'/use-cases/msp',
|
|
'/use-cases/mittelstand',
|
|
'/en/platform/review-packs',
|
|
'/en/evaluation',
|
|
'/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();
|
|
});
|