lecheminvert.tech/tests/app.spec.ts
Adam Belkacemi cf6ee64e31
Some checks are pending
CI / tests (push) Waiting to run
CI / deploiement (push) Blocked by required conditions
Import initial : code en production sur Hetzner
2026-09-07 18:21:38 +00:00

138 lines
No EOL
5.6 KiB
TypeScript

import { expect, test } from '@nuxt/test-utils/playwright'
import type { Page } from '@playwright/test'
test.describe('API de geocodage', () => {
test('resout une ville en coordonnees', async ({ request }) => {
const res = await request.get('/api/geocode?q=lille')
expect(res.ok()).toBeTruthy()
const places = await res.json()
expect(places.length).toBeGreaterThan(0)
expect(places[0].lat).toBeCloseTo(50.63, 1)
expect(places[0].lon).toBeCloseTo(3.05, 1)
})
test('trouve une ville hors de France', async ({ request }) => {
const res = await request.get('/api/geocode?q=Bruxelles')
const places = await res.json()
// La BAN ignore la Belgique : sans Photon, on retomberait sur un hameau du Loiret.
const belge = places.find((p: { label: string }) => p.label.includes('Belgique'))
expect(belge).toBeDefined()
expect(belge.lat).toBeCloseTo(50.85, 1)
})
test('renvoie un tableau vide sur une saisie trop courte', async ({ request }) => {
const res = await request.get('/api/geocode?q=li')
expect(await res.json()).toEqual([])
})
})
test.describe('Comparateur', () => {
const bouton = (page: Page) => page.getByRole('button', { name: 'Comparer', exact: true })
/** Remplit un champ puis choisit une proposition dans SA propre liste. */
async function choisir(page: Page, id: string, champ: string, saisie: string, index = 0) {
await page.getByLabel(champ).fill(saisie)
await page.locator(`#${id}-list [role="option"]`).nth(index).click()
}
async function comparer(page: Page) {
await choisir(page, 'from', 'Départ', 'Lille', 0)
await choisir(page, 'to', 'Arrivée', 'Bruxelles', 1)
await bouton(page).click()
}
test.beforeEach(async ({ page }) => {
// On simule les appels sortants : un test e2e ne doit dependre
// ni du quota OpenRouteService ni de la disponibilite de Transitous.
await page.route('**/api/geocode**', route => route.fulfill({
json: [
{ label: 'Lille, France', lat: 50.6292, lon: 3.0573 },
{ label: 'Bruxelles, Belgique', lat: 50.8503, lon: 4.3517 },
],
}))
await page.route('**/api/compare', route => route.fulfill({
json: [
{ mode: 'bike', distanceKm: 116, durationMin: 402, co2Grams: 0 },
{ mode: 'train', distanceKm: 97, durationMin: 136, co2Grams: 2339, transfers: 2 },
{ mode: 'bus', distanceKm: 97, durationMin: 137, co2Grams: 3321, transfers: 2 },
{ mode: 'electric_car', distanceKm: 118, durationMin: 86, co2Grams: 12176 },
],
}))
await page.goto('/')
})
test('interdit le calcul tant qu aucun lieu n est choisi', async ({ page }) => {
await expect(bouton(page)).toBeDisabled()
})
test('ferme la liste apres selection sans la rouvrir', async ({ page }) => {
await choisir(page, 'from', 'Départ', 'Lille', 0)
// Le watch se redeclenchait sur la valeur ecrite par select() et rouvrait
// la liste : le champ suivant devenait inatteignable a la souris.
await page.waitForTimeout(600)
await expect(page.locator('#from-list')).toHaveCount(0)
})
test('reflete le modele quand il change de l exterieur', async ({ page }) => {
await choisir(page, 'from', 'Départ', 'Lille', 0)
await choisir(page, 'to', 'Arrivée', 'Bruxelles', 1)
// Sans watch sur le modele, l'inversion laissait les champs inchanges
// et l'affichage mentait sur ce qui allait etre calcule.
await page.getByRole('button', { name: 'Inverser' }).click()
await expect(page.getByLabel('Départ')).toHaveValue('Bruxelles, Belgique')
await expect(page.getByLabel('Arrivée')).toHaveValue('Lille, France')
})
test('interdit le calcul si la saisie ne correspond plus au lieu choisi', async ({ page }) => {
await choisir(page, 'from', 'Départ', 'Lille', 0)
await choisir(page, 'to', 'Arrivée', 'Bruxelles', 1)
await expect(bouton(page)).toBeEnabled()
await page.getByLabel('Arrivée').fill('Bruxel')
await expect(bouton(page)).toBeDisabled()
})
test('lance un calcul depuis un trajet d exemple', async ({ page }) => {
await page.getByRole('button', { name: 'Lille → Paris' }).click()
await expect(page.getByRole('heading', { level: 3 })).toHaveCount(4)
})
test('affiche les quatre modes du plus sobre au plus emetteur', async ({ page }) => {
await comparer(page)
// Le titre porte aussi le chevron et la duree : on cherche le libelle
// dans son contenu plutot qu'une egalite stricte.
const titles = page.getByRole('heading', { level: 3 })
await expect(titles).toHaveCount(4)
await expect(titles.nth(0)).toContainText('Vélo')
await expect(titles.nth(3)).toContainText('Voiture électrique')
})
test('rappelle les lieux reellement compares', async ({ page }) => {
await comparer(page)
const rappel = page.getByRole('heading', { level: 2 })
await expect(rappel).toContainText('Lille, France')
await expect(rappel).toContainText('Bruxelles, Belgique')
})
test('affiche les correspondances des transports en commun', async ({ page }) => {
await comparer(page)
await expect(page.getByText('2 corresp.').first()).toBeVisible()
})
test('convertit les emissions en kilogrammes au-dela de mille grammes', async ({ page }) => {
await comparer(page)
await expect(page.getByText('12.2 kg CO₂e')).toBeVisible()
await expect(page.getByText('0 g CO₂e')).toBeVisible()
})
test('previent l utilisateur quand le calcul echoue', async ({ page }) => {
await page.route('**/api/compare', route => route.fulfill({ status: 500, json: {} }))
await comparer(page)
await expect(page.getByRole('alert')).toBeVisible()
})
})