utopia-ui/TESTING_STRATEGY.md
mahula 8e5c6a0907 Revert "docs: add TipTap migration testing strategy"
This reverts commit 64b6e60951dbefee64a54e688396099f370ee150.
2026-01-16 12:20:13 +01:00

15 KiB

Teststrategie für TipTap Markdown-Migration

Zusammenfassung der Analyse

Die TipTap-Migration umfasst folgende Kernkomponenten:

Komponente Beschreibung Komplexität
lib/src/Components/TipTap/utils/preprocessMarkdown.ts 6-stufige Preprocessing-Pipeline Hoch
lib/src/Components/TipTap/utils/simpleMarkdownToHtml.tsx Statische HTML-Konvertierung Mittel
lib/src/Components/TipTap/extensions/Hashtag.tsx Custom Extension mit Tokenizer Mittel
lib/src/Components/TipTap/extensions/ItemMention.tsx Custom Extension mit Tokenizer Mittel
lib/src/Components/TipTap/extensions/VideoEmbed.tsx Block-Element für Videos Mittel
lib/src/Components/Input/RichTextEditor.tsx Haupt-Editor-Komponente Hoch
lib/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx Read-Only Editor Mittel
lib/src/Components/Map/Subcomponents/ItemPopupComponents/TextViewStatic.tsx Lightweight Static Renderer Niedrig

Empfohlene Teststrategie: Testing Pyramid

                    ┌─────────────────┐
                    │   E2E Tests     │  ← Wenige, kritische User Journeys
                    │   (Cypress)     │
                    └────────┬────────┘
                             │
                    ┌────────┴────────┐
                    │  Integration    │  ← TipTap + Extensions zusammen
                    │  Tests (Vitest) │
                    └────────┬────────┘
                             │
         ┌───────────────────┴───────────────────┐
         │           Unit Tests (Vitest)          │  ← Utility-Funktionen isoliert
         │  preprocessMarkdown, simpleMarkdownToHtml  │
         └────────────────────────────────────────┘

Begründung der Strategie

  1. Unit Tests für Utility-Funktionen (Hauptfokus)

    • preprocessMarkdown.ts und simpleMarkdownToHtml.tsx sind pure Funktionen ohne Abhängigkeiten
    • Extrem schnelle Ausführung, hohe Coverage möglich
    • Einfach zu warten und zu debuggen
    • Hier liegt die meiste Geschäftslogik der Markdown-Verarbeitung
  2. Integration Tests für TipTap Extensions

    • Extensions benötigen einen Editor-Kontext
    • Testen der Markdown ↔ JSON ↔ HTML Roundtrips
    • Mäßiger Aufwand, gute Fehlererkennung
  3. E2E Tests nur für kritische User Journeys

    • Hoher Wartungsaufwand
    • Langsame Ausführung
    • Für Smoke Tests und Regressionsschutz

Detaillierte Testfälle

1. Unit Tests für preprocessMarkdown.ts

A) convertNakedUrls

