From 76475cebcc4d1d34959e137062f2e565071fc0ad Mon Sep 17 00:00:00 2001 From: Robert DeLuca Date: Tue, 3 Feb 2026 01:25:15 -0600 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8=20Log=20screenshot=20events=20for?= =?UTF-8?q?=20menubar=20display?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each screenshot comparison now logs to server.log with: - screenshot name - status (passed/failed/new/etc) - diffPercentage --- src/server/handlers/tdd-handler.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/server/handlers/tdd-handler.js b/src/server/handlers/tdd-handler.js index ef8a90b..6c2b54c 100644 --- a/src/server/handlers/tdd-handler.js +++ b/src/server/handlers/tdd-handler.js @@ -481,6 +481,13 @@ export const createTddHandler = ( // Update comparison in report data file updateComparison(newComparison); + // Log screenshot event for menubar + output.info(`Screenshot: ${sanitizedName}`, { + screenshot: sanitizedName, + status: comparison.status, + diffPercentage: comparison.diffPercentage || 0, + }); + // Visual diffs return 200 with status: 'diff' - they're not errors // The SDK/user can decide whether to fail tests based on this if (comparison.status === 'failed') { From d9c3b113cb517dd5ad031e327f7012833a5c30b1 Mon Sep 17 00:00:00 2001 From: Robert DeLuca Date: Tue, 3 Feb 2026 02:04:53 -0600 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9C=A8=20Normalize=20screenshot=20loggin?= =?UTF-8?q?g=20and=20add=20accept/reject=20events?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Normalize status in logs ('failed' -> 'diff') to match HTTP response - Add structured screenshot logging to accept/reject handlers for menubar - Add tests for screenshot event logging behavior --- src/server/handlers/tdd-handler.js | 18 ++- tests/server/handlers/tdd-handler.test.js | 145 +++++++++++++++++++++- 2 files changed, 155 insertions(+), 8 deletions(-) diff --git a/src/server/handlers/tdd-handler.js b/src/server/handlers/tdd-handler.js index 6c2b54c..a4017d5 100644 --- a/src/server/handlers/tdd-handler.js +++ b/src/server/handlers/tdd-handler.js @@ -482,9 +482,11 @@ export const createTddHandler = ( updateComparison(newComparison); // Log screenshot event for menubar + // Normalize status to match HTTP response ('failed' -> 'diff') + let logStatus = comparison.status === 'failed' ? 'diff' : comparison.status; output.info(`Screenshot: ${sanitizedName}`, { screenshot: sanitizedName, - status: comparison.status, + status: logStatus, diffPercentage: comparison.diffPercentage || 0, }); @@ -579,7 +581,12 @@ export const createTddHandler = ( updateComparison(updatedComparison); - output.info(`Baseline accepted for comparison ${comparisonId}`); + // Log screenshot event for menubar + output.info(`Screenshot: ${comparison.name}`, { + screenshot: comparison.name, + status: 'accepted', + diffPercentage: 0, + }); return result; } catch (error) { output.error(`Failed to accept baseline for ${comparisonId}:`, error); @@ -609,7 +616,12 @@ export const createTddHandler = ( updateComparison(updatedComparison); - output.info(`Changes rejected for comparison ${comparisonId}`); + // Log screenshot event for menubar + output.info(`Screenshot: ${comparison.name}`, { + screenshot: comparison.name, + status: 'rejected', + diffPercentage: comparison.diffPercentage || 0, + }); return { success: true, id: comparisonId }; } catch (error) { output.error(`Failed to reject baseline for ${comparisonId}:`, error); diff --git a/tests/server/handlers/tdd-handler.test.js b/tests/server/handlers/tdd-handler.test.js index b348bb3..b98bc2f 100644 --- a/tests/server/handlers/tdd-handler.test.js +++ b/tests/server/handlers/tdd-handler.test.js @@ -934,6 +934,93 @@ describe('server/handlers/tdd-handler', () => { assert.strictEqual(result.statusCode, 400); assert.ok(result.body.error.includes('Invalid image input')); }); + + it('logs screenshot event with normalized status for menubar consumption', async () => { + let deps = createMockDeps({ + tddServiceOverrides: { + compareScreenshot: name => ({ + id: `comp-${name}`, + name, + status: 'failed', + diffPercentage: 5.5, + baseline: '/baselines/test.png', + current: '/current/test.png', + diff: '/diffs/test.png', + threshold: 2.0, + }), + }, + }); + let handler = createTddHandler({}, '/test', null, null, false, deps); + + await handler.handleScreenshot('build-1', 'homepage', 'base64data', {}); + + // Find the screenshot log - menubar looks for logs starting with "Screenshot:" + let screenshotLog = deps._mockOutput.calls.find( + c => c.method === 'info' && c.args[0].startsWith('Screenshot:') + ); + + assert.ok(screenshotLog, 'Screenshot event should be logged'); + assert.strictEqual(screenshotLog.args[1].screenshot, 'homepage'); + // Status should be normalized to 'diff' (not 'failed') to match HTTP response + assert.strictEqual(screenshotLog.args[1].status, 'diff'); + assert.strictEqual(screenshotLog.args[1].diffPercentage, 5.5); + }); + + it('logs passed screenshots with match-equivalent status', async () => { + let deps = createMockDeps({ + tddServiceOverrides: { + compareScreenshot: name => ({ + id: `comp-${name}`, + name, + status: 'passed', + baseline: '/baselines/test.png', + current: '/current/test.png', + diff: null, + }), + }, + }); + let handler = createTddHandler({}, '/test', null, null, false, deps); + + await handler.handleScreenshot('build-1', 'button', 'base64data', {}); + + let screenshotLog = deps._mockOutput.calls.find( + c => c.method === 'info' && c.args[0].startsWith('Screenshot:') + ); + + assert.ok(screenshotLog); + assert.strictEqual(screenshotLog.args[1].status, 'passed'); + assert.strictEqual(screenshotLog.args[1].diffPercentage, 0); + }); + + it('logs new baseline screenshots', async () => { + let deps = createMockDeps({ + tddServiceOverrides: { + compareScreenshot: name => ({ + id: `comp-${name}`, + name, + status: 'new', + baseline: '/baselines/test.png', + current: '/current/test.png', + diff: null, + }), + }, + }); + let handler = createTddHandler({}, '/test', null, null, false, deps); + + await handler.handleScreenshot( + 'build-1', + 'new-component', + 'base64data', + {} + ); + + let screenshotLog = deps._mockOutput.calls.find( + c => c.method === 'info' && c.args[0].startsWith('Screenshot:') + ); + + assert.ok(screenshotLog); + assert.strictEqual(screenshotLog.args[1].status, 'new'); + }); }); describe('getResults', () => { @@ -992,6 +1079,43 @@ describe('server/handlers/tdd-handler', () => { /Comparison not found/ ); }); + + it('logs screenshot event on acceptance for menubar consumption', async () => { + let deps = createMockDeps({ + tddServiceOverrides: { + acceptBaseline: () => ({ success: true }), + }, + }); + + let reportData = { + timestamp: Date.now(), + comparisons: [ + { + id: 'comp-1', + name: 'login-form', + status: 'failed', + diffPercentage: 2.1, + }, + ], + groups: [], + summary: { total: 1, passed: 0, failed: 1, errors: 0 }, + }; + deps._fileSystem['/test/.vizzly/report-data.json'] = + JSON.stringify(reportData); + + let handler = createTddHandler({}, '/test', null, null, false, deps); + + await handler.acceptBaseline('comp-1'); + + // Menubar looks for screenshot logs with structured metadata + let screenshotLog = deps._mockOutput.calls.find( + c => c.method === 'info' && c.args[0].startsWith('Screenshot:') + ); + assert.ok(screenshotLog, 'Screenshot event should be logged'); + assert.strictEqual(screenshotLog.args[1].screenshot, 'login-form'); + assert.strictEqual(screenshotLog.args[1].status, 'accepted'); + assert.strictEqual(screenshotLog.args[1].diffPercentage, 0); + }); }); describe('rejectBaseline', () => { @@ -1086,12 +1210,19 @@ describe('server/handlers/tdd-handler', () => { assert.strictEqual(comparison.properties.browser, 'chrome'); }); - it('logs info message on successful rejection', async () => { + it('logs screenshot event on rejection for menubar consumption', async () => { let deps = createMockDeps(); let reportData = { timestamp: Date.now(), - comparisons: [{ id: 'comp-1', name: 'test', status: 'failed' }], + comparisons: [ + { + id: 'comp-1', + name: 'button-hover', + status: 'failed', + diffPercentage: 3.2, + }, + ], groups: [], summary: { total: 1, passed: 0, failed: 1, errors: 0 }, }; @@ -1102,10 +1233,14 @@ describe('server/handlers/tdd-handler', () => { await handler.rejectBaseline('comp-1'); - let infoCall = deps._mockOutput.calls.find( - c => c.method === 'info' && c.args[0].includes('rejected') + // Menubar looks for screenshot logs with structured metadata + let screenshotLog = deps._mockOutput.calls.find( + c => c.method === 'info' && c.args[0].startsWith('Screenshot:') ); - assert.ok(infoCall); + assert.ok(screenshotLog, 'Screenshot event should be logged'); + assert.strictEqual(screenshotLog.args[1].screenshot, 'button-hover'); + assert.strictEqual(screenshotLog.args[1].status, 'rejected'); + assert.strictEqual(screenshotLog.args[1].diffPercentage, 3.2); }); });