From b27f841831d32b45adcae34c1bf0461c4a1de948 Mon Sep 17 00:00:00 2001 From: DavertMik Date: Sun, 11 Oct 2026 22:50:39 +0300 Subject: [PATCH 1/2] fix(CDPBrowser): report connection errors on failed steps, skip screenshots without rendering Helper _before errors are only logged by core, so when the browser could not be started or reached the first step failed with "Cannot read properties of null (reading 'send')". The connection error is now kept and rethrown before each step, so the failure summary shows the real cause. saveScreenshot and saveElementScreenshot no longer throw on browsers without a rendering engine (Lightpanda); they print a debug message and return. Co-Authored-By: Claude Opus 5.5 --- docs/helpers/Lightpanda.md | 2 +- lib/helper/CDPBrowser.js | 20 +++++++++++++++++--- lib/helper/Lightpanda.js | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/helpers/Lightpanda.md b/docs/helpers/Lightpanda.md index ec888a564..6c44bedb2 100644 --- a/docs/helpers/Lightpanda.md +++ b/docs/helpers/Lightpanda.md @@ -62,7 +62,7 @@ These are set automatically and only need overriding for unusual setups: ## Limitations -* No screenshots and no screencast: `saveScreenshot` throws. +* No screenshots and no screencast: `saveScreenshot` and `saveElementScreenshot` do nothing (a debug message is printed). * No frames or popups. * No scrolling: scroll positions always stay at 0. * Computed styles cover what visibility checks need (`display`, `visibility`, `opacity`, diff --git a/lib/helper/CDPBrowser.js b/lib/helper/CDPBrowser.js index 9edf74eb7..f55814576 100644 --- a/lib/helper/CDPBrowser.js +++ b/lib/helper/CDPBrowser.js @@ -357,8 +357,20 @@ class CDPBrowser extends Helper { await this.cdp.connect() } + _beforeStep() { + if (this._connectError) throw new Error(this._connectError.message) + } + async _before() { - if (!this.cdp || !this.cdp.isConnected) await this._connect() + this._connectError = null + if (!this.cdp || !this.cdp.isConnected) { + try { + await this._connect() + } catch (err) { + this._connectError = err + throw err + } + } this._navStartWaiters = [] this._lastMainFrameNav = { loaderId: null, events: {} } const { targetId } = await this.cdp.send('Target.createTarget', { url: 'about:blank' }) @@ -1343,7 +1355,8 @@ class CDPBrowser extends Helper { */ async saveScreenshot(fileName) { if (this.capabilities.screenshot === false) { - throw new Error('saveScreenshot is not supported: this browser has no rendering engine') + this.debug('saveScreenshot skipped: this browser has no rendering engine') + return } const outputDir = global.output_dir || '.' const res = await this.cdp.send('Page.captureScreenshot', { format: 'png' }, this.sessionId) @@ -1355,7 +1368,8 @@ class CDPBrowser extends Helper { */ async saveElementScreenshot(locator, fileName) { if (this.capabilities.screenshot === false) { - throw new Error('saveElementScreenshot is not supported: this browser has no rendering engine') + this.debug('saveElementScreenshot skipped: this browser has no rendering engine') + return } const res = await this._run(this._candidates(locator), 'rect') if (!res.found) throw new ElementNotFound(locator) diff --git a/lib/helper/Lightpanda.js b/lib/helper/Lightpanda.js index cdc45b67f..e3bc19563 100644 --- a/lib/helper/Lightpanda.js +++ b/lib/helper/Lightpanda.js @@ -72,7 +72,7 @@ const config = {} * * ## Limitations * - * - No screenshots and no screencast: `saveScreenshot` throws. + * - No screenshots and no screencast: `saveScreenshot` and `saveElementScreenshot` do nothing (a debug message is printed). * - No frames or popups. * - No scrolling: scroll positions always stay at 0. * - Computed styles cover what visibility checks need (`display`, `visibility`, `opacity`, From f2e301e160521c98c10630d435bd1ae428c62e45 Mon Sep 17 00:00:00 2001 From: DavertMik Date: Sun, 11 Oct 2026 23:01:18 +0300 Subject: [PATCH 2/2] test(CDPBrowser): expect screenshots to be skipped without a rendering engine Co-Authored-By: Claude Opus 5.5 --- test/helper/CDPBrowser_lightpanda_test.js | 10 +++------- test/helper/CDPBrowser_obscura_test.js | 12 ++++-------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/test/helper/CDPBrowser_lightpanda_test.js b/test/helper/CDPBrowser_lightpanda_test.js index 439a749e9..47607e6bf 100644 --- a/test/helper/CDPBrowser_lightpanda_test.js +++ b/test/helper/CDPBrowser_lightpanda_test.js @@ -66,14 +66,10 @@ describe('Lightpanda helper (self-managed lightpanda serve)', function () { await I.dontSeeElement('#does-not-exist') }) - it('rejects screenshots with a clear message', async () => { + it('skips screenshots without failing', async () => { await I.amOnPage('/') - try { - await I.saveScreenshot('nope.png') - throw new Error('should have thrown') - } catch (err) { - expect(err.message).to.include('no rendering engine') - } + await I.saveScreenshot('nope.png') + expect(fs.existsSync(path.join(global.output_dir || '.', 'nope.png'))).to.be.false }) webApiTests.tests() diff --git a/test/helper/CDPBrowser_obscura_test.js b/test/helper/CDPBrowser_obscura_test.js index c3b90cd92..bbba0c960 100644 --- a/test/helper/CDPBrowser_obscura_test.js +++ b/test/helper/CDPBrowser_obscura_test.js @@ -80,15 +80,11 @@ describe('Obscura helper (self-managed obscura serve)', function () { } }) - it('rejects screenshots with a clear message when there is no rendering engine', async function () { + it('skips screenshots without failing when there is no rendering engine', async function () { await I.amOnPage('/') - if (I.capabilities.screenshot !== false) this.skip() // this binary can render; saveScreenshot works instead of throwing - try { - await I.saveScreenshot('nope.png') - throw new Error('should have thrown') - } catch (err) { - expect(err.message).to.include('no rendering engine') - } + if (I.capabilities.screenshot !== false) this.skip() // this binary can render; saveScreenshot writes a file instead + await I.saveScreenshot('nope.png') + expect(fs.existsSync(path.join(global.output_dir || '.', 'nope.png'))).to.be.false }) webApiTests.tests()