300 lines
14 KiB
Bash
Executable File
300 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
# ╔═══════════════════════════════════════════════════════════════════╗
|
|
# ║ Example State Persistence Test ║
|
|
# ╠═══════════════════════════════════════════════════════════════════╣
|
|
# ║ Tests: mutate state → scroll away → scroll back → verify state ║
|
|
# ║ ║
|
|
# ║ CUSTOMIZE: Set STATE_PROPERTY and SCREEN_EXPLORE in qa.config.sh ║
|
|
# ║ ║
|
|
# ║ Usage: bash .pi/skills/qa-automation/qa-state-persistence/flows/example-state-test.sh
|
|
# ╚═══════════════════════════════════════════════════════════════════╝
|
|
|
|
set -euo pipefail
|
|
|
|
# ── Source Libraries ─────────────────────────────────────────────────
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
source "$SCRIPT_DIR/../lib/state-helpers.sh"
|
|
|
|
# ── Test Setup ───────────────────────────────────────────────────────
|
|
TEST_NAME="state-persistence"
|
|
setup_test "$TEST_NAME"
|
|
|
|
run_setup_guard || {
|
|
echo "FATAL: Setup guard failed."
|
|
teardown_test
|
|
exit 1
|
|
}
|
|
|
|
declare -a TEST_RESULTS=()
|
|
|
|
add_test_result() {
|
|
local name="$1"
|
|
local status="$2"
|
|
local error="${3:-}"
|
|
local screenshots="${4:-}"
|
|
TEST_RESULTS+=("$(cat <<RESULT
|
|
{
|
|
"name": "$name",
|
|
"suite": "State Persistence",
|
|
"status": "$status",
|
|
"error": "$error",
|
|
"screenshots": [$screenshots]
|
|
}
|
|
RESULT
|
|
)")
|
|
}
|
|
|
|
# ════════════════════════════════════════════════════════════════════
|
|
# Step 0 — Install Hooks
|
|
# ════════════════════════════════════════════════════════════════════
|
|
step "Install debug hooks"
|
|
assert_app_foreground || add_test_result "App Check" "failed" "Not foreground"
|
|
|
|
hook_result=$(install_debug_hook 2>&1 || echo '{"error":"failed"}')
|
|
log_info "Video hook: $hook_result"
|
|
|
|
take_screenshot "00-initial"
|
|
|
|
# ════════════════════════════════════════════════════════════════════
|
|
# Step 1 — Navigate to Feed
|
|
# CUSTOMIZE: Change nav_explore to your feed navigation
|
|
# ════════════════════════════════════════════════════════════════════
|
|
step "Navigate to feed screen"
|
|
nav_home 2>/dev/null || true
|
|
sleep 1
|
|
nav_explore
|
|
sleep 4
|
|
|
|
overlay=$(check_error_overlay)
|
|
[ "$overlay" = "visible" ] && dismiss_error_overlay && sleep 1
|
|
|
|
# Reset to index 0
|
|
current_idx=$(get_current_feed_index)
|
|
if [ "$current_idx" != "0" ] && [ "$current_idx" != "-1" ]; then
|
|
scroll_to_index 0
|
|
sleep 3
|
|
fi
|
|
|
|
# Install state hook after feed is mounted
|
|
state_hook_result=$(install_state_debug_hook 2>&1 || echo '{"error":"failed"}')
|
|
log_info "State hook: $state_hook_result"
|
|
|
|
take_screenshot "01-feed"
|
|
add_test_result "Navigate to Feed" "passed" "" "\"01-feed.png\""
|
|
|
|
# ════════════════════════════════════════════════════════════════════
|
|
# Step 2 — Record Item Identity
|
|
# ════════════════════════════════════════════════════════════════════
|
|
step "Record first item identity"
|
|
|
|
first_item=$(query_item_state 0 2>&1 || echo '{"error":"query failed"}')
|
|
log_info "First item: $first_item"
|
|
|
|
item_id=$(echo "$first_item" | node -e "
|
|
let d='';process.stdin.on('data',c=>d+=c);
|
|
process.stdin.on('end',()=>{try{console.log(JSON.parse(d).id||'unknown')}catch(e){console.log('unknown')}});
|
|
" 2>/dev/null || echo "unknown")
|
|
|
|
has_ok=$(echo "$first_item" | node -e "
|
|
let d='';process.stdin.on('data',c=>d+=c);
|
|
process.stdin.on('end',()=>{try{console.log(JSON.parse(d).ok?'yes':'no')}catch(e){console.log('no')}});
|
|
" 2>/dev/null || echo "no")
|
|
|
|
if [ "$has_ok" = "yes" ]; then
|
|
log_pass "Item captured: $item_id"
|
|
add_test_result "Record Identity" "passed" ""
|
|
else
|
|
# Retry with re-installed hook
|
|
install_state_debug_hook >/dev/null 2>&1 || true
|
|
sleep 2
|
|
first_item=$(query_item_state 0 2>&1 || echo '{"error":"retry failed"}')
|
|
has_ok=$(echo "$first_item" | node -e "
|
|
let d='';process.stdin.on('data',c=>d+=c);
|
|
process.stdin.on('end',()=>{try{console.log(JSON.parse(d).ok?'yes':'no')}catch(e){console.log('no')}});
|
|
" 2>/dev/null || echo "no")
|
|
|
|
if [ "$has_ok" = "yes" ]; then
|
|
add_test_result "Record Identity" "passed" "Retry succeeded"
|
|
else
|
|
add_test_result "Record Identity" "skipped" "Could not read data"
|
|
fi
|
|
fi
|
|
|
|
# ════════════════════════════════════════════════════════════════════
|
|
# Step 3 — Verify Initial State
|
|
# ════════════════════════════════════════════════════════════════════
|
|
step "Verify initial state (${STATE_PROPERTY} should be false)"
|
|
|
|
initial_value=$(echo "$first_item" | node -e "
|
|
let d='';process.stdin.on('data',c=>d+=c);
|
|
process.stdin.on('end',()=>{try{console.log(JSON.parse(d).${STATE_PROPERTY}?'true':'false')}catch(e){console.log('unknown')}});
|
|
" 2>/dev/null || echo "unknown")
|
|
|
|
take_screenshot "02-initial-state"
|
|
|
|
if [ "$initial_value" = "true" ]; then
|
|
log_warn "State is already true — toggling off for clean baseline"
|
|
toggle_item_property_cdp >/dev/null 2>&1
|
|
sleep 2
|
|
add_test_result "Initial State" "passed" "Reset to false"
|
|
elif [ "$initial_value" = "false" ]; then
|
|
log_pass "Clean baseline (${STATE_PROPERTY}=false)"
|
|
add_test_result "Initial State" "passed" "" "\"02-initial-state.png\""
|
|
else
|
|
add_test_result "Initial State" "skipped" "Unknown" "\"02-initial-state.png\""
|
|
fi
|
|
|
|
# ════════════════════════════════════════════════════════════════════
|
|
# Step 4 — Mutate State
|
|
# ════════════════════════════════════════════════════════════════════
|
|
step "Mutate state (set ${STATE_PROPERTY}=true)"
|
|
|
|
toggle_result=$(toggle_item_property_cdp 2>&1 || echo '{"error":"failed"}')
|
|
log_info "Toggle result: $toggle_result"
|
|
take_screenshot "03-after-mutate"
|
|
|
|
after_value=$(query_item_state 0 2>&1 || echo '{"error":"failed"}')
|
|
after_prop=$(echo "$after_value" | node -e "
|
|
let d='';process.stdin.on('data',c=>d+=c);
|
|
process.stdin.on('end',()=>{try{console.log(JSON.parse(d).${STATE_PROPERTY}?'true':'false')}catch(e){console.log('unknown')}});
|
|
" 2>/dev/null || echo "unknown")
|
|
|
|
if [ "$after_prop" = "true" ]; then
|
|
log_pass "State mutated successfully"
|
|
add_test_result "Mutate State" "passed" "" "\"03-after-mutate.png\""
|
|
else
|
|
log_warn "Could not verify mutation: $after_prop"
|
|
add_test_result "Mutate State" "skipped" "Verification failed" "\"03-after-mutate.png\""
|
|
fi
|
|
|
|
# ════════════════════════════════════════════════════════════════════
|
|
# Step 5 — Scroll Away
|
|
# ════════════════════════════════════════════════════════════════════
|
|
for i in $(seq 1 $STATE_SCROLL_COUNT); do
|
|
step "Scroll to item #$i"
|
|
scroll_to_next_video
|
|
sleep 2
|
|
take_screenshot "04-scroll-${i}"
|
|
add_test_result "Scroll to Item $i" "passed" "" "\"04-scroll-${i}.png\""
|
|
done
|
|
|
|
# Verify we scrolled away
|
|
away_index=$(get_current_feed_index)
|
|
log_info "Current index: $away_index"
|
|
take_screenshot "05-scrolled-away"
|
|
add_test_result "Scrolled Away" "passed" "Index: $away_index" "\"05-scrolled-away.png\""
|
|
|
|
# ════════════════════════════════════════════════════════════════════
|
|
# Step 6 — Scroll Back
|
|
# ════════════════════════════════════════════════════════════════════
|
|
step "Scroll back to first item"
|
|
scroll_to_index 0
|
|
sleep 3
|
|
|
|
back_index=$(get_current_feed_index)
|
|
take_screenshot "06-scrolled-back"
|
|
|
|
if [ "$back_index" = "0" ]; then
|
|
log_pass "Back at first item"
|
|
add_test_result "Scroll Back" "passed" "" "\"06-scrolled-back.png\""
|
|
else
|
|
scroll_back_to_start
|
|
sleep 2
|
|
add_test_result "Scroll Back" "passed" "Used fallback" "\"06-scrolled-back.png\""
|
|
fi
|
|
|
|
# ════════════════════════════════════════════════════════════════════
|
|
# Step 7 — KEY ASSERTION: State Persisted
|
|
# ════════════════════════════════════════════════════════════════════
|
|
step "★ KEY ASSERTION: Verify ${STATE_PROPERTY} persisted"
|
|
|
|
# Re-install hook (fiber tree may have shifted)
|
|
install_state_debug_hook >/dev/null 2>&1 || true
|
|
sleep 2
|
|
|
|
persist_state=$(query_item_state 0 2>&1 || echo '{"error":"failed"}')
|
|
persist_value=$(echo "$persist_state" | node -e "
|
|
let d='';process.stdin.on('data',c=>d+=c);
|
|
process.stdin.on('end',()=>{try{console.log(JSON.parse(d).${STATE_PROPERTY}?'true':'false')}catch(e){console.log('unknown')}});
|
|
" 2>/dev/null || echo "unknown")
|
|
|
|
take_screenshot "07-persisted"
|
|
|
|
if [ "$persist_value" = "true" ]; then
|
|
log_pass "★ STATE PERSISTED! ${STATE_PROPERTY} is still true after scrolling away and back"
|
|
add_test_result "State Persisted" "passed" "" "\"07-persisted.png\""
|
|
elif [ "$persist_value" = "unknown" ]; then
|
|
log_warn "Could not verify — CDP state read failed"
|
|
add_test_result "State Persisted" "skipped" "CDP failed" "\"07-persisted.png\""
|
|
else
|
|
log_fail "★ STATE LOST! ${STATE_PROPERTY} is false after scrolling back"
|
|
add_test_result "State Persisted" "failed" "${STATE_PROPERTY}=false after scroll" "\"07-persisted.png\""
|
|
fi
|
|
|
|
# ════════════════════════════════════════════════════════════════════
|
|
# Step 8 — Cleanup
|
|
# ════════════════════════════════════════════════════════════════════
|
|
step "Cleanup: restore original state"
|
|
|
|
if [ "$persist_value" = "true" ]; then
|
|
toggle_item_property_cdp >/dev/null 2>&1 || true
|
|
sleep 1
|
|
log_pass "State restored"
|
|
add_test_result "Cleanup" "passed" ""
|
|
else
|
|
log_info "No cleanup needed"
|
|
add_test_result "Cleanup" "passed" "Not needed"
|
|
fi
|
|
|
|
take_screenshot "08-final"
|
|
|
|
# ════════════════════════════════════════════════════════════════════
|
|
# Generate Report
|
|
# ════════════════════════════════════════════════════════════════════
|
|
step "Generating report"
|
|
|
|
SKIP_COUNT=0
|
|
for r in "${TEST_RESULTS[@]}"; do
|
|
echo "$r" | grep -q '"status": "skipped"' && SKIP_COUNT=$((SKIP_COUNT + 1))
|
|
done
|
|
|
|
TESTS_JSON=""
|
|
for r in "${TEST_RESULTS[@]}"; do
|
|
[ -n "$TESTS_JSON" ] && TESTS_JSON+=","
|
|
TESTS_JSON+="$r"
|
|
done
|
|
|
|
REPORT_FILE="$TEST_OUTPUT_DIR/${TEST_NAME}-report.json"
|
|
end_time=$(date +%s)
|
|
duration=$((end_time - TEST_START_TIME))
|
|
|
|
cat > "$REPORT_FILE" <<REPORT
|
|
{
|
|
"title": "State Persistence QA",
|
|
"generatedAt": "$(date -u '+%Y-%m-%dT%H:%M:%SZ')",
|
|
"suites": [{
|
|
"name": "State Persistence (${STATE_PROPERTY})",
|
|
"type": "e2e",
|
|
"passed": $PASS_COUNT,
|
|
"failed": $FAIL_COUNT,
|
|
"skipped": $SKIP_COUNT,
|
|
"duration": $((duration * 1000)),
|
|
"tests": [$TESTS_JSON],
|
|
"screenshotDir": "$SCREENSHOT_DIR/$TEST_NAME"
|
|
}],
|
|
"totalPassed": $PASS_COUNT,
|
|
"totalFailed": $FAIL_COUNT,
|
|
"totalSkipped": $SKIP_COUNT,
|
|
"totalDuration": $((duration * 1000))
|
|
}
|
|
REPORT
|
|
|
|
log_info "Report: $REPORT_FILE"
|
|
teardown_test
|
|
|
|
echo ""
|
|
echo "State persistence test completed!"
|
|
echo " Screenshots: $SCREENSHOT_DIR/$TEST_NAME/"
|
|
echo " Report: $REPORT_FILE"
|
|
echo ""
|