Skip to content

Commit cfdbbbf

Browse files
committed
🔍 Add verbose timing logs to debug cloud mode slowness
Adds detailed timing breakdowns when VIZZLY_LOG_LEVEL=debug: - Navigation: mode selection and duration for each story - Task steps: viewport, navigate, hook, screenshot timing - Screenshot: capture vs send timing breakdown - Client SDK: HTTP call duration - Server handlers: request received/returned timing Enabled verbose logging in CI for Storybook E2E tests.
1 parent b4d72d6 commit cfdbbbf

7 files changed

Lines changed: 93 additions & 7 deletions

File tree

.github/workflows/sdk-e2e.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,14 @@ jobs:
175175
run: ../../bin/vizzly.js tdd run "npm run test:e2e"
176176
env:
177177
CI: true
178+
VIZZLY_LOG_LEVEL: debug
178179

179180
- name: Run E2E tests (Cloud mode)
180181
working-directory: ./clients/storybook
181182
run: ../../bin/vizzly.js run "npm run test:e2e"
182183
env:
183184
CI: true
185+
VIZZLY_LOG_LEVEL: debug
184186
VIZZLY_TOKEN: ${{ secrets.VIZZLY_STORYBOOK_CLIENT_TOKEN }}
185187
VIZZLY_COMMIT_MESSAGE: ${{ github.event.pull_request.head.commit.message || github.event.head_commit.message }}
186188
VIZZLY_COMMIT_SHA: ${{ github.event.pull_request.head.sha || github.event.head_commit.id }}