Testfall Input Expected Output
Happy Path Check https://example.com out Check [example.com](https://example.com) out
www entfernen https://www.example.com [example.com](https://example.com)
URL in Markdown-Link (Skip) [link](https://example.com) Unverändert
URL in Autolink (Skip) <https://example.com> Unverändert
Mehrere URLs https://a.com and https://b.com Beide konvertiert
URL am Satzende mit Punkt Visit https://example.com. Punkt nicht Teil der URL
URL mit Klammern (https://example.com) Klammern korrekt behandelt
URL mit Query-Params https://example.com?a=1&b=2 Vollständig konvertiert
Testfall Input Expected Output
YouTube Standard <https://www.youtube.com/watch?v=abc123> <video-embed provider="youtube" video-id="abc123">
YouTube Short URL <https://youtu.be/abc123> Korrekt konvertiert
YouTube Markdown Link [Video](https://youtube.com/watch?v=abc123) Korrekt konvertiert
Rumble Embed <https://rumble.com/embed/xyz789> <video-embed provider="rumble"...>
URL mit Extra-Params <https://youtube.com/watch?v=abc&t=120> Nur Video-ID extrahiert
Kein Video-Link <https://example.com> Unverändert
Gemischter Content Text <https://youtu.be/x> more Nur Video konvertiert

C) preprocessHashtags

Testfall Input Expected Output
Einfacher Hashtag Hello #world Hello <span data-hashtag...>#world</span>
Hashtag mit Umlauten #München Korrekt erkannt
Hashtag mit Zahlen #test123 Korrekt erkannt
Hashtag in Link (Skip) [#tag](#anchor) Unverändert
Hashtag nach Klammer (Skip) (#section) Unverändert
Mehrere Hashtags #one #two #three Alle konvertiert
Ungültiger Hashtag # Unverändert (kein Text)
Hashtag mit Underscore #my_tag Korrekt erkannt

D) preprocessItemMentions

Testfall Input Expected Output
Standard Format [@Person](/item/uuid-123) <span data-item-mention...>@Person</span>
Mit Layer (Legacy) [@Name](/item/layer/uuid) Korrekt konvertiert
Relativer Pfad [@Name](item/uuid) Korrekt konvertiert
Mehrere Mentions [@A](/item/1) und [@B](/item/2) Beide konvertiert
Kein Item-Link [@Name](/other/path) Unverändert
UUID Case-Insensitive [@Name](/item/ABC-def-123) Korrekt erkannt
Label mit Sonderzeichen [@Max Müller](/item/uuid) Korrekt konvertiert

E) truncateMarkdown

Testfall Input Limit Expected
Unter Limit Short text 100 Unverändert
Über Limit (Plain) A very long text... 10 A very lo...
Hashtag nicht schneiden Text #verylonghashtag more 15 Vollständiger Hashtag oder davor abschneiden
Mention nicht schneiden Hi [@Person](/item/x) bye 10 Vollständige Mention oder davor
Link nicht schneiden See [link](url) more 8 Vollständiger Link oder davor
Newlines nicht zählen Line1\n\nLine2 10 Newlines ignoriert bei Zählung
Gemischter Content #tag [@m](/item/1) text 20 Tokens atomar

F) removeMarkdownSyntax

Testfall Input Expected
Bold **bold** bold
Italic *italic* italic
Headers # Heading Heading
Links [text](url) text
Item Mentions erhalten [@Name](/item/x) Erhalten
Bilder entfernen ![alt](img.png) Leer
Code `code` code

2. Unit Tests für simpleMarkdownToHtml.tsx

Testfall Input Expected HTML
Bold **bold** <strong>bold</strong>
Italic *italic* <em>italic</em>
Link [text](url) <a href="url">text</a>
External Link [text](https://ext.com) <a href="..." target="_blank"...>
Header H1 # Title <h1>Title</h1>
Header H2-H6 ## ... ###### Entsprechende h-Tags
Inline Code `code` <code>code</code>
Blockquote > quote <blockquote>quote</blockquote>
Video Embed <video-embed provider="youtube"...> <iframe src="youtube-nocookie...">
Hashtag mit Farbe Preprocessed Hashtag + Tag mit color Style mit korrekter Farbe
Item Mention Preprocessed Mention + Item Link mit korrekter Farbe
XSS Prevention <script>alert('xss')</script> Escaped, kein Script-Tag
Newlines Line1\n\nLine2 </p><p> Trennung

3. Integration Tests für TipTap Extensions

Diese Tests benötigen einen TipTap Editor-Kontext. Setup via @tiptap/core:

// Test-Setup Beispiel
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import { Markdown } from '@tiptap/markdown'
import { Hashtag } from './Hashtag'

A) Hashtag Extension

Testfall Beschreibung
Markdown → JSON #tag wird zu { type: 'hashtag', attrs: { label: 'tag' } }
JSON → Markdown Hashtag-Node wird zu #tag serialisiert
HTML Parse <span data-hashtag data-label="x">#x</span> wird erkannt
HTML Render Node rendert korrekte HTML-Struktur
Tokenizer Start /(?<!\[)#[a-zA-Z]/ matched korrekt
Click Handler (read-only) onTagClick wird aufgerufen
No Click (editable) Kein Click-Handler im Edit-Modus

B) ItemMention Extension

Testfall Beschreibung
Markdown → JSON [@Name](/item/uuid) wird zu korrektem Node
JSON → Markdown Node wird korrekt serialisiert
Mit Layer Legacy-Format wird korrekt geparst
UUID Case-Insensitive Groß-/Kleinschreibung egal
Farbe aus Item getItemColor wird korrekt verwendet

C) VideoEmbed Extension

Testfall Beschreibung
YouTube Parse Autolink zu Video-Node
Rumble Parse Embed-URL zu Video-Node
Iframe Render Korrekter youtube-nocookie.com Embed
Paste Handler Video-URL wird beim Einfügen erkannt

D) Roundtrip Tests

Testfall Flow
Markdown Roundtrip Markdown → Editor → getMarkdown() → identisch
Komplexer Content Text + #tag + @mention + Video → Roundtrip
Preserve Formatting Bold, Italic, Listen bleiben erhalten

4. Component Tests (React Testing Library)

RichTextEditor

