Tests & Konfiguration

CodeMole wählt die passenden Checks pro Repo automatisch — und du kannst alles per .codemole.yml überschreiben. Hier steht, welche Checks es gibt, was sie tun und wie du sie steuerst.

01Wie Tests gewählt werden

Für jeden PR löst CodeMole die Checks in drei Stufen auf — die erste, die greift, gewinnt:

1.  .codemole.yml im Repo?   → exakt das nutzen   (versioniert, pro Branch/PR)
2.  sonst: Repo-Analyse      → Profil automatisch erkennen
3.  immer, für alle          → secret-scan + conflict-markers + sensitive-files + ai-review

Im Report-Kopf steht transparent, was gewählt wurde:

## 🧪 CodeMole — automations.yaml
Profil: ha-config · automatisch erkannt · ⚙ überschreibbar

02Auto-Erkennung

Ohne .codemole.yml bestimmt CodeMole das Profil über Marker-Dateien im Repo — deterministisch, kein Raten:

Marker im RepoProfil
configuration.yaml / automations.yamlha-config
manifest.json + custom_components/ha-component
blocks/ + package.json (EDS)aem-eds
nichts davongeneric
Mehrdeutig? Greifen mehrere Marker, gewinnt das spezifischste Profil. Willst du sichergehen, setz profile: in der .codemole.yml — dann entfällt die Erkennung.

03Profile

Ein Profil ist nur ein benanntes Bündel Checks. universell läuft zusätzlich immer mit.

ProfilChecks
ha-configyamllintha-validateincludessecret-refsduplicate-idsautomation-safetyentity-existsdiff-size
ha-componentpython-syntaxruffmanifesthacstranslationsjson-validdiff-size
aem-edsfile-guardpr-vollständigkeitjs-lintcss-lintplaceholder-keysunit-testsvisual-testsmerge-freshnessstatic-scans
genericdiff-size
universellsecret-scanconflict-markerssensitive-filesai-review
aem-eds ist besonders: das Profil läuft über einen spezialisierten Node-Runner — die 9 Checks oben sind dessen fester Umfang, zusätzlich steuerbar über testing-rules.json im Repo. disable/ignore aus der .codemole.yml gelten trotzdem. Dazu optional (alle Profile): page-audit lighthouse — siehe Katalog.

04Check-Katalog

Jeder Check ist ein eigenständiger Baustein. Kurzüberblick unten — die ausführliche Check-Referenz erklärt jeden Check mit Beispiel-Finding und Fix (die Check-Namen in PR-Reports verlinken direkt dorthin):

Wie Funde erscheinen: Checks mit Zeilenbezug (entity-exists, automation-safety, yamllint) und ai-review posten ihre Funde zeilengenau als Inline-Kommentare am Diff; im Report steht dann nur ein Kurz-Zähler. Checks ohne feste Zeile erscheinen als Zusammenfassung im Report. Auf jeden Inline-Kommentar kannst du antworten — der Bot erklärt oder räumt ein und schließt den Thread (siehe Mit dem Bot reden).

🔍 ai-review

Ein LLM (aktuell Claude) liest den Diff gegen deine echten Projekt-Konventionen und findet Logik-Bugs, die kein Linter sieht. Postet zeilengenaue Inline-Kommentare.

z.B. „State-Trigger feuert beim Neustart — from: 'on' ergänzen."

🛡️ secret-scan

Sucht Klartext-Secrets im Diff (Passwörter, API-Keys, Tokens). Schlägt fehl, sobald etwas durchrutscht.

z.B. „2 mögliche Klartext-Secrets in const.py."

📏 yamllint

yamllint nur auf den geänderten Zeilen, mit HA-tauglichen Regeln — keine tausenden Vorbestand-Warnungen.

z.B. „1 neuer Lint-Fehler in geänderten Zeilen."

✅ ha-validate

