ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git), ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
224 lines
8.4 KiB
Bash
Executable File
224 lines
8.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# MARK-06, MARK-07 live: find, replace, Tab, lists, tables, images.
|
|
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 drop; do
|
|
swiftc -O -o "$work/$tool" "$root/tests/scripts/$tool.swift"
|
|
done
|
|
mkdir "$work/notes"
|
|
printf 'alpha beta alpha\n\ngamma alpha\n' >"$work/notes/t.md"
|
|
# A two-pixel PNG.
|
|
python3 -c 'import struct,zlib,sys
|
|
c=lambda t,d: struct.pack(">I",len(d))+t+d+struct.pack(">I",zlib.crc32(t+d))
|
|
open(sys.argv[1],"wb").write(b"\x89PNG\r\n\x1a\n"+c(b"IHDR",struct.pack(">IIBBBBB",2,2,8,2,0,0,0))+c(b"IDAT",zlib.compress(b"\0"+b"\xff\0\0"*2+b"\0"+b"\0\xff\0"*2))+c(b"IEND",b""))' "$work/red pic.png"
|
|
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)"
|
|
}
|
|
key() { osascript -e "tell application \"System Events\" to $1" -e 'delay 0.8' >/dev/null; }
|
|
press() {
|
|
for _ in $(seq 20); do
|
|
[ "$("$work/ax-press" "$app" "$1")" = pressed ] && { sleep 0.8; return 0; }
|
|
sleep 0.25
|
|
done
|
|
fail "could not press $1"
|
|
}
|
|
# Clicks the find box's `$1`th text field, top first.
|
|
field() {
|
|
local found
|
|
found=$("$work/ax-values" "$app" | awk -F'\t' '$1 == "AXTextField" { sub("at=", "", $5); print $5 }' | sort -t, -k2 -n | sed -n "$1p")
|
|
[ -n "$found" ] || fail "no find field $1"
|
|
IFS=, read -r x y <<<"$found"
|
|
"$work/click" "$x" "$y"
|
|
sleep 0.5
|
|
}
|
|
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"
|
|
}
|
|
is() { [ "$(cat "$work/notes/t.md")" = "$1" ] || fail "$2: $(cat "$work/notes/t.md")"; }
|
|
|
|
launch "$work/notes/t.md"
|
|
waits 'mark: shown as "visual"'
|
|
[ "$("$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 2' >/dev/null
|
|
# Replace all in the visual editor.
|
|
key 'keystroke "f" using command down'
|
|
key 'keystroke "alpha"'
|
|
press "Toggle replace"
|
|
field 2
|
|
key 'keystroke "omega"'
|
|
press "Replace all"
|
|
saves
|
|
is $'omega beta omega\n\ngamma omega' "replace all"
|
|
# A regular expression in the source editor.
|
|
press Source
|
|
waits 'mark: shown as "source"'
|
|
field 1
|
|
key 'keystroke "a" using command down'
|
|
key 'keystroke "g.mma"'
|
|
press "Regular expression"
|
|
field 2
|
|
key 'keystroke "a" using command down'
|
|
key 'keystroke "delta"'
|
|
press Replace
|
|
saves
|
|
is $'omega beta omega\n\ndelta omega' "regex replace"
|
|
# Tab indents the selected line; Esc closes the box first.
|
|
field 1
|
|
key 'key code 53'
|
|
key 'key code 48'
|
|
saves
|
|
grep -qx '[[:space:]]\{1,\}delta omega' "$work/notes/t.md" || fail "tab: $(cat "$work/notes/t.md")"
|
|
key 'key code 48 using shift down'
|
|
saves
|
|
is $'omega beta omega\n\ndelta omega' "shift-tab"
|
|
# A list item continues on Return.
|
|
key 'key code 125 using command down'
|
|
key 'key code 36'
|
|
key 'keystroke "- one"'
|
|
key 'key code 36'
|
|
key 'keystroke "two"'
|
|
saves
|
|
grep -qx -- '- two' "$work/notes/t.md" || fail "list: $(cat "$work/notes/t.md")"
|
|
# Tables: insert, then a column and a row.
|
|
press Visual
|
|
waits 'mark: shown as "visual"'
|
|
# Closing the find box hands the keys to the page.
|
|
key 'keystroke "f" using command down'
|
|
key 'key code 53'
|
|
# Command arrows move though AppKit sends no edit command.
|
|
key 'key code 125 using command down'
|
|
press "Insert Table"
|
|
press "Add Column Right"
|
|
press "Add Row Below"
|
|
saves
|
|
header=$(grep -m1 '^ *|' "$work/notes/t.md" || true)
|
|
[ "$(tr -cd '|' <<<"$header" | wc -c | tr -d ' ')" = 5 ] || fail "table columns: $(cat "$work/notes/t.md")"
|
|
[ "$(grep -c '^ *|' "$work/notes/t.md")" = 5 ] || fail "table rows: $(cat "$work/notes/t.md")"
|
|
[ "$(head -n 1 "$work/notes/t.md")" = "omega beta omega" ] || fail "table not at the end: $(cat "$work/notes/t.md")"
|
|
press "Delete Column"
|
|
saves
|
|
header=$(grep -m1 '^ *|' "$work/notes/t.md" || true)
|
|
[ "$(tr -cd '|' <<<"$header" | wc -c | tr -d ' ')" = 4 ] || fail "delete column: $(cat "$work/notes/t.md")"
|
|
# A dropped image is copied beside the file, name escaped.
|
|
window=$("$work/ax-values" "$app" | awk -F'\t' '$1 == "AXWindow" { sub("at=", "", $5); print $5; exit }')
|
|
IFS=, read -r x y <<<"$window"
|
|
[ "$("$work/drop" "$work/red pic.png" "$x" "$y")" = dropped ] || fail "the drop was refused"
|
|
waits "mark: attached 1 images"
|
|
saves
|
|
cmp -s "$work/red pic.png" "$work/notes/assets/red pic.png" || fail "dropped copy: $(ls -R "$work/notes")"
|
|
grep -qF '](assets/red%20pic.png)' "$work/notes/t.md" || fail "drop link: $(cat "$work/notes/t.md")"
|
|
# A pasted image lands in assets beside the file.
|
|
if [ "${ELY_CLIPBOARD_TEST:-}" = 1 ]; then
|
|
osascript -e "set the clipboard to (read (POSIX file \"$work/red pic.png\") as «class PNGf»)"
|
|
key 'key code 125 using command down'
|
|
key 'key code 36'
|
|
key 'keystroke "v" using command down'
|
|
saves
|
|
png=$(ls "$work/notes/assets"/pasted-*.png 2>/dev/null | head -n 1)
|
|
[ -n "$png" ] || fail "no pasted image: $(ls -R "$work/notes")"
|
|
grep -qF "](assets/$(basename "$png"))" "$work/notes/t.md" || fail "image link: $(cat "$work/notes/t.md")"
|
|
fi
|
|
# Saved elsewhere mid-copy: no link to nowhere.
|
|
mkfifo "$work/slow.png"
|
|
mkdir "$work/elsewhere"
|
|
window=$("$work/ax-values" "$app" | awk -F'\t' '$1 == "AXWindow" { sub("at=", "", $5); print $5; exit }')
|
|
IFS=, read -r x y <<<"$window"
|
|
[ "$("$work/drop" "$work/slow.png" "$x" "$y")" = dropped ] || fail "the slow drop was refused"
|
|
press "Save As…"
|
|
key 'keystroke "g" using {command down, shift down}'
|
|
key "keystroke \"$work/elsewhere\""
|
|
key 'key code 36'
|
|
key 'key code 36'
|
|
waits "elsewhere/t.md"
|
|
(printf 'x' >"$work/slow.png" &)
|
|
waits "the file moved while images copied"
|
|
! grep -q "slow" "$work/elsewhere/t.md" || fail "a link to the old folder: $(cat "$work/elsewhere/t.md")"
|
|
kill "$app"
|
|
wait "$app" 2>/dev/null || true
|
|
app=
|
|
! grep -q "panicked" "$work/err" || fail "panics: $(grep -m3 panicked "$work/err")"
|
|
# Two tabs; typing a replacement keeps the hit.
|
|
printf 'alpha beta alpha beta alpha\n' >"$work/notes/v.md"
|
|
printf 'alpha beta\n' >"$work/notes/w.md"
|
|
cp "$work/red pic.png" "$work/a]b.png"
|
|
launch "$work/notes/v.md" "$work/notes/w.md"
|
|
waits 'mark: shown as "visual"'
|
|
osascript -e "tell application \"System Events\" to set frontmost of (first process whose unix id is $app) to true" -e 'delay 2' >/dev/null
|
|
press v.md
|
|
sleep 1
|
|
key 'keystroke "f" using command down'
|
|
key 'keystroke "alpha"'
|
|
press "Toggle replace"
|
|
field 2
|
|
key 'keystroke "XY"'
|
|
press Replace
|
|
saves
|
|
[ "$(cat "$work/notes/v.md")" = "XY beta alpha beta alpha" ] || fail "the hit moved: $(cat "$work/notes/v.md")"
|
|
# Each tab follows the box, not its last query.
|
|
press w.md
|
|
field 1
|
|
key 'keystroke "a" using command down'
|
|
key 'keystroke "beta"'
|
|
field 2
|
|
key 'keystroke "a" using command down'
|
|
key 'keystroke "Y"'
|
|
press v.md
|
|
field 1
|
|
key 'keystroke "a" using command down'
|
|
key 'keystroke "alpha"'
|
|
field 2
|
|
key 'keystroke "a" using command down'
|
|
key 'keystroke "Z"'
|
|
press w.md
|
|
press "Replace all"
|
|
saves
|
|
[ "$(cat "$work/notes/w.md")" = "Z beta" ] || fail "a stale query: $(cat "$work/notes/w.md")"
|
|
# Alt text in source closes no brackets.
|
|
press Source
|
|
waits 'mark: shown as "source"'
|
|
window=$("$work/ax-values" "$app" | awk -F'\t' '$1 == "AXWindow" { sub("at=", "", $5); print $5; exit }')
|
|
IFS=, read -r x y <<<"$window"
|
|
[ "$("$work/drop" "$work/a]b.png" "$x" "$y")" = dropped ] || fail "the bracket drop was refused"
|
|
waits "mark: attached 1 images"
|
|
saves
|
|
grep -qF '![a\]b](assets/a%5Db.png)' "$work/notes/w.md" || fail "alt escape: $(cat "$work/notes/w.md")"
|
|
kill "$app"
|
|
wait "$app" 2>/dev/null || true
|
|
app=
|
|
! grep -q "panicked" "$work/err" || fail "panics: $(grep -m3 panicked "$work/err")"
|
|
echo "ok: mark tools"
|