Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

fix: remove ansi escape sequences from escaped xunit output #4527

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
7 changes: 6 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@ exports.inherits = util.inherits;
* @return {string}
*/
exports.escape = function (html) {
return he.encode(String(html), {useNamedReferences: false});
return he
.encode(String(html), {useNamedReferences: false})
.replace(/&#x([0-9A-F]);/g, (match) => {
const val = Number.parseInt(match);
return val === 0x9 || val === 0xA || val === 0xD || (val >= 0xE000 && val <= 0xFFFD) || (val >= 0x10000 && val <= 0x10FFFF) ? `&#x${match};` : '';
});
Comment on lines +36 to +41
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion:

Turns out that he already has this in itself, just doesn't expose it other than through strict: true, which throws when it encounters it.

Maybe we could use that regexp and try to land a PR that either exposes it or adds a skipInvalid: true option or such?

Suggested change
return he
.encode(String(html), {useNamedReferences: false})
.replace(/&#x([0-9A-F]);/g, (match) => {
const val = Number.parseInt(match);
return val === 0x9 || val === 0xA || val === 0xD || (val >= 0xE000 && val <= 0xFFFD) || (val >= 0x10000 && val <= 0x10FFFF) ? `&#x${match};` : '';
});
return he.encode(String(html).replace(regexInvalidRawCodePoint, ''), {useNamedReferences: false});

Requires this to be added as well:

// From https://github.com/mathiasbynens/he/blob/36afe179392226cf1b6ccdb16ebbb7a5a844d93a/he.js#L53
var regexInvalidRawCodePoint = /[\0-\x08\x0B\x0E-\x1F\x7F-\x9F\uFDD0-\uFDEF\uFFFE\uFFFF]|[\uD83F\uD87F\uD8BF\uD8FF\uD93F\uD97F\uD9BF\uD9FF\uDA3F\uDA7F\uDABF\uDAFF\uDB3F\uDB7F\uDBBF\uDBFF][\uDFFE\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/g;

};

