Initial: pi-skill — 68 skills, 43 extensions, 11 themes for Pi
This commit is contained in:
299
skills/qa-automation/qa-state-persistence/flows/example-state-test.sh
Executable file
299
skills/qa-automation/qa-state-persistence/flows/example-state-test.sh
Executable file
@@ -0,0 +1,299 @@
|
||||
#!/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 ""
|
||||
Reference in New Issue
Block a user