Skip to content

Commit 4761f4e

Browse files
committed
Snapshot feature fix
1 parent 14f6955 commit 4761f4e

6 files changed

Lines changed: 51 additions & 176 deletions

File tree

example/wdio.conf.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export const config: Options.Testrunner = {
6969
'--headless',
7070
'--disable-gpu',
7171
'--remote-allow-origins=*',
72-
'--window-size=1280,800'
72+
'--window-size=1600,1200'
7373
]
7474
}
7575
// }, {

packages/app/src/components/browser/snapshot.ts

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -217,14 +217,7 @@ export class DevtoolsBrowser extends Element {
217217
)
218218

219219
async #renderCommandScreenshot(command?: CommandLog) {
220-
const screenshot = command?.screenshot
221-
if (!screenshot) {
222-
// Clicking a command that has no screenshot clears any previous overlay.
223-
this.#screenshotData = null
224-
this.requestUpdate()
225-
return
226-
}
227-
this.#screenshotData = screenshot
220+
this.#screenshotData = command?.screenshot ?? null
228221
this.requestUpdate()
229222
}
230223

@@ -443,10 +436,8 @@ export class DevtoolsBrowser extends Element {
443436
}
444437

445438
const hasMutations = this.mutations && this.mutations.length
446-
// Explicit user selection takes priority; fall back to latest auto-screenshot
447-
// so the preview always shows the most recently executed command's state
448-
// (important for Nightwatch mode where there are no DOM mutations).
449-
const displayScreenshot = this.#screenshotData ?? this.#latestAutoScreenshot
439+
const autoScreenshot = hasMutations ? null : this.#latestAutoScreenshot
440+
const displayScreenshot = this.#screenshotData ?? autoScreenshot
450441

451442
return html`
452443
<section
@@ -467,26 +458,28 @@ export class DevtoolsBrowser extends Element {
467458
<span class="truncate">${this.#activeUrl}</span>
468459
</div>
469460
</header>
470-
${hasMutations
461+
${this.#screenshotData
471462
? html`
472463
<div class="iframe-wrapper">
473-
<iframe class="origin-top-left"></iframe>
474-
${displayScreenshot
475-
? html`<div class="screenshot-overlay">
476-
<img src="data:image/png;base64,${displayScreenshot}" />
477-
</div>`
478-
: ''}
464+
<div class="screenshot-overlay" style="position:relative;flex:1;min-height:0;">
465+
<img src="data:image/png;base64,${this.#screenshotData}" />
466+
</div>
479467
</div>`
480-
: displayScreenshot
468+
: hasMutations
481469
? html`
482470
<div class="iframe-wrapper">
483-
<div class="screenshot-overlay" style="position:relative;flex:1;min-height:0;">
484-
<img src="data:image/png;base64,${displayScreenshot}" />
485-
</div>
471+
<iframe class="origin-top-left"></iframe>
486472
</div>`
487-
: html`<wdio-devtools-placeholder
488-
style="height: 100%"
489-
></wdio-devtools-placeholder>`}
473+
: displayScreenshot
474+
? html`
475+
<div class="iframe-wrapper">
476+
<div class="screenshot-overlay" style="position:relative;flex:1;min-height:0;">
477+
<img src="data:image/png;base64,${displayScreenshot}" />
478+
</div>
479+
</div>`
480+
: html`<wdio-devtools-placeholder
481+
style="height: 100%"
482+
></wdio-devtools-placeholder>`}
490483
</section>
491484
`
492485
}

