From 34d664bd4b3a5da47e66a886d7baebd6567a0152 Mon Sep 17 00:00:00 2001 From: Mike McCready <66998419+MikeMcC399@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:48:14 +0100 Subject: [PATCH] misc: correct newline wrapping in logs for strings with ANSI codes Handles strings containing ANSI escape code sequences If printable string length is within allowed width, does not wrap Converts strings to plain string without ANSI escape code sequences when printable width exceeded, then wraps accordingly Resolves issue displaying longer Node.js paths such as encountered on GitHub Actions in Ubuntu and macOS runners --- cli/CHANGELOG.md | 4 +++ packages/server/lib/util/newlines.js | 12 ++++++++ .../server/test/unit/util/newlines_spec.ts | 30 +++++++++++++++---- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 9b2bbbe894..2635bf1296 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -7,6 +7,10 @@ _Released 02/08/2026 (PENDING)_ - Fixed an issue on Windows where extracting the Studio or Prompt bundle could fail with `EPERM: operation not permitted` when renaming extracted files. The extract step now retries on EPERM/EACCES with a short delay to handle transient file locks. Addressed in [#33330](https://github.com/cypress-io/cypress/pull/33330). +**Misc:** + +- The Node.js path is now displayed correctly in run log headers for typical GitHub Actions paths. ANSI escape sequences are no longer incorrectly displayed for longer Node.js paths. Addresses [#32736](https://github.com/cypress-io/cypress/issues/32736). + ## 15.10.0 _Released 02/03/2026_ diff --git a/packages/server/lib/util/newlines.js b/packages/server/lib/util/newlines.js index 960d31c73f..b2c1c18c62 100644 --- a/packages/server/lib/util/newlines.js +++ b/packages/server/lib/util/newlines.js @@ -1,3 +1,5 @@ +const util = require('util') + const addNewlineAtEveryNChar = (str, n) => { if (!str) { return str @@ -6,6 +8,16 @@ const addNewlineAtEveryNChar = (str, n) => { let result = [] let idx = 0 + let printableString = util.stripVTControlCharacters(str) + + if (printableString.length !== str.length) { + if (printableString.length <= n) { + return str + } else { + str = printableString + } + } + while (idx < str.length) { result.push(str.slice(idx, idx += n)) } diff --git a/packages/server/test/unit/util/newlines_spec.ts b/packages/server/test/unit/util/newlines_spec.ts index a5f06ca4aa..c1987e5ee0 100644 --- a/packages/server/test/unit/util/newlines_spec.ts +++ b/packages/server/test/unit/util/newlines_spec.ts @@ -3,13 +3,31 @@ import '../../spec_helper' import newlines from '../../../lib/util/newlines' describe('lib/util/newlines', function () { - it('inserts newline at each n char', function () { - expect(newlines.addNewlineAtEveryNChar('123456789', 3)).to.eq('123\n456\n789') + context('regular strings', function () { + it('inserts newline at each n char', function () { + expect(newlines.addNewlineAtEveryNChar('123456789', 3)).to.eq('123\n456\n789') + }) + + it('does not insert newline if str length <= n', function () { + expect(newlines.addNewlineAtEveryNChar('123', 3)).to.eq('123') + }) + + it('returns undefined if str not defined', function () { + expect(newlines.addNewlineAtEveryNChar(undefined, 3)).to.eq(undefined) + }) }) -}) -describe('lib/util/newlines', function () { - it('returns undefined if str not defined', function () { - expect(newlines.addNewlineAtEveryNChar(undefined, 3)).to.eq(undefined) + context('strings with ANSI codes', function () { + it('returns str unchanged if ANSI stripped length <= n', function () { + const shortAnsiString = '\u001B[31m123\u001B[39m' // "123" in red + + expect(newlines.addNewlineAtEveryNChar(shortAnsiString, 3)).to.eq(shortAnsiString) + }) + + it('returns str with ANSI stripped if printing length > n', function () { + const longAnsiString = '\u001B[31m123456789\u001B[39m' // "123456789" in red + + expect(newlines.addNewlineAtEveryNChar(longAnsiString, 3)).to.eq('123\n456\n789') + }) }) })