This commit is contained in:
team3
2026-09-22 13:14:06 +02:00
parent a62b587340
commit 2472e89b9c
15 changed files with 461 additions and 28 deletions

View File

@@ -7,7 +7,8 @@ rm -rf /tmp/desktop-app-nested/data/desktop-app
# Mit SYNC_URL bekommt die Test-Shell einen Server-Zugang (Token "test")
if [ -n "$SYNC_URL" ]; then
mkdir -p /tmp/desktop-app-nested/data/desktop-app
echo "{\"url\": \"$SYNC_URL\", \"token\": \"test\"}" > /tmp/desktop-app-nested/data/desktop-app/sync.json
rm -rf /tmp/desktop-app-nested/claude-projects
echo "{\"url\": \"$SYNC_URL\", \"token\": \"test\", \"files\": \"/tmp/desktop-app-nested/claude-projects\"}" > /tmp/desktop-app-nested/data/desktop-app/sync.json
fi
sleep 2
(nohup "$DIR/nested.sh" "$1" > /tmp/desktop-app-nested.log 2>&1 &)

View File

@@ -162,9 +162,15 @@ check "Notiz mit Überschrift wird nicht abgeschnitten" "$(val '(l => Math.round
check "Textfarbe aus dem Stil bleibt erhalten" "$(val 'notes._content.clutter_text.get_attributes() !== null')" "true"
click 'notes._viewScroll'
check "Klick in die Fläche startet den Editor" "$(val '[notes._editing, global.stage.get_key_focus() === notes._editor.clutter_text].join(" ")')" "true true"
type_ " mehr"
type_ " mehr"; key Return; type_ "- eins"; key Return
check "Enter führt die Liste fort" "$(val 'JSON.stringify(notes._editor.get_text().slice(-14))')" '"mehr\n- eins\n- "'
type_ "zwei"; key Return; key Return
check "Enter auf leerem Punkt beendet die Liste" "$(val 'JSON.stringify(notes._editor.get_text().slice(-14))')" '"- eins\n- zwei\n"'
type_ "1. a"; key Return
check "Nummerierte Liste zählt weiter" "$(val 'JSON.stringify(notes._editor.get_text().slice(-8))')" '"1. a\n2. "'
key Return
click 'find(notes, "Allgemein")'
check "Wechsel speichert sofort" "$(val 'read(store.notesDir + "/Testnotiz.md").endsWith("**Notiz** mehr") + " " + notes._selected.name')" "true Allgemein"
check "Wechsel speichert sofort" "$(val 'read(store.notesDir + "/Testnotiz.md").endsWith("- zwei\\n1. a\\n") + " " + notes._selected.name')" "true Allgemein"
mclick 'find(notes, "Testnotiz")'
check "Mittlere Taste öffnet Umbenennen am Zeiger" "$(val 'labels(grabbed())[0] + " " + entries(grabbed())[0]')" "Umbenennen Testnotiz"
check "Popover am Mauszeiger (Notiz)" "$(atpointer)" "true"

View File