/**
Expand Down
13 changes: 13 additions & 0 deletions test/integration/fixtures/ansi.fixture.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict';

var assert = require('assert');

describe('suite', function () {
it('test1', function () {
assert(true);
});

it('test2', function () {
assert.ok(false, '\u001b[31mnot ok\u001b[39m');

Check failure on line 11 in test/integration/fixtures/ansi.fixture.js

View workflow job for this annotation

GitHub Actions / Node.js [v14 / ubuntu-latest]

expected '<testsuite name="Mocha Tests" tests="2" failures="0" errors="1" skipped="0" timestamp="Thu, 30 May 2024 20:41:03 GMT" time="0.003">\n<testcase classname="suite" name="test1" file="/home/runner/work/mocha/mocha/test/integration/fixtures/ansi.fixture.js" time="0"/>\n<testcase classname="suite" name="test2" file="/home/runner/work/mocha/mocha/test/integration/fixtures/ansi.fixture.js" time="0"><failure>&#x1B;[31mnot ok&#x1B;[39m\n\n + expected - actual\n\n -false\n +true\n \nAssertionError [ERR_ASSERTION]: &#x1B;[31mnot ok&#x1B;[39m\n at Context.&#x3C;anonymous&#x3E; (test/integration/fixtures/ansi.fixture.js:11:12)\n at callFn (lib/runnable.js:366:21)\n at Test.Runnable.run (lib/runnable.js:354:5)\n at Runner.runTest (lib/runner.js:677:10)\n at /home/runner/work/mocha/mocha/lib/runner.js:800:12\n at next (lib/runner.js:592:14)\n at /home/runner/work/mocha/mocha/lib/runner.js:602:7\n at next (lib/runner.js:485:14)\n at Immediate._onImmediate (lib/runner.js:570:5)\n at processImmediate (internal/timers.js:464:21)</failure></testcase>\n</testsuite>\n' not to contain '&#x1B;' <testsuite name="Mocha Tests" tests="2" failures="0" errors="1" skipped="0" timestamp="Thu, 30 May 2024 20:41:03 GMT" time="0.003"> <testcase classname="suite" name="test1" file="/home/runner/work/mocha/mocha/test/integration/fixtures/ansi.fixture.js" time="0"/> <testcase classname="suite" name="test2" file="/home/runner/work/mocha/mocha/test/integration/fixtures/ansi.fixture.js" time="0"><failure>&#x1B;[31mnot ok&#x1B;[39m ^^^^^^ ^^^^^^ + expected - actual -false +true AssertionError [ERR_ASSERTION]: &#x1B;[31mnot ok&#x1B;[39m ^^^^^^ ^^^^^^ at Context.&#x3C;anonymous&#x3E; (test/integration/fixtures/ansi.fixture.js:11:12) at callFn (lib/runnable.js:366:21) at Test.Runnable.run (lib/runnable.js:354:5) at Runner.runTest (lib/runner.js:677:10) at /home/runner/work/mocha/mocha/lib/runner.js:800:12 at next (lib/runner.js:592:14) at /home/runner/work/mocha/mocha/lib/runner.js:602:7 at next (lib/runner.js:485:14) at Immediate._onImmediate (lib/runner.js:570:5) at processImmediate (internal/timers.js:464:21)</failure></testcase> </testsuite>

Check failure on line 11 in test/integration/fixtures/ansi.fixture.js

View workflow job for this annotation

GitHub Actions / Node.js [v18 / ubuntu-latest]

expected '<testsuite name="Mocha Tests" tests="2" failures="0" errors="1" skipped="0" timestamp="Thu, 30 May 2024 20:40:54 GMT" time="0.003">\n<testcase classname="suite" name="test1" file="/home/runner/work/mocha/mocha/test/integration/fixtures/ansi.fixture.js" time="0"/>\n<testcase classname="suite" name="test2" file="/home/runner/work/mocha/mocha/test/integration/fixtures/ansi.fixture.js" time="0.001"><failure>&#x1B;[31mnot ok&#x1B;[39m\n\n + expected - actual\n\n -false\n +true\n \nAssertionError [ERR_ASSERTION]: &#x1B;[31mnot ok&#x1B;[39m\n at Context.&#x3C;anonymous&#x3E; (test/integration/fixtures/ansi.fixture.js:11:12)\n at callFn (lib/runnable.js:366:21)\n at Runnable.run (lib/runnable.js:354:5)\n at Runner.runTest (lib/runner.js:677:10)\n at /home/runner/work/mocha/mocha/lib/runner.js:800:12\n at next (lib/runner.js:592:14)\n at /home/runner/work/mocha/mocha/lib/runner.js:602:7\n at next (lib/runner.js:485:14)\n at Immediate._onImmediate (lib/runner.js:570:5)\n at process.processImmediate (node:internal/timers:476:21)</failure></testcase>\n</testsuite>\n' not to contain '&#x1B;' <testsuite name="Mocha Tests" tests="2" failures="0" errors="1" skipped="0" timestamp="Thu, 30 May 2024 20:40:54 GMT" time="0.003"> <testcase classname="suite" name="test1" file="/home/runner/work/mocha/mocha/test/integration/fixtures/ansi.fixture.js" time="0"/> <testcase classname="suite" name="test2" file="/home/runner/work/mocha/mocha/test/integration/fixtures/ansi.fixture.js" time="0.001"><failure>&#x1B;[31mnot ok&#x1B;[39m ^^^^^^ ^^^^^^ + expected - actual -false +true AssertionError [ERR_ASSERTION]: &#x1B;[31mnot ok&#x1B;[39m ^^^^^^ ^^^^^^ at Context.&#x3C;anonymous&#x3E; (test/integration/fixtures/ansi.fixture.js:11:12) at callFn (lib/runnable.js:366:21) at Runnable.run (lib/runnable.js:354:5) at Runner.runTest (lib/runner.js:677:10) at /home/runner/work/mocha/mocha/lib/runner.js:800:12 at next (lib/runner.js:592:14) at /home/runner/work/mocha/mocha/lib/runner.js:602:7 at next (lib/runner.js:485:14) at Immediate._onImmediate (lib/runner.js:570:5) at process.processImmediate (node:internal/timers:476:21)</failure></testcase> </testsuite>

Check failure on line 11 in test/integration/fixtures/ansi.fixture.js

View workflow job for this annotation

GitHub Actions / Node.js [v20 / ubuntu-latest]

expected '<testsuite name="Mocha Tests" tests="2" failures="0" errors="1" skipped="0" timestamp="Thu, 30 May 2024 20:40:50 GMT" time="0.003">\n<testcase classname="suite" name="test1" file="/home/runner/work/mocha/mocha/test/integration/fixtures/ansi.fixture.js" time="0"/>\n<testcase classname="suite" name="test2" file="/home/runner/work/mocha/mocha/test/integration/fixtures/ansi.fixture.js" time="0.001"><failure>&#x1B;[31mnot ok&#x1B;[39m\n\n + expected - actual\n\n -false\n +true\n \nAssertionError [ERR_ASSERTION]: &#x1B;[31mnot ok&#x1B;[39m\n at Context.&#x3C;anonymous&#x3E; (test/integration/fixtures/ansi.fixture.js:11:12)\n at callFn (lib/runnable.js:366:21)\n at Runnable.run (lib/runnable.js:354:5)\n at Runner.runTest (lib/runner.js:677:10)\n at /home/runner/work/mocha/mocha/lib/runner.js:800:12\n at next (lib/runner.js:592:14)\n at /home/runner/work/mocha/mocha/lib/runner.js:602:7\n at next (lib/runner.js:485:14)\n at Immediate._onImmediate (lib/runner.js:570:5)\n at process.processImmediate (node:internal/timers:478:21)</failure></testcase>\n</testsuite>\n' not to contain '&#x1B;' <testsuite name="Mocha Tests" tests="2" failures="0" errors="1" skipped="0" timestamp="Thu, 30 May 2024 20:40:50 GMT" time="0.003"> <testcase classname="suite" name="test1" file="/home/runner/work/mocha/mocha/test/integration/fixtures/ansi.fixture.js" time="0"/> <testcase classname="suite" name="test2" file="/home/runner/work/mocha/mocha/test/integration/fixtures/ansi.fixture.js" time="0.001"><failure>&#x1B;[31mnot ok&#x1B;[39m ^^^^^^ ^^^^^^ + expected - actual -false +true AssertionError [ERR_ASSERTION]: &#x1B;[31mnot ok&#x1B;[39m ^^^^^^ ^^^^^^ at Context.&#x3C;anonymous&#x3E; (test/integration/fixtures/ansi.fixture.js:11:12) at callFn (lib/runnable.js:366:21) at Runnable.run (lib/runnable.js:354:5) at Runner.runTest (lib/runner.js:677:10) at /home/runner/work/mocha/mocha/lib/runner.js:800:12 at next (lib/runner.js:592:14) at /home/runner/work/mocha/mocha/lib/runner.js:602:7 at next (lib/runner.js:485:14) at Immediate._onImmediate (lib/runner.js:570:5) at process.processImmediate (node:internal/timers:478:21)</failure></testcase> </testsuite>
});
});
26 changes: 26 additions & 0 deletions test/integration/reporters.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,32 @@ describe('reporters', function () {
done(err);
});
});

it('does not include ansi escape sequences (issue: 4526)', function(done) {
var randomStr = crypto.randomBytes(8).toString('hex');
var tmpDir = os.tmpdir().replace(new RegExp(path.sep + '$'), '');
var tmpFile = tmpDir + path.sep + 'test-issue-4526-' + randomStr + '.xml';

var args = [
'--reporter=xunit',
'--reporter-options',
'output=' + tmpFile
];
var unexpectedOutput = ['&#x1B;', '&#x1b;'];

run('ansi.fixture.js', args, function(err, result) {
if (err) return done(err);

var xml = fs.readFileSync(tmpFile, 'utf8');
fs.unlinkSync(tmpFile);

unexpectedOutput.forEach(function(line) {
expect(xml, 'not to contain', line);
});

done(err);
});
});
});

describe('loader', function () {
Expand Down
Loading