From 0cb651f00312fb526bdc2cd41678386d3d390013 Mon Sep 17 00:00:00 2001 From: Nicolas Mehlei Date: Wed, 28 Oct 2026 15:41:24 +0100 Subject: [PATCH] fix(sparkasse): read the plot area on plot cards --- lib/provider/sparkasse.js | 19 +++++++++++++++++-- test/provider/sparkasse.test.js | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/provider/sparkasse.js b/lib/provider/sparkasse.js index adc5a144..2221ab7e 100755 --- a/lib/provider/sparkasse.js +++ b/lib/provider/sparkasse.js @@ -203,6 +203,19 @@ function readAttributes(estate) { return Object.fromEntries(entries); } +/** + * The card's size: the living area, or for a plot card the plot area. A house card carries both and the two texts + * arrive joined ("120 m² Wohnfläche774 m² Grundstücksfläche"). + * + * @param {string|null|undefined} text + * @returns {number|null} + */ +function readSize(text) { + if (text == null) return null; + const living = /([\d.,]+)\s*m²\s*Wohnfläche/.exec(text)?.[1]; + return extractNumber(living ?? text); +} + /** * @param {any} o * @returns {ParsedListing} @@ -217,7 +230,7 @@ function normalize(o, runUrl) { link, title: o.title || '', price: extractNumber(o.price), - size: extractNumber(o.size), + size: readSize(o.size), rooms: extractNumber(o.rooms), address: o.address, image: o.image, @@ -248,7 +261,9 @@ const config = { title: '[data-testid="estate-teaser-title"] | trim', price: '[data-testid="estate-teaser-price"] | trim', // main facts: "115 m² Wohnfläche", "774 m² Grundstücksfläche", "4 Zimmer"; each one is optional, so match on the label - size: '[data-testid="estate-teaser-mainfacts"] span:contains("Wohnfläche") | trim', + // a plot card carries "Grundstücksfläche" instead of a living area; a house card may carry both, and the cheerio + // text of both runs together, so `readSize` picks the living area when there is one + size: '[data-testid="estate-teaser-mainfacts"] span:contains("Wohnfläche"), [data-testid="estate-teaser-mainfacts"] span:contains("Grundstücksfläche") | trim', rooms: '[data-testid="estate-teaser-mainfacts"] span:contains("Zimmer") | trim', address: '[data-testid="estate-teaser-subtitle"] | trim', image: 'img@src', diff --git a/test/provider/sparkasse.test.js b/test/provider/sparkasse.test.js index d9ca8431..3c524273 100644 --- a/test/provider/sparkasse.test.js +++ b/test/provider/sparkasse.test.js @@ -116,6 +116,25 @@ describe('#sparkasse testsuite()', () => { } }); + it('reads the living area of a house card and the plot area of a plot card', () => { + const card = (facts) => + `
T
${facts.map((fact) => `${fact}`).join('')}
`; + const sizeOf = (facts) => { + const [parsed] = parse( + provider.config.crawlContainer, + provider.config.crawlFields, + card(facts), + 'https://immobilien.sparkasse.de/x', + ); + return provider.createConfig({ url: 'https://immobilien.sparkasse.de/x' }).normalize(parsed).size; + }; + + expect(sizeOf(['120 m² Wohnfläche', '774 m² Grundstücksfläche', '4 Zimmer'])).toBe(120); + expect(sizeOf(['450 m² Grundstücksfläche'])).toBe(450); + expect(sizeOf(['75 m² Wohnfläche', '3 Zimmer'])).toBe(75); + expect(sizeOf(['1 Zimmer'])).toBeNull(); + }); + describe('with provider_details enabled', () => { beforeEach(() => { vi.spyOn(mockStore, 'getUserSettings').mockReturnValue({ provider_details: [provider.metaInformation.id] });