Files
App/scripts/maestro-android.js
skalthoff cb068da8bf enhance player queue management with Zustand store integration. (#574)
What is the change

    Implement persistent player queue state with a dedicated Zustand store (MMKV-backed): queue, currentTrack, currentIndex, shuffled, unShuffledQueue, and queueRef.
    Restore the queue on app start: new initialization reads persisted state and rebuilds the TrackPlayer queue and active index.
    Unify queue mutations to keep React Native Track Player and the persisted store in sync:
        Loading a new queue sets queueRef, honors shuffle, computes start index, and writes to both RNTP and store.
        "Play Next" and "Add to Queue" update RNTP, the live queue, and unShuffledQueue consistently.
        Active track/index changes now propagate to the store and query cache.
    Tighten/refine player query/invalidations so UI reflects the latest RNTP state.
    Minor housekeeping in config/scripts to support the above (ESLint flat config, metro/jest/script updates).

What does this address

    Fixes queue not restoring after relaunch or crash; playback position and the current track persist correctly.
    Preserves shuffle state and the original unshuffled ordering for reliable shuffle toggle behavior.
    Ensures "Play Next"/"Add to Queue" behave consistently with what the user sees, avoiding duplication or desyncs.
    Reduces UI state drift by syncing RNTP events to both React Query and persistent store.


Co-authored-by: Violet Caulfield <42452695+anultravioletaurora@users.noreply.github.com>
2025-10-24 05:41:22 -05:00

178 lines
4.9 KiB
JavaScript

const { execSync, exec, spawn } = require('child_process')
const path = require('path')
const fs = require('fs')
// Read arguments from CLI
const [, , serverAddress, username] = process.argv
if (!serverAddress || !username) {
console.error('Usage: node runMaestro.js <server_address> <username>')
process.exit(1)
}
// Function to recursively find all YAML files in maestro/tests directory
function findYamlFiles(dir) {
const files = []
function scanDirectory(currentDir) {
const items = fs.readdirSync(currentDir)
for (const item of items) {
const fullPath = path.join(currentDir, item)
const stat = fs.statSync(fullPath)
if (stat.isDirectory()) {
scanDirectory(fullPath)
} else if (item.endsWith('.yaml') || item.endsWith('.yml')) {
files.push(fullPath)
}
}
}
scanDirectory(dir)
return files.sort() // Sort for consistent ordering
}
// Get all YAML files from maestro/tests directory
const MAESTRO_TESTS_DIR = './maestro/tests'
const FLOW_FILES = findYamlFiles(MAESTRO_TESTS_DIR)
console.log(`🔍 Found ${FLOW_FILES.length} YAML test files:`)
FLOW_FILES.forEach((file, index) => {
console.log(` ${index + 1}. ${file}`)
})
if (FLOW_FILES.length === 0) {
console.error('❌ No YAML test files found in maestro/testsdirectory')
process.exit(1)
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
async function stopRecording(pid, videoName, deviceVideoPath) {
try {
// Kill the adb screenrecord process
process.kill(pid, 'SIGINT')
// Wait 3 seconds for file to finalize
await sleep(3000)
// Pull the recorded file with custom name
execSync(`adb pull ${deviceVideoPath} ${videoName}`, { stdio: 'inherit' })
// Optionally delete the file on device
execSync(`adb shell rm ${deviceVideoPath}`)
console.log(`✅ Recording pulled and saved as ${videoName}`)
} catch (err) {
console.error('❌ Failed to stop or pull recording:', err.message)
}
}
async function runSingleTest(flowPath, serverAddress, username, testIndex) {
const flowName = path.basename(flowPath, '.yaml')
const relativePath = path.relative(MAESTRO_TESTS_DIR, flowPath)
const videoName = `test_${testIndex}_${flowName}.mp4`
const deviceVideoPath = `/sdcard/screen_${testIndex}_${flowName}.mp4`
console.log(`\n🚀 Starting test ${testIndex + 1}/${FLOW_FILES.length}: ${relativePath}`)
console.log(`📹 Video will be saved as: ${videoName}`)
// Start screen recording
const recording = spawn(
'adb',
['shell', 'screenrecord', '--time-limit=1800', deviceVideoPath],
{
stdio: 'ignore',
detached: true,
},
)
const pid = recording.pid
try {
const MAESTRO_PATH = path.join(process.env.HOME, '.maestro', 'bin', 'maestro')
const command = `${MAESTRO_PATH} test ${flowPath} \
--env server_address=${serverAddress} \
--env username=${username}`
const output = execSync(command, { stdio: 'inherit', env: process.env })
console.log(`✅ Test ${testIndex + 1} (${relativePath}) completed successfully`)
await stopRecording(pid, videoName, deviceVideoPath)
return { success: true, flowName, relativePath, videoName }
} catch (error) {
await stopRecording(pid, videoName, deviceVideoPath)
console.error(`❌ Test ${testIndex + 1} (${relativePath}) failed: ${error.message}`)
return { success: false, flowName, relativePath, videoName, error: error.message }
}
}
;(async () => {
console.log('📱 Installing app...')
execSync('adb install ./artifacts/app-universal-release.apk', {
stdio: 'inherit',
env: process.env,
})
console.log('🚀 Launching app...')
execSync(`adb shell monkey -p com.jellify 1`, { stdio: 'inherit' })
// Wait a bit for app to launch
await sleep(2000)
const results = []
console.log(`\n🔄 Starting test suite with ${FLOW_FILES.length} tests...`)
for (let i = 0; i < FLOW_FILES.length; i++) {
const flowPath = FLOW_FILES[i]
// Check if flow file exists
if (!fs.existsSync(flowPath)) {
console.log(`⚠️ Skipping ${flowPath} - file not found`)
continue
}
const result = await runSingleTest(flowPath, serverAddress, username, i)
results.push(result)
// Wait between tests to ensure clean state
if (i < FLOW_FILES.length - 1) {
console.log('⏳ Waiting 3 seconds before next test...')
await sleep(3000)
}
}
// Print summary
console.log('\n📊 Test Results Summary:')
console.log('========================')
let passed = 0
let failed = 0
results.forEach((result, index) => {
const status = result.success ? '✅ PASS' : '❌ FAIL'
console.log(`${index + 1}. ${result.relativePath}: ${status}`)
if (result.success) {
passed++
} else {
failed++
console.log(` Error: ${result.error}`)
}
console.log(` Video: ${result.videoName}`)
})
console.log(`\n📈 Final Results: ${passed} passed, ${failed} failed`)
if (failed === 0) {
console.log('🎉 All tests passed!')
process.exit(0)
} else {
console.log('⚠️ Some tests failed. Check the videos for details.')
process.exit(1)
}
})()