@@ -1,6 +1,7 @@
// Tests der reinen Module: gjs -m dev/tests/run.js
import * as D from '../../desktop@marha.de/dates.js';
import {renderMarkdown} from '../../desktop@marha.de/markdown.js';
import {continueList, renderMarkdown} from '../../desktop@marha.de/markdown.js';
import {batches, fileStamp, planPull, planPush, stampToSeconds} from '../../desktop@marha.de/files.js';
let failed = 0;
function eq(actual, expected, name) {
@@ -61,6 +62,33 @@ eq(renderMarkdown('---'), '<span foreground="#666666">────────
eq(renderMarkdown('~~weg~~'), '<s>weg</s>', 'md strike');
eq(renderMarkdown('a\n\nb'), 'a\n\nb', 'md leere Zeile bleibt');
eq(continueList('- eins'), '- ', 'Liste: nächster Punkt');
eq(continueList(' * eins'), ' * ', 'Liste: Einrückung und Marker bleiben');
eq(continueList('- [x] fertig'), '- [ ] ', 'Liste: Checkbox leer weiter');
eq(continueList('3. drei'), '4. ', 'Liste: Nummer zählt hoch');
eq(continueList('- '), '', 'Liste: leerer Punkt beendet');
eq(continueList('2. '), '', 'Liste: leerer nummerierter Punkt beendet');
eq(continueList('nur Text'), null, 'Liste: kein Marker');
eq(continueList('---'), null, 'Liste: Trennlinie ist keine Liste');
const H = 3600, NOW = 1_800_000_000;
eq(fileStamp(NOW), '2027-01-15T08:00:00.000Z', 'fileStamp');
eq(stampToSeconds('2027-01-15T08:00:00.000Z'), NOW, 'stampToSeconds');
const local = new Map([['a/s.jsonl', NOW - 2 * H], ['a/frisch.jsonl', NOW - 10], ['m/MEMORY.md', NOW - 3 * H]]);
eq(planPush(local, {'m/MEMORY.md': fileStamp(NOW - 3 * H), 'weg.md': fileStamp(NOW - 5 * H), 'alt.md': null}, NOW, H),
{upload: [{id: 'a/s.jsonl', updated: fileStamp(NOW - 2 * H)}], remove: ['weg.md']},
'planPush: nur ruhende, geänderte Dateien hoch, Verschwundene als Tombstone, Gemeldete nicht nochmal');
eq(planPull([
{id: 'a/s.jsonl', updated: fileStamp(NOW - 1 * H), content: 'x'},
{id: 'a/s.jsonl', updated: fileStamp(NOW - 3 * H), content: 'x'},
{id: 'neu.md', updated: fileStamp(NOW), content: 'y'},
{id: 'm/MEMORY.md', updated: fileStamp(NOW), deleted: true},
{id: 'nie.md', updated: fileStamp(NOW), deleted: true},
], local).map(p => `${p.file.id}:${p.action}`),
['a/s.jsonl:write', 'a/s.jsonl:skip', 'neu.md:write', 'm/MEMORY.md:delete', 'nie.md:skip'], 'planPull: neuer schreibt, älter überspringt, Tombstone löscht');
eq(batches([1, 2, 3, 4], n => n, 5), [[1, 2], [3], [4]], 'batches nach Größe');
eq(batches([], n => n, 5), [], 'batches leer');
print(failed ? `\n${failed} FEHLER` : '\nalle Tests ok');
if (failed)
imports.system.exit(1);

View File

@@ -153,6 +153,33 @@ check "Ordner vom Server gelöscht" "$(val 'GLib.file_test(store.notesDir + "/Vo
sleep 3
check "Lokal gelöschter Ordner auf dem Server als gelöscht" "$(folders | python3 -c 'import sys; print("Voll/Tief\x27, True" in sys.stdin.read())')" "True"
echo "=== Dateien: Claude-Sitzungen zwischen PCs"
FILES=/tmp/desktop-app-nested/claude-projects
files() { api -X POST "$URL/api/sync" -d '{"since": 0, "files": []}' | python3 -c 'import json,sys; print(sorted((f["id"], bool(f.get("deleted"))) for f in json.load(sys.stdin)["files"]))'; }
filescycle() { "$E" "$PRE ext._sync._filesCycle(); 'ok'" >/dev/null; sleep 3; }
mkdir -p "$FILES/-home-x/memory"
echo '{"a":1}' > "$FILES/-home-x/alt.jsonl"; touch -d '2 hours ago' "$FILES/-home-x/alt.jsonl"
echo '# Memo' > "$FILES/-home-x/memory/MEMORY.md"; touch -d '90 minutes ago' "$FILES/-home-x/memory/MEMORY.md"
echo 'läuft noch' > "$FILES/-home-x/frisch.jsonl"
filescycle
check "Ruhende Dateien auf dem Server, frische nicht" "$(files)" "[('-home-x/alt.jsonl', False), ('-home-x/memory/MEMORY.md', False)]"
check "Normale Anfrage bekommt keine Dateien" "$(api -X POST "$URL/api/sync" -d '{"since": 0}' | python3 -c 'import json,sys; d=json.load(sys.stdin); print("files" in d, "events" in d)')" "False True"
check "Stand der Dateien getrennt gemerkt" "$(val 'Object.keys(state().files.pushed).sort().join(",") + " " + (state().files.cursor > 0)')" "-home-x/alt.jsonl,-home-x/memory/MEMORY.md true"
push '{"files": [{"id": "-home-y/s2.jsonl", "updated": "2026-09-01T00:00:00.000Z", "content": "vom anderen PC"}]}'
filescycle
check "Datei vom Server geschrieben, Änderungszeit übernommen" "$(cat "$FILES/-home-y/s2.jsonl") $(stat -c %Y "$FILES/-home-y/s2.jsonl")" "vom anderen PC $(date -u -d 2026-09-01T00:00:00Z +%s)"
echo '{"a":2}' > "$FILES/-home-x/alt.jsonl"; touch -d '70 minutes ago' "$FILES/-home-x/alt.jsonl"
push '{"files": [{"id": "-home-x/alt.jsonl", "updated": "'$(date -u -d '3 hours ago' +%Y-%m-%dT%H:%M:%S.000Z)'", "content": "veraltet"}]}'
filescycle
check "Lokal neuer gewinnt und geht hoch" "$(api -X POST "$URL/api/sync" -d '{"since": 0, "files": []}' | python3 -c 'import json,sys; print([f["content"].strip() for f in json.load(sys.stdin)["files"] if f["id"] == "-home-x/alt.jsonl"])')" "['{\"a\":2}']"
rm "$FILES/-home-x/memory/MEMORY.md"
push '{"files": [{"id": "-home-y/s2.jsonl", "updated": "'$(now)'", "deleted": true}]}'
filescycle
check "Gelöscht in beide Richtungen" "$(files | python3 -c 'import sys; print("MEMORY.md\x27, True" in sys.stdin.read())') $([ -e "$FILES/-home-y/s2.jsonl" ] && echo da || echo weg)" "True weg"
check "Gemeldete Löschung nicht nochmal" "$(val 'state().files.pushed["-home-x/memory/MEMORY.md"] === null && state().files.pushed["-home-y/s2.jsonl"] === null')" "true"
filescycle
check "Zweiter Lauf schickt nichts erneut" "$(files)" "[('-home-x/alt.jsonl', False), ('-home-x/memory/MEMORY.md', True), ('-home-y/s2.jsonl', True)]"
echo "=== Server weg und wieder da"
kill $SERVER_PID; sleep 1
"$E" "$PRE store.saveTodo({title: 'Offline', date: null, time: null}); 'ok'" >/dev/null