Testfall Beschreibung
Render mit Default Editor rendert mit initialem Content
onChange Callback updateFormValue erhält Markdown
Placeholder Placeholder wird angezeigt
Hashtag Suggestion # triggert Suggestion Popup
Item Mention Suggestion @ triggert Suggestion Popup
Keyboard Navigation Arrow Keys in Suggestions
Suggestion Select Enter fügt Tag ein

TextView

Testfall Beschreibung
Read-Only Editor ist nicht editierbar
Truncation Langer Text wird gekürzt + ...
Hashtag Click addFilterTag wird aufgerufen
Internal Link React Router Navigation
External Link Neuer Tab

TextViewStatic

Testfall Beschreibung
HTML Render Markdown wird zu HTML
Hashtag Farbe Farbe aus Tags-Array
Item Mention Link Korrekter /item/ Link
Video Embed Iframe wird gerendert

5. E2E Tests (Cypress)

Nur kritische User Journeys - minimaler Scope für maximale Stabilität:

A) Editor Flow

describe('Rich Text Editor', () => {
  it('should create and save content with hashtags and mentions', () => {
    // 1. Neues Item erstellen
    // 2. Text eingeben mit #tag und @mention
    // 3. Speichern
    // 4. Popup öffnen und Rendering prüfen
  })

  it('should handle video embeds', () => {
    // YouTube URL einfügen → Video-Embed sichtbar
  })
})

B) Display Flow

describe('Text Display', () => {
  it('should render hashtags clickable in popup', () => {
    // Item mit Hashtag öffnen
    // Hashtag klicken
    // Filter wird aktiviert
  })

  it('should navigate to mentioned items', () => {
    // Item mit @mention öffnen
    // Mention klicken
    // Navigation zum verlinkten Item
  })
})

Priorisierung der Implementierung

Phase 1: Unit Tests (Höchste Priorität)

Datei Geschätzte Tests Grund
preprocessMarkdown.spec.ts ~40 Tests Pure Functions, schnell, hohe Coverage
simpleMarkdownToHtml.spec.ts ~25 Tests Pure Function, XSS-kritisch

Phase 2: Integration Tests

Datei Geschätzte Tests Grund
Hashtag.spec.ts ~15 Tests Custom Extension mit Tokenizer
ItemMention.spec.ts ~15 Tests Custom Extension mit Tokenizer
VideoEmbed.spec.ts ~10 Tests Block-Element

Phase 3: Component Tests

Datei Geschätzte Tests Grund
RichTextEditor.spec.tsx ~15 Tests Haupt-Editor
TextView.spec.tsx ~10 Tests Read-Only Variante
TextViewStatic.spec.tsx ~8 Tests Lightweight Renderer

Phase 4: E2E Tests (Niedrigste Priorität)

Datei Geschätzte Tests Grund
editor-flow.cy.ts 3-5 Tests Kritische User Journey

Edge Cases und Error Handling

Besonders wichtige Grenzfälle

  1. Leerer Input - Alle Funktionen sollten mit '', null, undefined umgehen
  2. Sehr langer Text - Performance bei >10.000 Zeichen
  3. Verschachtelte Syntax - **#bold-hashtag**, [[@mention](/item/x)](url)
  4. Unicode - Emojis, RTL-Text, Sonderzeichen
  5. Malformed Markdown - Ungeschlossene Tags: **bold, [link(
  6. XSS Vectors - <script>, Event-Handler in Links
  7. Concurrent Tokens - #tag1#tag2 (ohne Leerzeichen)
  8. URLs in Code-Blöcken - Sollten nicht konvertiert werden

Test-Setup Empfehlungen

Vitest Setup erweitern (lib/setupTest.ts)

import '@testing-library/jest-dom'
import { vi } from 'vitest'

// TipTap DOM-Mocks für Vitest (basierend auf Community-Empfehlungen)
Range.prototype.getBoundingClientRect = () => ({
  bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0, x: 0, y: 0,
  toJSON: vi.fn(),
})
Range.prototype.getClientRects = () => ({
  item: () => null, length: 0, [Symbol.iterator]: vi.fn(),
})
Document.prototype.elementFromPoint = vi.fn()

Fazit

Die empfohlene Strategie fokussiert auf Unit Tests für die Markdown-Utility-Funktionen, da hier:

  • Die meiste Geschäftslogik liegt
  • Pure Functions einfach testbar sind
  • Hohe Coverage mit geringem Aufwand erreichbar ist

E2E Tests sollten auf ein Minimum beschränkt bleiben und nur kritische User Journeys abdecken. Dies folgt der Testing Pyramid Best Practice und sorgt für:

  • Schnelles Feedback (Unit Tests <1s)
  • Hohe Wartbarkeit (keine flaky UI-Tests)
  • Gute Fehlerlokalisierung (isolierte Tests)

Quellen