import type { Page } from 'playwright' import { accessFor } from '../src/test/access' import { siteLanguagesBody } from '../src/test/storyLists' import { articleBody, fails, narrow, ok, press, settle, site, state, type Setup, } from './editorLocalizationStates' import type { ApiMock, VisualState } from './states' const PATTERN = 'https://journal.example.com/{lang}/{slug}' const FRENCH_SLUG = 'comment-nous-chiffrons-une-renovation' const FRENCH_TITLE = 'Comment nous chiffrons une rénovation de salle de bain' const alternates = [ { id: 10, lang: 'en', story_id: 5, slug: 'how-we-price-a-bathroom-renovation', url: 'https://journal.example.com/en/how-we-price-a-bathroom-renovation', }, { id: 11, lang: 'de', story_id: 5, slug: 'so-kalkulieren-wir-eine-badsanierung', url: 'https://journal.example.com/de/so-kalkulieren-wir-eine-badsanierung', }, ] function resolved(locale: string, canonical = `https://journal.example.com/fr/${FRENCH_SLUG}`) { return { title: `${FRENCH_TITLE} | Journal`, description: '', canonical_url: canonical, robots: 'index, follow', og: { type: 'article', title: '', description: '', image: '', image_alt: '', url: canonical, site_name: 'Journal', locale, locale_alternates: [], }, twitter: { card: 'summary' }, image: { url: '', alt: '', source: 'none' }, authors: [], published_at: null, modified_at: '2026-10-14T09:12:00Z', fallbacks: { title: 'title', description: 'none', image: 'none', canonical: 'pattern', site_text_skipped: true, }, json_ld: {}, lang: 'fr', alternates: canonical.includes('/fr/') ? alternates : [], } } function languages(social: boolean) { const body = siteLanguagesBody('journal') return { ...body, languages: body.languages.map((row) => row.lang === 'fr' ? { ...row, social_locale: social ? 'fr_FR' : null, social_locale_state: social ? 'set' : 'not_set', } : row, ), } } function frenchArticle(setup: Setup) { return { ...articleBody(setup, 'fr'), slug: FRENCH_SLUG, title: FRENCH_TITLE } } export async function openSeoPanel(page: Page) { if (narrow(page)) { await page .getByRole('button', { name: /^Details/ }) .first() .click() await page.getByRole('dialog').waitFor() } const body = page.locator('[data-testid="seo-body"]:visible') if (!(await body.count())) { await page.locator('[data-testid="seo-toggle"]:visible').first().click() } await page.locator('[data-testid="seo-edit"]:visible').first().click() await page.locator('[data-testid="seo-language-cards"]').waitFor() await settle(page) } export function seoState( setup: Setup, pattern: string | null, locale: string, social: boolean, steps: (page: Page) => Promise = openSeoPanel, ): VisualState { const canonical = pattern ? `https://journal.example.com/fr/${FRENCH_SLUG}` : `https://journal.example.com/${FRENCH_SLUG}` const overrides: Record = { 'GET /v1/site/': ok([{ ...site, public_url_pattern: pattern }]), 'GET /v1/auth/me': { status: 200, body: { id: 7, is_verified: true, username: 'marta', email: 'marta@renew.example', display_name: 'Marta Alves', access: accessFor('owner'), }, }, 'GET /v1/article/12': ok(frenchArticle(setup)), 'GET /v1/article/12/seo': ok(resolved(locale, canonical)), 'POST /v1/article/12/seo/check': ok({ status: 'ready', suggestions: [], slug: { available: true, suggestion: null }, }), } return state({ languages: languages(social), ...setup }, steps, overrides) } async function openEditSlug(page: Page) { await openSeoPanel(page) await press(page, 'Edit slug') await page.getByRole('dialog').last().waitFor() await settle(page) } export const metadataApiStates: Record = { 'loc.seo.noFallback': seoState({ suggestion: false }, PATTERN, 'fr_FR', true), 'loc.slug.published': seoState( { suggestion: false, online: { fr: true } }, PATTERN, 'fr_FR', true, openEditSlug, ), 'loc.url.unset': seoState({ suggestion: false }, null, 'fr_FR', true), 'loc.l10.noSocial': seoState({ suggestion: false }, PATTERN, '', false), 'loc.l10.api': { path: '/integration', api: { 'GET /v1/auth/me': { status: 200, body: { id: 7, is_verified: true, username: 'marta', email: 'marta@renew.example', display_name: 'Marta Alves', access: accessFor('owner'), }, }, 'GET /v1/image-variants': fails(500), }, steps: async (page: Page) => { await page.locator('[data-testid="integration-languages"]').scrollIntoViewIfNeeded() await settle(page) }, }, }