## Summary - rename the website product page to `/platform` - add a redirect from `/product` to `/platform` and update navigation/content links - refresh footer/layout metadata and align smoke tests with the new route - add spec artifacts for 401-tenantial-platform-page ## Testing - not run in this step Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #391
392 lines
17 KiB
TypeScript
392 lines
17 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
|
|
import {
|
|
expectCompatibilityRedirect,
|
|
expectCtaHierarchy,
|
|
expectDisclosureLayer,
|
|
expectFooterLinks,
|
|
expectHomepageHeroCtaPair,
|
|
expectHomepageHeroOrder,
|
|
expectHomepageHeroRouteTargets,
|
|
expectHomepageHeroStructure,
|
|
expectHomepageHeroVisibleOnMobile,
|
|
expectHomepageSectionOrder,
|
|
expectMobileReadability,
|
|
expectNavigationVsCtaDifferentiation,
|
|
expectNoBodyHorizontalOverflow,
|
|
expectOnwardRouteReachable,
|
|
expectPageFamily,
|
|
expectPrimaryNavigation,
|
|
expectShell,
|
|
visitPage,
|
|
} from './smoke-helpers';
|
|
|
|
const forbiddenHomepageTerms = [
|
|
'AstroDeck',
|
|
'Open Source',
|
|
'MIT Licensed',
|
|
'TenantCTRL',
|
|
'TenantPilot',
|
|
'TenantAtlas',
|
|
] as const;
|
|
|
|
const forbiddenPlatformTerms: Array<[string, RegExp]> = [
|
|
['AstroDeck', /AstroDeck/i],
|
|
['TemplateDeck', /TemplateDeck/i],
|
|
['Open Source', /Open Source/i],
|
|
['MIT', /\bMIT\b/i],
|
|
['TenantAtlas', /TenantAtlas/i],
|
|
['TenantPilot', /TenantPilot/i],
|
|
['TenantCTRL', /TenantCTRL/i],
|
|
] as const;
|
|
|
|
const unsupportedPlatformClaims = [
|
|
/SOC 2/i,
|
|
/\bISO(?:\s?\d+)?\b/i,
|
|
/HIPAA/i,
|
|
/blanket GDPR/i,
|
|
/end-to-end encryption/i,
|
|
/guaranteed recovery/i,
|
|
/guaranteed compliance/i,
|
|
/zero drift/i,
|
|
/real-time everywhere/i,
|
|
/trusted by/i,
|
|
/customer logos/i,
|
|
/Microsoft certification/i,
|
|
/Microsoft partnership/i,
|
|
] as const;
|
|
|
|
async function expectForbiddenHomepageResidueAbsent(page: import('@playwright/test').Page): Promise<void> {
|
|
const body = page.locator('body');
|
|
const metadata = await page.locator('head').evaluate((head) => {
|
|
const title = document.title;
|
|
const meta = Array.from(head.querySelectorAll('meta'))
|
|
.map((element) => element.getAttribute('content') ?? '')
|
|
.join(' ');
|
|
const canonical = head.querySelector('link[rel="canonical"]')?.getAttribute('href') ?? '';
|
|
|
|
return `${title} ${meta} ${canonical}`;
|
|
});
|
|
|
|
for (const term of forbiddenHomepageTerms) {
|
|
await expect(body).not.toContainText(new RegExp(term, 'i'));
|
|
expect(metadata, `Homepage metadata should not contain ${term}`).not.toMatch(new RegExp(term, 'i'));
|
|
}
|
|
}
|
|
|
|
async function expectPlatformResidueAbsent(page: import('@playwright/test').Page): Promise<void> {
|
|
const body = page.locator('body');
|
|
const metadata = await page.locator('head').evaluate((head) => {
|
|
const title = document.title;
|
|
const meta = Array.from(head.querySelectorAll('meta'))
|
|
.map((element) => element.getAttribute('content') ?? '')
|
|
.join(' ');
|
|
const canonical = head.querySelector('link[rel="canonical"]')?.getAttribute('href') ?? '';
|
|
|
|
return `${title} ${meta} ${canonical}`;
|
|
});
|
|
|
|
for (const [term, pattern] of forbiddenPlatformTerms) {
|
|
await expect(body).not.toContainText(pattern);
|
|
expect(metadata, `Platform metadata should not contain ${term}`).not.toMatch(pattern);
|
|
}
|
|
}
|
|
|
|
test('home first read positions Tenantial with one clear action hierarchy', async ({ page }) => {
|
|
await visitPage(page, '/');
|
|
await expectShell(page, 'Evidence-first governance for Microsoft tenants.');
|
|
await expectPageFamily(page, 'landing');
|
|
await expectDisclosureLayer(page, '1');
|
|
await expectDisclosureLayer(page, '2');
|
|
await expectPrimaryNavigation(page);
|
|
await expectNavigationVsCtaDifferentiation(page);
|
|
await expectFooterLinks(page);
|
|
await expectCtaHierarchy(page, 'Book a demo', 'Explore the platform');
|
|
await expect(page.getByRole('main').getByRole('link', { name: 'Book a demo' }).first()).toBeVisible();
|
|
await expect(page.getByRole('main').getByRole('link', { name: 'Explore the platform' }).first()).toBeVisible();
|
|
await expect(page.getByRole('heading', { level: 1 })).toHaveCount(1);
|
|
await expectForbiddenHomepageResidueAbsent(page);
|
|
|
|
const skipLink = page.getByRole('link', { name: 'Skip to content' });
|
|
|
|
await page.keyboard.press('Tab');
|
|
await expect(skipLink).toBeFocused();
|
|
});
|
|
|
|
test('homepage hero explains Tenantial, Microsoft tenant context, and platform CTA route', async ({ page }) => {
|
|
await visitPage(page, '/');
|
|
await expectHomepageHeroStructure(page);
|
|
await expect(page.locator('[data-homepage-hero="true"] [data-hero-eyebrow]')).toContainText(
|
|
/governance that earns trust/i,
|
|
);
|
|
await expect(
|
|
page.locator('[data-homepage-hero="true"] [data-hero-heading]').getByRole('heading', {
|
|
level: 1,
|
|
name: 'Evidence-first governance for Microsoft tenants.',
|
|
}),
|
|
).toBeVisible();
|
|
await expect(page.locator('[data-homepage-hero="true"] [data-hero-supporting-copy]')).toContainText(
|
|
/backup and restore with confidence\. detect drift before it becomes audit work\. preserve snapshot history/i,
|
|
);
|
|
await expectHomepageHeroCtaPair(page, 'Book a demo', 'Explore the platform');
|
|
await expectHomepageHeroRouteTargets(page, ['/contact', '/platform']);
|
|
});
|
|
|
|
test('homepage uses neutral trust statements and the six required feature pillars', async ({ page }) => {
|
|
await visitPage(page, '/');
|
|
await expect(page.locator('[data-section="trustbar"]')).toBeVisible();
|
|
await expect(page.locator('[data-section="trustbar"]')).toContainText(/Microsoft tenant focused/i);
|
|
await expect(page.locator('[data-section="trustbar"]')).toContainText(/Evidence-oriented workflows/i);
|
|
await expect(page.locator('[data-section="trustbar"]')).toContainText(/Designed for audit review/i);
|
|
await expect(page.locator('[data-section="trustbar"]')).toContainText(/Operator-led governance/i);
|
|
|
|
const pillars = page.locator('[data-section="feature-pillars"]');
|
|
await expect(pillars).toBeVisible();
|
|
|
|
for (const capability of ['Backup', 'Restore', 'Drift Detection', 'Evidence', 'Audit Trail', 'Governance Reviews']) {
|
|
await expect(pillars.getByRole('heading', { name: capability, exact: true })).toBeVisible();
|
|
}
|
|
|
|
await expect(page.locator('body')).not.toContainText(/SOC 2|ISO\s?\d*|99\.9%|trusted by|customer logos/i);
|
|
});
|
|
|
|
test('homepage dashboard preview is static, responsive, and explains status without color alone', async ({
|
|
page,
|
|
}) => {
|
|
await visitPage(page, '/');
|
|
|
|
const preview = page.locator('[data-dashboard-preview]').first();
|
|
|
|
await expect(preview).toBeVisible();
|
|
await expect(preview).toContainText('Static demo preview');
|
|
await expect(preview).toContainText('Demo values');
|
|
await expect(preview).toContainText('92%');
|
|
await expect(preview).toContainText('14');
|
|
await expect(preview).toContainText('7');
|
|
await expect(preview).toContainText('1,248');
|
|
await expect(preview).toContainText('98%');
|
|
|
|
for (const panel of [
|
|
'Recent findings',
|
|
'Drift timeline',
|
|
'Backups and restores',
|
|
'Governance reviews',
|
|
'Evidence spotlight',
|
|
]) {
|
|
await expect(preview.getByText(panel, { exact: true })).toBeVisible();
|
|
}
|
|
|
|
for (const status of ['Healthy', 'Review required', 'Critical', 'Review-ready']) {
|
|
await expect(preview.getByText(status, { exact: true }).first()).toBeVisible();
|
|
}
|
|
|
|
await expect(preview).not.toContainText(/real[- ]?time|streaming|live tenant feed|May 19|Updated 2m ago/i);
|
|
});
|
|
|
|
test('homepage keeps the final CTA and launch-readiness sections in order', async ({ page }) => {
|
|
await visitPage(page, '/');
|
|
await expectHomepageSectionOrder(page, ['hero', 'trustbar', 'feature-pillars', 'cta']);
|
|
await expect(page.locator('[data-section="cta"]')).toContainText(
|
|
'Build tenant governance on evidence, not assumptions.',
|
|
);
|
|
await expectOnwardRouteReachable(page, ['/platform', '/contact']);
|
|
});
|
|
|
|
test.describe('homepage mobile', () => {
|
|
test.use({ viewport: { width: 390, height: 844 } });
|
|
|
|
test('homepage remains readable on narrow screens without body overflow', async ({ page }) => {
|
|
await visitPage(page, '/');
|
|
await expectMobileReadability(page);
|
|
await expectNoBodyHorizontalOverflow(page);
|
|
await expect(page.locator('[data-section="trustbar"]')).toBeVisible();
|
|
await expect(page.locator('[data-section="feature-pillars"]')).toBeVisible();
|
|
await expect(page.locator('[data-dashboard-preview]').first()).toBeVisible();
|
|
});
|
|
|
|
test('homepage hero preserves meaning order and hero route intent on narrow screens', async ({ page }) => {
|
|
await visitPage(page, '/');
|
|
await expectHomepageHeroOrder(page, [
|
|
'eyebrow',
|
|
'headline',
|
|
'supporting-copy',
|
|
'cta-pair',
|
|
'trust-subclaims',
|
|
'product-near-visual',
|
|
]);
|
|
await expectHomepageHeroVisibleOnMobile(page);
|
|
await expectHomepageHeroRouteTargets(page, ['/contact', '/platform']);
|
|
});
|
|
});
|
|
|
|
test('platform hero explains Tenantial governance model', async ({ page }) => {
|
|
await visitPage(page, '/platform');
|
|
await expectShell(page, /govern Microsoft tenants through evidence/i);
|
|
await expectPageFamily(page, 'landing');
|
|
await expectDisclosureLayer(page, '1');
|
|
await expectDisclosureLayer(page, '2');
|
|
await expectPrimaryNavigation(page);
|
|
await expectNavigationVsCtaDifferentiation(page);
|
|
await expectFooterLinks(page);
|
|
|
|
const main = page.getByRole('main');
|
|
|
|
await expect(main.locator('[data-hero-eyebrow]').first()).toContainText(/TENANTIAL PLATFORM/i);
|
|
await expect(page.getByRole('heading', { level: 1 })).toHaveCount(1);
|
|
await expect(page.getByRole('heading', { level: 1 })).toContainText(
|
|
/govern Microsoft tenants through evidence/i,
|
|
);
|
|
await expect(main.locator('[data-hero-supporting-copy]').first()).toContainText(/backup records/i);
|
|
await expect(main.locator('[data-hero-supporting-copy]').first()).toContainText(/configuration drift/i);
|
|
await expect(main.locator('[data-hero-supporting-copy]').first()).toContainText(/structured reviews/i);
|
|
await expectCtaHierarchy(page, 'Book a demo', 'See the governance loop');
|
|
await expect(main.locator('a[href="/contact"]').filter({ hasText: 'Book a demo' }).first()).toBeVisible();
|
|
await expect(main.locator('a[href="#governance-loop"]').filter({ hasText: 'See the governance loop' }).first()).toBeVisible();
|
|
await expect(main.locator('[data-platform-dashboard-preview]').first()).toBeVisible();
|
|
await expect(main.locator('[data-platform-dashboard-preview]').first()).toContainText(/Governance overview/i);
|
|
await expect(main.locator('[data-platform-dashboard-preview]').first()).toContainText(/Static demo preview/i);
|
|
await expect(main.locator('[data-platform-dashboard-preview]').first()).toContainText(/Evidence spotlight/i);
|
|
|
|
for (const signal of [
|
|
'Microsoft tenant focused',
|
|
'Evidence-oriented workflows',
|
|
'Reviewable decisions',
|
|
'Operator-led governance',
|
|
]) {
|
|
await expect(main.locator('[data-hero-trust-subclaims]').getByText(signal, { exact: true })).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('platform metadata is Tenantial clean', async ({ page }) => {
|
|
await visitPage(page, '/platform');
|
|
|
|
await expect(page).toHaveTitle(/Tenantial Platform/i);
|
|
await expect(page).toHaveTitle(/Evidence-first governance/i);
|
|
await expect(page.locator('meta[name="description"]')).toHaveAttribute('content', /backup/i);
|
|
await expect(page.locator('meta[name="description"]')).toHaveAttribute('content', /restore/i);
|
|
await expect(page.locator('meta[name="description"]')).toHaveAttribute('content', /drift detection/i);
|
|
await expect(page.locator('meta[name="description"]')).toHaveAttribute('content', /findings/i);
|
|
await expect(page.locator('meta[name="description"]')).toHaveAttribute('content', /evidence/i);
|
|
await expect(page.locator('meta[name="description"]')).toHaveAttribute('content', /audit trails/i);
|
|
await expect(page.locator('meta[name="description"]')).toHaveAttribute('content', /exceptions/i);
|
|
await expect(page.locator('meta[name="description"]')).toHaveAttribute('content', /reviews/i);
|
|
await expect(page.locator('link[rel="canonical"]')).toHaveAttribute('href', /\/platform$/);
|
|
await expectPlatformResidueAbsent(page);
|
|
});
|
|
|
|
test('platform governance flow explains reviewable evidence', async ({ page }) => {
|
|
await visitPage(page, '/platform');
|
|
|
|
const operatingModel = page.locator('[data-section="operating-model"]');
|
|
const governanceLoop = page.locator('[data-section="governance-loop"]');
|
|
|
|
await expect(operatingModel).toBeVisible();
|
|
|
|
await expect(operatingModel.locator('[data-operating-flow]')).toBeVisible();
|
|
|
|
for (const step of ['Snapshot', 'Drift', 'Finding', 'Review', 'Evidence', 'Audit trail']) {
|
|
await expect(operatingModel.getByText(step, { exact: true })).toBeVisible();
|
|
}
|
|
|
|
await expect(governanceLoop).toBeVisible();
|
|
await expect(governanceLoop.locator('[data-governance-loop-diagram]')).toBeVisible();
|
|
|
|
for (const label of [
|
|
'Source of truth',
|
|
'Snapshot',
|
|
'Diff',
|
|
'Finding',
|
|
'Exception',
|
|
'Review',
|
|
'Evidence',
|
|
'Audit trail',
|
|
]) {
|
|
await expect(governanceLoop.getByText(label, { exact: true })).toBeVisible();
|
|
}
|
|
|
|
await expect(governanceLoop).toContainText(/operator reviews/i);
|
|
await expect(governanceLoop).toContainText(/evidence/i);
|
|
await expect(governanceLoop).toContainText(/auditability/i);
|
|
});
|
|
|
|
test('platform governance flow avoids automation guarantees', async ({ page }) => {
|
|
await visitPage(page, '/platform');
|
|
|
|
await expect(page.locator('body')).not.toContainText(
|
|
/automatic remediation|guaranteed recovery|live device actions|real-time tenant operations/i,
|
|
);
|
|
});
|
|
|
|
test('platform capabilities cover governance primitives', async ({ page }) => {
|
|
await visitPage(page, '/platform');
|
|
|
|
const capabilities = page.locator('[data-section="platform-capabilities"]');
|
|
|
|
await expect(capabilities).toBeVisible();
|
|
|
|
await expect(capabilities.locator('[data-capability-system-grid]')).toBeVisible();
|
|
await expect(capabilities.locator('[data-capability-primary]')).toBeVisible();
|
|
await expect(capabilities.getByRole('heading', { name: 'Backup & Restore', exact: true })).toBeVisible();
|
|
|
|
for (const capability of [
|
|
'Drift Detection',
|
|
'Findings',
|
|
'Evidence',
|
|
'Audit Trail',
|
|
'Exceptions',
|
|
'Governance Reviews',
|
|
]) {
|
|
await expect(capabilities.getByRole('heading', { name: capability, exact: true })).toBeVisible();
|
|
}
|
|
});
|
|
|
|
test('platform boundaries keep claims conservative', async ({ page }) => {
|
|
await visitPage(page, '/platform');
|
|
|
|
const boundaries = page.locator('[data-section="platform-boundaries"]');
|
|
|
|
await expect(boundaries).toBeVisible();
|
|
await expect(boundaries).toContainText(/Built for governance of record/i);
|
|
await expect(boundaries).toContainText(/do not take actions on your behalf/i);
|
|
await expect(boundaries).toContainText(/do not manage devices/i);
|
|
await expect(boundaries).toContainText(/do not replace your ITSM/i);
|
|
await expect(boundaries).toContainText(/support reviewable decisions/i);
|
|
|
|
for (const claim of unsupportedPlatformClaims) {
|
|
await expect(page.locator('body')).not.toContainText(claim);
|
|
}
|
|
});
|
|
|
|
test.describe('platform mobile', () => {
|
|
test.use({ viewport: { width: 390, height: 844 } });
|
|
|
|
test('platform mobile layout stays readable without overflow', async ({ page }) => {
|
|
await visitPage(page, '/platform');
|
|
await expectMobileReadability(page);
|
|
await expectNoBodyHorizontalOverflow(page);
|
|
await expect(page.locator('[data-section="platform-capabilities"]').first()).toBeVisible();
|
|
await expect(page.locator('[data-section="platform-boundaries"]').first()).toBeVisible();
|
|
await expect(page.locator('[data-section="platform-capabilities"]').first()).toContainText(/Backup/i);
|
|
await expect(page.locator('[data-truth-layer-stack]').first()).toBeVisible();
|
|
await expect(page.locator('[data-governance-loop-diagram]').first()).toBeVisible();
|
|
await expect(page.locator('[data-section="platform-boundaries"]').first()).toContainText(/governance of record/i);
|
|
expect(await page.locator('[data-color-meaning="with-text"]').count()).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test('primary navigation and footer point Platform links to platform', async ({ page }) => {
|
|
await visitPage(page, '/platform');
|
|
await expectPrimaryNavigation(page);
|
|
await expectFooterLinks(page);
|
|
await expect(page.getByRole('contentinfo')).toHaveAttribute('data-footer-intent', 'quiet');
|
|
await expect(page.locator('[data-section="platform-final-cta"]')).toBeVisible();
|
|
await expect(page.getByRole('contentinfo')).not.toContainText(/Build tenant governance on evidence/i);
|
|
});
|
|
|
|
test('product redirects to platform', async ({ page }) => {
|
|
await expectCompatibilityRedirect(page, '/product', '/platform');
|
|
await expect(page.locator('body')).not.toContainText(
|
|
/AstroDeck|TemplateDeck|Open Source|MIT Licensed|TenantAtlas|TenantPilot|TenantCTRL/i,
|
|
);
|
|
});
|