ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git), ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
189 lines
6.2 KiB
Bash
Executable File
189 lines
6.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# MARK-05, MARK-10 live: folder tree, outline, search, recent.
|
|
set -euo pipefail
|
|
|
|
root=$(cd "$(dirname "$0")/../.." && pwd)
|
|
[ "$(uname)" = Darwin ] || { echo "SKIP: macOS only"; exit 0; }
|
|
[ -d "${ELY_CEF:-$root/engines/cef}/Chromium Embedded Framework.framework" ] || {
|
|
echo "SKIP: no CEF; run scripts/fetch-cef.sh"
|
|
exit 0
|
|
}
|
|
command -v npm >/dev/null || { echo "SKIP: no npm"; exit 0; }
|
|
work=$(mktemp -d)
|
|
app=
|
|
trap 'if [ -n "$app" ]; then kill "$app" 2>/dev/null || true; wait "$app" 2>/dev/null || true; fi; rm -rf "$work"' EXIT
|
|
fail() {
|
|
echo "FAIL: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
"$root/scripts/build-mark-content.sh" >"$work/build" 2>&1 || fail "build: $(tail -n 5 "$work/build")"
|
|
cargo build -q -p ely-mark --manifest-path "$root/Cargo.toml"
|
|
for tool in ax-values ax-press click; do
|
|
swiftc -O -o "$work/$tool" "$root/tests/scripts/$tool.swift"
|
|
done
|
|
mkdir -p "$work/notes/sub"
|
|
# A heading inside a quote Milkdown rewrites: one raw block.
|
|
printf '# Alpha\n\nSome text.\n\n> ## Beta ##\n>\n> More words here.\n' >"$work/notes/a.md"
|
|
# A hit hidden in a link's address comes first.
|
|
printf '# Other\n\nwords [link](words)\nwords too\n' >"$work/notes/sub/b.md"
|
|
printf 'banana\nana too\n\n[link](words)\nwords two' >"$work/notes/d.md"
|
|
printf -- '---\ntitle: T\n---\n# Heading\n\nneedle here.\n\n# Other\n\nother paragraph.\n' >"$work/notes/c.md"
|
|
printf 'words\n' >"$work/notes/skip.txt"
|
|
# A file the search cannot read.
|
|
printf 'words\n' >"$work/notes/bad.md"
|
|
chmod 000 "$work/notes/bad.md"
|
|
launch() {
|
|
ELY_LOG=info ELY_LOCALE=en ELY_NO_DIALOG=1 HOME="$work" \
|
|
"$root/target/debug/ely-mark" "$@" >"$work/out" 2>"$work/err" &
|
|
app=$!
|
|
}
|
|
waits() {
|
|
for _ in $(seq 120); do
|
|
grep -qF "$1" "$work/err" && return 0
|
|
kill -0 "$app" 2>/dev/null || fail "ElyMark quit: $(tail -n 5 "$work/err")"
|
|
sleep 0.25
|
|
done
|
|
fail "never saw '$1': $(grep -E 'mark|office' "$work/err" | tail -n 5)"
|
|
}
|
|
# Sets x and y to element `$1`'s center.
|
|
at() {
|
|
local found
|
|
for _ in $(seq 40); do
|
|
found=$("$work/ax-values" "$app" | awk -F'\t' -v want="title=$1" '$1 != "AXMenuItem" && $3 == want { sub("at=", "", $5); print $5; exit }')
|
|
if [ -n "$found" ]; then
|
|
IFS=, read -r x y <<<"$found"
|
|
return 0
|
|
fi
|
|
sleep 0.25
|
|
done
|
|
fail "no '$1' in the AX tree"
|
|
}
|
|
key() { osascript -e "tell application \"System Events\" to $1" -e 'delay 0.8' >/dev/null; }
|
|
press() { [ "$("$work/ax-press" "$app" "$1")" = pressed ] || fail "could not press $1"; sleep 0.8; }
|
|
saves() {
|
|
local before
|
|
before=$(grep -c "office: saved" "$work/err" || true)
|
|
key 'keystroke "s" using command down'
|
|
for _ in $(seq 40); do
|
|
[ "$(grep -c "office: saved" "$work/err" || true)" -gt "$before" ] && return 0
|
|
sleep 0.25
|
|
done
|
|
fail "no save"
|
|
}
|
|
|
|
launch "$work/notes" "$work/notes/a.md"
|
|
waits 'mark: shown as'
|
|
[ "$("$work/ax-press" $$ none)" != untrusted ] || fail "needs Accessibility permission"
|
|
osascript -e "tell application \"System Events\" to set frontmost of (first process whose unix id is $app) to true" -e 'delay 1' >/dev/null
|
|
# Tree rows are tree items; Return opens a picked one.
|
|
at b.md
|
|
"$work/click" "$x" "$y"
|
|
key 'key code 36'
|
|
waits "opened b.md"
|
|
# An outline heading takes the caret there.
|
|
press a.md
|
|
press Outline
|
|
at Outline
|
|
"$work/click" "$((x - 64))" "$((y + 64))"
|
|
key 'keystroke "Z"'
|
|
saves
|
|
grep -qx '> ## ZBeta ##' "$work/notes/a.md" || fail "outline jump: $(cat "$work/notes/a.md")"
|
|
# Folder search finds lines; a hit opens there.
|
|
press Search
|
|
at Folder
|
|
"$work/click" "$x" "$y"
|
|
press "This file"
|
|
"$work/click" "$x" "$((y - 37))"
|
|
key 'keystroke "words"'
|
|
key 'key code 36'
|
|
press Folder
|
|
at "> More words here."
|
|
at "words too"
|
|
at "bad.md was not searched"
|
|
! "$work/ax-values" "$app" | grep -q "title=words " || fail "a .txt file was searched"
|
|
chmod 600 "$work/notes/bad.md"
|
|
# Inside a paragraph the caret lands on the hit's line.
|
|
press "words too"
|
|
key 'key code 36'
|
|
key 'keystroke "Q"'
|
|
saves
|
|
[ "$(cat "$work/notes/sub/b.md")" = $'# Other\n\nwords [link](words)\nQwords too' ] || fail "search hit: $(cat "$work/notes/sub/b.md")"
|
|
# Lines count from the file's top, past front matter.
|
|
press Search
|
|
at Folder
|
|
"$work/click" "$x" "$((y - 37))"
|
|
key 'keystroke "a" using command down'
|
|
key 'keystroke "needle"'
|
|
key 'key code 36'
|
|
at "needle here."
|
|
press "needle here."
|
|
key 'key code 36'
|
|
waits "opened c.md"
|
|
sleep 1
|
|
key 'keystroke "Q"'
|
|
saves
|
|
grep -qx 'Qneedle here.' "$work/notes/c.md" || fail "front matter offset: $(cat "$work/notes/c.md")"
|
|
# This file's results follow the shown tab.
|
|
press Search
|
|
press "This file"
|
|
at "Qneedle here."
|
|
press b.md
|
|
sleep 1.5
|
|
! "$work/ax-values" "$app" | grep -q "title=Qneedle here." || fail "results stayed with the last tab"
|
|
# Overlapping hits; a hit hidden in a link's address.
|
|
press Search
|
|
at Folder
|
|
"$work/click" "$x" "$((y - 37))"
|
|
key 'keystroke "a" using command down'
|
|
key 'keystroke "ana"'
|
|
key 'key code 36'
|
|
press Folder
|
|
press "ana too"
|
|
key 'key code 36'
|
|
waits "opened d.md"
|
|
sleep 1
|
|
key 'keystroke "Q"'
|
|
saves
|
|
[ "$(cat "$work/notes/d.md")" = $'banana\nQana too\n\n[link](words)\nwords two' ] || fail "overlapping hit: $(cat "$work/notes/d.md")"
|
|
press Search
|
|
at Folder
|
|
"$work/click" "$x" "$((y - 37))"
|
|
key 'keystroke "a" using command down'
|
|
key 'keystroke "words"'
|
|
key 'key code 36'
|
|
press Folder
|
|
press "[link](words)"
|
|
key 'key code 36'
|
|
sleep 1
|
|
key 'keystroke "R"'
|
|
saves
|
|
[ "$(cat "$work/notes/d.md")" = $'banana\nQana too\n\n[Rlink](words)\nwords two' ] || fail "hidden hit: $(cat "$work/notes/d.md")"
|
|
# A rename shows in the folder's tree.
|
|
press Files
|
|
key 'key code 120'
|
|
key 'keystroke "a" using command down'
|
|
key 'keystroke "moved.md"'
|
|
key 'key code 36'
|
|
waits "renamed"
|
|
at moved.md
|
|
! "$work/ax-values" "$app" | grep -q "^AXRow.*title=d.md " || fail "the tree kept the old name"
|
|
kill "$app"
|
|
wait "$app" 2>/dev/null || true
|
|
app=
|
|
# The recent list outlives the app.
|
|
python3 -c 'import json,sys; l=json.load(open(sys.argv[1])); sys.exit(0 if any(p.endswith("notes") for p in l) and any(p.endswith("a.md") for p in l) else 1)' \
|
|
"$(find "$work/Library" -name recent.json)" || fail "recent: $(cat "$(find "$work/Library" -name recent.json)")"
|
|
launch
|
|
waits 'ElyMark window open'
|
|
at notes
|
|
press notes
|
|
key 'key code 36'
|
|
waits "opens folder"
|
|
at Outline
|
|
kill "$app"
|
|
wait "$app" 2>/dev/null || true
|
|
app=
|
|
! grep -q "panicked" "$work/err" || fail "panics: $(grep -m3 panicked "$work/err")"
|
|
echo "ok: mark places"
|