diff --git a/package.json b/package.json index 2501c72..07ed8ed 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "node": ">=4" }, "scripts": { - "test": "xo && nyc mocha", + "test": "xo && nyc ava", "bench": "matcha benchmark.js", "coveralls": "nyc report --reporter=text-lcov | coveralls" }, @@ -45,11 +45,11 @@ "supports-color": "^4.0.0" }, "devDependencies": { + "ava": "*", "coveralls": "^2.11.2", "execa": "^0.7.0", "import-fresh": "^2.0.0", "matcha": "^0.7.0", - "mocha": "*", "nyc": "^11.0.2", "resolve-from": "^3.0.0", "xo": "*" diff --git a/test.js b/test.js deleted file mode 100644 index fe7a14e..0000000 --- a/test.js +++ /dev/null @@ -1,353 +0,0 @@ -'use strict'; -const assert = require('assert'); -const importFresh = require('import-fresh'); -const resolveFrom = require('resolve-from'); -const execa = require('execa'); - -// Spoof supports-color -require.cache[resolveFrom(__dirname, 'supports-color')] = { - exports: { - level: 3, - hasBasic: true, - has256: true, - has16m: true - } -}; - -const chalk = require('.'); - -console.log('TERM:', process.env.TERM || '[none]'); -console.log('platform:', process.platform || '[unknown]'); - -describe('chalk', () => { - it('should not add any styling when called as the base function', () => { - assert.equal(chalk('foo'), 'foo'); - }); - - it('should style string', () => { - assert.equal(chalk.underline('foo'), '\u001B[4mfoo\u001B[24m'); - assert.equal(chalk.red('foo'), '\u001B[31mfoo\u001B[39m'); - assert.equal(chalk.bgRed('foo'), '\u001B[41mfoo\u001B[49m'); - }); - - it('should support applying multiple styles at once', () => { - assert.equal(chalk.red.bgGreen.underline('foo'), '\u001B[31m\u001B[42m\u001B[4mfoo\u001B[24m\u001B[49m\u001B[39m'); - assert.equal(chalk.underline.red.bgGreen('foo'), '\u001B[4m\u001B[31m\u001B[42mfoo\u001B[49m\u001B[39m\u001B[24m'); - }); - - it('should support nesting styles', () => { - assert.equal( - chalk.red('foo' + chalk.underline.bgBlue('bar') + '!'), - '\u001B[31mfoo\u001B[4m\u001B[44mbar\u001B[49m\u001B[24m!\u001B[39m' - ); - }); - - it('should support nesting styles of the same type (color, underline, bg)', () => { - assert.equal( - chalk.red('a' + chalk.yellow('b' + chalk.green('c') + 'b') + 'c'), - '\u001B[31ma\u001B[33mb\u001B[32mc\u001B[33mb\u001B[31mc\u001B[39m' - ); - }); - - it('should reset all styles with `.reset()`', () => { - assert.equal(chalk.reset(chalk.red.bgGreen.underline('foo') + 'foo'), '\u001B[0m\u001B[31m\u001B[42m\u001B[4mfoo\u001B[24m\u001B[49m\u001B[39mfoo\u001B[0m'); - }); - - it('should be able to cache multiple styles', () => { - const red = chalk.red; - const green = chalk.green; - const redBold = red.bold; - const greenBold = green.bold; - - assert.notEqual(red('foo'), green('foo')); - assert.notEqual(redBold('bar'), greenBold('bar')); - assert.notEqual(green('baz'), greenBold('baz')); - }); - - it('should alias gray to grey', () => { - assert.equal(chalk.grey('foo'), '\u001B[90mfoo\u001B[39m'); - }); - - it('should support variable number of arguments', () => { - assert.equal(chalk.red('foo', 'bar'), '\u001B[31mfoo bar\u001B[39m'); - }); - - it('should support falsy values', () => { - assert.equal(chalk.red(0), '\u001B[31m0\u001B[39m'); - }); - - it('shouldn\'t output escape codes if the input is empty', () => { - assert.equal(chalk.red(), ''); - assert.equal(chalk.red.blue.black(), ''); - }); - - it('should keep Function.prototype methods', () => { - assert.equal(chalk.grey.apply(null, ['foo']), '\u001B[90mfoo\u001B[39m'); - assert.equal(chalk.reset(chalk.red.bgGreen.underline.bind(null)('foo') + 'foo'), '\u001B[0m\u001B[31m\u001B[42m\u001B[4mfoo\u001B[24m\u001B[49m\u001B[39mfoo\u001B[0m'); - assert.equal(chalk.red.blue.black.call(null), ''); - }); - - it('line breaks should open and close colors', () => { - assert.equal(chalk.grey('hello\nworld'), '\u001B[90mhello\u001B[39m\n\u001B[90mworld\u001B[39m'); - }); - - it('should properly convert RGB to 16 colors on basic color terminals', () => { - assert.equal(new chalk.constructor({level: 1}).hex('#FF0000')('hello'), '\u001B[91mhello\u001B[39m'); - assert.equal(new chalk.constructor({level: 1}).bgHex('#FF0000')('hello'), '\u001B[101mhello\u001B[49m'); - }); - - it('should properly convert RGB to 256 colors on basic color terminals', () => { - assert.equal(new chalk.constructor({level: 2}).hex('#FF0000')('hello'), '\u001B[38;5;196mhello\u001B[39m'); - assert.equal(new chalk.constructor({level: 2}).bgHex('#FF0000')('hello'), '\u001B[48;5;196mhello\u001B[49m'); - }); - - it('should properly convert RGB to 256 colors on basic color terminals', () => { - assert.equal(new chalk.constructor({level: 3}).hex('#FF0000')('hello'), '\u001B[38;2;255;0;0mhello\u001B[39m'); - assert.equal(new chalk.constructor({level: 3}).bgHex('#FF0000')('hello'), '\u001B[48;2;255;0;0mhello\u001B[49m'); - }); - - it('should not emit RGB codes if level is 0', () => { - assert.equal(new chalk.constructor({level: 0}).hex('#FF0000')('hello'), 'hello'); - assert.equal(new chalk.constructor({level: 0}).bgHex('#FF0000')('hello'), 'hello'); - }); -}); - -describe('chalk on Windows', () => { - let originalEnv; - let originalPlatform; - - before(() => { - originalEnv = process.env; - originalPlatform = process.platform; - }); - - after(() => { - process.env = originalEnv; - Object.defineProperty(process, 'platform', {value: originalPlatform}); - }); - - beforeEach(() => { - process.env = {}; - Object.defineProperty(process, 'platform', {value: 'win32'}); - // Since chalk internally modifies `ansiStyles.blue.open`, `ansi-styles` needs - // to be removed from the require cache for `require-uncached` to work - delete require.cache[resolveFrom(__dirname, 'ansi-styles')]; - }); - - it('should detect a simple term if TERM isn\'t set', () => { - delete process.env.TERM; - const chalkCtx = importFresh('.'); - assert.equal(chalkCtx.blue('foo'), '\u001B[94mfoo\u001B[39m'); - }); - - it('should replace blue foreground color in cmd.exe', () => { - process.env.TERM = 'dumb'; - const chalkCtx = importFresh('.'); - assert.equal(chalkCtx.blue('foo'), '\u001B[94mfoo\u001B[39m'); - }); - - it('shouldn\'t replace blue foreground color in xterm based terminals', () => { - process.env.TERM = 'xterm-256color'; - const chalkCtx = importFresh('.'); - assert.equal(chalkCtx.blue('foo'), '\u001B[34mfoo\u001B[39m'); - }); - - it('should not apply dimmed styling on gray strings, see https://github.com/chalk/chalk/issues/58', () => { - process.env.TERM = 'dumb'; - const chalkCtx = importFresh('.'); - assert.equal(chalkCtx.gray.dim('foo'), '\u001B[90mfoo\u001B[22m\u001B[39m'); - }); - - it('should apply dimmed styling on xterm compatible terminals', () => { - process.env.TERM = 'xterm'; - const chalkCtx = importFresh('.'); - assert.equal(chalkCtx.gray.dim('foo'), '\u001B[90m\u001B[2mfoo\u001B[22m\u001B[39m'); - }); - - it('should apply dimmed styling on strings of other colors', () => { - process.env.TERM = 'dumb'; - const chalkCtx = importFresh('.'); - assert.equal(chalkCtx.blue.dim('foo'), '\u001B[94m\u001B[2mfoo\u001B[22m\u001B[39m'); - }); -}); - -describe('chalk.level', () => { - it('should not output colors when manually disabled', () => { - const oldLevel = chalk.level; - chalk.level = 0; - assert.equal(chalk.red('foo'), 'foo'); - chalk.level = oldLevel; - }); - - it('should enable/disable colors based on overall chalk enabled property, not individual instances', () => { - const oldLevel = chalk.level; - chalk.level = 1; - const red = chalk.red; - assert.equal(red.level, 1); - chalk.level = 0; - assert.equal(red.level, chalk.level); - chalk.level = oldLevel; - }); - - it('should propagate enable/disable changes from child colors', () => { - const oldLevel = chalk.level; - chalk.level = 1; - const red = chalk.red; - assert.equal(red.level, 1); - assert.equal(chalk.level, 1); - red.level = 0; - assert.equal(red.level, 0); - assert.equal(chalk.level, 0); - chalk.level = 1; - assert.equal(red.level, 1); - assert.equal(chalk.level, 1); - chalk.level = oldLevel; - }); - - it('should disable colors if they are not supported', () => { - return execa('node', ['fixture']) - .then(result => { - assert.equal(result.stdout, 'test'); - }); - }); -}); - -describe('chalk.enabled', () => { - it('should not output colors when manually disabled', () => { - chalk.enabled = false; - assert.equal(chalk.red('foo'), 'foo'); - chalk.enabled = true; - }); - - it('should enable/disable colors based on overall chalk enabled property, not individual instances', () => { - chalk.enabled = false; - const red = chalk.red; - assert.equal(red.enabled, false); - chalk.enabled = true; - assert.equal(red.enabled, true); - chalk.enabled = true; - }); - - it('should propagate enable/disable changes from child colors', () => { - chalk.enabled = false; - const red = chalk.red; - assert.equal(red.enabled, false); - assert.equal(chalk.enabled, false); - red.enabled = true; - assert.equal(red.enabled, true); - assert.equal(chalk.enabled, true); - chalk.enabled = false; - assert.equal(red.enabled, false); - assert.equal(chalk.enabled, false); - chalk.enabled = true; - }); -}); - -describe('chalk.constructor()', () => { - it('should create an isolated context where colors can be disabled (by level)', () => { - const ctx = new chalk.constructor({level: 0, enabled: true}); - assert.equal(ctx.red('foo'), 'foo'); - assert.equal(chalk.red('foo'), '\u001B[31mfoo\u001B[39m'); - ctx.level = 2; - assert.equal(ctx.red('foo'), '\u001B[31mfoo\u001B[39m'); - }); - - it('should create an isolated context where colors can be disabled (by enabled flag)', () => { - const ctx = new chalk.constructor({enabled: false}); - assert.equal(ctx.red('foo'), 'foo'); - assert.equal(chalk.red('foo'), '\u001B[31mfoo\u001B[39m'); - ctx.enabled = true; - assert.equal(ctx.red('foo'), '\u001B[31mfoo\u001B[39m'); - }); -}); - -describe('tagged template literal', () => { - it('should return an empty string for an empty literal', () => { - const ctx = chalk.constructor(); - assert.equal(ctx``, ''); - }); - - it('should return a regular string for a literal with no templates', () => { - const ctx = chalk.constructor({level: 0}); - assert.equal(ctx`hello`, 'hello'); - }); - - it('should correctly perform template parsing', () => { - const ctx = chalk.constructor({level: 0}); - assert.equal(ctx`{bold Hello, {cyan World!} This is a} test. {green Woo!}`, - ctx.bold('Hello,', ctx.cyan('World!'), 'This is a') + ' test. ' + ctx.green('Woo!')); - }); - - it('should correctly perform template substitutions', () => { - const ctx = chalk.constructor({level: 0}); - const name = 'Sindre'; - const exclamation = 'Neat'; - assert.equal(ctx`{bold Hello, {cyan.inverse ${name}!} This is a} test. {green ${exclamation}!}`, - ctx.bold('Hello,', ctx.cyan.inverse(name + '!'), 'This is a') + ' test. ' + ctx.green(exclamation + '!')); - }); - - it('should correctly parse and evaluate color-convert functions', () => { - const ctx = chalk.constructor({level: 3}); - assert.equal(ctx`{bold.rgb(144,10,178).inverse Hello, {~inverse there!}}`, - '\u001B[0m\u001B[1m\u001B[38;2;144;10;178m\u001B[7mHello, ' + - '\u001B[27m\u001B[39m\u001B[22m\u001B[0m\u001B[0m\u001B[1m' + - '\u001B[38;2;144;10;178mthere!\u001B[39m\u001B[22m\u001B[0m'); - - assert.equal(ctx`{bold.bgRgb(144,10,178).inverse Hello, {~inverse there!}}`, - '\u001B[0m\u001B[1m\u001B[48;2;144;10;178m\u001B[7mHello, ' + - '\u001B[27m\u001B[49m\u001B[22m\u001B[0m\u001B[0m\u001B[1m' + - '\u001B[48;2;144;10;178mthere!\u001B[49m\u001B[22m\u001B[0m'); - }); - - it('should properly handle escapes', () => { - const ctx = chalk.constructor({level: 3}); - assert.equal(ctx`{bold hello \{in brackets\}}`, - '\u001B[0m\u001B[1mhello {in brackets}\u001B[22m\u001B[0m'); - }); - - it('should throw if there is an unclosed block', () => { - const ctx = chalk.constructor({level: 3}); - try { - console.log(ctx`{bold this shouldn't appear ever\}`); - assert.fail(); - } catch (err) { - assert.equal(err.message, 'Template literal has an unclosed block'); - } - }); - - it('should throw if there is an invalid style', () => { - const ctx = chalk.constructor({level: 3}); - try { - console.log(ctx`{abadstylethatdoesntexist this shouldn't appear ever}`); - assert.fail(); - } catch (err) { - assert.equal(err.message, 'Invalid Chalk style: abadstylethatdoesntexist'); - } - }); - - it('should properly style multiline color blocks', () => { - const ctx = chalk.constructor({level: 3}); - assert.equal( - ctx`{bold - Hello! This is a - ${'multiline'} block! - :) - } {underline - I hope you enjoy - }`, - '\u001B[0m\u001B[1m\u001B[22m\u001B[0m\n' + - '\u001B[0m\u001B[1m\t\t\t\tHello! This is a\u001B[22m\u001B[0m\n' + - '\u001B[0m\u001B[1m\t\t\t\tmultiline block!\u001B[22m\u001B[0m\n' + - '\u001B[0m\u001B[1m\t\t\t\t:)\u001B[22m\u001B[0m\n' + - '\u001B[0m\u001B[1m\t\t\t\u001B[22m\u001B[0m\u001B[0m \u001B[0m\u001B[0m\u001B[4m\u001B[24m\u001B[0m\n' + - '\u001B[0m\u001B[4m\t\t\t\tI hope you enjoy\u001B[24m\u001B[0m\n' + - '\u001B[0m\u001B[4m\t\t\t\u001B[24m\u001B[0m' - ); - }); - - it('should escape interpolated values', () => { - const ctx = chalk.constructor({level: 0}); - assert.equal(ctx`Hello {bold hi}`, 'Hello hi'); - assert.equal(ctx`Hello ${'{bold hi}'}`, 'Hello {bold hi}'); - }); -}); diff --git a/fixture.js b/test/_fixture.js similarity index 66% rename from fixture.js rename to test/_fixture.js index a57f562..5f1771d 100644 --- a/fixture.js +++ b/test/_fixture.js @@ -1,4 +1,4 @@ 'use strict'; -const chalk = require('.'); +const chalk = require('..'); console.log(chalk.hex('#ff6159')('test')); diff --git a/test/_supports-color.js b/test/_supports-color.js new file mode 100644 index 0000000..d6e82bb --- /dev/null +++ b/test/_supports-color.js @@ -0,0 +1,13 @@ +'use strict'; +const resolveFrom = require('resolve-from'); + +module.exports = dir => { + require.cache[resolveFrom(dir, 'supports-color')] = { + exports: { + level: 3, + hasBasic: true, + has256: true, + has16m: true + } + }; +}; diff --git a/test/chalk.js b/test/chalk.js new file mode 100644 index 0000000..46060d4 --- /dev/null +++ b/test/chalk.js @@ -0,0 +1,96 @@ +import test from 'ava'; + +// Spoof supports-color +require('./_supports-color')(__dirname); + +const m = require('..'); + +console.log('TERM:', process.env.TERM || '[none]'); +console.log('platform:', process.platform || '[unknown]'); + +test('don\'t add any styling when called as the base function', t => { + t.is(m('foo'), 'foo'); +}); + +test('style string', t => { + t.is(m.underline('foo'), '\u001B[4mfoo\u001B[24m'); + t.is(m.red('foo'), '\u001B[31mfoo\u001B[39m'); + t.is(m.bgRed('foo'), '\u001B[41mfoo\u001B[49m'); +}); + +test('support applying multiple styles at once', t => { + t.is(m.red.bgGreen.underline('foo'), '\u001B[31m\u001B[42m\u001B[4mfoo\u001B[24m\u001B[49m\u001B[39m'); + t.is(m.underline.red.bgGreen('foo'), '\u001B[4m\u001B[31m\u001B[42mfoo\u001B[49m\u001B[39m\u001B[24m'); +}); + +test('support nesting styles', t => { + t.is( + m.red('foo' + m.underline.bgBlue('bar') + '!'), + '\u001B[31mfoo\u001B[4m\u001B[44mbar\u001B[49m\u001B[24m!\u001B[39m' + ); +}); + +test('support nesting styles of the same type (color, underline, bg)', t => { + t.is( + m.red('a' + m.yellow('b' + m.green('c') + 'b') + 'c'), + '\u001B[31ma\u001B[33mb\u001B[32mc\u001B[33mb\u001B[31mc\u001B[39m' + ); +}); + +test('reset all styles with `.reset()`', t => { + t.is(m.reset(m.red.bgGreen.underline('foo') + 'foo'), '\u001B[0m\u001B[31m\u001B[42m\u001B[4mfoo\u001B[24m\u001B[49m\u001B[39mfoo\u001B[0m'); +}); + +test('support caching multiple styles', t => { + const red = m.red; + const green = m.green; + const redBold = red.bold; + const greenBold = green.bold; + + t.not(red('foo'), green('foo')); + t.not(redBold('bar'), greenBold('bar')); + t.not(green('baz'), greenBold('baz')); +}); + +test('alias gray to grey', t => { + t.is(m.grey('foo'), '\u001B[90mfoo\u001B[39m'); +}); + +test('support variable number of arguments', t => { + t.is(m.red('foo', 'bar'), '\u001B[31mfoo bar\u001B[39m'); +}); + +test('support falsy values', t => { + t.is(m.red(0), '\u001B[31m0\u001B[39m'); +}); + +test('don\'t output escape codes if the input is empty', t => { + t.is(m.red(), ''); + t.is(m.red.blue.black(), ''); +}); + +test('keep Function.prototype methods', t => { + t.is(m.grey.apply(null, ['foo']), '\u001B[90mfoo\u001B[39m'); + t.is(m.reset(m.red.bgGreen.underline.bind(null)('foo') + 'foo'), '\u001B[0m\u001B[31m\u001B[42m\u001B[4mfoo\u001B[24m\u001B[49m\u001B[39mfoo\u001B[0m'); + t.is(m.red.blue.black.call(null), ''); +}); + +test('line breaks should open and close colors', t => { + t.is(m.grey('hello\nworld'), '\u001B[90mhello\u001B[39m\n\u001B[90mworld\u001B[39m'); +}); + +test('properly convert RGB to 16 colors on basic color terminals', t => { + t.is(new m.constructor({level: 1}).hex('#FF0000')('hello'), '\u001B[91mhello\u001B[39m'); + t.is(new m.constructor({level: 1}).bgHex('#FF0000')('hello'), '\u001B[101mhello\u001B[49m'); +}); + +test('properly convert RGB to 256 colors on basic color terminals', t => { + t.is(new m.constructor({level: 2}).hex('#FF0000')('hello'), '\u001B[38;5;196mhello\u001B[39m'); + t.is(new m.constructor({level: 2}).bgHex('#FF0000')('hello'), '\u001B[48;5;196mhello\u001B[49m'); + t.is(new m.constructor({level: 3}).bgHex('#FF0000')('hello'), '\u001B[48;2;255;0;0mhello\u001B[49m'); +}); + +test('don\'t emit RGB codes if level is 0', t => { + t.is(new m.constructor({level: 0}).hex('#FF0000')('hello'), 'hello'); + t.is(new m.constructor({level: 0}).bgHex('#FF0000')('hello'), 'hello'); +}); diff --git a/test/constructor.js b/test/constructor.js new file mode 100644 index 0000000..d592e82 --- /dev/null +++ b/test/constructor.js @@ -0,0 +1,22 @@ +import test from 'ava'; + +// Spoof supports-color +require('./_supports-color')(__dirname); + +const m = require('..'); + +test('create an isolated context where colors can be disabled (by level)', t => { + const ctx = new m.constructor({level: 0, enabled: true}); + t.is(ctx.red('foo'), 'foo'); + t.is(m.red('foo'), '\u001B[31mfoo\u001B[39m'); + ctx.level = 2; + t.is(ctx.red('foo'), '\u001B[31mfoo\u001B[39m'); +}); + +test('create an isolated context where colors can be disabled (by enabled flag)', t => { + const ctx = new m.constructor({enabled: false}); + t.is(ctx.red('foo'), 'foo'); + t.is(m.red('foo'), '\u001B[31mfoo\u001B[39m'); + ctx.enabled = true; + t.is(ctx.red('foo'), '\u001B[31mfoo\u001B[39m'); +}); diff --git a/test/enabled.js b/test/enabled.js new file mode 100644 index 0000000..3270e54 --- /dev/null +++ b/test/enabled.js @@ -0,0 +1,35 @@ +import test from 'ava'; + +// Spoof supports-color +require('./_supports-color')(__dirname); + +const m = require('..'); + +test('don\'t output colors when manually disabled', t => { + m.enabled = false; + t.is(m.red('foo'), 'foo'); + m.enabled = true; +}); + +test('enable/disable colors based on overall chalk enabled property, not individual instances', t => { + m.enabled = false; + const red = m.red; + t.false(red.enabled); + m.enabled = true; + t.true(red.enabled); + m.enabled = true; +}); + +test('propagate enable/disable changes from child colors', t => { + m.enabled = false; + const red = m.red; + t.false(red.enabled); + t.false(m.enabled); + red.enabled = true; + t.true(red.enabled); + t.true(m.enabled); + m.enabled = false; + t.false(red.enabled); + t.false(m.enabled); + m.enabled = true; +}); diff --git a/test/level.js b/test/level.js new file mode 100644 index 0000000..9caf6c4 --- /dev/null +++ b/test/level.js @@ -0,0 +1,44 @@ +import path from 'path'; +import test from 'ava'; +import execa from 'execa'; + +// Spoof supports-color +require('./_supports-color')(__dirname); + +const m = require('..'); + +test('don\'t output colors when manually disabled', t => { + const oldLevel = m.level; + m.level = 0; + t.is(m.red('foo'), 'foo'); + m.level = oldLevel; +}); + +test('enable/disable colors based on overall chalk enabled property, not individual instances', t => { + const oldLevel = m.level; + m.level = 1; + const red = m.red; + t.is(red.level, 1); + m.level = 0; + t.is(red.level, m.level); + m.level = oldLevel; +}); + +test('propagate enable/disable changes from child colors', t => { + const oldLevel = m.level; + m.level = 1; + const red = m.red; + t.is(red.level, 1); + t.is(m.level, 1); + red.level = 0; + t.is(red.level, 0); + t.is(m.level, 0); + m.level = 1; + t.is(red.level, 1); + t.is(m.level, 1); + m.level = oldLevel; +}); + +test.failing('disable colors if they are not supported', async t => { + t.is(await execa.stdout('node', [path.join(__dirname, '_fixture')]), 'test'); +}); diff --git a/test/template-literal.js b/test/template-literal.js new file mode 100644 index 0000000..0b969ba --- /dev/null +++ b/test/template-literal.js @@ -0,0 +1,95 @@ +import test from 'ava'; + +// Spoof supports-color +require('./_supports-color')(__dirname); + +const m = require('..'); + +test('return an empty string for an empty literal', t => { + const ctx = m.constructor(); + t.is(ctx``, ''); +}); + +test('return a regular string for a literal with no templates', t => { + const ctx = m.constructor({level: 0}); + t.is(ctx`hello`, 'hello'); +}); + +test('correctly perform template parsing', t => { + const ctx = m.constructor({level: 0}); + t.is(ctx`{bold Hello, {cyan World!} This is a} test. {green Woo!}`, + ctx.bold('Hello,', ctx.cyan('World!'), 'This is a') + ' test. ' + ctx.green('Woo!')); +}); + +test('correctly perform template substitutions', t => { + const ctx = m.constructor({level: 0}); + const name = 'Sindre'; + const exclamation = 'Neat'; + t.is(ctx`{bold Hello, {cyan.inverse ${name}!} This is a} test. {green ${exclamation}!}`, + ctx.bold('Hello,', ctx.cyan.inverse(name + '!'), 'This is a') + ' test. ' + ctx.green(exclamation + '!')); +}); + +test('correctly parse and evaluate color-convert functions', t => { + const ctx = m.constructor({level: 3}); + t.is(ctx`{bold.rgb(144,10,178).inverse Hello, {~inverse there!}}`, + '\u001B[0m\u001B[1m\u001B[38;2;144;10;178m\u001B[7mHello, ' + + '\u001B[27m\u001B[39m\u001B[22m\u001B[0m\u001B[0m\u001B[1m' + + '\u001B[38;2;144;10;178mthere!\u001B[39m\u001B[22m\u001B[0m'); + + t.is(ctx`{bold.bgRgb(144,10,178).inverse Hello, {~inverse there!}}`, + '\u001B[0m\u001B[1m\u001B[48;2;144;10;178m\u001B[7mHello, ' + + '\u001B[27m\u001B[49m\u001B[22m\u001B[0m\u001B[0m\u001B[1m' + + '\u001B[48;2;144;10;178mthere!\u001B[49m\u001B[22m\u001B[0m'); +}); + +test('properly handle escapes', t => { + const ctx = m.constructor({level: 3}); + t.is(ctx`{bold hello \{in brackets\}}`, + '\u001B[0m\u001B[1mhello {in brackets}\u001B[22m\u001B[0m'); +}); + +test('throw if there is an unclosed block', t => { + const ctx = m.constructor({level: 3}); + try { + console.log(ctx`{bold this shouldn't appear ever\}`); + t.fail(); + } catch (err) { + t.is(err.message, 'Template literal has an unclosed block'); + } +}); + +test('throw if there is an invalid style', t => { + const ctx = m.constructor({level: 3}); + try { + console.log(ctx`{abadstylethatdoesntexist this shouldn't appear ever}`); + t.fail(); + } catch (err) { + t.is(err.message, 'Invalid Chalk style: abadstylethatdoesntexist'); + } +}); + +test('properly style multiline color blocks', t => { + const ctx = m.constructor({level: 3}); + t.is( + ctx`{bold + Hello! This is a + ${'multiline'} block! + :) + } {underline + I hope you enjoy + }`, + '\u001B[0m\u001B[1m\u001B[22m\u001B[0m\n' + + '\u001B[0m\u001B[1m\t\t\t\tHello! This is a\u001B[22m\u001B[0m\n' + + '\u001B[0m\u001B[1m\t\t\t\tmultiline block!\u001B[22m\u001B[0m\n' + + '\u001B[0m\u001B[1m\t\t\t\t:)\u001B[22m\u001B[0m\n' + + '\u001B[0m\u001B[1m\t\t\t\u001B[22m\u001B[0m\u001B[0m \u001B[0m\u001B[0m\u001B[4m\u001B[24m\u001B[0m\n' + + '\u001B[0m\u001B[4m\t\t\t\tI hope you enjoy\u001B[24m\u001B[0m\n' + + '\u001B[0m\u001B[4m\t\t\t\u001B[24m\u001B[0m' + ); +}); + +test('escape interpolated values', t => { + const ctx = m.constructor({level: 0}); + t.is(ctx`Hello {bold hi}`, 'Hello hi'); + t.is(ctx`Hello ${'{bold hi}'}`, 'Hello {bold hi}'); +}); diff --git a/test/windows.js b/test/windows.js new file mode 100644 index 0000000..2f2815e --- /dev/null +++ b/test/windows.js @@ -0,0 +1,63 @@ +import test from 'ava'; +import importFresh from 'import-fresh'; +import resolveFrom from 'resolve-from'; + +// Spoof supports-color +require('./_supports-color')(__dirname); + +let originalEnv; +let originalPlatform; + +test.before(() => { + originalEnv = process.env; + originalPlatform = process.platform; +}); + +test.after(() => { + process.env = originalEnv; + Object.defineProperty(process, 'platform', {value: originalPlatform}); +}); + +test.beforeEach(() => { + process.env = {}; + Object.defineProperty(process, 'platform', {value: 'win32'}); + // Since chalk internally modifies `ansiStyles.blue.open`, `ansi-styles` needs + // to be removed from the require cache for `require-uncached` to work + delete require.cache[resolveFrom(__dirname, 'ansi-styles')]; +}); + +test('detect a simple term if TERM isn\'t set', t => { + delete process.env.TERM; + const m = importFresh('..'); + t.is(m.blue('foo'), '\u001B[94mfoo\u001B[39m'); +}); + +test('replace blue foreground color in cmd.exe', t => { + process.env.TERM = 'dumb'; + const m = importFresh('..'); + t.is(m.blue('foo'), '\u001B[94mfoo\u001B[39m'); +}); + +test('don\'t replace blue foreground color in xterm based terminals', t => { + process.env.TERM = 'xterm-256color'; + const m = importFresh('..'); + t.is(m.blue('foo'), '\u001B[34mfoo\u001B[39m'); +}); + +test('don\'t apply dimmed styling on gray strings, see https://github.com/chalk/chalk/issues/58', t => { + process.env.TERM = 'dumb'; + const m = importFresh('..'); + t.is(m.gray.dim('foo'), '\u001B[90mfoo\u001B[22m\u001B[39m'); +}); + +test('apply dimmed styling on xterm compatible terminals', t => { + process.env.TERM = 'xterm'; + const m = importFresh('..'); + t.is(m.gray.dim('foo'), '\u001B[90m\u001B[2mfoo\u001B[22m\u001B[39m'); +}); + +test('apply dimmed styling on strings of other colors', t => { + process.env.TERM = 'dumb'; + const m = importFresh('..'); + t.is(m.blue.dim('foo'), '\u001B[94m\u001B[2mfoo\u001B[22m\u001B[39m'); +});