node/test/parallel/test-util-styletext.js
sangwook a62f6411ae
util: allow color aliases in styleText
Fixes an issue where `util.styleText()` would throw an error for
valid color aliases like 'grey' in Node.js >= 25.7.0. It now uses
`ObjectGetOwnPropertyNames` instead of `ObjectKeys` to fetch both
keys and aliases.

Fixes: https://github.com/nodejs/node/issues/62177
PR-URL: https://github.com/nodejs/node/pull/62180
Reviewed-By: René <contact.9a5d6388@renegade334.me.uk>
Reviewed-By: Zeyu "Alex" Yang <himself65@outlook.com>
2026-03-17 07:57:07 +00:00

223 lines
6.1 KiB
JavaScript

'use strict';
const common = require('../common');
const assert = require('node:assert');
const util = require('node:util');
const { WriteStream } = require('node:tty');
const styled = '\u001b[31mtest\u001b[39m';
const noChange = 'test';
[
undefined,
null,
false,
5n,
5,
Symbol(),
() => {},
{},
].forEach((invalidOption) => {
assert.throws(() => {
util.styleText(invalidOption, 'test');
}, {
code: 'ERR_INVALID_ARG_VALUE',
}, invalidOption);
assert.throws(() => {
util.styleText('red', invalidOption);
}, {
code: 'ERR_INVALID_ARG_TYPE'
}, invalidOption);
});
assert.throws(() => {
util.styleText('invalid', 'text');
}, {
code: 'ERR_INVALID_ARG_VALUE',
});
assert.strictEqual(
util.styleText('red', 'test', { validateStream: false }),
'\u001b[31mtest\u001b[39m',
);
assert.strictEqual(
util.styleText('gray', 'test', { validateStream: false }),
'\u001b[90mtest\u001b[39m',
);
assert.strictEqual(
util.styleText('grey', 'test', { validateStream: false }),
'\u001b[90mtest\u001b[39m',
);
assert.strictEqual(
util.styleText(['bold', 'red'], 'test', { validateStream: false }),
'\u001b[1m\u001b[31mtest\u001b[39m\u001b[22m',
);
assert.strictEqual(
util.styleText('red',
'A' + util.styleText('blue', 'B', { validateStream: false }) + 'C',
{ validateStream: false }),
'\u001b[31mA\u001b[34mB\u001b[31mC\u001b[39m'
);
assert.strictEqual(
util.styleText('red',
'red' +
util.styleText('blue', 'blue', { validateStream: false }) +
'red' +
util.styleText('blue', 'blue', { validateStream: false }) +
'red',
{ validateStream: false }
),
'\x1B[31mred\x1B[34mblue\x1B[31mred\x1B[34mblue\x1B[31mred\x1B[39m'
);
assert.strictEqual(
util.styleText('red',
'red' +
util.styleText('blue', 'blue', { validateStream: false }) +
'red' +
util.styleText('red', 'red', { validateStream: false }) +
'red' +
util.styleText('blue', 'blue', { validateStream: false }),
{ validateStream: false }
),
'\x1b[31mred\x1b[34mblue\x1b[31mred\x1b[31mred\x1b[31mred\x1b[34mblue\x1b[39m\x1b[39m'
);
assert.strictEqual(
util.styleText('red',
'A' + util.styleText(['bgRed', 'blue'], 'B', { validateStream: false }) +
'C', { validateStream: false }),
'\x1B[31mA\x1B[41m\x1B[34mB\x1B[31m\x1B[49mC\x1B[39m'
);
assert.strictEqual(
util.styleText('dim',
'dim' +
util.styleText('bold', 'bold', { validateStream: false }) +
'dim', { validateStream: false }),
'\x1B[2mdim\x1B[1mbold\x1B[22m\x1B[2mdim\x1B[22m'
);
assert.strictEqual(
util.styleText('blue',
'blue' +
util.styleText('red',
'red' +
util.styleText('green', 'green', { validateStream: false }) +
'red', { validateStream: false }) +
'blue', { validateStream: false }),
'\x1B[34mblue\x1B[31mred\x1B[32mgreen\x1B[31mred\x1B[34mblue\x1B[39m'
);
assert.strictEqual(
util.styleText(
'red',
'red' +
util.styleText(
'blue',
'blue' + util.styleText('red', 'red', {
validateStream: false,
}) + 'blue',
{
validateStream: false,
}
) + 'red', {
validateStream: false,
}
),
'\x1b[31mred\x1b[34mblue\x1b[31mred\x1b[34mblue\x1b[31mred\x1b[39m'
);
assert.strictEqual(
util.styleText(['bold', 'red'], 'test', { validateStream: false }),
util.styleText(
'bold',
util.styleText('red', 'test', { validateStream: false }),
{ validateStream: false },
),
);
assert.throws(() => {
util.styleText(['invalid'], 'text');
}, {
code: 'ERR_INVALID_ARG_VALUE',
});
assert.throws(() => {
util.styleText('red', 'text', { stream: {} });
}, {
code: 'ERR_INVALID_ARG_TYPE',
});
// Color aliases should be accepted (e.g. 'grey' is an alias for 'gray')
// See https://github.com/nodejs/node/issues/62177
assert.strictEqual(
util.styleText('grey', 'test', { validateStream: false }),
util.styleText('gray', 'test', { validateStream: false }),
);
assert.strictEqual(
util.styleText('bgGrey', 'test', { validateStream: false }),
util.styleText('bgGray', 'test', { validateStream: false }),
);
assert.strictEqual(
util.styleText('blackBright', 'test', { validateStream: false }),
util.styleText('gray', 'test', { validateStream: false }),
);
assert.strictEqual(
util.styleText('faint', 'test', { validateStream: false }),
util.styleText('dim', 'test', { validateStream: false }),
);
assert.strictEqual(
util.styleText(['grey', 'bold'], 'test', { validateStream: false }),
util.styleText(['gray', 'bold'], 'test', { validateStream: false }),
);
// does not throw
util.styleText('red', 'text', { stream: {}, validateStream: false });
assert.strictEqual(
util.styleText('red', 'test', { validateStream: false }),
styled,
);
assert.strictEqual(util.styleText('none', 'test'), 'test');
const fd = common.getTTYfd();
if (fd !== -1) {
const writeStream = new WriteStream(fd);
const originalEnv = process.env;
[
{ isTTY: true, env: {}, expected: styled },
{ isTTY: false, env: {}, expected: noChange },
{ isTTY: true, env: { NODE_DISABLE_COLORS: '1' }, expected: noChange },
{ isTTY: true, env: { NO_COLOR: '1' }, expected: noChange },
{ isTTY: true, env: { FORCE_COLOR: '1' }, expected: styled },
{ isTTY: true, env: { FORCE_COLOR: '1', NODE_DISABLE_COLORS: '1' }, expected: styled },
{ isTTY: false, env: { FORCE_COLOR: '1', NO_COLOR: '1', NODE_DISABLE_COLORS: '1' }, expected: styled },
{ isTTY: true, env: { FORCE_COLOR: '1', NO_COLOR: '1', NODE_DISABLE_COLORS: '1' }, expected: styled },
].forEach((testCase) => {
writeStream.isTTY = testCase.isTTY;
process.env = {
...process.env,
...testCase.env
};
{
const output = util.styleText('red', 'test', { stream: writeStream });
assert.strictEqual(output, testCase.expected);
}
{
// Check that when passing an array of styles, the output behaves the same
const output = util.styleText(['red'], 'test', { stream: writeStream });
assert.strictEqual(output, testCase.expected);
}
process.env = originalEnv;
});
} else {
common.skip('Could not create TTY fd');
}