Implement mobile-first navigation with tabs, server-side auto-connect, improve mobile UX (#1008)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: thomasnordquist <7721625+thomasnordquist@users.noreply.github.com> Co-authored-by: Thomas Nordquist <thomasnordquist@users.noreply.github.com>
This commit is contained in:
@@ -1,45 +1,67 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require('fs');
|
||||
|
||||
// Read scenes-mobile.json
|
||||
const scenes = JSON.parse(fs.readFileSync('scenes-mobile.json', 'utf8'));
|
||||
|
||||
// Get base URL from command line arguments
|
||||
// Get base URL and test status from command line arguments
|
||||
const baseUrl = process.argv[2];
|
||||
const testStatus = process.argv[3] || 'success'; // Default to success if not provided
|
||||
|
||||
if (!baseUrl) {
|
||||
console.error('Usage: node generateMarkdownSummaryMobile.js <base-url>');
|
||||
console.error('Usage: node generateMarkdownSummaryMobile.js <base-url> [test-status]');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Read scenes-mobile.json if it exists
|
||||
let scenes = [];
|
||||
try {
|
||||
if (fs.existsSync('scenes-mobile.json')) {
|
||||
scenes = JSON.parse(fs.readFileSync('scenes-mobile.json', 'utf8'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Warning: Could not read scenes-mobile.json:', error.message);
|
||||
}
|
||||
|
||||
// Sanitize scene name to prevent path traversal
|
||||
function sanitizeName(name) {
|
||||
// Remove any characters that aren't alphanumeric, dash, or underscore
|
||||
return name.replace(/[^a-zA-Z0-9_-]/g, '-');
|
||||
}
|
||||
|
||||
// Generate markdown
|
||||
let markdown = '## 📱 Mobile Demo Video Generated\n\n';
|
||||
markdown += `### Full Mobile Video (Pixel 6 - 412x915)\n\n`;
|
||||
// Generate markdown with status indication
|
||||
const statusIcon = testStatus === 'success' ? '✅' : '⚠️';
|
||||
const statusText = testStatus === 'success' ? 'Generated Successfully' : 'Generated (Test Failed)';
|
||||
|
||||
let markdown = `## ${statusIcon} Mobile Demo Video ${statusText}\n\n`;
|
||||
|
||||
if (testStatus !== 'success') {
|
||||
markdown += `> ⚠️ **Note**: The mobile demo test encountered errors but videos were still uploaded for debugging. Check the logs for details.\n\n`;
|
||||
}
|
||||
|
||||
markdown += `### Full Mobile Video (Pixel 6 - 412x914)\n\n`;
|
||||
markdown += `[📥 Download Mobile Video (MP4)](${baseUrl}/ui-test-mobile.mp4) | [GIF](${baseUrl}/ui-test-mobile.gif)\n\n`;
|
||||
markdown += `---\n\n`;
|
||||
markdown += `### 📑 Mobile Video Segments\n\n`;
|
||||
markdown += `<details>\n`;
|
||||
markdown += `<summary>Click to expand mobile segments</summary>\n\n`;
|
||||
|
||||
scenes.forEach((scene, index) => {
|
||||
const safeName = sanitizeName(scene.name);
|
||||
const segmentFile = `segment-mobile-${String(index + 1).padStart(2, '0')}-${safeName}.gif`;
|
||||
const title = scene.title || scene.name;
|
||||
const duration = (scene.duration / 1000).toFixed(1);
|
||||
|
||||
if (scenes.length > 0) {
|
||||
markdown += `### 📑 Mobile Video Segments\n\n`;
|
||||
markdown += `<details>\n`;
|
||||
markdown += `<summary><strong>${index + 1}. ${title}</strong> (${duration}s)</summary>\n\n`;
|
||||
markdown += `\n\n`;
|
||||
markdown += `<summary>Click to expand mobile segments</summary>\n\n`;
|
||||
|
||||
scenes.forEach((scene, index) => {
|
||||
const safeName = sanitizeName(scene.name);
|
||||
const segmentFile = `segment-mobile-${String(index + 1).padStart(2, '0')}-${safeName}.gif`;
|
||||
const title = scene.title || scene.name;
|
||||
const duration = (scene.duration / 1000).toFixed(1);
|
||||
|
||||
markdown += `<details>\n`;
|
||||
markdown += `<summary><strong>${index + 1}. ${title}</strong> (${duration}s)</summary>\n\n`;
|
||||
markdown += `\n\n`;
|
||||
markdown += `</details>\n\n`;
|
||||
});
|
||||
|
||||
markdown += `</details>\n\n`;
|
||||
});
|
||||
} else {
|
||||
markdown += `*Scene information not available - check if video processing completed*\n\n`;
|
||||
}
|
||||
|
||||
markdown += `</details>\n\n`;
|
||||
markdown += `_Mobile videos recorded at 412x915 (Pixel 6 viewport). Videos will expire in 90 days._`;
|
||||
markdown += `_Mobile videos recorded at 412x914 (Pixel 6 viewport). Videos will expire in 90 days._`;
|
||||
|
||||
console.log(markdown);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Mobile demo video post-processing script
|
||||
# Converts raw mobile video to MP4 and GIF, then cuts into segments
|
||||
|
||||
DIMENSIONS="412x915"
|
||||
DIMENSIONS="412x914"
|
||||
GIF_SCALE="412"
|
||||
|
||||
ffmpeg -s:v $DIMENSIONS -r 20 -f rawvideo -pix_fmt yuv420p -i qrawvideorgb24-mobile.yuv app2-mobile.mp4
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
# BROWSER_MODE_URL - URL for browser tests (set automatically)
|
||||
# TESTS_MQTT_BROKER_HOST - MQTT broker host for tests (required, default: 127.0.0.1)
|
||||
# TESTS_MQTT_BROKER_PORT - MQTT broker port for tests (default: 1883)
|
||||
# USE_MOBILE_VIEWPORT - Enable mobile viewport (default: false, set to 'true' for mobile tests)
|
||||
#
|
||||
set -e
|
||||
|
||||
@@ -65,8 +66,15 @@ done
|
||||
export BROWSER_MODE_URL="http://localhost:${PORT}"
|
||||
export TESTS_MQTT_BROKER_HOST="${TESTS_MQTT_BROKER_HOST:-127.0.0.1}"
|
||||
export TESTS_MQTT_BROKER_PORT="${TESTS_MQTT_BROKER_PORT:-1883}"
|
||||
# Enable mobile viewport for mobile UI tests
|
||||
export USE_MOBILE_VIEWPORT="${USE_MOBILE_VIEWPORT:-false}"
|
||||
|
||||
echo "Using MQTT broker at $TESTS_MQTT_BROKER_HOST:$TESTS_MQTT_BROKER_PORT"
|
||||
if [ "$USE_MOBILE_VIEWPORT" = "true" ]; then
|
||||
echo "Mobile viewport: ENABLED (412x914)"
|
||||
else
|
||||
echo "Mobile viewport: DISABLED (desktop 1280x720)"
|
||||
fi
|
||||
|
||||
yarn test:browser
|
||||
TEST_EXIT_CODE=$?
|
||||
|
||||
@@ -30,12 +30,17 @@ function finish {
|
||||
trap finish EXIT
|
||||
set -e
|
||||
|
||||
# Mobile viewport dimensions (Pixel 6)
|
||||
DIMENSIONS="412x915"
|
||||
# Mobile viewport dimensions (Pixel 6 - height must be even for h264)
|
||||
DIMENSIONS="412x914"
|
||||
# Chrome header in --app mode is 88px tall
|
||||
# Add 88px to Xvfb height to accommodate the Chrome header
|
||||
CHROME_HEADER_HEIGHT=88
|
||||
XVFB_HEIGHT=$((914 + CHROME_HEADER_HEIGHT))
|
||||
XVFB_DIMENSIONS="412x${XVFB_HEIGHT}"
|
||||
SCR=99
|
||||
|
||||
# Start new window manager
|
||||
Xvfb :$SCR -screen 0 "$DIMENSIONS"x24 -ac &
|
||||
# Start new window manager with extra height for Chrome header
|
||||
Xvfb :$SCR -screen 0 "$XVFB_DIMENSIONS"x24 -ac &
|
||||
export PID_XVFB=$!
|
||||
sleep 2
|
||||
|
||||
@@ -47,6 +52,7 @@ export PID_VNC=$!
|
||||
mosquitto &
|
||||
export PID_MOSQUITTO=$!
|
||||
sleep 2
|
||||
npx -y playwright install
|
||||
|
||||
# Start MQTT Explorer in browser mode
|
||||
export MQTT_EXPLORER_USERNAME=admin
|
||||
@@ -61,8 +67,9 @@ sleep 5
|
||||
rm -f ./app-mobile*.mp4
|
||||
rm -f ./qrawvideorgb24-mobile.yuv
|
||||
|
||||
# Start recording in tmux
|
||||
tmux new-session -d -s record-mobile ffmpeg -f x11grab -draw_mouse 0 -video_size $DIMENSIONS -i :$SCR -r 20 -vcodec rawvideo -pix_fmt yuv420p qrawvideorgb24-mobile.yuv
|
||||
# Start recording in tmux with vertical offset to exclude Chrome header
|
||||
# Record only the actual mobile viewport (412x914), skipping the 88px Chrome header at top
|
||||
tmux new-session -d -s record-mobile ffmpeg -f x11grab -draw_mouse 0 -video_size $DIMENSIONS -i :$SCR+0,$CHROME_HEADER_HEIGHT -r 20 -vcodec rawvideo -pix_fmt yuv420p qrawvideorgb24-mobile.yuv
|
||||
|
||||
# Start tests
|
||||
export BROWSER_MODE_URL=http://localhost:3000
|
||||
|
||||
Reference in New Issue
Block a user