import { expect, test } from '@playwright/test'; test('home page shows the product shell and discovery feed with reusable thing cards', async ({ page }) => { await page.goto('/'); // Persistent product shell. await expect(page.getByRole('link', { name: 'Polymodel home' })).toBeVisible(); await expect(page.getByRole('searchbox', { name: 'Search Polymodel' })).toBeVisible(); await expect(page.getByRole('link', { name: 'Publish' })).toBeVisible(); await expect(page.getByRole('banner').getByRole('button', { name: 'Sign in' })).toBeVisible(); // Discovery feed selector (Hot default; Following now resolves to a signed-out empty state). await expect(page.getByRole('tab', { name: 'Hot' })).toHaveAttribute('aria-selected', 'true'); await expect(page.getByRole('tab', { name: 'Recent' })).toHaveAttribute('aria-selected', 'false'); await expect(page.getByRole('tab', { name: 'Following' })).toHaveAttribute('aria-selected', 'false'); // No inline catalog search box on the home surface anymore. await expect(page.getByRole('searchbox', { name: 'Search model projects' })).toHaveCount(0); const results = page.getByLabel('Browse results'); await expect(results).toBeVisible(); expect(await results.getByRole('article').count()).toBeGreaterThanOrEqual(3); await expect(page.getByRole('heading', { name: 'Parametric enclosure kit' })).toBeVisible(); await expect(page.getByText('by Ari Chen')).toBeVisible(); await expect(page.getByText('#parametric')).toBeVisible(); await expect(page.getByText('3 models')).toBeVisible(); await expect(page.getByText('18 parts')).toBeVisible(); await expect(page.getByRole('img', { name: 'Rendered preview of a parametric electronics enclosure' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Untitled thing' })).toBeVisible(); await expect(page.getByText('by mira.tools')).toBeVisible(); await expect(page.getByRole('heading', { name: 'Modular calibration tower' })).toBeVisible(); expect(await page.getByRole('img', { name: 'Blueprint placeholder for missing preview media' }).count()).toBeGreaterThanOrEqual(2); await expect(page.getByLabel('Browse results placeholder')).toHaveCount(0); await expect(page.getByText('Empty')).toHaveCount(0); }); test('global shell search navigates to /search', async ({ page }) => { await page.goto('/'); // Gate on a rendered feed card so the shell has loaded before we use search. // Search is a native GET form, so this works before and after hydration. await expect(page.getByLabel('Browse results').getByRole('article').first()).toBeVisible(); const search = page.getByRole('searchbox', { name: 'Search Polymodel' }); await search.fill('enclosure'); await page.getByRole('button', { name: 'Search' }).click(); await expect(page).toHaveURL(/\/search\?q=enclosure$/); await expect(page.getByRole('region', { name: 'Search', exact: true })).toBeVisible(); await expect(page.getByText('"enclosure"')).toBeVisible(); await expect(page.getByLabel('Search results')).toBeVisible(); await expect(page.getByRole('heading', { name: 'Parametric enclosure kit' })).toBeVisible(); }); test('shell header is present on a deep route', async ({ page }) => { await page.goto('/profile'); await expect(page.getByRole('link', { name: 'Polymodel home' })).toBeVisible(); await expect(page.getByRole('searchbox', { name: 'Search Polymodel' })).toBeVisible(); await expect(page.getByRole('link', { name: 'Publish' })).toBeVisible(); }); test('placeholder routes resolve with honest, non-faked content', async ({ page }) => { await page.goto('/search'); await expect(page.getByRole('region', { name: 'Search', exact: true })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Search from the header' })).toBeVisible(); await expect(page.getByText('Find indexed projects by name, tag, author, or description.')).toBeVisible(); }); test('home thing cards remain readable at narrow widths', async ({ page }) => { await page.setViewportSize({ width: 390, height: 900 }); await page.goto('/'); await expect(page.getByLabel('Browse results')).toBeVisible(); await expect(page.getByRole('heading', { name: 'Parametric enclosure kit' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Untitled thing' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Modular calibration tower' })).toBeVisible(); expect(await page.getByRole('img', { name: 'Blueprint placeholder for missing preview media' }).count()).toBeGreaterThanOrEqual(2); }); test('foundation route shows foundation primitives and reusable states', async ({ page }) => { await page.goto('/foundation'); await expect(page.getByRole('heading', { name: 'Foundation', exact: true })).toBeVisible(); await expect(page.getByText('Token and asset check')).toBeVisible(); await expect(page.getByLabel('Foundation primitives')).toBeVisible(); await expect(page.getByLabel('Reusable states')).toBeVisible(); await expect(page.getByText('No projects yet')).toBeVisible(); await expect(page.getByText('Feed unavailable')).toBeVisible(); }); test('home page no longer uses workflow roadmap demo copy as the product surface', async ({ page }) => { await page.goto('/'); await expect(page.getByText('Thingiverse on atproto')).toHaveCount(0); await expect(page.getByText('Project lexicon')).toHaveCount(0); await expect(page.getByText('Jira and Confluence')).toHaveCount(0); await expect(page.getByText('Polytoken workflow')).toHaveCount(0); await expect(page.getByText('Front-end foundation')).toHaveCount(0); });