From a1e2368468e02ad22a97e50f2b0a5d29ed9d7555 Mon Sep 17 00:00:00 2001 From: Skyler Lehmkuhl Date: Sat, 8 Nov 2025 10:03:51 -0500 Subject: [PATCH] test improvements --- tests/helpers/app.js | 22 +- tests/helpers/canvas.js | 21 +- tests/helpers/manual.js | 68 ++++++ tests/specs/manual/timeline-manual.test.js | 254 +++++++++++++++++++++ 4 files changed, 354 insertions(+), 11 deletions(-) create mode 100644 tests/helpers/manual.js create mode 100644 tests/specs/manual/timeline-manual.test.js diff --git a/tests/helpers/app.js b/tests/helpers/app.js index f869ef7..b7d86f2 100644 --- a/tests/helpers/app.js +++ b/tests/helpers/app.js @@ -9,11 +9,23 @@ export async function waitForAppReady(timeout = 5000) { await browser.waitForApp(); - // Check for "Create New File" dialog and click Create if present - const createButton = await browser.$('button*=Create'); - if (await createButton.isExisting()) { - await createButton.click(); - await browser.pause(500); // Wait for dialog to close + // Check for "Animation" card on start screen and click it if present + // The card has a label div with text "Animation" + const animationCard = await browser.$('.focus-card-label*=Animation'); + if (await animationCard.isExisting()) { + // Click the parent focus-card element + const card = await animationCard.parentElement(); + await card.waitForClickable({ timeout: 2000 }); + await card.click(); + await browser.pause(1000); // Wait longer for animation view to load + } else { + // Legacy: Check for "Create New File" dialog and click Create if present + const createButton = await browser.$('button*=Create'); + if (await createButton.isExisting()) { + await createButton.waitForClickable({ timeout: 2000 }); + await createButton.click(); + await browser.pause(500); // Wait for dialog to close + } } // Wait for the main canvas to be present diff --git a/tests/helpers/canvas.js b/tests/helpers/canvas.js index 6304f7f..14607ca 100644 --- a/tests/helpers/canvas.js +++ b/tests/helpers/canvas.js @@ -31,17 +31,21 @@ export async function dragCanvas(fromX, fromY, toX, toY) { * @param {number} width - Rectangle width * @param {number} height - Rectangle height * @param {boolean} filled - Whether to fill the shape (default: true) + * @param {string} color - Fill color in hex format (e.g., '#ff0000') */ -export async function drawRectangle(x, y, width, height, filled = true) { +export async function drawRectangle(x, y, width, height, filled = true, color = null) { // Select the rectangle tool await selectTool('rectangle'); - // Set fill option - await browser.execute((filled) => { + // Set fill option and color if provided + await browser.execute((filled, color) => { if (window.context) { window.context.fillShape = filled; + if (color) { + window.context.fillStyle = color; + } } - }, filled); + }, filled, color); // Draw by dragging from start to end point await dragCanvas(x, y, x + width, y + height); @@ -192,14 +196,19 @@ export async function doubleClickCanvas(x, y) { export async function setPlayheadTime(time) { await browser.execute(function(timeValue) { if (window.context && window.context.activeObject) { + // Set time on both the active object and timeline state window.context.activeObject.currentTime = timeValue; - // Update timeline widget if it exists if (window.context.timelineWidget && window.context.timelineWidget.timelineState) { window.context.timelineWidget.timelineState.currentTime = timeValue; } + + // Trigger timeline redraw to show updated playhead position + if (window.context.timelineWidget && window.context.timelineWidget.requestRedraw) { + window.context.timelineWidget.requestRedraw(); + } } }, time); - await browser.pause(100); + await browser.pause(200); } /** diff --git a/tests/helpers/manual.js b/tests/helpers/manual.js new file mode 100644 index 0000000..d056d46 --- /dev/null +++ b/tests/helpers/manual.js @@ -0,0 +1,68 @@ +/** + * Manual testing utilities for user-in-the-loop verification + * These helpers pause execution and wait for user confirmation + */ + +/** + * Pause and wait for user to verify something visually with a confirm dialog + * @param {string} message - What the user should verify + * @param {boolean} waitForConfirm - If true, show confirm dialog and wait for user input + * @throws {Error} If user clicks Cancel to indicate verification failed + */ +export async function verifyManually(message, waitForConfirm = true) { + console.log('\n=== MANUAL VERIFICATION ==='); + console.log(message); + console.log('===========================\n'); + + if (waitForConfirm) { + // Show a confirm dialog in the browser and wait for user response + const result = await browser.execute(function(msg) { + return confirm(msg); + }, message); + + if (!result) { + console.log('User clicked Cancel - verification failed'); + throw new Error('Manual verification failed: User clicked Cancel'); + } else { + console.log('User clicked OK - verification passed'); + } + + return result; + } else { + // Just pause for observation + await browser.pause(3000); + return true; + } +} + +/** + * Add a visual marker/annotation to describe what should be visible + * @param {string} description - Description of current state + */ +export async function logStep(description) { + console.log(`\n>>> STEP: ${description}`); +} + +/** + * Extended pause with a description of what's happening + * @param {string} action - What action just occurred + * @param {number} pauseTime - How long to pause + */ +export async function pauseAndDescribe(action, pauseTime = 2000) { + console.log(`>>> ${action}`); + await browser.pause(pauseTime); +} + +/** + * Ask user a yes/no question via confirm dialog + * @param {string} question - Question to ask the user + * @returns {Promise} True if user clicked OK, false if Cancel + */ +export async function askUser(question) { + console.log(`\n>>> QUESTION: ${question}`); + const result = await browser.execute(function(msg) { + return confirm(msg); + }, question); + console.log(`User answered: ${result ? 'YES (OK)' : 'NO (Cancel)'}`); + return result; +} diff --git a/tests/specs/manual/timeline-manual.test.js b/tests/specs/manual/timeline-manual.test.js new file mode 100644 index 0000000..8784584 --- /dev/null +++ b/tests/specs/manual/timeline-manual.test.js @@ -0,0 +1,254 @@ +/** + * MANUAL Timeline Animation Tests + * Run these with visual verification - watch the app window as tests execute + * + * To run: pnpm wdio run wdio.conf.js --spec tests/specs/manual/timeline-manual.test.js + */ + +import { describe, it, before, afterEach } from 'mocha'; +import { waitForAppReady } from '../../helpers/app.js'; +import { + drawRectangle, + selectMultipleShapes, + dragCanvas, + setPlayheadTime, + getPlayheadTime, + addKeyframe, + useKeyboardShortcut +} from '../../helpers/canvas.js'; +import { verifyManually, logStep, pauseAndDescribe } from '../../helpers/manual.js'; + +describe('MANUAL: Timeline Animation', () => { + before(async () => { + await waitForAppReady(); + }); + + afterEach(async () => { + // Close any open dialogs by accepting them + try { + await browser.execute(function() { + // Close any open confirm/alert dialogs + // This is a no-op if no dialog is open + }); + } catch (e) { + // Ignore errors + } + + // Pause briefly to show final state before ending + await browser.pause(1000); + console.log('\n>>> Test completed. Session will restart for next test.\n'); + }); + + it('TEST 1: Group animation - draw, group, keyframe, move group', async () => { + await logStep('Drawing a RED rectangle at (100, 100) with size 100x100'); + await drawRectangle(100, 100, 100, 100, true, '#ff0000'); + await pauseAndDescribe('RED rectangle drawn', 2000); + + await verifyManually( + 'VERIFY: Do you see a RED filled rectangle at the top-left area?\n' + + 'It should be centered around (150, 150)\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Selecting the RED rectangle by dragging a selection box over it'); + await selectMultipleShapes([{ x: 150, y: 150 }]); + await pauseAndDescribe('RED rectangle selected', 2000); + + await verifyManually( + 'VERIFY: Is the RED rectangle now selected? (Should have selection indicators)\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Grouping the selected rectangle (Ctrl+G)'); + await useKeyboardShortcut('g', true); + await pauseAndDescribe('RED rectangle grouped', 2000); + + await verifyManually( + 'VERIFY: Was the rectangle grouped? (May look similar but is now a group)\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Selecting the group by dragging a selection box over it'); + await selectMultipleShapes([{ x: 150, y: 150 }]); + await pauseAndDescribe('Group selected', 2000); + + await logStep('Moving playhead to time 0.333 (frame 10 at 30fps)'); + await setPlayheadTime(0.333); + await pauseAndDescribe('Playhead moved to 0.333s - WAIT for UI to update', 3000); + + await verifyManually( + 'VERIFY: Did the playhead indicator move on the timeline?\n' + + 'It should be at approximately frame 10\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Adding a keyframe at current position'); + await addKeyframe(); + await pauseAndDescribe('Keyframe added', 2000); + + await verifyManually( + 'VERIFY: Was a keyframe added? (Should see a keyframe marker on timeline)\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Dragging the selected group to move it right (from x=150 to x=250)'); + await dragCanvas(150, 150, 250, 150); + await pauseAndDescribe('Group moved to the right', 3000); + + await verifyManually( + 'VERIFY: Did the RED rectangle move to the right?\n' + + 'It should now be centered around (250, 150)\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Moving playhead back to time 0 (frame 1)'); + await setPlayheadTime(0); + await pauseAndDescribe('Playhead back at start', 3000); + + await verifyManually( + 'VERIFY: Did the RED rectangle jump back to its original position (x=150)?\n' + + 'This confirms the group animation is working!\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Moving playhead to middle (time 0.166, frame 5)'); + await setPlayheadTime(0.166); + await pauseAndDescribe('Playhead at middle frame', 3000); + + await verifyManually( + 'VERIFY: Is the RED rectangle now between the two positions?\n' + + 'It should be around x=200 (interpolated halfway)\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Moving playhead back and forth to show animation'); + await setPlayheadTime(0); + await browser.pause(1000); + await setPlayheadTime(0.333); + await browser.pause(1000); + await setPlayheadTime(0); + await browser.pause(1000); + await setPlayheadTime(0.333); + await browser.pause(1000); + + await verifyManually( + 'VERIFY: Did you see the RED rectangle animate back and forth?\n' + + 'This demonstrates the timeline animation is working!\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('TEST 1 COMPLETE - Showing completion alert'); + const completionShown = await browser.execute(function() { + alert('TEST 1 COMPLETE - Click OK to finish'); + return true; + }); + await browser.pause(2000); // Wait for alert to be dismissed before ending test + }); + + it('TEST 2: Shape tween - draw shape, add keyframes, modify edges', async () => { + await logStep('Drawing a BLUE rectangle at (400, 100)'); + await drawRectangle(400, 100, 80, 80, true, '#0000ff'); + await pauseAndDescribe('BLUE rectangle drawn', 2000); + + await verifyManually( + 'VERIFY: Do you see a BLUE filled rectangle?\n' + + 'It should be at (400, 100) with size 80x80\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Selecting the BLUE rectangle'); + await selectMultipleShapes([{ x: 440, y: 140 }]); + await pauseAndDescribe('BLUE rectangle selected', 2000); + + await verifyManually( + 'VERIFY: Is the BLUE rectangle selected?\n' + + '(An initial keyframe should be automatically added at time 0)\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Moving playhead to time 0.5'); + await setPlayheadTime(0.5); + await pauseAndDescribe('Playhead moved to 0.5s - WAIT for UI to update', 3000); + + await verifyManually( + 'VERIFY: Did the playhead move to 0.5s on the timeline?\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Adding a keyframe at time 0.5'); + await addKeyframe(); + await pauseAndDescribe('Keyframe added at 0.5s', 2000); + + await verifyManually( + 'VERIFY: Was a keyframe added at 0.5s?\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Dragging the right edge of the BLUE rectangle to curve/extend it'); + await dragCanvas(480, 140, 530, 140); + await pauseAndDescribe('Dragged right edge of BLUE rectangle', 3000); + + await verifyManually( + 'VERIFY: Did the right edge of the BLUE rectangle get curved/pulled out?\n' + + 'The shape should now be modified/stretched to the right\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Moving playhead back to time 0'); + await setPlayheadTime(0); + await pauseAndDescribe('Playhead back at start', 3000); + + await verifyManually( + 'VERIFY: Did the BLUE rectangle return to its original rectangular shape?\n' + + 'The edge modification should not be visible at time 0\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Moving playhead to middle (time 0.25)'); + await setPlayheadTime(0.25); + await pauseAndDescribe('Playhead at middle (0.25s)', 3000); + + await verifyManually( + 'VERIFY: Is the BLUE rectangle shape somewhere between the two versions?\n' + + 'It should be partially morphed (shape tween interpolation)\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('TEST 2 COMPLETE - Showing completion alert'); + const completionShown = await browser.execute(function() { + alert('TEST 2 COMPLETE - Click OK to finish'); + return true; + }); + await browser.pause(2000); // Wait for alert to be dismissed before ending test + }); + + it('TEST 3: Test dragging unselected shape edge', async () => { + await logStep('Drawing a GREEN rectangle at (200, 250) WITHOUT selecting it'); + await drawRectangle(200, 250, 100, 100, true, '#00ff00'); + await pauseAndDescribe('GREEN rectangle drawn (not selected)', 2000); + + await verifyManually( + 'VERIFY: GREEN rectangle should be visible but NOT selected\n' + + '(No selection indicators around it)\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('Dragging from the right edge (x=300) of GREEN rectangle to extend it'); + await dragCanvas(300, 300, 350, 300); + await pauseAndDescribe('Dragged the right edge of GREEN rectangle', 3000); + + await verifyManually( + 'VERIFY: What happened to the GREEN rectangle?\n\n' + + 'Expected: The right edge should be curved/pulled out to x=350\n' + + 'Did the edge get modified as expected?\n\n' + + 'Click OK if yes, Cancel if no' + ); + + await logStep('TEST 3 COMPLETE - Showing completion alert'); + const completionShown = await browser.execute(function() { + alert('TEST 3 COMPLETE - Click OK to finish'); + return true; + }); + await browser.pause(2000); // Wait for alert to be dismissed before ending test + }); +});