Files
App/scripts/maestro-android.js
skalthoff 05cf340e8f feat: Add new Maestro tests for album, playlist, artist, and favorites, and enhance existing tests with additional screenshots and refined playback flows. (#887)
* feat: Add new Maestro tests for album, playlist, artist, and favorites, and enhance existing tests with additional screenshots and refined playback flows.

* feat: Enhance Maestro CI/CD with improved caching, updated Maestro version, and refined emulator configurations for better performance and artifact collection.

* feat: Run Maestro Android tests using a single orchestrated flow file and collect project screenshots.

* fix: Add extended wait conditions to login test to improve stability and prevent flakiness.

* feat: Add test IDs to login, authentication, and library selection screens and update Maestro tests to use them.

* test: make sign out confirmation assertion optional

* feat: Improve login flow robustness by delegating app launch to the login test, increasing wait times, and adding more optional dialog dismissals.

* feat: Add APK caching to Maestro test workflow and conditionally run build steps.

* feat: make sign-out and cancellation steps in test more robust with optional taps and back key presses, and update login page assertion.

* test: remove optional flag from `pressKey: back` action in settings test.

---------

Co-authored-by: Violet Caulfield <42452695+anultravioletaurora@users.noreply.github.com>
Co-authored-by: Ritesh Shukla <riteshshukla2381@gmail.com>
2026-01-05 04:46:38 -06:00

155 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const { execSync, 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 maestro-android.js <server_address> <username>')
process.exit(1)
}
// Use the orchestrated flow file instead of individual tests
// flow-0.yaml clears state and runs all tests in the correct order
const FLOW_FILE = './maestro/flows/flow-0.yaml'
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 runFullFlow(serverAddress, username) {
const videoName = 'maestro_full_test.mp4'
const deviceVideoPath = '/sdcard/maestro_full_test.mp4'
console.log(`\n🚀 Running full Maestro test flow: ${FLOW_FILE}`)
console.log(`📹 Video will be saved as: ${videoName}`)
console.log(`🔗 Server: ${serverAddress}`)
console.log(`👤 Username: ${username}`)
// 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 = process.env.HOME + '/.maestro/bin/maestro'
const command = `${MAESTRO_PATH} test ${FLOW_FILE} \
--env server_address=${serverAddress} \
--env username=${username}`
console.log(`\n🎭 Executing: maestro test ${FLOW_FILE}`)
execSync(command, { stdio: 'inherit', env: process.env })
console.log('✅ Full test flow completed successfully!')
await stopRecording(pid, videoName, deviceVideoPath)
return { success: true }
} catch (error) {
console.error('❌ Test flow failed:', error.message)
await stopRecording(pid, videoName, deviceVideoPath)
return { success: false, 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.cosmonautical.jellify 1`, { stdio: 'inherit' })
// Wait for app to launch
await sleep(3000)
const result = await runFullFlow(serverAddress, username)
// Collect screenshots
const screenshotDir = './.maestro/screenshots'
const screenshotOutputDir = './screenshots-output'
if (fs.existsSync(screenshotDir)) {
console.log('\n📸 Collecting screenshots...')
try {
if (!fs.existsSync(screenshotOutputDir)) {
fs.mkdirSync(screenshotOutputDir, { recursive: true })
}
const screenshots = fs.readdirSync(screenshotDir)
screenshots.forEach((file) => {
const srcPath = path.join(screenshotDir, file)
const destPath = path.join(screenshotOutputDir, file)
fs.copyFileSync(srcPath, destPath)
console.log(` 📷 ${file}`)
})
console.log(`✅ Collected ${screenshots.length} screenshots to ${screenshotOutputDir}`)
} catch (err) {
console.error('❌ Failed to collect screenshots:', err.message)
}
} else {
console.log('\n📸 No screenshots directory found at .maestro/screenshots')
}
// Also collect from project screenshots folder
const projectScreenshots = './screenshots'
if (fs.existsSync(projectScreenshots)) {
console.log('\n📸 Collecting project screenshots...')
try {
if (!fs.existsSync(screenshotOutputDir)) {
fs.mkdirSync(screenshotOutputDir, { recursive: true })
}
const screenshots = fs.readdirSync(projectScreenshots).filter((f) => f.endsWith('.png'))
screenshots.forEach((file) => {
const srcPath = path.join(projectScreenshots, file)
const destPath = path.join(screenshotOutputDir, file)
fs.copyFileSync(srcPath, destPath)
console.log(` 📷 ${file}`)
})
console.log(`✅ Collected ${screenshots.length} screenshots from project folder`)
} catch (err) {
console.error('❌ Failed to collect screenshots:', err.message)
}
}
if (result.success) {
console.log('\n🎉 All tests passed!')
process.exit(0)
} else {
console.log('\n⚠ Tests failed. Check the video for details.')
process.exit(1)
}
})()