Validiert die HA-Konfiguration mit einem aktuellen HA-Core (2026.x) — Zwei-Pass: Base und Branch werden geprüft, gemeldet werden nur neue Fehler (Umgebungs-Bestandsfehler wie „Unknown device" zählen nicht), inkl. konkreter Fehlerzeilen im Report.

z.B. „1 neuer Validierungsfehler: Automation 'X' could not be validated — Service kaputt_ohne_domain …"

🔗 includes

Prüft Änderungen an !include-Strukturen auf gebrochene Referenzen.

z.B. „Keine include-Änderungen im Diff."

⏰ automation-safety

HA-Automations-Fallen in geänderten Zeilen: State-Trigger mit to: ohne from: (feuert beim HA-Neustart, unavailable→on) und device_id: statt entity_id:.

z.B. „automations.yaml:34 State-Trigger mit to: ohne from: — feuert beim HA-Neustart."

🆔 entity-exists opt-in

Prüft referenzierte entity_ids in neuen Zeilen live gegen die HA-Instanz — fängt Tippfehler, die häufigste Fehlerklasse. Aktivierung: ha_url + verschlüsselter ha_token in der .codemole.yml (Secrets-Tool).

z.B. „binary_sensor.flur_obenn existiert nicht in der HA-Instanz."

🔐 secret-refs / ♊ duplicate-ids

Gebrochene !secret-Referenzen gegen secrets.yaml; doppelte Automation-id/alias (überschreiben sich sonst still).

z.B. „!secret wifi_pw2 nicht definiert." / „id doppelt vergeben."

🐍 python-syntax / ruff

Kompilier-Check + Ruff-Lint auf geänderten Python-Dateien — Style + häufige Fehler.

z.B. „Blocking-Call requests.get im async-Loop."

🧾 json-valid / 🪢 conflict-markers / 🗂️ sensitive-files

Geänderte JSON-Dateien müssen parsen; übrig gebliebene Git-Konflikt-Marker; Warnung bei sensiblen Dateien im PR (.env, Keys, .storage …).

z.B. „Konflikt-Marker in automations.yaml:88."

📦 manifest / hacs

Prüft manifest.json-Pflichtfelder und hacs.json fürs HACS-Listing.

z.B. „manifest.json: version fehlt."

🌐 translations

Stellt sicher, dass die Pflicht-Sprache en.json existiert.

z.B. „en.json fehlt — keine Pflicht-Sprache."

🧹 js-lint / css-lint aem-eds

ESLint + Stylelint auf den geänderten Dateien (EDS-Regelwerk).

z.B. „Import ohne .js-Endung."

🧩 static-scans aem-eds

Framework-Imports (React/Vue/jQuery), outline:none (WCAG), JS-Bundle-Zuwachs und Token-Compliance (hardcodierte Hex/rgba-Farben statt var(--…) in neuen CSS-Zeilen).

z.B. „Hardcodierte Farbe statt var(--…) in teaser.css: color: #e30613."

🛟 file-guard / merge-freshness / pr-vollständigkeit aem-eds

Gelöschte/geleerte Dateien, Branch-Rückstand zur Base, Pflicht-Abschnitte in der PR-Beschreibung.

z.B. „Branch ist 48 Commits hinter wcms-2777-tokens (bitte rebasen)."

📊 diff-size

Meldet Umfang des Diffs (Zeilen/Dateien) — Orientierung für Reviewer, warnt bei XXL-PRs.

z.B. „Diff +37/−0 in 1 Datei."

🖼️ visual-tests aem-eds

Visuelle Regression pro Block gegen committete Baselines (Playwright) — Anlegen: siehe unten.

z.B. „2 Spec(s) gematcht (via button→teaser-xl)."

🔎 page-audit opt-in

Rendert konfigurierte Seiten auf Base- und Branch-Preview und prüft mit axe-core (WCAG 2.1 A/AA + Best Practice: Kontrast, Semantik, Landmarks, Labels) — gemeldet werden nur neue Findings. Timing (DCL/Load/KB) informativ. Aktivierung: page-audit: in der .codemole.yml.

z.B. „button-name (critical): 3 Element(e) — Buttons must have discernible text."

⚡ lighthouse on-demand

Voller Lighthouse-Score-Vergleich (Performance/A11y/Best-Practices/SEO + LCP/CLS) Base↔Branch. Langsam (Minuten) — läuft nicht bei jedem Push, sondern nur, wenn du das Label lighthouse an den PR hängst.

z.B. „Perf 88 (−7) · LCP 3760 ms — Verschlechterung gegenüber Base."

05Konfiguration: .codemole.yml

Leg die Datei ins Repo-Root (oder unter .github/). Sie ist optional — ohne sie greift die Auto-Erkennung. Vollständiges Beispiel mit allen Möglichkeiten:

# Sprache der Bot-Ausgaben erzwingen (sonst automatisch aus PR-Titel/-Text erkannt)
lang: en                  # de | en

# Profil erzwingen (überschreibt die Auto-Erkennung)
profile: ha-config          # ha-config | ha-component | aem-eds | generic

# ODER Checks explizit wählen (statt profile):
# checks: [yamllint, secret-scan, ai-review]

disable: [diff-size]        # einzelne Checks ausschalten

ignore:                   # Pfade global ausnehmen (glob)
  - "**/*.generated.yaml"
  - "vendor/**"

# Per-Check-Optionen:
yamllint:
  changed-lines-only: true
ai-review:
  focus: "native HA-Trigger statt Jinja, entity_id statt device_id"
  severity: major     # nur major-Findings posten (major | minor)

# page-audit aktivieren (Frontend-Repos mit Branch-Previews; {branch} wird ersetzt):
page-audit:
  base_url: "https://{branch}--mein-site--meine-org.aem.page"
  pages: ["/de/de/", "/de/de/products/"]   # max. 5 Seiten; gilt auch für Lighthouse (Label)
Versioniert & pro Branch. Die Config liegt im Repo — ein PR kann sie mitändern, und der PR wird gegen seine eigene .codemole.yml geprüft. Kein zentrales Setting, kein Drift.

🔐 Secrets sicher hinterlegen (z. B. HA-Token für entity-exists)

Manche Checks brauchen ein Secret — z. B. prüft entity-exists Entity-IDs live gegen deine Home-Assistant-Instanz und braucht dafür einen Long-Lived Access Token (HA: Profil → Sicherheit → Token erstellen). Secrets gehören nie im Klartext ins Repo. Deshalb: hier im Browser verschlüsseln (nichts verlässt deinen Browser — WebCrypto gegen den CodeMole-Public-Key) und den Blob in die .codemole.yml legen. Nur der CodeMole-Server kann ihn entschlüsseln.

entity-exists:
  ha_url: "http://homeassistant.local:8123"
  ha_token: "enc:v1:<hier den Blob aus dem Tool>"
Wird nichts gespeichert oder getrackt? Nein. Das Tool oben verschlüsselt per WebCrypto direkt in deinem Browser — der Token verlässt die Seite nie, es gibt keinen Netzwerk-Aufruf, keinen Server-Kontakt, kein Logging. Der CodeMole-Server bekommt ausschließlich den fertigen enc:v1:-Blob zu sehen (in deiner .codemole.yml im Repo) und kann ihn nur entschlüsseln, weil er den passenden privaten Schlüssel hält.

Lieber lokal per Skript? (maximale Sicherheit, kein Browser)

Wer ganz sichergehen will, verschlüsselt offline auf dem eigenen Rechner — nur openssl + der eingebettete öffentliche Schlüssel, kein Netzwerk:

# Skript laden (Klartext — vorher reinschauen!) und ausführen:
curl -O https://web.skycryer.com/codemole/encrypt-secret.sh
bash encrypt-secret.sh          # fragt das Secret versteckt ab, gibt enc:v1:… aus

Das Skript ist bewusst kurz und lesbar: es ruft nur openssl pkeyutl -encrypt mit dem öffentlichen Schlüssel auf und macht keinen einzigen Netzwerk-Aufruf — du kannst es vor dem Ausführen komplett prüfen. Ergebnis 1:1 wie beim Browser-Tool. Skript ansehen →

06Überschreiben & Ausschalten

Kein App-Change nötig. Alles, was ein Check tut — an, aus, enger, ausgenommen — steuerst du aus deinem Repo (.codemole.yml + die Config des jeweiligen Tools). Du musst uns nie bitten, den CodeMole-Code für dein Projekt anzupassen.

Ein Check meldet Unsinn? — so löst du's selbst

  • Findings auf generierten/Vendor-Dateien (Build-Bundles, Fonts, Fixtures) → das steht in der Ignore deines Tools (.stylelintignore, .eslintignore). CodeMole respektiert die und überstimmt sie nie — nichts zu tun.
  • Eine Lint-Regel ist dir zu streng (z. B. at-rule-empty-line-before) → schalt sie in deiner Tool-Config ab (.stylelintrc, .eslintrc). CodeMole nutzt deine Config, nicht seine eigene.
  • Der Check ist hier grundsätzlich irrelevant → disable: [check-name].
  • Nur bestimmte Pfade nerven → ignore: ["pfad/**"].
  • Einmalige Ausnahme (aem-eds) → testing-rules.json im Repo.
Leitregel: Ein Check überstimmt nie die Config deines eigenen Tools. Braucht dein Projekt eine Sonderregel, steht sie in deinem Repo — nicht in unserem App-Code. So bleibt CodeMole für alle Projekte identisch, und jedes Team konfiguriert selbst.

Profil erzwingen

Erkennung daneben? profile: setzen — dann zählt nur das.

Einzelne Checks aus

disable: [diff-size, visual] entfernt Checks aus dem aufgelösten Satz (Profil bleibt sonst gleich).

Pfade ignorieren

ignore: mit Glob-Mustern — generierte Dateien, Vendor-Code, Fixtures. Findings auf diesen Pfaden werden unterdrückt.

Sprache festlegen

Standardmäßig erkennt CodeMole die Sprache automatisch (deutscher PR → deutsche Ausgaben, englischer PR → englische). Mit lang: de bzw. lang: en in der .codemole.yml erzwingst du eine feste Sprache für alle Bot-Ausgaben (Report, Findings, Antworten) — sinnvoll bei sehr kurzen oder gemischtsprachigen PR-Texten.

Wie die Auto-Erkennung funktioniert: Ausgewertet werden PR-Titel + Beschreibung (nicht der Code-Diff — Code ist immer englisch). Eine schnelle, dependency-freie Heuristik vergibt Punkte:

de_score = deutsche Stopwörter (der/die/und/nicht/für…) + Umlaute/ß × 3
en_score = englische Stopwörter (the/and/with/for/this…)
→ "en" wenn en_score > de_score, sonst "de"

Umlaute/ß zählen dreifach, weil ein einziges „ä/ö/ü/ß" ein sehr starkes Deutsch-Signal ist. Bei Gleichstand, leerem Text oder Fehler gilt de (Fail-safe). Die Heuristik ist bewusst simpel — für kurze oder gemischte Texte lieber lang: setzen.

Komplett pausieren

checks: [] (leer) schaltet alles bis auf nichts ab — oder häng das Repo einfach aus der App-Installation aus.

Im Zweifel: Der Report-Kopf zeigt immer, welches Profil und welche Quelle (.codemole.yml oder Auto) gegriffen hat — so siehst du sofort, ob deine Config angekommen ist.

07Neues Repo anbinden

Für ein Repo in der eigenen Organisation ist das Onboarding ein Klick — kein Workflow, kein Webhook, kein Token:

1.  App installieren → Repo anhaken   → fertig, läuft beim nächsten PR
2.  optional: .codemole.yml ins Repo    → Profil/Checks feinsteuern (Sektion 05)

Das Profil wird automatisch erkannt (Sektion 02). Ohne passende Marker läuft generic (diff-size + universelle Checks + KI-Review) — schon das ist brauchbar.

Wann doch Hand anlegen nötig ist

SituationWas zu tun ist
Repo in fremder OrganisationApp auf „Any account" stellen + Org in die Handler-Whitelist (ALLOWED_OWNERS)
Neuer Repo-Typ braucht eigene ChecksProfil in resolve-profile.py + Check-Module ergänzen (sonst: generic)
entity-exists aktivierenha_url + verschlüsselter ha_token in der .codemole.yml (Secrets-Tool) — kein Server-Zugriff nötig
page-audit/lighthouse gewünschtnur page-audit:-Block in der .codemole.yml (Sektion 05)

08Mit dem Bot reden

CodeMole ist kein Einweg-Reporter — er reagiert auf dich, direkt im PR:

Du machst…CodeMole…
Antwortest auf ein Inline-Finding im Diffantwortet im selben Thread — verteidigt den Hinweis mit Begründung oder räumt ihn ein
Dein Einwand ist berechtigtzieht den Hinweis zurück und resolved die Conversation automatisch ✔
Schreibst einen PR-Kommentar mit @the-codemole + Fragebeantwortet die Frage mit Kontext (PR-Beschreibung, Diff, bisheriger Verlauf)
Hängst das Label lighthouse an den PRfährt den vollen Lighthouse-Vergleich und postet die Score-Tabelle
Pushst neue Commitsaktualisiert Report & Findings (bestehende Kommentare werden geupdatet, keine Flut)
Ohne Mention bleibt er still: Auf normale menschliche Kommentare (ohne @the-codemole) reagiert der Bot bewusst nicht — er grätscht nicht in Unterhaltungen.

⚡ Lighthouse richtig einsetzen (Best Practice)

Lighthouse ist der schwere Lauf (~1–2 min pro Seite, Zwei-Pass, max. 2 Seiten) und läuft deshalb nie automatisch — nur wenn du das Label lighthouse setzt. Er läuft komplett auf unserer eigenen Infrastruktur (kein Google/PageSpeed — keine API-Limits).

Wann das Label setzen?
→ einmal, wenn der PR performance-relevant ist:
   CSS-/JS-Umbau, Bild-Handling, neue Blöcke, Lazy-Loading, Fonts

Empfohlener Ablauf
1.  PR fertig entwickeln (normale Checks laufen bei jedem Push mit)
2.  vor dem Review/Merge: Label lighthouse setzen → Score-Tabelle abwarten
3.  Verschlechterung? fixen & pushen — dann Label entfernen + neu setzen für den Re-Run

Gut zu wissen
·  neue Pushes triggern Lighthouse NICHT erneut (bewusst — nur das Label zählt)
·  der Ergebnis-Kommentar wird aktualisiert, nicht dupliziert
·  Scores schwanken CDN-bedingt um ±2–3 Punkte — erst ab größeren Deltas reagieren

09Visual Tests einrichten

Visual-Regression heißt: CodeMole macht einen Screenshot eines Blocks und vergleicht ihn pixelgenau gegen ein gespeichertes Baseline-Bild. Weicht etwas ab, gibt's ein Finding. Technisch ist das Playwright (@playwright/test) — im JUMO-Repo bereits eingerichtet. Du legst nur pro Block eine kleine Test-Datei + ein Referenzbild an. Schritt für Schritt (auch wenn du das noch nie gemacht hast):

1 · Spec anlegen — zwei Layouts (beide gleichwertig)

CodeMole unterstützt zwei Spec-Layouts gleichberechtigt — nimm das, was im Repo schon zum jeweiligen Block passt. In beiden landen die Baselines daneben in <name>.spec.js-snapshots/.

A · Styleguide / nested — tests/visual-styleguide/<block>/

Ordner = Block-Name, Spec heißt ex-N.spec.js. Ein Einzeiler über einen Helper, kein Boilerplate — der Helper rendert die Block-Demo aus dem Pattern-Styleguide (/patterns/styleguide/#demo/<block>/<index>/…) und screenshottet sie:

// tests/visual-styleguide/contact-overlay-role/ex-0.spec.js
import { patternScreenshot } from '../_helpers/pattern-screenshot.js';

// (Block-Id, Beispiel-Index im Styleguide, Snapshot-Name)
patternScreenshot('contact-overlay-role', 0, 'ex-0');

Mehrere Varianten eines Blocks → ex-0, ex-1, … (ein Aufruf bzw. File pro Beispiel-Index).

B · Component-Page / flach — tests/visual/<block>.spec.js

Dateiname = Block-Name, voller Playwright-Spec: öffnet die echte Demo-Seite und screenshottet den Block-Wrapper. Die URL ist projektspezifisch (bei JUMO die Component-Library):

import { test, expect } from '@playwright/test';
import { getBaseUrl } from './helpers/test-setup.js';

// projektspezifische Demo-Seite; ?wcmmode=disabled blendet die AEM-Author-UI aus
const TEST_URL = `${getBaseUrl()}/de/de/components/base/teaser-xl/columns-blockquote`;

test.beforeEach(async ({ page }) => {
  await page.goto(`${TEST_URL}?wcmmode=disabled`);
  await page.waitForLoadState('networkidle');   // (+ Cookie-Banner wegklicken)
});

test('blockquote should match reference', async ({ page }) => {
  const block = page.locator('.blockquote-wrapper').first();
  await expect(block).toHaveScreenshot('blockquote.png', { maxDiffPixels: 500 });
});

2 · Baseline-Bild erzeugen (einmalig pro Spec)

Beim ersten Mal gibt es noch kein Vergleichsbild — einmal erzeugen und mitcommitten (npm-Scripts liegen schon im Repo, gilt für beide Layouts):

# Vergleich laufen:        npm run test:visual
# Baselines schreiben/updaten: npm run test:visual:update
git add tests/**/*-snapshots/   # erzeugte .png-Baselines mit committen

Ohne committete Baseline kann CodeMole nichts vergleichen.

3 · Was CodeMole dann automatisch macht

  • PR ändert Dateien eines Blocks → CodeMole findet dessen Spec (nested: Ordnername = Block · flach: Dateiname = Block) und führt ihn gegen die Preview-Hosts (BASE_URL_DEV / BASE_URL_BRANCH) aus.
  • Screenshot weicht von der Baseline ab → Finding mit Diff-Bild. Passt alles → grün.
  • Atoms (button, text, image, link) triggern via block-deps.json automatisch die Specs aller Organisms, die sie einbetten.
  • Kein Spec für einen geänderten Block? → CodeMole warnt: „Kein Visual-Spec für: <block>". Dann einen anlegen (Layout A oder B).
Kurzfassung: neuer Block → entweder tests/visual-styleguide/<block>/ex-0.spec.js (Einzeiler via patternScreenshot) oder tests/visual/<block>.spec.js (voller Spec) → npm run test:visual:update → die *-snapshots/-PNGs mitcommitten. Den Rest macht CodeMole.