Skip to content
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
3e772ee
wip: system test to reproduce
cacieprins Aug 7, 2025
d830ed8
system test for alsa stderr
cacieprins Aug 8, 2025
5f8528a
split cypress from 3rd party stderr at parent process to electron child
cacieprins Aug 8, 2025
f618cd6
rm garbage warning regexes
cacieprins Aug 8, 2025
de717a7
fix newline behavior when parsing internal stderr
cacieprins Aug 8, 2025
5fa8203
migrate left over console errors
cacieprins Aug 8, 2025
734e611
clean up system test name
cacieprins Aug 8, 2025
866415f
fix typed import
cacieprins Aug 8, 2025
87d5fa9
extract stderr splitting to separate pkg so runner can use @packages/…
cacieprins Aug 8, 2025
7d9898e
rm new err log from packherd-quire
cacieprins Aug 11, 2025
dd646b3
handle backpressure
cacieprins Aug 11, 2025
a3f935e
docs
cacieprins Aug 11, 2025
a893e29
some unit tests & coverage for stderr-filtering
cacieprins Aug 11, 2025
69487a0
unit tests
cacieprins Aug 11, 2025
b2ab667
no longer test regexp specific output in spawn unit tests
cacieprins Aug 12, 2025
a60d4f8
filter enabled debug namespaces rather than just cypress namespacesc
cacieprins Aug 13, 2025
c5eae1c
revise stream splitting et al
cacieprins Aug 13, 2025
2bbf290
try to fix v8 snapshot build??
cacieprins Aug 13, 2025
53764e9
Merge branch 'develop' into fix/audio-error-stdout
cacieprins Aug 14, 2025
bb32eb6
fix console.log assertion
cacieprins Aug 14, 2025
3bf6824
add missing eslint config
cacieprins Aug 14, 2025
256ee49
rm unused spies
cacieprins Aug 14, 2025
bc87235
fix regexp for optional leading wsp and ansi on debug entries
cacieprins Aug 14, 2025
4760d92
update unit tests because sinon
cacieprins Aug 14, 2025
7139dfc
lint
cacieprins Aug 14, 2025
ecd3ebd
colon..
cacieprins Aug 14, 2025
3f205cc
build stderr-filtering before checking if binary exists
cacieprins Aug 15, 2025
b5a2877
adds TagStream transform stream, fixes stderr from child proc config
cacieprins Aug 15, 2025
0415b4e
add build-prod script for stderr-filtering
cacieprins Aug 15, 2025
443f669
changelog
cacieprins Aug 15, 2025
b5035ac
Merge branch 'develop' into fix/audio-error-stdout
cacieprins Aug 15, 2025
2cc0265
properly handle backpressure in prefixed content transform stream
cacieprins Aug 15, 2025
a49a6ea
use standard tsconfig?
cacieprins Aug 15, 2025
62589f9
better tsconfig
cacieprins Aug 15, 2025
09d5937
Add pkgStderrFiltering to monorepoPaths
jennifer-shehane Aug 15, 2025
3953456
add \"files\" manifest
cacieprins Aug 18, 2025
b24aa0b
pipe all stderr to stderr when CYPRESS_INTERNAL_DEBUG_ELECTRON is ena…
cacieprins Aug 18, 2025
33e6b54
Merge branch 'develop' into fix/audio-error-stdout
jennifer-shehane Aug 18, 2025
b1f1b69
rm explicit build of stderr-filtering in check-if-binary-exists step
cacieprins Aug 18, 2025
7ae230e
ensure all dependencies of scripts/ are built before scripts are exec…
cacieprins Aug 18, 2025
0ddf849
fix dev version ref
cacieprins Aug 18, 2025
72a6a34
swap logic
cacieprins Aug 18, 2025
b241c67
add stdin piping
cacieprins Aug 18, 2025
8a4f1ea
fix exec name on the run-on-dependencies command to be more useful
cacieprins Aug 18, 2025
6e35818
use correct env
cacieprins Aug 18, 2025
8d82ccb
Merge branch 'develop' into fix/audio-error-stdout
cacieprins Aug 18, 2025
c9aa13e
rm obsolete type refs
cacieprins Aug 18, 2025
96f9b9e
Merge branch 'develop' into fix/audio-error-stdout
cacieprins Aug 18, 2025
b93c9fe
simplify stderr-filtering public iface, pipe cy-in-cy stderr through …
cacieprins Aug 19, 2025
e4f02d2
bust cache
cacieprins Aug 19, 2025
0c2b8b3
fix mocks
cacieprins Aug 19, 2025
ae43933
Merge branch 'develop' into fix/audio-error-stdout
cacieprins Aug 19, 2025
5e653d6
fix v8-snapshot
AtofStryker Aug 19, 2025
eba73d0
move stderrfiltering to dev pkg in cli
cacieprins Aug 19, 2025
8ebc620
skip integrity check in ci, if they are out of date things should fai…
cacieprins Aug 19, 2025
52e67f2
copypasta over a portion of stderr-filtering to cli, since cli cannot…
cacieprins Aug 19, 2025
996429f
Delete issues.md
cacieprins Aug 19, 2025
9bd788b
rm special filtering for cy in cy
cacieprins Aug 19, 2025
fea55c1
rm too narrow rules file
cacieprins Aug 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/cache-version.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Bump this version to force CI to re-create the cache from scratch.
8-13-2025
8-19-2025
19 changes: 19 additions & 0 deletions .circleci/workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1268,13 +1268,32 @@ commands:
check-if-binary-exists:
steps:
- run-on-dependencies:
package: internal-scripts
command: build
- run:
name: Check if binary exists, exit if it does
command: |
source ./scripts/ensure-node.sh
yarn gulp e2eTestScaffold
yarn check-binary-on-cdn --version $(node ./scripts/get-next-version.js) --type binary --file cypress.zip
run-on-dependencies:
parameters:
package:
description: Package to build all dependencies for. All dependencies in the
dependency chain must have a build script.
type: string
command:
description: Command to run on the dependencies.
type: string
steps:
- run:
name: Exec << parameters.command >> recursively on << parameters.package >>
dependencies.
command: |
yarn lerna run <<parameters.command>> --scope=<<parameters.package>> --include-dependencies
build-and-package-binary:
steps:
- run:
Expand Down
1 change: 1 addition & 0 deletions cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ _Released 08/12/2025 (PENDING)_
- Fixed an issue where `.fixture()` would not return updated content after the underlying file was modified via `.writeFile()`. The fixture cache is now properly invalidated when the backing file is written to, ensuring updated content is returned in subsequent `.fixture()` calls. Fixes [#4716](https://github.com/cypress-io/cypress/issues/4716).
- Fixed an issue where `.fixture()` calls with a specified encoding would sometimes still attempt to parse the file based on its extension. Files with an explicit encoding are now always treated as raw content. Fixes [#32139](https://github.com/cypress-io/cypress/issues/32139).
- Fixed an issue where `.fixture()` calls with different encoding options would return inconsistent content based on execution order. Fixes [#32138](https://github.com/cypress-io/cypress/issues/32138).
- Filters content written to stderr to prevent Electron from spamming with inconsequential errors/warnings. This stderr content can be viewed by enabling the `cypress:internal-stderr` debug namespace. Fixes [#32070](https://github.com/cypress-io/cypress/issues/32070)
- Fixed an issue where Angular Component Testing was printing extraneous warnings to the console by default. By default, errors only will now print to the console. This can still be overridden by passing in a custom webpack config or setting the `verbose` option inside your `angular.json`. Addresses [#26456](https://github.com/cypress-io/cypress/issues/26456).
- Fixed an issue where `ts-loader` was improperly being detected inside `@cypress/webpack-preprocessor`. Fixes [#32265](https://github.com/cypress-io/cypress/issues/32265).

Expand Down
158 changes: 24 additions & 134 deletions cli/lib/exec/spawn.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,125 +4,17 @@ const cp = require('child_process')
const path = require('path')
const Promise = require('bluebird')
const debug = require('debug')('cypress:cli')
const debugVerbose = require('debug')('cypress-verbose:cli')

const util = require('../util')
const state = require('../tasks/state')
const xvfb = require('./xvfb')
const verify = require('../tasks/verify')
const errors = require('../errors')
const readline = require('readline')
const { FilterTaggedContent } = require('../stderr-filtering/FilterTaggedContent')

const isXlibOrLibudevRe = /^(?:Xlib|libudev)/
const isHighSierraWarningRe = /\*\*\* WARNING/
const isRenderWorkerRe = /\.RenderWorker-/
// This is a warning that occurs when running in a container on Linux.
// https://github.com/cypress-io/cypress/issues/29563
// Example:
// [437:1212/125803.148706:ERROR:zygote_host_impl_linux.cc(273)] Failed to adjust OOM score of renderer with pid 610: Permission denied (13)
const isOOMScoreWarningRe = /Failed to adjust OOM score of renderer with pid/

// Chromium (which Electron uses) always makes several attempts to connect to the system dbus.
// This works fine in most desktop environments, but in a docker container, there is no dbus service
// and Chromium emits several error lines, similar to these:

// [1957:0406/160550.146820:ERROR:bus.cc(392)] Failed to connect to the bus: Failed to connect to socket /var/run/dbus/system_bus_socket: No such file or directory
// [1957:0406/160550.147994:ERROR:bus.cc(392)] Failed to connect to the bus: Address does not contain a colon

// These warnings are absolutely harmless. Failure to connect to dbus means that electron won't be able to access the user's
// credential wallet (none exists in a docker container) and won't show up in the system tray (again, none exists).
// Failure to connect is expected and normal here, but users frequently misidentify these errors as the cause of their problems.

// https://github.com/cypress-io/cypress/issues/19299
const isDbusWarning = /Failed to connect to the bus:/

// Electron began logging these on self-signed certs with 17.0.0-alpha.4.
// Once this is fixed upstream this regex can be removed: https://github.com/electron/electron/issues/34583
// Sample:
// [3801:0606/152837.383892:ERROR:cert_verify_proc_builtin.cc(681)] CertVerifyProcBuiltin for www.googletagmanager.com failed:
// ----- Certificate i=0 (OU=Cypress Proxy Server Certificate,O=Cypress Proxy CA,L=Internet,ST=Internet,C=Internet,CN=www.googletagmanager.com) -----
// ERROR: No matching issuer found
const isCertVerifyProcBuiltin = /(^\[.*ERROR:cert_verify_proc_builtin\.cc|^----- Certificate i=0 \(OU=Cypress Proxy|^ERROR: No matching issuer found$)/

/**
* Electron logs benign warnings about Vulkan when run on hosts that do not have a GPU. This is coming from the primary Electron process,
* and not the browser being used for tests.
* Samples:
* Warning: loader_scanned_icd_add: Driver /usr/lib/x86_64-linux-gnu/libvulkan_intel.so supports Vulkan 1.2, but only supports loader interface version 4. Interface version 5 or newer required to support this version of Vulkan (Policy #LDP_DRIVER_7)
* Warning: loader_scanned_icd_add: Driver /usr/lib/x86_64-linux-gnu/libvulkan_lvp.so supports Vulkan 1.1, but only supports loader interface version 4. Interface version 5 or newer required to support this version of Vulkan (Policy #LDP_DRIVER_7)
* Warning: loader_scanned_icd_add: Driver /usr/lib/x86_64-linux-gnu/libvulkan_radeon.so supports Vulkan 1.2, but only supports loader interface version 4. Interface version 5 or newer required to support this verison of Vulkan (Policy #LDP_DRIVER_7)
* Warning: Layer VK_LAYER_MESA_device_select uses API version 1.2 which is older than the application specified API version of 1.3. May cause issues.
*/

const isHostVulkanDriverWarning = /^Warning:.+(#LDP_DRIVER_7|VK_LAYER_MESA_device_select).+/

/**
* Electron logs benign warnings about Vulkan when run in docker containers whose host does not have a GPU. This is coming from the primary
* Electron process, and not the browser being used for tests.
* Sample:
* Warning: vkCreateInstance: Found no drivers!
* Warning: vkCreateInstance failed with VK_ERROR_INCOMPATIBLE_DRIVER
* at CheckVkSuccessImpl (../../third_party/dawn/src/dawn/native/vulkan/VulkanError.cpp:88)
* at CreateVkInstance (../../third_party/dawn/src/dawn/native/vulkan/BackendVk.cpp:458)
* at Initialize (../../third_party/dawn/src/dawn/native/vulkan/BackendVk.cpp:344)
* at Create (../../third_party/dawn/src/dawn/native/vulkan/BackendVk.cpp:266)
* at operator() (../../third_party/dawn/src/dawn/native/vulkan/BackendVk.cpp:521)
*/

const isContainerVulkanDriverWarning = /^Warning: vkCreateInstance/

const isContainerVulkanStack = /^\s*at (CheckVkSuccessImpl|CreateVkInstance|Initialize|Create|operator).+(VulkanError|BackendVk).cpp/

/**
* In Electron 32.0.0 a new debug scenario log message started appearing when iframes navigate to about:blank. This is a benign message.
* https://github.com/electron/electron/issues/44368
* Sample:
* [78887:1023/114920.074882:ERROR:debug_utils.cc(14)] Hit debug scenario: 4
*/
const isDebugScenario4 = /^\[[^\]]+debug_utils\.cc[^\]]+\] Hit debug scenario: 4/

/**
* In Electron 32.0.0 a new EGL driver message started appearing when running on Linux. This is a benign message.
* https://github.com/electron/electron/issues/43415
* Sample:
* [78887:1023/114920.074882:ERROR:gl_display.cc(14)] EGL Driver message (Error) eglQueryDeviceAttribEXT: Bad attribute.
*/
const isEGLDriverMessage = /^\[[^\]]+gl_display\.cc[^\]]+\] EGL Driver message \(Error\) eglQueryDeviceAttribEXT: Bad attribute\./

/**
* Mesa/GLX related warnings that occur in certain Linux environments without proper GPU support
* or when running in containers. These are benign warnings that don't affect functionality.
* Samples:
* error: XDG_RUNTIME_DIR is invalid or not set in the environment.
* MESA: error: ZINK: failed to choose pdev
* glx: failed to create drisw screen
*/
const isXdgRuntimeError = /^error: XDG_RUNTIME_DIR is invalid or not set/
const isMesaZinkError = /^MESA: error: ZINK: failed to choose pdev/
const isGlxDriverError = /^glx: failed to create drisw screen/

const GARBAGE_WARNINGS = [
isXlibOrLibudevRe,
isHighSierraWarningRe,
isRenderWorkerRe,
isOOMScoreWarningRe,
isDbusWarning,
isCertVerifyProcBuiltin,
isHostVulkanDriverWarning,
isContainerVulkanDriverWarning,
isContainerVulkanStack,
isDebugScenario4,
isEGLDriverMessage,
isXdgRuntimeError,
isMesaZinkError,
isGlxDriverError,
]

const isGarbageLineWarning = (str) => {
return _.some(GARBAGE_WARNINGS, (re) => {
return re.test(str)
})
}
export const START_TAG = '<<<CYPRESS.STDERR.START>>>'

export const END_TAG = '<<<CYPRESS.STDERR.END>>>'

function isPlatform (platform) {
return os.platform() === platform
Expand Down Expand Up @@ -161,8 +53,6 @@ function getStdio (needsXvfb) {
}

module.exports = {
isGarbageLineWarning,

start (args, options = {}) {
const needsXvfb = xvfb.isNeeded()
let executable = state.getPathToExecutable(state.getBinaryDir())
Expand Down Expand Up @@ -312,26 +202,26 @@ module.exports = {
// if this is defined then we are manually piping for linux
// to filter out the garbage
if (child.stderr) {
debug('piping child STDERR to process STDERR')
child.stderr.on('data', (data) => {
const str = data.toString()

// bail if this is warning line garbage
if (isGarbageLineWarning(str)) {
debugVerbose(str)

return
}

// if we have a callback and this explicitly returns
// false then bail
if (onStderrData && onStderrData(str)) {
return
}

// else pass it along!
process.stderr.write(data)
})
if (process.env.CYPRESS_INTERNAL_E2E_TESTING_SELF_PARENT_PROJECT) {
// strip out tags from stderr output during cy in cy tests
const tagFilter = new FilterTaggedContent(START_TAG, END_TAG, process.stderr)

child.stderr.pipe(tagFilter).pipe(process.stderr)
} else {
debug('piping child STDERR to process STDERR')
child.stderr.on('data', (data) => {
const str = data.toString()

// if we have a callback and this explicitly returns
// false then bail
if (onStderrData && onStderrData(str)) {
return
}

// else pass it along!
process.stderr.write(data)
})
}
}

// https://github.com/cypress-io/cypress/issues/1841
Expand Down
166 changes: 166 additions & 0 deletions cli/lib/stderr-filtering/FilterTaggedContent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
'use strict'
let __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { 'default': mod }
}

Object.defineProperty(exports, '__esModule', { value: true })
exports.FilterTaggedContent = void 0

const stream_1 = require('stream')
const string_decoder_1 = require('string_decoder')
const LineDecoder_1 = require('./LineDecoder')
const debug_1 = __importDefault(require('debug'))
const writeWithBackpressure_1 = require('./writeWithBackpressure')
const debug = (0, debug_1.default)('cypress:stderr-filtering:FilterTaggedContent')

/**
* Filters content based on start and end tags, supporting multi-line tagged content.
*
* This transform stream processes incoming data and routes content between two output streams
* based on tag detection. Content between start and end tags is sent to the filtered stream,
* while content outside tags is sent to the main output stream. The class handles cases where
* tags span multiple lines by maintaining state across line boundaries.
*
* Example usage:
* ```typescript
* const filter = new FilterTaggedContent('<secret>', '</secret>', filteredStream)
* inputStream.pipe(filter).pipe(outputStream)
* ```
*/
class FilterTaggedContent extends stream_1.Transform {
/**
* Creates a new FilterTaggedContent instance.
*
* @param startTag The string that marks the beginning of content to filter
* @param endTag The string that marks the end of content to filter
* @param filtered The writable stream for filtered content
*/
constructor (startTag, endTag, wasteStream) {
super({
transform: (chunk, encoding, next) => this.transform(chunk, encoding, next),
flush: (callback) => this.flush(callback),
})

this.startTag = startTag
this.endTag = endTag
this.wasteStream = wasteStream
this.inTaggedContent = false
/**
* Processes incoming chunks and routes content based on tag detection.
*
* @param chunk The buffer chunk to process
* @param encoding The encoding of the chunk
* @param next Callback to call when processing is complete
*/
this.transform = async (chunk, encoding, next) => {
let _a; let _b; let _c

try {
this.ensureDecoders(encoding)
const str = (_b = (_a = this.strDecoder) === null || _a === void 0 ? void 0 : _a.write(chunk)) !== null && _b !== void 0 ? _b : '';

(_c = this.lineDecoder) === null || _c === void 0 ? void 0 : _c.write(str)
debug('processing str for tags: "%s"', str)
for (const line of Array.from(this.lineDecoder || [])) {
await this.processLine(line)
}
next()
} catch (err) {
next(err)
}
}

/**
* Flushes any remaining buffered content when the stream ends.
*
* @param callback Callback to call when flushing is complete
*/
this.flush = async (callback) => {
let _a

debug('flushing')
this.ensureDecoders()
try {
for (const line of Array.from(((_a = this.lineDecoder) === null || _a === void 0 ? void 0 : _a.end()) || [])) {
await this.processLine(line)
}
callback()
} catch (err) {
callback(err)
}
}
}
ensureDecoders (encoding) {
let _a
const enc = (_a = (encoding === 'buffer' ? 'utf8' : encoding)) !== null && _a !== void 0 ? _a : 'utf8'

if (!this.lineDecoder) {
this.lineDecoder = new LineDecoder_1.LineDecoder()
}

if (!this.strDecoder) {
this.strDecoder = new string_decoder_1.StringDecoder(enc)
}
}
/**
* Processes a single line and routes content based on tag positions.
*
* This method handles the complex logic of detecting start and end tags within a line,
* maintaining state across lines, and routing content to the appropriate streams.
* It supports cases where both tags appear on the same line, only one tag appears,
* or no tags appear but the line is part of ongoing tagged content.
*
* @param line The line to process
*/
async processLine (line) {
const startPos = line.indexOf(this.startTag)
const endPos = line.lastIndexOf(this.endTag)

if (startPos >= 0 && endPos >= 0) {
// Both tags on same line
if (startPos > 0) {
await this.pass(line.slice(0, startPos))
}

await this.writeToWasteStream(line.slice(startPos + this.startTag.length, endPos))
if (endPos + this.endTag.length < line.length) {
await this.pass(line.slice(endPos + this.endTag.length))
}
} else if (startPos >= 0) {
// Start tag found
if (startPos > 0) {
await this.pass(line.slice(0, startPos))
}

await this.writeToWasteStream(line.slice(startPos + this.startTag.length))
this.inTaggedContent = true
} else if (endPos >= 0) {
// End tag found
await this.writeToWasteStream(line.slice(0, endPos))
if (endPos + this.endTag.length < line.length) {
await this.pass(line.slice(endPos + this.endTag.length))
}

this.inTaggedContent = false
} else if (this.inTaggedContent) {
// Currently in tagged content
await this.writeToWasteStream(line)
} else {
// Not in tagged content
await this.pass(line)
}
}
async writeToWasteStream (line, encoding) {
let _a

debug('writing to waste stream: "%s"', line)
await (0, writeWithBackpressure_1.writeWithBackpressure)(this.wasteStream, Buffer.from(line, (_a = (encoding === 'buffer' ? 'utf8' : encoding)) !== null && _a !== void 0 ? _a : 'utf8'))
}
async pass (line, encoding) {
let _a

debug('passing: "%s"', line)
this.push(Buffer.from(line, (_a = (encoding === 'buffer' ? 'utf8' : encoding)) !== null && _a !== void 0 ? _a : 'utf8'))
}
}
exports.FilterTaggedContent = FilterTaggedContent
Loading
Loading