Files
picpeak/tests/e2e/seo-settings.spec.ts
T
Paul Nothaft 50c09904a9 feat: add update instructions dialog, email notifications, and capture date sorting
- Add Update Instructions Dialog with environment-specific commands (Docker/Git/Standalone)
- Add email notification settings for new version alerts
- Add "Sort by Capture Date" option using EXIF metadata extraction
- Fix E2E tests by loading environment variables via dotenv
- Add test-images/ and backend/*.db to .gitignore

Closes #181
2026-02-16 16:23:57 +01:00

159 lines
6.1 KiB
TypeScript

import { test, expect } from '@playwright/test';
const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'admin@example.com';
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'Admin!234';
// Helper to login and navigate to settings
async function loginAndGoToSeoSettings(page) {
await page.goto('/admin/login');
await page.getByLabel(/Email|E-Mail/i).fill(ADMIN_EMAIL);
await page.getByLabel(/Password|Passwort/i).fill(ADMIN_PASSWORD);
await page.getByRole('button', { name: /Sign In|Log in|Anmelden/i }).click();
await expect(page.getByRole('heading', { name: /Dashboard|Übersicht/i })).toBeVisible({ timeout: 20000 });
await page.goto('/admin/settings');
// Click on SEO tab
const seoTab = page.getByRole('button', { name: /SEO|Robots/i });
await seoTab.click();
await expect(page.getByRole('heading', { name: /Search Engine Indexing|Suchmaschinen-Indexierung/i })).toBeVisible({ timeout: 10000 });
}
test.describe('SEO Settings Tab', () => {
test('SEO tab renders all sections correctly', async ({ page }, testInfo) => {
if (testInfo.project.name === 'mobile-chrome') {
test.skip('SEO settings UI validated on desktop viewport');
}
await loginAndGoToSeoSettings(page);
// Verify all three cards are visible
await expect(page.getByRole('heading', { name: /Search Engine Indexing|Suchmaschinen-Indexierung/i })).toBeVisible();
await expect(page.getByRole('heading', { name: /AI & Bot Blocking|KI- & Bot-Blockierung/i })).toBeVisible();
await expect(page.getByRole('heading', { name: /Meta Tags|Meta-Tags/i })).toBeVisible();
// Verify key form elements
await expect(page.getByLabel(/Allow search engine indexing|Suchmaschinen-Indexierung erlauben/i)).toBeVisible();
await expect(page.getByLabel(/Block AI\/LLM crawlers|KI-\/LLM-Crawler blockieren/i)).toBeVisible();
await expect(page.getByLabel(/Add noindex meta tag|noindex-Meta-Tag hinzufügen/i)).toBeVisible();
});
test('can toggle indexing and save settings', async ({ page }, testInfo) => {
if (testInfo.project.name === 'mobile-chrome') {
test.skip('SEO settings UI validated on desktop viewport');
}
await loginAndGoToSeoSettings(page);
const indexingToggle = page.getByLabel(/Allow search engine indexing|Suchmaschinen-Indexierung erlauben/i);
const initialState = await indexingToggle.isChecked();
// Toggle the setting
await indexingToggle.click();
// Save
const saveButton = page.getByRole('button', { name: /Save SEO Settings|SEO-Einstellungen speichern/i });
await saveButton.click();
// Wait for success toast
await expect(page.locator('.Toastify__toast--success')).toBeVisible({ timeout: 10000 });
// Verify toggle state changed
const newState = await indexingToggle.isChecked();
expect(newState).toBe(!initialState);
// Revert to original state
await indexingToggle.click();
await saveButton.click();
await expect(page.locator('.Toastify__toast--success')).toBeVisible({ timeout: 10000 });
});
test('robots.txt preview updates based on settings', async ({ page }, testInfo) => {
if (testInfo.project.name === 'mobile-chrome') {
test.skip('SEO settings UI validated on desktop viewport');
}
await loginAndGoToSeoSettings(page);
// Click show preview button
const previewButton = page.getByRole('button', { name: /Show robots\.txt preview|robots\.txt-Vorschau anzeigen/i });
await previewButton.click();
// Verify preview content is visible
const previewContent = page.locator('pre');
await expect(previewContent).toBeVisible();
// Verify it contains expected content
const previewText = await previewContent.textContent();
expect(previewText).toContain('User-agent');
expect(previewText).toContain('Disallow');
});
test('can add blocked AI agents', async ({ page }, testInfo) => {
if (testInfo.project.name === 'mobile-chrome') {
test.skip('SEO settings UI validated on desktop viewport');
}
await loginAndGoToSeoSettings(page);
// Find the blocked agents section
const agentInput = page.getByPlaceholder(/Enter agent name|Agentenname eingeben/i);
await expect(agentInput).toBeVisible();
// Add a new agent
const testAgent = 'TestBot-' + Date.now();
await agentInput.fill(testAgent);
await agentInput.press('Enter');
// Verify the agent tag appears in the list
await expect(page.getByText(testAgent)).toBeVisible();
// Save and verify it persists
const saveButton = page.getByRole('button', { name: /Save SEO Settings|SEO-Einstellungen speichern/i });
await saveButton.click();
// Wait for success toast
await expect(page.locator('.Toastify__toast--success')).toBeVisible({ timeout: 10000 });
// Refresh and verify it's still there
await page.reload();
await page.getByRole('button', { name: /SEO|Robots/i }).click();
await expect(page.getByText(testAgent)).toBeVisible({ timeout: 10000 });
});
});
test.describe('robots.txt Endpoint', () => {
test('returns valid robots.txt from backend', async ({ request }) => {
const response = await request.get('/robots.txt');
expect(response.status()).toBe(200);
expect(response.headers()['content-type']).toContain('text/plain');
const body = await response.text();
expect(body).toContain('User-agent');
expect(body).toContain('Disallow: /admin');
expect(body).toContain('Disallow: /api');
});
test('robots.txt blocks AI crawlers by default', async ({ request }) => {
const response = await request.get('/robots.txt');
const body = await response.text();
// Check for some of the default blocked AI agents
expect(body).toContain('GPTBot');
expect(body).toContain('Claude-Web');
expect(body).toContain('Google-Extended');
});
});
test.describe('SEO Meta Tags', () => {
test('public settings include SEO meta flags', async ({ request }) => {
const response = await request.get('/api/public/settings');
expect(response.status()).toBe(200);
const settings = await response.json();
expect(settings).toHaveProperty('seo_meta_noindex');
expect(settings).toHaveProperty('seo_meta_nofollow');
expect(settings).toHaveProperty('seo_meta_noai');
});
});