diff --git a/.editorconfig b/.editorconfig index c6c8b36..1c6314a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,12 @@ root = true [*] -indent_style = space -indent_size = 2 +indent_style = tab end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes index 391f0a4..6313b56 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ -* text=auto -*.js text eol=lf +* text=auto eol=lf diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..a1f7a82 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,21 @@ +name: CI +on: + - push + - pull_request +jobs: + test: + name: Node.js ${{ matrix.node-version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node-version: + - 20 + - 18 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: npm test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f98fed0..0000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: node_js -node_js: - - '12' - - '10' - - '8' diff --git a/cli.js b/cli.js index 9b8056a..5bf9b7a 100755 --- a/cli.js +++ b/cli.js @@ -1,24 +1,18 @@ #!/usr/bin/env node -'use strict'; -const pkg = require('./package.json'); -const yosay = require('.'); +import meow from 'meow'; +import yosay from './index.js'; -require('taketalk')({ - init(input, options) { - console.log(yosay(input, options)); - }, - help() { - console.log(` - ${pkg.description} +const cli = meow(` + Usage + $ yosay + $ yosay --maxLength 8 + $ echo | yosay - Usage - $ yosay - $ yosay --maxLength 8 - $ echo | yosay - - Example - $ yosay 'Sindre is a horse' - ${yosay('Sindre is a horse')}`); - }, - version: pkg.version + Example + $ yosay 'Sindre is a horse' + ${yosay('Sindre is a horse')} +`, { + importMeta: import.meta, }); + +console.log(yosay(cli.input[0], cli.flags)); diff --git a/index.js b/index.js index 714e89b..4e4e06f 100644 --- a/index.js +++ b/index.js @@ -1,195 +1,197 @@ -'use strict'; -const chalk = require('chalk'); -const pad = require('pad-component'); -const wrap = require('wrap-ansi'); -const stringWidth = require('string-width'); -const stripAnsi = require('strip-ansi'); -const ansiStyles = require('ansi-styles'); -const ansiRegex = require('ansi-regex')(); -const cliBoxes = require('cli-boxes'); +import chalk from 'chalk'; +import pad from 'pad-component'; +import wrapAnsi from 'wrap-ansi'; +import stringWidth from 'string-width'; +import stripAnsi from 'strip-ansi'; +import ansiStyles from 'ansi-styles'; +import ansiRegex from 'ansi-regex'; +import cliBoxes from 'cli-boxes'; const border = cliBoxes.round; const leftOffset = 17; -const defaultGreeting = - '\n _-----_ ' + - '\n | | ' + - '\n |' + chalk.red('--(o)--') + '| ' + - '\n `---------´ ' + - '\n ' + chalk.yellow('(') + ' _' + chalk.yellow('´U`') + '_ ' + chalk.yellow(')') + ' ' + - '\n /___A___\\ /' + - '\n ' + chalk.yellow('| ~ |') + ' ' + - '\n __' + chalk.yellow('\'.___.\'') + '__ ' + - '\n ´ ' + chalk.red('` |') + '° ' + chalk.red('´ Y') + ' ` '; - -module.exports = (message, options) => { - message = (message || 'Welcome to Yeoman, ladies and gentlemen!').trim(); - options = options || {}; - - /* - * What you're about to see may confuse you. And rightfully so. Here's an - * explanation. - * - * When yosay is given a string, we create a duplicate with the ansi styling - * sucked out. This way, the true length of the string is read by `pad` and - * `wrap`, so they can correctly do their job without getting tripped up by - * the "invisible" ansi. Along with the duplicated, non-ansi string, we store - * the character position of where the ansi was, so that when we go back over - * each line that will be printed out in the message box, we check the - * character position to see if it needs any styling, then re-insert it if - * necessary. - * - * Better implementations welcome :) - */ - - const styledIndexes = {}; - let maxLength = 24; - let topOffset = 4; - let completedString = ''; - - // Amount of characters of the yeoman character »column« → ` /___A___\ /` - const YEOMAN_CHARACTER_WIDTH = 17; - - // Amount of characters of the default top frame of the speech bubble → `╭──────────────────────────╮` - const DEFAULT_TOP_FRAME_WIDTH = 28; - - // The speech bubble will overflow the Yeoman character if the message is too long. - const MAX_MESSAGE_LINES_BEFORE_OVERFLOW = 7; - - // Amount of characters of a total line - let TOTAL_CHARACTERS_PER_LINE = YEOMAN_CHARACTER_WIDTH + DEFAULT_TOP_FRAME_WIDTH; - - if (options.maxLength) { - maxLength = stripAnsi(message).toLowerCase().split(' ').sort()[0].length; - - if (maxLength < options.maxLength) { - maxLength = options.maxLength; - TOTAL_CHARACTERS_PER_LINE = maxLength + YEOMAN_CHARACTER_WIDTH + topOffset; - } - } - - const regExNewLine = new RegExp(`\\s{${maxLength}}`); - const borderHorizontal = border.horizontal.repeat(maxLength + 2); - - const frame = { - top: border.topLeft + borderHorizontal + border.topRight, - side: ansiStyles.reset.open + border.vertical + ansiStyles.reset.open, - bottom: ansiStyles.reset.open + border.bottomLeft + borderHorizontal + border.bottomRight - }; - - message.replace(ansiRegex, (match, offset) => { - Object.keys(styledIndexes).forEach(key => { - offset -= styledIndexes[key].length; - }); - - styledIndexes[offset] = styledIndexes[offset] ? styledIndexes[offset] + match : match; - }); - - const strippedMessage = stripAnsi(message); - const spacesIndex = []; - - strippedMessage.split(' ').reduce((accu, cur) => { - spacesIndex.push(accu + cur.length); - return spacesIndex[spacesIndex.length - 1] + 1; - }, 0); - - return wrap(strippedMessage, maxLength, {hard: true}) - .split(/\n/) - .reduce((greeting, str, index, array) => { - if (!regExNewLine.test(str)) { - str = str.trim(); - } - - completedString += str; - - let offset = 0; - - for (let i = 0; i < spacesIndex.length; i++) { - const char = completedString[spacesIndex[i] - offset]; - if (char) { - if (char !== ' ') { - offset += 1; - } - } else { - break; - } - } - - str = completedString - .substr(completedString.length - str.length) - .replace(/./g, (char, charIndex) => { - charIndex += completedString.length - str.length + offset; - - let hasContinuedStyle = 0; - let continuedStyle; - - Object.keys(styledIndexes).forEach(offset => { - if (charIndex > offset) { - hasContinuedStyle++; - continuedStyle = styledIndexes[offset]; - } - - if (hasContinuedStyle === 1 && charIndex < offset) { - hasContinuedStyle++; - } - }); - - if (styledIndexes[charIndex]) { - return styledIndexes[charIndex] + char; - } - - if (hasContinuedStyle >= 2) { - return continuedStyle + char; - } - - return char; - }) - .trim(); - - const paddedString = pad({ - length: stringWidth(str), - valueOf() { - return ansiStyles.reset.open + str + ansiStyles.reset.open; - } - }, maxLength); - - if (index === 0) { - // Need to adjust the top position of the speech bubble depending on the - // amount of lines of the message. - if (array.length === 2) { - topOffset -= 1; - } - - if (array.length >= 3) { - topOffset -= 2; - } - - // The speech bubble will overflow the Yeoman character if the message - // is too long. So we vertically center the bubble by adding empty lines - // on top of the greeting. - if (array.length > MAX_MESSAGE_LINES_BEFORE_OVERFLOW) { - const emptyLines = Math.ceil((array.length - MAX_MESSAGE_LINES_BEFORE_OVERFLOW) / 2); - - for (let i = 0; i < emptyLines; i++) { - greeting.unshift(''); - } - - frame.top = pad.left(frame.top, TOTAL_CHARACTERS_PER_LINE); - } - - greeting[topOffset - 1] += frame.top; - } - - greeting[index + topOffset] = - (greeting[index + topOffset] || pad.left('', leftOffset)) + - frame.side + ' ' + paddedString + ' ' + frame.side; - - if (array.length === index + 1) { - greeting[index + topOffset + 1] = - (greeting[index + topOffset + 1] || pad.left('', leftOffset)) + - frame.bottom; - } - - return greeting; - }, defaultGreeting.split(/\n/)) - .join('\n') + '\n'; -}; + +const defaultGreeting + = '\n _-----_ ' + + '\n | | ' + + '\n |' + chalk.red('--(o)--') + '| ' + + '\n `---------´ ' + + '\n ' + chalk.yellow('(') + ' _' + chalk.yellow('´U`') + '_ ' + chalk.yellow(')') + ' ' + + '\n /___A___\\ /' + + '\n ' + chalk.yellow('| ~ |') + ' ' + + '\n __' + chalk.yellow('\'.___.\'') + '__ ' + + '\n ´ ' + chalk.red('` |') + '° ' + chalk.red('´ Y') + ' ` '; + +export default function yosay(message, options = {}) { + message = (message ?? 'Welcome to Yeoman, ladies and gentlemen!').trim(); + + /* + What you're about to see may confuse you. And rightfully so. Here's an explanation. + + When yosay is given a string, we create a duplicate with the ansi styling + sucked out. This way, the true length of the string is read by `pad` and + `wrap`, so they can correctly do their job without getting tripped up by + the "invisible" ansi. Along with the duplicated, non-ansi string, we store + the character position of where the ansi was, so that when we go back over + each line that will be printed out in the message box, we check the + character position to see if it needs any styling, then re-insert it if + necessary. + + Better implementations welcome :) + */ + + const styledIndexes = {}; + let maxLength = 24; + let topOffset = 4; + let completedString = ''; + + // Amount of characters of the yeoman character »column« → ` /___A___\ /` + const YEOMAN_CHARACTER_WIDTH = 17; + + // Amount of characters of the default top frame of the speech bubble → `╭──────────────────────────╮` + const DEFAULT_TOP_FRAME_WIDTH = 28; + + // The speech bubble will overflow the Yeoman character if the message is too long. + const MAX_MESSAGE_LINES_BEFORE_OVERFLOW = 7; + + // Amount of characters of a total line + let TOTAL_CHARACTERS_PER_LINE = YEOMAN_CHARACTER_WIDTH + DEFAULT_TOP_FRAME_WIDTH; + + if (options.maxLength) { + maxLength = stripAnsi(message).toLowerCase().split(' ').sort()[0].length; + + if (maxLength < options.maxLength) { + maxLength = options.maxLength; + TOTAL_CHARACTERS_PER_LINE = maxLength + YEOMAN_CHARACTER_WIDTH + topOffset; + } + } + + const regexNewline = new RegExp(`\\s{${maxLength}}`); + const borderHorizontal = border.top.repeat(maxLength + 2); + + const frame = { + top: border.topLeft + borderHorizontal + border.topRight, + side: ansiStyles.reset.open + border.left + ansiStyles.reset.open, + bottom: ansiStyles.reset.open + border.bottomLeft + borderHorizontal + border.bottomRight, + }; + + message.replace(ansiRegex(), (match, offset) => { + for (const value of Object.values(styledIndexes)) { + offset -= value.length; + } + + styledIndexes[offset] = styledIndexes[offset] ? styledIndexes[offset] + match : match; + }); + + const strippedMessage = stripAnsi(message); + const spacesIndex = []; + + // TODO: Remove `.reduce`. + // eslint-disable-next-line unicorn/no-array-reduce + strippedMessage.split(' ').reduce((accumulator, currentValue) => { + spacesIndex.push(accumulator + currentValue.length); + return spacesIndex.at(-1) + 1; + }, 0); + + return wrapAnsi(strippedMessage, maxLength, {hard: true}) + .split(/\n/) + // TODO: Remove `.reduce`. + // eslint-disable-next-line unicorn/no-array-reduce + .reduce((greeting, string_, index, array) => { + if (!regexNewline.test(string_)) { + string_ = string_.trim(); + } + + completedString += string_; + + let offset = 0; + + for (const element of spacesIndex) { + const character = completedString[element - offset]; + if (character) { + if (character !== ' ') { + offset += 1; + } + } else { + break; + } + } + + string_ = completedString + .slice(completedString.length - string_.length) + .replaceAll(/./g, (character, characterIndex) => { + characterIndex += completedString.length - string_.length + offset; + + let hasContinuedStyle = 0; + let continuedStyle; + + for (const offset of Object.keys(styledIndexes)) { + if (characterIndex > offset) { + hasContinuedStyle++; + continuedStyle = styledIndexes[offset]; + } + + if (hasContinuedStyle === 1 && characterIndex < offset) { + hasContinuedStyle++; + } + } + + if (styledIndexes[characterIndex]) { + return styledIndexes[characterIndex] + character; + } + + if (hasContinuedStyle >= 2) { + return continuedStyle + character; + } + + return character; + }) + .trim(); + + const paddedString = pad({ + length: stringWidth(string_), + valueOf() { + return ansiStyles.reset.open + string_ + ansiStyles.reset.open; + }, + }, maxLength); + + if (index === 0) { + // Need to adjust the top position of the speech bubble depending on the + // amount of lines of the message. + if (array.length === 2) { + topOffset -= 1; + } + + if (array.length >= 3) { + topOffset -= 2; + } + + // The speech bubble will overflow the Yeoman character if the message + // is too long. So we vertically center the bubble by adding empty lines + // on top of the greeting. + if (array.length > MAX_MESSAGE_LINES_BEFORE_OVERFLOW) { + const emptyLines = Math.ceil((array.length - MAX_MESSAGE_LINES_BEFORE_OVERFLOW) / 2); + + for (let i = 0; i < emptyLines; i++) { + greeting.unshift(''); + } + + frame.top = pad.left(frame.top, TOTAL_CHARACTERS_PER_LINE); + } + + greeting[topOffset - 1] += frame.top; + } + + greeting[index + topOffset] + = (greeting[index + topOffset] || pad.left('', leftOffset)) + + frame.side + ' ' + paddedString + ' ' + frame.side; + + if (array.length === index + 1) { + greeting[index + topOffset + 1] + = (greeting[index + topOffset + 1] || pad.left('', leftOffset)) + + frame.bottom; + } + + return greeting; + }, defaultGreeting.split(/\n/)) + .join('\n') + '\n'; +} diff --git a/package.json b/package.json index 128739a..d6d7f8e 100644 --- a/package.json +++ b/package.json @@ -1,49 +1,47 @@ { - "name": "yosay", - "version": "2.0.2", - "description": "Tell Yeoman what to say", - "license": "BSD-2-Clause", - "repository": "yeoman/yosay", - "homepage": "yeoman.io", - "author": "Yeoman", - "bin": "cli.js", - "engines": { - "node": ">=4" - }, - "scripts": { - "test": "xo && mocha" - }, - "files": [ - "index.js", - "cli.js" - ], - "keywords": [ - "cli-app", - "cli", - "yeoman", - "yo", - "cowsay", - "say", - "box", - "message", - "ansi" - ], - "dependencies": { - "ansi-regex": "^4.1.0", - "ansi-styles": "^4.0.0", - "chalk": "^2.4.2", - "cli-boxes": "^2.2.0", - "pad-component": "0.0.1", - "string-width": "^4.1.0", - "strip-ansi": "^5.2.0", - "taketalk": "^1.0.0", - "wrap-ansi": "^6.0.0" - }, - "devDependencies": { - "mocha": "^6.2.0", - "xo": "^0.24.0" - }, - "xo": { - "space": true - } + "name": "yosay", + "version": "2.0.2", + "description": "Tell Yeoman what to say", + "license": "BSD-2-Clause", + "repository": "yeoman/yosay", + "type": "module", + "bin": "./cli.js", + "exports": "./index.js", + "engines": { + "node": ">=18" + }, + "scripts": { + "test": "xo && FORCE_COLOR=1 mocha", + "manual-test": "node test/manual-test.js && node cli.js" + }, + "files": [ + "index.js", + "cli.js" + ], + "keywords": [ + "cli-app", + "cli", + "yeoman", + "yo", + "cowsay", + "say", + "box", + "message", + "ansi" + ], + "dependencies": { + "ansi-regex": "^6.0.1", + "ansi-styles": "^6.2.1", + "chalk": "^5.3.0", + "cli-boxes": "^3.0.0", + "meow": "^12.1.1", + "pad-component": "0.0.1", + "string-width": "^6.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^8.1.0" + }, + "devDependencies": { + "mocha": "^10.2.0", + "xo": "^0.56.0" + } } diff --git a/README.md b/readme.md similarity index 77% rename from README.md rename to readme.md index c6314b6..362551d 100644 --- a/README.md +++ b/readme.md @@ -1,4 +1,4 @@ -# yosay [![Build Status](https://travis-ci.org/yeoman/yosay.svg?branch=master)](https://travis-ci.org/yeoman/yosay) +# yosay > Tell Yeoman what to say @@ -6,18 +6,16 @@ Like [cowsay](https://en.wikipedia.org/wiki/Cowsay), but less cow. ![](screenshot.png) - ## Install -``` -$ npm install yosay +```sh +npm install yosay ``` - ## Usage ```js -const yosay = require('yosay'); +import yosay from 'yosay'; console.log(yosay('Hello, and welcome to my fantastic generator full of whimsy and bubble gum!')); @@ -31,16 +29,15 @@ console.log(yosay('Hello, and welcome to my fantastic generator full of whimsy a | ~ | __'.___.'__ ´ ` |° ´ Y ` - */ +*/ ``` -*You can style your text with [`chalk`](https://github.com/sindresorhus/chalk) before passing it to `yosay`.* - +*You can style your text with [`chalk`](https://github.com/chalk/chalk) before passing it to `yosay`.* ## CLI -``` -$ npm install --global yosay +```sh +npm install --global yosay ``` ``` @@ -64,8 +61,3 @@ $ yosay --help __'.___.'__ ´ ` |° ´ Y ` ``` - - -## License - -BSD-2-Clause © Google diff --git a/test/manual-test.js b/test/manual-test.js index f96cdf6..ea68465 100644 --- a/test/manual-test.js +++ b/test/manual-test.js @@ -1,13 +1,5 @@ -'use strict'; -const chalk = require('chalk'); -const yosay = require('..'); - -/* - * Yo. Fire this file locally with `node test/manual-test.js` at least after you - * have newly generated the text fixtures to double check that all available - * option have a correct looking output. - * Thanks (ᵔᴥᵔ) - */ +import chalk from 'chalk'; +import yosay from '../index.js'; console.log(yosay('Hello, and welcome to my fantastic generator full of whimsy and bubble gum!')); @@ -37,27 +29,27 @@ console.log(yosay('Lie on your belly and purr when you are asleep shove bum in o console.log(yosay('Lie on your belly and purr when you are asleep shove bum in owner’s face like camera lens. Cough furball. Cough furball. Cough furball. Cough furball. Cough furball. Cough furball. Cough furball. Cough furball. Cough furball. Cough furball. Cough furball. Cough furball. Cough furball.', {maxLength: 26})); console.log(yosay( - 'That’s it. Feel free to fire up the server with ' + - chalk.green('`npm run start:dev`') + - 'or use our subgenerator to create endpoints.' + 'That’s it. Feel free to fire up the server with ' + + chalk.green('`npm run start:dev`') + + 'or use our subgenerator to create endpoints.', )); console.log(yosay('That’s it. Feel free to fire up the server with `npm run start:dev` or use our subgenerator to create endpoints.')); console.log(yosay( - 'That’s it. Feel free to fire up the server with ' + - chalk.green('`npm run start:dev`') + '.' + 'That’s it. Feel free to fire up the server with ' + + chalk.green('`npm run start:dev`') + '.', )); console.log(yosay( - 'That’s it. Feel free to fire up the server with ' + - '`npm run start:dev`.' + 'That’s it. Feel free to fire up the server with ' + + '`npm run start:dev`.', )); console.log(yosay( - `Welcome to the polished ${chalk.red('something iloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicorns')} generator!` + `Welcome to the polished ${chalk.red('something iloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicorns')} generator!`, )); console.log(yosay( - `Welcome to the polished ${chalk.red('something iloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicorns')} generator! Another long sentence ${chalk.yellow('something iloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicorns')}normal text` + `Welcome to the polished ${chalk.red('something iloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicorns')} generator! Another long sentence ${chalk.yellow('something iloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicorns')}normal text`, )); diff --git a/test/test.js b/test/test.js index b8d970e..ef4b8f0 100644 --- a/test/test.js +++ b/test/test.js @@ -1,114 +1,95 @@ -'use strict'; /* eslint-env mocha */ -const assert = require('assert'); -const fs = require('fs'); -const path = require('path'); -const chalk = require('chalk'); -const yosay = require('..'); +import assert from 'node:assert'; +import fs from 'node:fs/promises'; +import {fileURLToPath} from 'node:url'; +import path from 'node:path'; +import chalk from 'chalk'; +import yosay from '../index.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); const getFixturePath = testName => path.join(__dirname, 'fixture', `${testName}.json`); -const getAssertResult = (testName, expected, done) => fs.readFile(getFixturePath(testName), (err, data) => { - assert.ifError(err); - assert.strict.equal(JSON.parse(data), expected); - done(); -}); +const getAssertResult = async (testName, expected) => { + try { + const data = await fs.readFile(getFixturePath(testName)); + assert.strictEqual(JSON.parse(data), expected); + } catch (error) { + assert.ifError(error); + } +}; console.log(yosay(chalk.red('WHAT DOES THE YO SAY??? ') + chalk.yellow('\'ALLO \'ALLO'))); describe('yosay', () => { - // New test template. - // - // it('should _____', function (done) { - // var testName = 'short-description'; - // var expected = yosay('String to test'); - // - // // Create fixture: run once, then remove from test: - // fs.writeFile(getFixturePath(testName), JSON.stringify(expected)); - // - // fs.readFile(getFixturePath(testName), function (err, data) { - // assert.ifError(err); - // assert.equal(JSON.parse(data), expected); - // done(); - // }); - // }); - - it('should return correctly formatted string', done => { - const testName = 'correctly-formatted'; - const expected = yosay('Hi'); - - getAssertResult(testName, expected, done); - }); - - it('should return correctly formatted string in two lines', done => { - const testName = 'correctly-formatted-two-lines'; - const expected = yosay('Welcome to Yeoman, ladies and gentlemen!'); - - getAssertResult(testName, expected, done); - }); - - it('should allow customization of line length', done => { - const testName = 'length-customization'; - const expected = yosay('Hi', {maxLength: 8}); - - getAssertResult(testName, expected, done); - }); - - it('should override a maxLength setting that is too short', done => { - const testName = 'override-maxLength'; - const expected = yosay('Hello, buddy!', {maxLength: 4}); - - getAssertResult(testName, expected, done); - }); - - describe('ansi', () => { - it('should display ansi styling correctly', done => { - const testName = 'ansi'; - const expected = yosay(chalk.red.bgWhite('Hi')); - - getAssertResult(testName, expected, done); - }); - - it('should handle part ansi and part not-ansi', done => { - const testName = 'half-ansi'; - const expected = yosay(chalk.red.bgWhite('Hi') + ' there, sir!'); - - getAssertResult(testName, expected, done); - }); - - it('should wrap ansi styling to the next line properly', done => { - const testName = 'wrap-ansi-styles'; - const expected = yosay(chalk.red.bgWhite('Hi') + ' there, sir! ' + chalk.bgBlue.white('you are looking') + ' swell today!'); - - getAssertResult(testName, expected, done); - }); - - it('should handle new line properly', done => { - const testName = 'handle-new-line'; - const expected = yosay('first line\nsecond line\n\nfourth line'); - - getAssertResult(testName, expected, done); - }); - - it('should handle fullwidth characters', done => { - const testName = 'handle-fullwidth'; - const expected = yosay('项目可以更新了'); - - getAssertResult(testName, expected, done); - }); - - it('should display long words correctly', done => { - const testName = 'long-words'; - const expected = yosay('iloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicorns'); - - getAssertResult(testName, expected, done); - }); - - it('should overflow when lines exceed the default greeting', done => { - const testName = 'overflow'; - const expected = yosay('Lie on your belly and purr when you are asleep shove bum in owner’s face like camera lens. Cough furball.', {maxLength: 11}); - - getAssertResult(testName, expected, done); - }); - }); + it('should return correctly formatted string', async () => { + const testName = 'correctly-formatted'; + const expected = yosay('Hi'); + await getAssertResult(testName, expected); + }); + + it('should return correctly formatted string in two lines', async () => { + const testName = 'correctly-formatted-two-lines'; + const expected = yosay('Welcome to Yeoman, ladies and gentlemen!'); + await getAssertResult(testName, expected); + }); + + it('should allow customization of line length', async () => { + const testName = 'length-customization'; + const expected = yosay('Hi', {maxLength: 8}); + await getAssertResult(testName, expected); + }); + + it('should override a maxLength setting that is too short', async () => { + const testName = 'override-maxLength'; + const expected = yosay('Hello, buddy!', {maxLength: 4}); + await getAssertResult(testName, expected); + }); + + describe('ansi', () => { + it('should display ansi styling correctly', async () => { + const testName = 'ansi'; + const expected = yosay(chalk.red.bgWhite('Hi')); + await getAssertResult(testName, expected); + }); + + it('should handle part ansi and part not-ansi', async () => { + const testName = 'half-ansi'; + const expected = yosay(chalk.red.bgWhite('Hi') + ' there, sir!'); + await getAssertResult(testName, expected); + }); + + it('should wrap ansi styling to the next line properly', async () => { + const testName = 'wrap-ansi-styles'; + const expected = yosay(chalk.red.bgWhite('Hi') + ' there, sir! ' + chalk.bgBlue.white('you are looking') + ' swell today!'); + await getAssertResult(testName, expected); + }); + + it('should handle new line properly', async () => { + const testName = 'handle-new-line'; + const expected = yosay('first line\nsecond line\n\nfourth line'); + await getAssertResult(testName, expected); + }); + + it('should handle fullwidth characters', async () => { + const testName = 'handle-fullwidth'; + const expected = yosay('项目可以更新了'); + await getAssertResult(testName, expected); + }); + + it('should display long words correctly', async () => { + const testName = 'long-words'; + const expected = yosay('iloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicornsiloveunicorns'); + await getAssertResult(testName, expected); + }); + + it('should overflow when lines exceed the default greeting', async () => { + const testName = 'overflow'; + const expected = yosay( + 'Lie on your belly and purr when you are asleep shove bum in owner’s face like camera lens. Cough furball.', + {maxLength: 11}, + ); + await getAssertResult(testName, expected); + }); + }); });