328fcf86d7
P1 Critical: - Add 300ms debounce on editor preview to prevent AST re-parse per keystroke - Hoist Regex constants in ReadingTimeCalculator and TableOfContents - Add URI scheme allowlist to editor onLinkClick (was missing vs reader) - Add MAX_CONTENT_BYTES (100KB) validation before publish P2 Important: - Delete MarkdownSpikeScreen (278 LOC spike artifact) - Delete HighlightCreator (95 LOC, zero callers — YAGNI) - Delete DraftLongTextNoteEvent (162 LOC, no consumers — YAGNI) - Replace ArticleMediaRenderer interface with onLinkClick lambda - Extract inline subscription to FilterBuilders.longFormByAddress() - Replace SimpleDateFormat with thread-safe DateTimeFormatter - Remove dead placeholders (bookmark button, reactions row, unused param) - Cache draft index in memory to avoid redundant disk reads - Add TODO for publish-before-relay-ack issue P3 Nice-to-have: - Remove timestamp from subscription subId for stability - Remove redundant slug ".." removal (regex handles it) - Add metadata field length limits (title 256, summary 1024) - Fix identical isMacOS branches in editor - Validate image URLs (http/https only) in ArticleHeader Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
157 lines
7.4 KiB
Markdown
157 lines
7.4 KiB
Markdown
---
|
|
title: "Long-Form Reads — Manual Testing Sheet"
|
|
date: 2026-03-24
|
|
branch: features/long-form-content
|
|
---
|
|
|
|
# Long-Form Reads — Manual Testing Sheet
|
|
|
|
**Run:** `cd AmethystMultiplatform-long-form && ./gradlew :desktopApp:run`
|
|
|
|
## Pre-Test Setup
|
|
|
|
- [x] App launches without crash
|
|
- [x] Login with existing account (needs relay connections)
|
|
- [x] Navigate to Reads tab in sidebar
|
|
|
|
---
|
|
|
|
## 1. ReadsScreen Feed
|
|
|
|
| # | Test | Steps | Expected | Pass? |
|
|
|---|------|-------|----------|-------|
|
|
| 1.1 | Feed loads articles | Click Reads in sidebar | Long-form article cards appear | |
|
|
| 1.2 | Reading time shown | Check article cards | "X min read" displayed on each card | |
|
|
| 1.3 | Global/Following toggle | Click Global/Following chips | Feed switches between modes | |
|
|
| 1.4 | Article click navigates | Click any article card | ArticleReaderScreen opens (not ThreadScreen) | |
|
|
|
|
---
|
|
|
|
## 2. ArticleReaderScreen
|
|
|
|
| # | Test | Steps | Expected | Pass? |
|
|
|---|------|-------|----------|-------|
|
|
| 2.1 | Article loads | Click article from Reads feed | Content renders with markdown formatting | |
|
|
| 2.2 | Title + metadata | Check header area | Title, author name, reading time, date displayed | |
|
|
| 2.3 | Banner image | Open article with banner | Hero image renders at top (if article has `image` tag) | |
|
|
| 2.4 | Markdown headings | Scroll through article | H1-H3 render with different sizes | |
|
|
| 2.5 | Bold/italic/code | Check formatting | **Bold**, *italic*, `inline code` render correctly | |
|
|
| 2.6 | Code blocks | Find code block | Monospace font, distinct background | |
|
|
| 2.7 | Links clickable | Click a URL link | Opens in system browser | |
|
|
| 2.8 | nostr: links | Click a nostr: link | Does NOT open OS error dialog (scheme filtered) | |
|
|
| 2.9 | Images in content | Find article with images | Images render via Coil | |
|
|
| 2.10 | Back button | Click ← Back | Returns to ReadsScreen | |
|
|
| 2.11 | Content width | Check article body | Max ~680dp centered column | |
|
|
| 2.12 | Loading state | Open article (watch transition) | "Loading article..." shown briefly | |
|
|
| 2.13 | Error state | Open invalid address tag (if testable) | "Article not found" message | |
|
|
|
|
---
|
|
|
|
## 3. Table of Contents
|
|
|
|
| # | Test | Steps | Expected | Pass? |
|
|
|---|------|-------|----------|-------|
|
|
| 3.1 | ToC visible | Open article on wide window (>1100dp) | ToC sidebar appears on left with heading list | |
|
|
| 3.2 | ToC hidden | Resize window to <900dp | ToC sidebar disappears | |
|
|
| 3.3 | Heading hierarchy | Check ToC entries | H2 indented less than H3 | |
|
|
| 3.4 | Click heading | Click a ToC entry | Active entry highlights (scroll-to is TODO) | |
|
|
| 3.5 | No headings | Open article with no markdown headings | ToC sidebar not shown or empty | |
|
|
|
|
---
|
|
|
|
## 4. Article Editor
|
|
|
|
| # | Test | Steps | Expected | Pass? |
|
|
|---|------|-------|----------|-------|
|
|
| 4.1 | Navigate to editor | Drafts tab → New Draft (or via menu) | Editor screen opens with split pane | |
|
|
| 4.2 | Split pane | Check layout | Source left, preview right | |
|
|
| 4.3 | Live preview | Type markdown in source pane | Preview updates after ~300ms | |
|
|
| 4.4 | Toolbar: Bold | Click B button | `**text**` inserted at cursor | |
|
|
| 4.5 | Toolbar: Italic | Click I button | `*text*` inserted | |
|
|
| 4.6 | Toolbar: Heading | Click H button | `## ` inserted | |
|
|
| 4.7 | Toolbar: Link | Click link button | `[text](url)` inserted | |
|
|
| 4.8 | Toolbar: Code | Click code button | Backticks inserted | |
|
|
| 4.9 | Toolbar: Quote | Click quote button | `> ` inserted | |
|
|
| 4.10 | Metadata: Title | Enter title | Title field accepts input, max 256 chars | |
|
|
| 4.11 | Metadata: Summary | Enter summary | Summary field accepts input, max 1024 chars | |
|
|
| 4.12 | Metadata: Tags | Type tag + Enter | Tag chip added | |
|
|
| 4.13 | Metadata: Slug | Enter slug | Auto-sanitized (no special chars) | |
|
|
| 4.14 | Ctrl+S save | Press Ctrl+S (or Cmd+S) | Draft saved to disk | |
|
|
| 4.15 | Back button | Click ← Back | Returns to previous screen | |
|
|
| 4.16 | Preview link safety | Add `[click](javascript:alert(1))` in source | Link NOT clickable in preview (scheme blocked) | |
|
|
|
|
---
|
|
|
|
## 5. Draft Storage
|
|
|
|
| # | Test | Steps | Expected | Pass? |
|
|
|---|------|-------|----------|-------|
|
|
| 5.1 | Draft saved | Create draft, save, check filesystem | `~/.amethyst/drafts/<slug>.md` exists | |
|
|
| 5.2 | Index file | Check filesystem | `~/.amethyst/drafts/index.json` exists with metadata | |
|
|
| 5.3 | Drafts screen | Navigate to Drafts | Lists saved drafts with title, date | |
|
|
| 5.4 | Resume editing | Click a draft in list | Editor opens with content restored | |
|
|
| 5.5 | Delete draft | Click delete on a draft | Confirmation dialog → draft removed | |
|
|
| 5.6 | Slug sanitization | Try slug with `../` or special chars | Slug sanitized to safe characters | |
|
|
| 5.7 | Directory permissions | `ls -la ~/.amethyst/drafts/` | Dir permissions 700 (Unix) | |
|
|
|
|
---
|
|
|
|
## 6. Publish
|
|
|
|
| # | Test | Steps | Expected | Pass? |
|
|
|---|------|-------|----------|-------|
|
|
| 6.1 | Publish button | Fill title + content, click Publish | Event signed and sent to relays | |
|
|
| 6.2 | Publish feedback | After publish | Success snackbar / confirmation | |
|
|
| 6.3 | Published in feed | After publish, check Reads feed | Your article appears in Global feed | |
|
|
| 6.4 | Re-publish (replace) | Edit same draft, publish again | Article updated (same d-tag) | |
|
|
| 6.5 | Size limit | Try publishing >100KB content | Error message about content too large | |
|
|
|
|
---
|
|
|
|
## 7. Typography (Visual)
|
|
|
|
| # | Test | Steps | Expected | Pass? |
|
|
|---|------|-------|----------|-------|
|
|
| 7.1 | Body text | Read article body | Georgia-style serif, ~18sp, generous line height | |
|
|
| 7.2 | Content centered | Check horizontal layout | Content centered with max ~680dp width | |
|
|
| 7.3 | Dark mode | Check dark theme | Text ~#E0E0E0 on dark background, comfortable contrast | |
|
|
| 7.4 | Blockquotes | Find blockquote | Left border/indent, slightly larger text | |
|
|
| 7.5 | Tables | Find table | Renders with columns and rows | |
|
|
|
|
---
|
|
|
|
## 8. Security Checks
|
|
|
|
| # | Test | Steps | Expected | Pass? |
|
|
|---|------|-------|----------|-------|
|
|
| 8.1 | XSS in markdown | Article with `<script>alert(1)</script>` | Rendered as literal text, no execution | |
|
|
| 8.2 | URI scheme: javascript | Link `[x](javascript:alert(1))` in article | Link not clickable / filtered | |
|
|
| 8.3 | URI scheme: file | Link `[x](file:///etc/passwd)` in article | Link not clickable / filtered | |
|
|
| 8.4 | Image URL validation | Article with `file:///etc/passwd` as banner | Image not loaded | |
|
|
| 8.5 | Slug traversal | Set slug to `../../.ssh/keys` | Sanitized to safe string | |
|
|
|
|
---
|
|
|
|
## 9. Edge Cases
|
|
|
|
| # | Test | Steps | Expected | Pass? |
|
|
|---|------|-------|----------|-------|
|
|
| 9.1 | Empty article | Article with no content | Reader shows empty body, no crash | |
|
|
| 9.2 | No relays connected | Disconnect all relays → open article | "Connecting to relays..." loading state | |
|
|
| 9.3 | Very long article | Article with 10k+ words | Renders without freeze (may be slow) | |
|
|
| 9.4 | No banner image | Article without `image` tag | Header renders without banner, no crash | |
|
|
| 9.5 | No author metadata | Article from unknown pubkey | Shows pubkey hex, no profile pic | |
|
|
| 9.6 | Window resize | Resize during article reading | Layout adapts, ToC shows/hides | |
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
_Record any bugs, unexpected behavior, or UX issues here:_
|
|
|
|
| | Issue | Severity | Notes |
|
|
|--|-------|----------|-------|
|
|
| | | | |
|
|
| | | | |
|
|
| | | | |
|