feat(t5): Wächter vollständig, Hinweis Sonnenmodus, Nacharbeiten (T5a A10)
- Baukasten-Wächter: jede Regel prüft eigene Beispiele (muss fangen, muss ignorieren), veraltete Ausnahmen und Ordner-Ausschlüsse schlagen fehl; neue Regeln für text-primary ohne -ink, dark:, Schrift unter 13 px (Tailwind, SVG, CSS, style, *_SIZE-Konstanten) und Verlaufs-Hintergründe. Aufgeteilt in baukasten-guard, touch-target-guard und einen Scan-Helfer. - Hinweis „Neu: Sonnenmodus für draußen“ auf dem Dashboard, einmal je Gerät, „Ausprobieren“ schaltet live um; Fokus geht danach zur Überschrift. - Varroa-Diagramm: Beschriftungen als HTML mit fester Größe statt SVG-Text. - Medikamente zeigen die Tabs auch leer; Zahlen in Tabs nicht beim Laden oder Fehler; inaktive Tabs mit Hover für die Maus; TabPicker (Auswahl unter 640 px) für Automatisierung und Statistik; Rollen-Dialoge breit genug für die Rechte-Matrix. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
355a353671
commit
427969d8b9
42 files changed
+1053
-400
No files matched your search
@@ -2,7 +2,9 @@ import { useState } from "react";
|
||||
import { render, screen, within } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ActiveHistoryTabs, type EntityLifecycleTab } from "./active-history-tabs";
|
||||
import { readdirSync, readFileSync, statSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { ActiveHistoryTabs, countOf, type EntityLifecycleTab } from "./active-history-tabs";
|
||||
|
||||
function Harness({ onValueChange }: { onValueChange?: (value: EntityLifecycleTab) => void }) {
|
||||
const [value, setValue] = useState<EntityLifecycleTab>("active");
|
||||
@@ -112,4 +114,29 @@ describe("ActiveHistoryTabs (spec 10.4)", () => {
|
||||
expect(classes).toContain("pointer-fine:h-9");
|
||||
}
|
||||
});
|
||||
|
||||
it("countOf counts only loaded data: no number while loading or after a failed first load", () => {
|
||||
expect(countOf(undefined, [])).toBeUndefined();
|
||||
expect(countOf([], [])).toBe(0);
|
||||
expect(countOf([1, 2, 3], [1, 3])).toBe(2);
|
||||
});
|
||||
|
||||
it("every list passes its counts through countOf, so a failed load never says (0)", () => {
|
||||
const src = join(__dirname, "..");
|
||||
const files = (function walk(dir: string): string[] {
|
||||
return readdirSync(dir).flatMap((name) => {
|
||||
const path = join(dir, name);
|
||||
if (statSync(path).isDirectory()) return walk(path);
|
||||
return /\.tsx$/.test(name) && !/\.spec\./.test(name) ? [path] : [];
|
||||
});
|
||||
})(src);
|
||||
const users = files.filter((file) => /<ActiveHistoryTabs\b/.test(readFileSync(file, "utf8")));
|
||||
expect(users.length).toBeGreaterThanOrEqual(10);
|
||||
const wrong = users.filter((file) => {
|
||||
const text = readFileSync(file, "utf8");
|
||||
const counts = [...text.matchAll(/(?:active|history)Count=\{([^}]*)\}/g)].map((match) => match[1]);
|
||||
return counts.length !== 2 || counts.some((expression) => !/^countOf\(/.test(expression.trim()));
|
||||
});
|
||||
expect(wrong).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user