clients/storybook/src/navigation.js

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,28 @@ export function generateStoryUrl(baseUrl, storyId) {
2828
export async function navigateToStory(tab, storyId, baseUrl, options = {}) {
2929
let { timeout = 30000 } = options;
3030
let entry = tab._poolEntry;
31+
let verbose = process.env.VIZZLY_LOG_LEVEL === 'debug';
32+
33+
// Debug: log navigation mode
34+
let navMode = !entry?.storybookInitialized
35+
? 'full-page-init'
36+
: entry.currentStoryId === storyId
37+
? 'skip-same-story'
38+
: 'client-side';
39+
40+
if (verbose) {
41+
console.error(` [nav] ${storyId}: ${navMode} (poolEntry: ${!!entry})`);
42+
}
3143

3244
// First time this tab visits Storybook: full page load
3345
if (!entry?.storybookInitialized) {
46+
let start = Date.now();
3447
await fullPageNavigation(tab, storyId, baseUrl, timeout);
3548

49+
if (verbose) {
50+
console.error(` [nav] ${storyId}: full-page took ${Date.now() - start}ms`);
51+
}
52+
3653
if (entry) {
3754
entry.storybookInitialized = true;
3855
entry.currentStoryId = storyId;
@@ -42,20 +59,32 @@ export async function navigateToStory(tab, storyId, baseUrl, options = {}) {
4259

4360
// Same story (maybe different viewport) - no navigation needed
4461
if (entry.currentStoryId === storyId) {
62+
if (verbose) {
63+
console.error(` [nav] ${storyId}: skip (same story)`);
64+
}
4565
return;
4666
}
4767

4868
// Subsequent visit: use client-side navigation
4969
try {
70+
let start = Date.now();
5071
await clientSideNavigation(tab, storyId, timeout);
72+
73+
if (verbose) {
74+
console.error(` [nav] ${storyId}: client-side took ${Date.now() - start}ms`);
75+
}
5176
entry.currentStoryId = storyId;
5277
} catch (error) {
53-
// Log and fallback to full navigation if client-side fails
54-
console.debug?.(
55-
`Client-side navigation failed for ${storyId}, falling back to full page load:`,
56-
error.message
78+
// Log fallback - always show since this is unexpected behavior
79+
console.error(
80+
` [nav] ${storyId}: client-side failed, falling back to full-page: ${error.message}`
5781
);
82+
let start = Date.now();
5883
await fullPageNavigation(tab, storyId, baseUrl, timeout);
84+
85+
if (verbose) {
86+
console.error(` [nav] ${storyId}: fallback full-page took ${Date.now() - start}ms`);
87+
}
5988
entry.currentStoryId = storyId;
6089
}
6190
}

clients/storybook/src/screenshot.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,19 @@ export async function captureAndSendScreenshot(
6767
screenshotOptions = {}
6868
) {
6969
let name = generateScreenshotName(story, viewport);
70+
let verbose = process.env.VIZZLY_LOG_LEVEL === 'debug';
71+
72+
let t0 = Date.now();
7073
let screenshot = await captureScreenshot(page, screenshotOptions);
74+
let captureTime = Date.now() - t0;
7175

76+
let t1 = Date.now();
7277
await vizzlyScreenshot(name, screenshot);
78+
let sendTime = Date.now() - t1;
79+
80+
if (verbose) {
81+
console.error(
82+
` [screenshot] ${name}: capture=${captureTime}ms send=${sendTime}ms`
83+
);
84+
}
7385
}

clients/storybook/src/tasks.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,20 +79,39 @@ export async function processTask(tab, task, deps = {}) {
7979
};
8080

8181
let { story, viewport, hook, storyId, baseUrl, screenshotOptions } = task;
82+
let verbose = process.env.VIZZLY_LOG_LEVEL === 'debug';
83+
let timings = {};
8284

8385
// Set viewport (tab is reused, so always set)
86+
let t0 = Date.now();
8487
await setViewport(tab, viewport);
88+
timings.viewport = Date.now() - t0;
8589

8690
// Navigate to the story (smart: uses client-side navigation when possible)
91+
let t1 = Date.now();
8792
await navigateToStory(tab, storyId, baseUrl);
93+
timings.navigate = Date.now() - t1;
8894

8995
// Run interaction hook if provided
9096
if (hook && typeof hook === 'function') {
97+
let t2 = Date.now();
9198
await hook(tab);
99+
timings.hook = Date.now() - t2;
92100
}
93101

94102
// Capture and send screenshot
103+
let t3 = Date.now();
95104
await captureAndSendScreenshot(tab, story, viewport, screenshotOptions);
105+
timings.screenshot = Date.now() - t3;
106+
107+
// Log timing breakdown in verbose mode
108+
if (verbose) {
109+
let total = timings.viewport + timings.navigate + (timings.hook || 0) + timings.screenshot;
110+
let hookStr = timings.hook ? ` hook=${timings.hook}ms` : '';
111+
console.error(
112+
` [timing] ${storyId}@${viewport.name}: viewport=${timings.viewport}ms nav=${timings.navigate}ms${hookStr} screenshot=${timings.screenshot}ms (total=${total}ms)`
113+
);
114+
}
96115
}
97116

98117
/**

src/client/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ function createSimpleClient(serverUrl) {
199199
let image = isFilePath ? imageBuffer : imageBuffer.toString('base64');
200200
let type = isFilePath ? 'file-path' : 'base64';
201201

202+
let httpStart = Date.now();
202203
const { status, json } = await httpPost(
203204
`${serverUrl}/screenshot`,
204205
{
@@ -211,6 +212,11 @@ function createSimpleClient(serverUrl) {
211212
},
212213
DEFAULT_TIMEOUT_MS
213214
);
215+
let httpMs = Date.now() - httpStart;
216+
217+
if (shouldLogClient('debug')) {
218+
console.debug(`[vizzly-client] ${name} HTTP completed in ${httpMs}ms`);
219+
}
214220

215221
if (status < 200 || status >= 300) {
216222
// In TDD mode, if we get 422 (visual difference), don't throw

src/server/handlers/api-handler.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ export const createApiHandler = (
5656
properties = {},
5757
type
5858
) => {
59+
let handlerStart = Date.now();
60+
output.debug('upload', `${name} received`, { buildId: buildId?.slice(0, 8) });
61+
5962
if (vizzlyDisabled) {
6063
output.debug('upload', `${name} (disabled)`);
6164
return {
@@ -137,6 +140,7 @@ export const createApiHandler = (
137140
};
138141
}
139142
screenshotCount++;
143+
let uploadStart = Date.now();
140144

141145
// Fire upload in background - DON'T AWAIT!
142146
let uploadPromise = uploadScreenshot(
@@ -147,26 +151,34 @@ export const createApiHandler = (
147151
properties ?? {}
148152
)
149153
.then(result => {
154+
let duration = Date.now() - uploadStart;
150155
if (!result.skipped) {
151-
output.debug('upload', name);
156+
output.debug('upload', `${name} completed`, { ms: duration });
157+
} else {
158+
output.debug('upload', `${name} skipped (dedup)`, { ms: duration });
152159
}
153-
return { success: true, name, result };
160+
return { success: true, name, result, duration };
154161
})
155162
.catch(uploadError => {
163+
let duration = Date.now() - uploadStart;
156164
output.debug('upload', `${name} failed`, {
157165
error: uploadError.message,
166+
ms: duration,
158167
});
159168
vizzlyDisabled = true;
160169
output.warn(
161170
'Vizzly disabled due to upload error - continuing tests without visual testing'
162171
);
163-
return { success: false, name, error: uploadError };
172+
return { success: false, name, error: uploadError, duration };
164173
});
165174

166175
// Collect promise for later flushing
167176
uploadPromises.push(uploadPromise);
168177

169178
// Return immediately - test continues without waiting!
179+
let handlerMs = Date.now() - handlerStart;
180+
output.debug('upload', `${name} handler returning`, { ms: handlerMs });
181+
170182
return {
171183
statusCode: 200,
172184
body: {

src/server/handlers/tdd-handler.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,9 @@ export const createTddHandler = (
332332
properties = {},
333333
type
334334
) => {
335+
let handlerStart = Date.now();
336+
output.debug('tdd', `${name} received`);
337+
335338
// Validate and sanitize screenshot name
336339
let sanitizedName;
337340
try {
@@ -522,6 +525,9 @@ export const createTddHandler = (
522525
}
523526

524527
// Match or new baseline
528+
let handlerMs = Date.now() - handlerStart;
529+
output.debug('tdd', `${name} handler returning`, { ms: handlerMs, status: comparison.status });
530+
525531
return {
526532
statusCode: 200,
527533
body: {

0 commit comments

Comments
 (0)