packages/nightwatch-devtools/example/nightwatch.conf.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ module.exports = {
2222
desiredCapabilities: {
2323
browserName: 'chrome',
2424
'goog:chromeOptions': {
25-
args: ['--headless', '--no-sandbox', '--disable-dev-shm-usage']
25+
args: ['--headless', '--no-sandbox', '--disable-dev-shm-usage', '--window-size=1600,1200']
2626
},
2727
'goog:loggingPrefs': { performance: 'ALL' }
2828
},

packages/nightwatch-devtools/src/helpers/browserProxy.ts

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -44,26 +44,17 @@ export class BrowserProxy {
4444
this.sessionCapturer = capturer
4545
}
4646

47-
/**
48-
* Reset command tracking for new test
49-
*/
5047
resetCommandTracking(): void {
5148
this.commandStack = []
5249
this.lastCommandSig = null
5350
this.lastCapturedSig = null
5451
this.lastCapturedId = null
5552
}
5653

57-
/**
58-
* Get current test file path
59-
*/
6054
getCurrentTestFullPath(): string | null {
6155
return this.currentTestFullPath
6256
}
6357

64-
/**
65-
* Set current test file path
66-
*/
6758
setCurrentTestFullPath(path: string | null): void {
6859
this.currentTestFullPath = path
6960
}
@@ -117,9 +108,6 @@ export class BrowserProxy {
117108
log.info('✓ Script injection wrapped')
118109
}
119110

120-
/**
121-
* Wrap all browser commands to capture them
122-
*/
123111
wrapBrowserCommands(browser: NightwatchBrowser): void {
124112
if (this.proxiedBrowsers.has(browser as object)) {
125113
return
@@ -166,41 +154,25 @@ export class BrowserProxy {
166154
log.info(`✓ Wrapped ${wrappedMethods.length} browser methods`)
167155
}
168156

169-
/**
170-
* Handle command execution with tracking.
171-
*
172-
* Injects a result-capturing callback as the last argument so that the actual
173-
* WebDriver result value (from Nightwatch's command queue) is available when
174-
* the command finishes rather than being `undefined`.
175-
*/
176157
private handleCommandExecution(
177158
browser: NightwatchBrowser,
178159
browserAny: any,
179160
methodName: string,
180161
originalMethod: Function,
181162
args: any[]
182163
): any {
183-
// Detect test boundaries
184164
const currentNightwatchTest = browserAny.currentTest
185-
const currentTestName = this.testManager.detectTestBoundary(
186-
currentNightwatchTest
187-
)
188-
189-
// Start test if this is its first command
165+
const currentTestName = this.testManager.detectTestBoundary(currentNightwatchTest)
190166
this.testManager.startTestIfPending(currentTestName)
191167

192-
// Get call source
193168
const callInfo = getCallSourceFromStack()
194169
if (callInfo.filePath && !this.currentTestFullPath) {
195170
this.currentTestFullPath = callInfo.filePath
196171
}
197172

198-
// Separate any user-supplied callback from the positional args so we can
199-
// inject our own result-capturing wrapper without losing the user's cb.
200173
const lastArg = args[args.length - 1]
201174
const hasUserCallback = typeof lastArg === 'function'
202175
const userCallback: Function | null = hasUserCallback ? lastArg : null
203-
// The "logical" args that we will log (without callback)
204176
const logArgs = hasUserCallback ? args.slice(0, -1) : args
205177

206178
// Check for duplicate commands (based on method + logical args)
@@ -220,7 +192,6 @@ export class BrowserProxy {
220192
this.lastCommandSig = cmdSig
221193
}
222194

223-
// Snapshot test context at call time (may change by callback time)
224195
const testAtCallTime = this.getCurrentTest()
225196
const testUid = testAtCallTime?.uid
226197
const callSource = callInfo.callSource
@@ -231,7 +202,6 @@ export class BrowserProxy {
231202
* command completes. This is where we get the *actual* result value.
232203
*/
233204
const captureCallback = (callbackResult: any) => {
234-
// Pop stack frame (if not already done for duplicates)
235205
const stackFrame = this.commandStack[this.commandStack.length - 1]
236206
if (
237207
stackFrame?.command === methodName &&
@@ -240,14 +210,6 @@ export class BrowserProxy {
240210
this.commandStack.pop()
241211
}
242212

243-
// ── Step 1: extract / normalise the result ───────────────────────────
244-
// All Nightwatch commands return {value: V, status: 0} to the callback.
245-
//
246-
// Commands that semantically return a boolean (waitFor*, is*, has*,
247-
// anything ending in Visible/Present/Enabled/Selected/NotVisible/
248-
// NotPresent) return {value: true} on success and {value: null} on
249-
// failure/timeout. Detected generically via BOOLEAN_COMMAND_PATTERN
250-
// (defined in constants.ts) — no hardcoded list needed.
251213
const isBooleanCommand = BOOLEAN_COMMAND_PATTERN.test(methodName)
252214

253215
let serializedResult: any = undefined
@@ -278,9 +240,6 @@ export class BrowserProxy {
278240
}
279241
}
280242

281-
// ── Step 2: capture & send ───────────────────────────────────────────
282-
// Use the test context that is current *now* (at completion time), but
283-
// fall back to the snapshot taken at call time.
284243
const currentTest = this.getCurrentTest()
285244
const effectiveUid = currentTest?.uid ?? testUid
286245

@@ -304,7 +263,6 @@ export class BrowserProxy {
304263
this.lastCapturedId = entry._id ?? null
305264
this.sessionCapturer.sendReplaceCommand(oldTimestamp, entry)
306265

307-
// Snapshot this entry for the screenshot perform below.
308266
const entryToScreenshot = entry
309267
if (typeof (browser as any).perform === 'function') {
310268
const ts = (entryToScreenshot as any).timestamp
@@ -343,7 +301,6 @@ export class BrowserProxy {
343301
.catch((err: any) =>
344302
log.error(`Failed to capture ${methodName}: ${err.message}`)
345303
)
346-
// Read the entry synchronously — it was already pushed above.
347304
const lastCommand =
348305
this.sessionCapturer.commandsLog[
349306
this.sessionCapturer.commandsLog.length - 1
@@ -353,11 +310,6 @@ export class BrowserProxy {
353310
this.sessionCapturer.sendCommand(lastCommand)
354311
}
355312

356-
// Queue a perform RIGHT HERE (inside captureCallback, where the entry
357-
// is already known) so it inserts immediately after the current command
358-
// — before the next test command and before end().
359-
// We snapshot `lastCommand` into a local const so it can never be
360-
// overwritten by a later captureCallback for a different command.
361313
const entryToScreenshot = lastCommand
362314
if (entryToScreenshot && typeof (browser as any).perform === 'function') {
363315
const ts = (entryToScreenshot as any).timestamp
@@ -389,20 +341,17 @@ export class BrowserProxy {
389341
}
390342
}
391343

392-
// Forward to the user's original callback (if any)
393344
if (userCallback) {
394345
return userCallback(callbackResult)
395346
}
396347
}
397348

398-
// Build modified args: logical args + our capturing callback
399349
const modifiedArgs = [...logArgs, captureCallback]
400350

401351
try {
402352
const result = originalMethod(...modifiedArgs)
403353
return result
404354
} catch (error) {
405-
// Handle synchronous queue-level errors (rare)
406355
const stackFrame = this.commandStack[this.commandStack.length - 1]
407356
if (
408357
stackFrame?.command === methodName &&
@@ -415,9 +364,6 @@ export class BrowserProxy {
415364
}
416365
}
417366

418-
/**
419-
* Capture command error
420-
*/
421367
private captureCommandError(
422368
methodName: string,
423369
args: any[],
@@ -451,9 +397,6 @@ export class BrowserProxy {
451397
}
452398
}
453399

454-
/**
455-
* Check if a specific browser instance is already proxied
456-
*/
457400
isProxied(browser: NightwatchBrowser): boolean {
458401
return this.proxiedBrowsers.has(browser as object)
459402
}

0 commit comments

Comments
 (0)