import { readFileSync } from 'node:fs'; import { describe, expect, it } from 'vitest'; import { cityFromSlug, isChallenge, listingRef, parseDetailPage, parseEuros, parseLocation, parseSearchPage, searchPageUrl } from './pararius'; import { listingDetailSchema, listingCardSchema } from './types'; const fixture = (name: string) => readFileSync(new URL(`./fixtures/${name}`, import.meta.url), 'utf8'); describe('pararius helpers', () => { it('reads listing urls', () => { expect(listingRef('/appartement-te-huur/gouda/541493b8/graaf-hendrikstraat')).toEqual({ sourceId: '541493b8', url: 'https://www.pararius.nl/appartement-te-huur/gouda/541493b8/graaf-hendrikstraat', offer: 'rent', propertyType: 'apartment', citySlug: 'gouda' }); expect(listingRef('https://www.pararius.nl/huis-te-koop/den-haag/0a1b2c3d/laan')?.offer).toBe( 'buy' ); expect(listingRef('/huurwoningen/gouda')).toBeNull(); }); it('parses Dutch and English prices', () => { expect(parseEuros('€ 1.558 per maand')).toBe(1558); expect(parseEuros('€1,600 per month')).toBe(1600); expect(parseEuros('€ 425.000 k.k.')).toBe(425000); expect(parseEuros('€ 1.250,50')).toBe(1250); expect(parseEuros('Prijs op aanvraag')).toBeNull(); }); it('splits a location line', () => { expect(parseLocation('2801 BC Gouda (Centrum)')).toEqual({ postalCode: '2801 BC', city: 'Gouda', neighbourhood: 'Centrum' }); expect(parseLocation('Alphen aan den Rijn')).toEqual({ postalCode: null, city: 'Alphen aan den Rijn', neighbourhood: null }); }); it('names cities from slugs', () => { expect(cityFromSlug('gouda')).toBe('Gouda'); expect(cityFromSlug('den-haag')).toBe('Den Haag'); expect(cityFromSlug('alphen-aan-den-rijn')).toBe('Alphen aan den Rijn'); }); it('builds page urls', () => { const base = 'https://www.pararius.nl/huurwoningen/gouda/1300-1700'; expect(searchPageUrl(base, 1)).toBe(base); expect(searchPageUrl(base, 3)).toBe(`${base}/page-3`); expect(searchPageUrl(`${base}/page-3`, 2)).toBe(`${base}/page-2`); }); it('detects Cloudflare challenges', () => { expect(isChallenge('Just a moment...')).toBe(true); expect(isChallenge('ok', 403)).toBe(true); expect(isChallenge(fixture('pararius-detail-house.html'))).toBe(false); }); }); describe('parseDetailPage', () => { it('reads a house listing', () => { const detail = parseDetailPage(fixture('pararius-detail-house.html'))!; expect(listingDetailSchema.parse(detail)).toBeTruthy(); expect(detail).toMatchObject({ sourceId: '8f976237', offer: 'rent', propertyType: 'house', externalId: '8f976237-2ce0-5613-adbe-883495268361', street: 'Kongsbergstraat 45', postalCode: '2804 XV', city: 'Gouda', neighbourhood: 'De Mammoet', priceEur: 1600, areaM2: 113, rooms: 5, bedrooms: 4, constructionYear: 1981, energyLabel: 'B', depositEur: 1600, agent: 'LIV Residential', offeredAt: '2026-09-30T09:47:00.000Z' }); expect(detail.lat).toBeCloseTo(52.0284, 3); expect(detail.lng).toBeCloseTo(4.7174, 3); }); it('reads interior and availability', () => { const detail = parseDetailPage(fixture('pararius-detail-apartment.html'))!; expect(detail).toMatchObject({ propertyType: 'apartment', priceEur: 1558, interior: 'gestoffeerd', availableFrom: 'Per 15-10-2026', contractType: 'Onbepaalde tijd', depositEur: 3316, offeredAt: '2026-10-02T05:35:37.000Z' }); }); it('keeps price-on-request listings', () => { const detail = parseDetailPage(fixture('pararius-detail-on-request.html'))!; expect(detail.priceEur).toBeNull(); expect(detail.interior).toBe('gemeubileerd'); expect(listingDetailSchema.parse(detail)).toBeTruthy(); }); it('rejects pages that are not listings', () => { expect(parseDetailPage('nothing')).toBeNull(); }); }); describe('parseSearchPage', () => { it('reads listing cards and pagination', () => { const page = parseSearchPage( fixture('pararius-search-synthetic.html'), 'https://www.pararius.nl/huurwoningen/gouda' ); expect(page.cards).toHaveLength(3); for (const card of page.cards) expect(listingCardSchema.parse(card)).toBeTruthy(); expect(page.cards[0]).toMatchObject({ sourceId: 'd5d8a72c', title: 'Appartement Raam', postalCode: '2801 VN', city: 'Gouda', neighbourhood: 'Binnenstad', priceEur: 1475, areaM2: 58, rooms: 3, interior: 'Gestoffeerd', agent: 'Voorbeeld Makelaardij' }); expect(page.cards[1].priceEur).toBe(1600); expect(page.cards[2].priceEur).toBeNull(); expect(page.nextPageUrl).toBe('https://www.pararius.nl/huurwoningen/gouda/page-2'); expect(page.totalResults).toBe(12); }); it('falls back to bare links when card markup changes', () => { const html = `
Huis Straat 1 € 1.400 per maand 80 m²
`; const page = parseSearchPage(html, 'https://www.pararius.nl/huurwoningen/gouda'); expect(page.cards).toHaveLength(1); expect(page.cards[0]).toMatchObject({ sourceId: '0a1b2c3d', priceEur: 1400, areaM2: 80 }); }); });