diff --git a/src/webapp/e2e/auth-redirect.spec.ts b/src/webapp/e2e/auth-redirect.spec.ts index 56576c35..93086d94 100644 --- a/src/webapp/e2e/auth-redirect.spec.ts +++ b/src/webapp/e2e/auth-redirect.spec.ts @@ -1,26 +1,22 @@ import { test, expect } from '@playwright/test'; -test('unauthenticated /settings visit redirects to /login', async ({ - page, -}) => { - const response = await page.goto('/settings'); - console.log('status:', response?.status(), 'final url:', page.url()); +/** + * Auth redirect tests. + * + * /home and /notifications have server-side guards (verifySessionOnServer). + * /settings/* and /profile (bare) redirect via the client-side useAuth fallback. + */ - await expect(page).toHaveURL('/login'); -}); +const protectedRoutes = [ + '/home', + '/notifications', + '/settings', + '/profile', +]; -test('unauthenticated /notifications visit redirects to /login', async ({ - page, -}) => { - const response = await page.goto('/notifications'); - console.log('status:', response?.status(), 'final url:', page.url()); - - await expect(page).toHaveURL('/login'); -}); - -test('unauthenticated /home visit redirects to /login', async ({ page }) => { - const response = await page.goto('/home'); - console.log('status:', response?.status(), 'final url:', page.url()); - - await expect(page).toHaveURL('/login'); -}); +for (const route of protectedRoutes) { + test(`unauthenticated ${route} redirects to /login`, async ({ page }) => { + await page.goto(route); + await expect(page).toHaveURL('/login'); + }); +} diff --git a/src/webapp/e2e/landing-page.spec.ts b/src/webapp/e2e/landing-page.spec.ts new file mode 100644 index 00000000..36238886 --- /dev/null +++ b/src/webapp/e2e/landing-page.spec.ts @@ -0,0 +1,46 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Landing page', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/'); + }); + + test('renders hero section with title and subtitle', async ({ page }) => { + const heading = page.getByRole('heading', { level: 1 }); + await expect(heading).toBeVisible(); + await expect(heading).toContainText('A social knowledge network for'); + }); + + test('shows sign up and log in buttons for unauthenticated users', async ({ + page, + }) => { + const signUp = page.getByRole('link', { name: 'Sign up' }); + const logIn = page.getByRole('link', { name: 'Log in' }); + + await expect(signUp).toBeVisible(); + await expect(logIn).toBeVisible(); + + await expect(signUp).toHaveAttribute('href', '/signup'); + await expect(logIn).toHaveAttribute('href', '/login'); + }); + + test('renders community highlights section', async ({ page }) => { + await expect( + page.getByText('Highlights from our community'), + ).toBeVisible(); + + await expect( + page.getByRole('link', { name: 'Explore' }), + ).toBeVisible(); + }); + + + test('renders footer with social and doc links', async ({ page }) => { + await expect( + page.getByRole('link', { name: 'Follow our blog' }), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: 'Semble Docs' }), + ).toBeVisible(); + }); +}); diff --git a/src/webapp/e2e/profile-tabs.spec.ts b/src/webapp/e2e/profile-tabs.spec.ts new file mode 100644 index 00000000..9cace5a8 --- /dev/null +++ b/src/webapp/e2e/profile-tabs.spec.ts @@ -0,0 +1,64 @@ +import { test, expect } from '@playwright/test'; + +const HANDLE = 'alice.bsky.social'; +const BASE = `/profile/${HANDLE}`; + +test.describe('Profile tab navigation', () => { + test.beforeEach(async ({ page }) => { + const response = await page.goto(BASE); + expect(response?.status()).toBe(200); + + // Wait for the tab bar to be present + await expect(page.getByRole('tab', { name: /Profile/ })).toBeVisible(); + }); + + test('Profile tab is active by default', async ({ page }) => { + const profileTab = page.getByRole('tab', { name: /Profile/ }); + await expect(profileTab).toHaveAttribute('data-active', 'true'); + await expect(page).toHaveURL(new RegExp(`${BASE}$`)); + }); + + test('can navigate to Cards tab', async ({ page }) => { + const tab = page.getByRole('tab', { name: /Cards/ }); + await tab.click(); + + await expect(page).toHaveURL(`${BASE}/cards`); + await expect(tab).toHaveAttribute('data-active', 'true'); + }); + + test('can navigate to Collections tab', async ({ page }) => { + const tab = page.getByRole('tab', { name: /Collections/ }); + await tab.click(); + + await expect(page).toHaveURL(`${BASE}/collections`); + await expect(tab).toHaveAttribute('data-active', 'true'); + }); + + test('can navigate to Connections tab', async ({ page }) => { + const tab = page.getByRole('tab', { name: /Connections/ }); + await tab.click(); + + await expect(page).toHaveURL(`${BASE}/connections`); + await expect(tab).toHaveAttribute('data-active', 'true'); + }); + + test('can navigate to Network tab', async ({ page }) => { + const tab = page.getByRole('tab', { name: /Network/ }); + await tab.click(); + + await expect(page).toHaveURL(`${BASE}/network`); + await expect(tab).toHaveAttribute('data-active', 'true'); + }); + + test('tabs do not redirect to /login (profile is public)', async ({ + page, + }) => { + const tabRoutes = ['cards', 'collections', 'connections', 'network']; + + for (const route of tabRoutes) { + const response = await page.goto(`${BASE}/${route}`); + expect(response?.status()).toBe(200); + await expect(page).toHaveURL(`${BASE}/${route}`); + } + }); +}); diff --git a/src/webapp/playwright.config.ts b/src/webapp/playwright.config.ts index 01dd0bef..ff788a8f 100644 --- a/src/webapp/playwright.config.ts +++ b/src/webapp/playwright.config.ts @@ -39,15 +39,15 @@ export default defineConfig({ use: { ...devices['Desktop Chrome'] }, }, - { - name: 'firefox', - use: { ...devices['Desktop Firefox'] }, - }, + // { + // name: 'firefox', + // use: { ...devices['Desktop Firefox'] }, + // }, - { - name: 'webkit', - use: { ...devices['Desktop Safari'] }, - }, + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, /* Test against mobile viewports. */ // {