import { test } from "node:test"; import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; import { detectFabricVendor, parseFabricCollectionPage, FabricParseError } from "../src/import/fabricParser.js"; import { parseLechCollectionPage } from "../src/import/fabricLech.js"; import { baseColorForShade, BASE_COLOR_VALUES } from "../src/domain/baseColor.js"; const FIXTURE = fs.readFileSync(path.join(import.meta.dirname, "fixtures", "lech-collection.html"), "utf8"); const LECH_URL = "https://lechfabrics.com/pl/kolekcja/lumo"; // --- parser Lech: czyste HTML, zero sieci ------------------------------------- test("lech parser: nazwa z og:title, parametry, odcienie z data-src, karta PDF", () => { const parsed = parseLechCollectionPage(FIXTURE, LECH_URL); assert.equal(parsed.code, "lumo"); assert.equal(parsed.name, "LUMO"); assert.equal(parsed.producer, "Lech Fabrics"); // parametry techniczne → kanoniczne klucze params_json assert.equal(parsed.params.composition, "100% PES"); assert.deepEqual(parsed.params.grammage_g_m2, { value: 300, tolerance: "±10%" }); assert.deepEqual(parsed.params.usable_width_cm, { value: 143, tolerance: "±2 cm" }); assert.deepEqual(parsed.params.martindale_cycles, { max: 30000 }); assert.deepEqual(parsed.params.light_fastness, { min: 5 }); assert.equal(parsed.params.flammability, "na zamówienie"); assert.equal(parsed.params.colors_count, 20); assert.equal(parsed.params.fabric_type, "szenil"); assert.deepEqual(parsed.params.care, ["PRANIE RĘCZNE", "NIE WYBIELAĆ", "NIE PRASOWAĆ", "NIE CZYŚCIĆ CHEMICZNIE", "NIE SUSZYĆ MECHANICZNIE"]); // odcienie: pełnowymiarowe próbki z data-src, kod z podpisu, bez duplikatów assert.deepEqual(parsed.shades.map((s) => s.code), ["05", "30", "40", "55", "60"]); assert.equal(parsed.shades[1]!.imageUrl, "https://lechfabrics.com/upload/import/colors/GO/LUMO/LUMO-30.jpg"); assert.equal(parsed.shades[1]!.vendorCode, "LUMO-30"); assert.equal(parsed.shades[1]!.name, null); assert.ok(parsed.shades.every((s) => !s.imageUrl.includes("assets/photo")), "tylko pełnowymiarowe próbki, nie miniatury"); // wpis kolekcji (data-caption="LUMO" bez numeru) nie jest odcieniem assert.ok(!parsed.shades.some((s) => s.code === "LUMO")); assert.equal(parsed.cardUrl, "https://lechfabrics.com/upload/import/files/Lumo_karta.pdf"); // błędy assert.throws(() => parseLechCollectionPage("nic", LECH_URL), FabricParseError); assert.throws(() => parseLechCollectionPage(FIXTURE, "https://example.com/x/"), /lechfabrics\.com/); }); test("router: lechfabrics.com wybiera adapter Lech", () => { assert.equal(detectFabricVendor(LECH_URL), "lech"); assert.equal(detectFabricVendor("https://www.lechfabrics.com/pl/kolekcja/x"), "lech"); const parsed = parseFabricCollectionPage(FIXTURE, LECH_URL); assert.equal(parsed.producer, "Lech Fabrics"); assert.equal(parsed.code, "lumo"); }); // --- słownik „Kolor” BaseLinkera ---------------------------------------------- test("baseColorForShade: nazwa handlowa → kategoria słownika; hex → klasyfikacja HSL", () => { // nazwy handlowe (deklaracja producenta ma pierwszeństwo) assert.equal(baseColorForShade({ name: "Grey 14" }), "Szary"); assert.equal(baseColorForShade({ name: "Beige 03" }), "Beżowy"); assert.equal(baseColorForShade({ name: "Anthracite" }), "Antracyt"); assert.equal(baseColorForShade({ name: "Navy Blue" }), "Niebieski"); assert.equal(baseColorForShade({ name: "Bottle Green" }), "Zielony"); // bez nazwy — klasyfikacja po hex próbki (realne wartości LUMO/TILIA) assert.equal(baseColorForShade({ color_hex: "#b4b5ab" }), "Kremowy"); // LUMO 60 — jasny ciepły neutralny assert.equal(baseColorForShade({ color_hex: "#716252" }), "Beżowy"); // LUMO 30 — taupe → rodzina beżu assert.equal(baseColorForShade({ color_hex: "#2f2622" }), "Brązowy"); // LUMO 40 — bardzo ciemny brąz assert.equal(baseColorForShade({ color_hex: "#a6a499" }), "Kremowy"); // LUMO 55 — jasny szaro-beż assert.equal(baseColorForShade({ color_hex: "#1f2222" }), "Czarny"); // LUMO 80 — prawie czarny assert.equal(baseColorForShade({ color_hex: "#2c363d" }), "Niebieski"); // LUMO 05 — ciemny granatowo-stalowy assert.equal(baseColorForShade({ color_hex: "#855238" }), "Brązowy"); // LUMO 110 — rdzawy // brak danych → bezpieczna wartość słownika assert.equal(baseColorForShade({}), "Inny kolor"); assert.equal(baseColorForShade({ color_hex: "nie-hex" }), "Inny kolor"); // wynik zawsze należy do słownika for (const v of ["#000000", "#ffffff", "#808080", "#ff0000", "#00ff00", "#0000ff", "#a83232", null]) { assert.ok(BASE_COLOR_VALUES.includes(baseColorForShade({ color_hex: v }) as (typeof BASE_COLOR_VALUES)[number]), `hex ${v} → wartość spoza słownika`); } });