Resource Manager: Materialien-Tab mit PBR-Kugel-Vorschau, Suche und Kategorie-Chips

Geteilter Offscreen-three.js-Renderer (ein Kontext, serielle Queue, Cache
per Map-Signatur) rendert 128px-Kugeln lazy via IntersectionObserver.
Kategorie-Chips aus der Bibliothek abgeleitet, uneinheitliche Manifest-
Schreibweisen normalisiert; Suche und Kategorie kombinierbar. Kachel-Klick
markiert aktiv (gleiche Sprache wie MaterialPicker). Puppeteer-Probe
scripts/probe-material-tiles.mjs prüft Grid, Lazy-Render und Filter.
This commit is contained in:
2026-07-03 02:04:33 +02:00
parent 519735c782
commit 87d12b976f
6 changed files with 597 additions and 1 deletions
+109
View File
@@ -0,0 +1,109 @@
// Probe (Materialien-Tab — PBR-Kugel-Vorschau + Filter): öffnet den Ressourcen-
// Manager, wechselt in den neuen „Materialien"-Tab, prüft dass Kacheln
// erscheinen, mindestens eine Kugel-Vorschau (Bild) gerendert wurde, und dass
// Such-/Kategorie-Filter die Trefferzahl verändern.
//
// Aufruf: PROBE_URL=http://localhost:5187/ node scripts/probe-material-tiles.mjs
import puppeteer from "puppeteer";
const URL = process.env.PROBE_URL || "http://localhost:5187/";
const OUT = "scripts";
const browser = await puppeteer.launch({
headless: "new",
args: ["--no-sandbox", "--use-gl=swiftshader", "--enable-unsafe-swiftshader"],
});
const page = await browser.newPage();
await page.setViewport({ width: 1360, height: 900, deviceScaleFactor: 2 });
const logs = [];
page.on("console", (m) => logs.push(`[${m.type()}] ${m.text()}`));
page.on("pageerror", (e) => logs.push(`[PAGEERROR] ${e.message}`));
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const shot = (n) => page.screenshot({ path: `${OUT}/${n}` });
async function clickByText(selector, text) {
const handle = await page.evaluateHandle(
(sel, t) =>
[...document.querySelectorAll(sel)].find((el) => el.textContent.includes(t)),
selector,
text,
);
const el = handle.asElement();
if (!el) throw new Error(`nicht gefunden: ${selector} ~ "${text}"`);
await el.click();
return el;
}
await page.goto(URL, { waitUntil: "domcontentloaded", timeout: 20000 });
await page.waitForSelector(".topbar", { timeout: 20000 });
await sleep(600);
// 1) Ressourcen öffnen → in den Materialien-Tab wechseln.
await page.waitForSelector('button[aria-label="Ressourcen"]', { timeout: 8000 });
await page.click('button[aria-label="Ressourcen"]');
await page.waitForSelector(".res-drawer", { timeout: 8000 });
await clickByText(".res-tab-btn", "Materialien");
await page.waitForSelector(".mat-lib-grid .mat-tile", { timeout: 8000 });
const tileCount = await page.$$eval(".mat-lib-grid .mat-tile", (els) => els.length);
console.log("Material-Kacheln (ungefiltert):", tileCount);
// 2) Warten, bis mindestens eine Kugel-Vorschau gerendert ist (Bild im Tile).
await page.waitForFunction(
() => document.querySelectorAll(".mat-sphere img").length > 0,
{ timeout: 10000 },
);
// Sichtbare Kacheln lazy nachladen lassen (Warteschlange rendert seriell).
await sleep(2500);
const renderedCount = await page.$$eval(".mat-sphere img", (els) => els.length);
console.log("Gerenderte Kugel-Vorschauen (sichtbarer Bereich):", renderedCount);
// Erstes Vorschaubild darf kein leeres/transparentes 1x1-Pixelbild sein.
const firstSrc = await page.$eval(".mat-sphere img", (img) => img.src);
console.log("Erste Vorschau-DataURL-Länge:", firstSrc.length);
await shot("probe-material-tiles-1-grid.png");
// 3) Kategorie-Filter: erste konkrete Kategorie-Chip anklicken (nicht "Alle").
const chipLabels = await page.$$eval(".mat-chip", (els) => els.map((e) => e.textContent));
console.log("Kategorie-Chips:", chipLabels);
const firstCategoryChip = chipLabels[1]; // Index 0 = "Alle".
await clickByText(".mat-chip", firstCategoryChip);
await sleep(300);
const filteredByCategory = await page.$$eval(".mat-lib-grid .mat-tile", (els) => els.length);
console.log(`Kacheln nach Kategorie-Filter ("${firstCategoryChip}"):`, filteredByCategory);
await shot("probe-material-tiles-2-category.png");
// Filter zurücksetzen ("Alle").
await clickByText(".mat-chip", "Alle");
await sleep(200);
// 4) Textsuche: nach "Holz" suchen (mehrere Bibliotheks-Einträge betroffen).
await page.type(".mat-search", "Holz");
await sleep(400);
const filteredByText = await page.$$eval(".mat-lib-grid .mat-tile", (els) => els.length);
console.log('Kacheln nach Textsuche ("Holz"):', filteredByText);
await shot("probe-material-tiles-3-search.png");
// 5) Kachel auswählen → aktiv-Zustand + Statuszeile prüfen.
await page.click(".mat-lib-grid .mat-tile");
await sleep(200);
const hasActive = await page.$(".mat-lib-grid .mat-tile.active");
const selectedLine = await page.$eval(".mat-lib-selected", (el) => el.textContent).catch(() => null);
console.log("Aktive Kachel vorhanden:", !!hasActive, "| Auswahl-Zeile:", selectedLine);
await shot("probe-material-tiles-4-selected.png");
console.log("--- console ---");
for (const l of logs.slice(-30)) console.log(l);
if (tileCount === 0) throw new Error("Keine Material-Kacheln gefunden");
if (renderedCount === 0) throw new Error("Keine Kugel-Vorschau gerendert");
if (firstSrc.length < 200) throw new Error("Vorschau-DataURL wirkt leer");
if (filteredByCategory >= tileCount) throw new Error("Kategorie-Filter hat nichts gefiltert");
if (filteredByText >= tileCount) throw new Error("Textsuche hat nichts gefiltert");
if (!hasActive) throw new Error("Kachel-Auswahl hat keinen aktiven Zustand gesetzt");
await browser.close();
console.log("done");