Files
Luncher/server/src/tests/scrapers.test.ts
T
mates 9a03dd1e5e
CI / Generate TypeScript types (push) Failing after 13m15s
CI / Server unit tests (push) Has been skipped
CI / Build server (push) Has been skipped
CI / Build client (push) Has been skipped
CI / Playwright E2E tests (push) Has been skipped
CI / Build and push Docker image (push) Has been skipped
CI / Notify (push) Successful in 2s
fix: oprava parsování TechTower po změně HTML struktury
2026-07-07 10:15:10 +02:00

160 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as fs from 'fs';
import * as path from 'path';
import axios from 'axios';
import { getMenuSladovnicka, getMenuTechTower, getMenuSenkSerikova, StaleWeekError } from '../restaurants';
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
const fixturesDir = path.join(__dirname, 'fixtures');
const loadFixture = (name: string) => fs.readFileSync(path.join(fixturesDir, name), 'utf-8');
// Pondělí 12.5.2025
const MONDAY = new Date('2025-05-12');
beforeEach(() => {
jest.resetAllMocks();
});
describe('Sladovnicka parser', () => {
beforeEach(() => {
mockedAxios.get = jest.fn().mockResolvedValue({ data: loadFixture('sladovnicka.html') });
});
test('vrátí pole o délce 5 (jeden záznam na každý pracovní den)', async () => {
const menu = await getMenuSladovnicka(MONDAY);
expect(menu).toHaveLength(5);
});
test('pondělní menu obsahuje aspoň jedno jídlo', async () => {
const menu = await getMenuSladovnicka(MONDAY);
expect(menu[0].length).toBeGreaterThan(0);
});
test('první položka pondělního dne je polévka (isSoup=true)', async () => {
const menu = await getMenuSladovnicka(MONDAY);
expect(menu[0][0].isSoup).toBe(true);
});
test('jídla mají name, price a amount', async () => {
const menu = await getMenuSladovnicka(MONDAY);
const jidlo = menu[0][1];
expect(jidlo.name).toBeTruthy();
expect(jidlo.price).toBeTruthy();
expect(jidlo.amount).toBeTruthy();
});
test('alergeny jsou naparsovány jako čísla', async () => {
const menu = await getMenuSladovnicka(MONDAY);
const polievka = menu[0][0];
expect(Array.isArray(polievka.allergens)).toBe(true);
expect(polievka.allergens!.length).toBeGreaterThan(0);
expect(typeof polievka.allergens![0]).toBe('number');
});
});
describe('TechTower parser', () => {
beforeEach(() => {
mockedAxios.get = jest.fn().mockResolvedValue({ data: loadFixture('techtower.html') });
});
test('vrátí pole o délce 5', async () => {
const menu = await getMenuTechTower(MONDAY);
expect(menu).toHaveLength(5);
});
test('pondělní menu obsahuje polévku a hlavní jídla', async () => {
const menu = await getMenuTechTower(MONDAY);
expect(menu[0].some(f => f.isSoup)).toBe(true);
expect(menu[0].some(f => !f.isSoup)).toBe(true);
});
test('TechTower hodí StaleWeekError, pokud datum v hlavičce neodpovídá', async () => {
// Fixture obsahuje "12.5.-16.5.2025" jiný týden = stale
const jinaStreda = new Date('2025-04-14');
await expect(getMenuTechTower(jinaStreda)).rejects.toBeInstanceOf(StaleWeekError);
});
test('StaleWeekError obsahuje naparsovaná data', async () => {
const jinaStreda = new Date('2025-04-14');
try {
await getMenuTechTower(jinaStreda);
} catch (e) {
expect(e).toBeInstanceOf(StaleWeekError);
const err = e as StaleWeekError;
expect(err.food).toHaveLength(5);
}
});
});
describe('TechTower parser nový formát 07/2026', () => {
// Pondělí 6.7.2026 (pondělí je svátek, hlavička začíná úterým "7. 7.-10. 7.")
const MONDAY_2026 = new Date('2026-07-06');
beforeEach(() => {
mockedAxios.get = jest.fn().mockResolvedValue({ data: loadFixture('techtower-2026-07.html') });
});
test('vrátí pole o délce 5', async () => {
const menu = await getMenuTechTower(MONDAY_2026);
expect(menu).toHaveLength(5);
});
test('středeční menu obsahuje polévku a hlavní jídla s cenou', async () => {
const menu = await getMenuTechTower(MONDAY_2026);
// index 2 = středa (Boršč + hlavní jídla)
expect(menu[2].some(f => f.isSoup)).toBe(true);
expect(menu[2].some(f => !f.isSoup && f.price.includes('Kč'))).toBe(true);
});
test('cena i alergeny jsou správně odděleny od názvu jídla', async () => {
const menu = await getMenuTechTower(MONDAY_2026);
const polevka = menu[2].find(f => f.isSoup);
expect(polevka).toBeDefined();
// "Boršč 1,7,9 — 45 Kč" → název bez ceny a bez alergenů, cena a alergeny zvlášť
expect(polevka!.name).not.toMatch(/Kč/);
expect(polevka!.name).not.toMatch(/\d/);
expect(polevka!.price).toContain('Kč');
expect(Array.isArray(polevka!.allergens)).toBe(true);
expect(polevka!.allergens!.length).toBeGreaterThan(0);
});
test('nehodí StaleWeekError pro aktuální týden, i když pondělí je svátek', async () => {
await expect(getMenuTechTower(MONDAY_2026)).resolves.toHaveLength(5);
});
test('hodí StaleWeekError pro jiný týden', async () => {
const jinyTyden = new Date('2026-06-01');
await expect(getMenuTechTower(jinyTyden)).rejects.toBeInstanceOf(StaleWeekError);
});
});
describe('SenkSerikova parser', () => {
beforeEach(() => {
// SenkSerikova parsuje arraybuffer musíme vrátit Buffer, ne string
mockedAxios.get = jest.fn().mockResolvedValue({
data: Buffer.from(loadFixture('senkserikova.html')),
headers: {}
});
});
test('parser provede HTTP request a vrátí pole', async () => {
const menu = await getMenuSenkSerikova(MONDAY);
expect(mockedAxios.get).toHaveBeenCalledTimes(1);
expect(Array.isArray(menu)).toBe(true);
});
test('výsledné dny s obsahem mají správnou strukturu (name, price, isSoup)', async () => {
const menu = await getMenuSenkSerikova(MONDAY);
// Protože MONDAY je v minulosti, parser vrátí placeholdery pro všechny pracovní
// dny a .menicka elementy přidá za ně hledáme aspoň jeden den s reálnými daty
const denSJidlem = menu.find(den =>
den.length > 0 && den[0].name !== 'Pro tento den není uveřejněna nabídka jídel'
);
if (denSJidlem) {
expect(typeof denSJidlem[0].name).toBe('string');
expect(typeof denSJidlem[0].isSoup).toBe('boolean');
}
});
});