diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index a023591..a768beb 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -10,9 +10,9 @@ jobs:
fail-fast: false
matrix:
node-version:
+ - 21
- 20
- 18
- - 16
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
diff --git a/estest/index.js b/estest/index.js
deleted file mode 100644
index 3f62756..0000000
--- a/estest/index.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import meow from '../source/index.js';
-
-meow(
- `
- Usage
- $ estest
-
- Options
- --rainbow, -r Include a rainbow
-
- Examples
- $ estest unicorns --rainbow
- 🌈 unicorns 🌈
- `,
- {
- importMeta: import.meta,
- flags: {
- rainbow: {
- type: 'boolean',
- shortFlag: 'r',
- },
- },
- },
-);
diff --git a/estest/package.json b/estest/package.json
deleted file mode 100644
index eedaaed..0000000
--- a/estest/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "name": "estest",
- "type": "module",
- "version": "1.2.3"
-}
diff --git a/package.json b/package.json
index f1a83ae..f000643 100644
--- a/package.json
+++ b/package.json
@@ -64,28 +64,29 @@
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/minimist": "^1.2.5",
- "ava": "^6.0.1",
- "camelcase-keys": "^9.1.2",
+ "ava": "^6.1.1",
+ "camelcase-keys": "^9.1.3",
"common-tags": "^2.0.0-alpha.1",
"decamelize": "^6.0.0",
"decamelize-keys": "^2.0.1",
"delete_comments": "^0.0.2",
"execa": "^8.0.1",
- "globby": "^14.0.0",
+ "globby": "^14.0.1",
"indent-string": "^5.0.0",
"minimist-options": "4.1.0",
"normalize-package-data": "^6.0.0",
"read-package-up": "^11.0.0",
"read-pkg": "^9.0.1",
"redent": "^4.0.0",
- "rollup": "^4.9.2",
+ "rollup": "^4.12.0",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-license": "^3.2.0",
+ "stack-utils": "^2.0.6",
"trim-newlines": "^5.0.0",
- "tsd": "^0.30.3",
- "type-fest": "^4.9.0",
- "typescript": "^5.3.3",
- "xo": "^0.56.0",
+ "tsd": "^0.30.7",
+ "type-fest": "^4.10.3",
+ "typescript": "~5.3.3",
+ "xo": "^0.57.0",
"yargs-parser": "^21.1.1"
},
"xo": {
@@ -96,10 +97,5 @@
"ignores": [
"build"
]
- },
- "ava": {
- "files": [
- "test/*"
- ]
}
}
diff --git a/source/index.js b/source/index.js
index bd048c3..7881e24 100644
--- a/source/index.js
+++ b/source/index.js
@@ -25,10 +25,7 @@ const buildResult = (options, parserOptions) => {
normalizePackageData(package_);
- let {description} = options;
- if (!description && description !== false) {
- ({description} = package_);
- }
+ let description = options.description ?? package_.description;
description &&= help ? redent(`\n${description}\n`, options.helpIndent) : `\n${description}`;
help = `${description || ''}${help}\n`;
diff --git a/source/parser.js b/source/parser.js
index c025581..6685c3e 100644
--- a/source/parser.js
+++ b/source/parser.js
@@ -23,7 +23,7 @@ const buildParserFlags = ({flags, booleanDefault}) => {
if (flag.isMultiple) {
flag.type = flag.type ? `${flag.type}-array` : 'array';
- flag.default = flag.default ?? [];
+ flag.default ??= [];
delete flag.isMultiple;
}
diff --git a/test/_utils.js b/test/_utils.js
index f09a405..31cec2b 100644
--- a/test/_utils.js
+++ b/test/_utils.js
@@ -1,24 +1,70 @@
-import path from 'node:path';
+/* eslint-disable ava/no-ignored-test-files */
import {fileURLToPath} from 'node:url';
+import test from 'ava';
import {execa} from 'execa';
+import {readPackage} from 'read-pkg';
import {createTag, stripIndentTransformer, trimResultTransformer} from 'common-tags';
+import StackUtils from 'stack-utils';
-export const __dirname = path.dirname(fileURLToPath(import.meta.url));
+export const defaultFixture = 'fixture.js';
-const getFixture = fixture => path.join(__dirname, 'fixtures', fixture);
+const getFixture = fixture => fileURLToPath(new URL(`fixtures/${fixture}`, import.meta.url));
-export const spawnFixture = async (fixture = 'fixture.js', args = []) => {
- // Allow calling with args first
+export const spawnFixture = async (fixture = defaultFixture, arguments_ = [], options = {}) => {
+ // Allow calling with arguments first
if (Array.isArray(fixture)) {
- args = fixture;
- fixture = 'fixture.js';
+ arguments_ = fixture;
+ fixture = defaultFixture;
}
- return execa(getFixture(fixture), args);
+ return execa(getFixture(fixture), arguments_, options);
};
+export {stripIndent} from 'common-tags';
+
// Use old behavior prior to zspecza/common-tags#165
-export const stripIndent = createTag(
+export const stripIndentTrim = createTag(
stripIndentTransformer(),
trimResultTransformer(),
);
+
+export const meowPackage = await readPackage();
+export const meowVersion = meowPackage.version;
+
+const stackUtils = new StackUtils();
+
+export const stackToErrorMessage = stack => stackUtils.clean(stack).split('\n').at(0);
+
+export const _verifyCli = (baseFixture = defaultFixture) => test.macro(
+ async (t, {fixture = baseFixture, args, execaOptions, expected, error}) => {
+ const assertions = await t.try(async tt => {
+ const arguments_ = args ? args.split(' ') : [];
+ const {all: output, exitCode} = await spawnFixture(fixture, arguments_, {reject: false, all: true, ...execaOptions});
+ tt.log('args:', arguments_);
+
+ if (error) {
+ tt.log(`error (code ${exitCode}):\n`, output);
+
+ if (typeof error === 'string') {
+ tt.is(output, error);
+ tt.is(exitCode, 2);
+ } else {
+ const error_ = error.clean ? stackToErrorMessage(output) : output;
+
+ tt.is(error_, error.message);
+ tt.is(exitCode, error.code);
+ }
+ } else {
+ tt.log('output:\n', output);
+
+ if (expected) {
+ tt.is(output, expected);
+ } else {
+ tt.pass();
+ }
+ }
+ });
+
+ assertions.commit({retainLogs: !assertions.passed});
+ },
+);
diff --git a/test/allow-unknown-flags.js b/test/allow-unknown-flags.js
deleted file mode 100644
index f4449bc..0000000
--- a/test/allow-unknown-flags.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import test from 'ava';
-import indentString from 'indent-string';
-import {readPackage} from 'read-pkg';
-import {spawnFixture} from './_utils.js';
-
-const fixtureFolder = 'allow-unknown-flags';
-
-const allowUnknownFlags = `${fixtureFolder}/fixture.js`;
-const allowUnknownFlagsWithHelp = `${fixtureFolder}/fixture-with-help.js`;
-
-test('spawn CLI and test specifying unknown flags', async t => {
- const {stderr} = await t.throwsAsync(
- spawnFixture(allowUnknownFlags, ['--foo', 'bar', '--unspecified-a', '--unspecified-b', 'input-is-allowed']),
- {message: /^Command failed with exit code 2/},
- );
-
- t.regex(stderr, /Unknown flags/);
- t.regex(stderr, /--unspecified-a/);
- t.regex(stderr, /--unspecified-b/);
- t.notRegex(stderr, /input-is-allowed/);
-});
-
-test('spawn CLI and test specifying known flags', async t => {
- const {stdout} = await spawnFixture(allowUnknownFlags, ['--foo', 'bar']);
- t.is(stdout, 'bar');
-});
-
-test('spawn CLI and test help as a known flag', async t => {
- const {stdout} = await spawnFixture(allowUnknownFlags, ['--help']);
- t.is(stdout, indentString('\nCustom description\n\nUsage\n foo \n\n', 2));
-});
-
-test('spawn CLI and test version as a known flag', async t => {
- const pkg = await readPackage();
- const {stdout} = await spawnFixture(allowUnknownFlags, ['--version']);
- t.is(stdout, pkg.version);
-});
-
-test('spawn CLI and test help as an unknown flag', async t => {
- const {stderr} = await t.throwsAsync(
- spawnFixture(allowUnknownFlags, ['--help', '--no-auto-help']),
- {message: /^Command failed with exit code 2/},
- );
-
- t.regex(stderr, /Unknown flag/);
- t.regex(stderr, /--help/);
-});
-
-test('spawn CLI and test version as an unknown flag', async t => {
- const {stderr} = await t.throwsAsync(
- spawnFixture(allowUnknownFlags, ['--version', '--no-auto-version']),
- {message: /^Command failed with exit code 2/},
- );
-
- t.regex(stderr, /Unknown flag/);
- t.regex(stderr, /--version/);
-});
-
-test('spawn CLI and test help with custom config', async t => {
- const {stdout} = await spawnFixture(allowUnknownFlagsWithHelp, ['-h']);
- t.is(stdout, indentString('\nCustom description\n\nUsage\n foo \n\n', 2));
-});
-
-test('spawn CLI and test version with custom config', async t => {
- const pkg = await readPackage();
- const {stdout} = await spawnFixture(allowUnknownFlagsWithHelp, ['-v']);
- t.is(stdout, pkg.version);
-});
diff --git a/test/build.js b/test/build.js
index 5607ef7..28411f8 100644
--- a/test/build.js
+++ b/test/build.js
@@ -1,7 +1,8 @@
import test from 'ava';
-import {readPackage} from 'read-pkg';
import meow from '../build/index.js';
-import {spawnFixture} from './_utils.js';
+import {_verifyCli, meowVersion} from './_utils.js';
+
+const verifyCli = _verifyCli();
test('main', t => {
const cli = meow(`
@@ -39,8 +40,7 @@ test('main', t => {
});
});
-test('spawn cli and show version', async t => {
- const pkg = await readPackage();
- const {stdout} = await spawnFixture(['--version']);
- t.is(stdout, pkg.version);
+test('spawn cli and show version', verifyCli, {
+ args: '--version',
+ expected: meowVersion,
});
diff --git a/test/choices.js b/test/choices.js
deleted file mode 100644
index 7fcf78b..0000000
--- a/test/choices.js
+++ /dev/null
@@ -1,230 +0,0 @@
-import test from 'ava';
-import meow from '../source/index.js';
-import {stripIndent} from './_utils.js';
-
-const importMeta = import.meta;
-
-test('choices - success case', t => {
- const cli = meow({
- importMeta,
- argv: ['--animal', 'cat', '--number=2.2'],
- flags: {
- animal: {
- choices: ['dog', 'cat', 'unicorn'],
- },
- number: {
- type: 'number',
- choices: [1.1, 2.2, 3.3],
- },
- },
- });
-
- t.is(cli.flags.animal, 'cat');
- t.is(cli.flags.number, 2.2);
-});
-
-test('choices - throws if input does not match choices', t => {
- t.throws(() => {
- meow({
- importMeta,
- argv: ['--animal', 'rainbow', '--number', 5],
- flags: {
- animal: {
- choices: ['dog', 'cat', 'unicorn'],
- },
- number: {
- choices: [1, 2, 3],
- },
- },
- });
- }, {
- message: stripIndent`
- Unknown value for flag \`--animal\`: \`rainbow\`. Value must be one of: [\`dog\`, \`cat\`, \`unicorn\`]
- Unknown value for flag \`--number\`: \`5\`. Value must be one of: [\`1\`, \`2\`, \`3\`]
- `,
- });
-});
-
-test('choices - throws if choices is not array', t => {
- t.throws(() => {
- meow({
- importMeta,
- argv: ['--animal', 'cat'],
- flags: {
- animal: {
- choices: 'cat',
- },
- },
- });
- }, {message: 'The option `choices` must be an array. Invalid flags: `--animal`'});
-});
-
-test('choices - does not throw error when isRequired is false', t => {
- t.notThrows(() => {
- meow({
- importMeta,
- argv: [],
- flags: {
- animal: {
- isRequired: false,
- choices: ['dog', 'cat', 'unicorn'],
- },
- },
- });
- });
-});
-
-test('choices - throw error when isRequired is true', t => {
- t.throws(() => {
- meow({
- importMeta,
- argv: [],
- flags: {
- animal: {
- isRequired: true,
- choices: ['dog', 'cat', 'unicorn'],
- },
- },
- });
- }, {message: 'Flag `--animal` has no value. Value must be one of: [`dog`, `cat`, `unicorn`]'});
-});
-
-test('choices - success with isMultiple', t => {
- const cli = meow({
- importMeta,
- argv: ['--animal=dog', '--animal=unicorn'],
- flags: {
- animal: {
- type: 'string',
- isMultiple: true,
- choices: ['dog', 'cat', 'unicorn'],
- },
- },
- });
-
- t.deepEqual(cli.flags.animal, ['dog', 'unicorn']);
-});
-
-test('choices - throws with isMultiple, one unknown value', t => {
- t.throws(() => {
- meow({
- importMeta,
- argv: ['--animal=dog', '--animal=rabbit'],
- flags: {
- animal: {
- type: 'string',
- isMultiple: true,
- choices: ['dog', 'cat', 'unicorn'],
- },
- },
- });
- }, {message: 'Unknown value for flag `--animal`: `rabbit`. Value must be one of: [`dog`, `cat`, `unicorn`]'});
-});
-
-test('choices - throws with isMultiple, multiple unknown value', t => {
- t.throws(() => {
- meow({
- importMeta,
- argv: ['--animal=dog', '--animal=rabbit'],
- flags: {
- animal: {
- type: 'string',
- isMultiple: true,
- choices: ['cat', 'unicorn'],
- },
- },
- });
- }, {message: 'Unknown values for flag `--animal`: `dog`, `rabbit`. Value must be one of: [`cat`, `unicorn`]'});
-});
-
-test('choices - throws with multiple flags', t => {
- t.throws(() => {
- meow({
- importMeta,
- argv: ['--animal=dog', '--plant=succulent'],
- flags: {
- animal: {
- type: 'string',
- choices: ['cat', 'unicorn'],
- },
- plant: {
- type: 'string',
- choices: ['tree', 'flower'],
- },
- },
- });
- }, {message: stripIndent`
- Unknown value for flag \`--animal\`: \`dog\`. Value must be one of: [\`cat\`, \`unicorn\`]
- Unknown value for flag \`--plant\`: \`succulent\`. Value must be one of: [\`tree\`, \`flower\`]
- `});
-});
-
-test('choices - choices must be of the same type', t => {
- t.throws(() => {
- meow({
- importMeta,
- flags: {
- number: {
- type: 'number',
- choices: [1, '2'],
- },
- boolean: {
- type: 'boolean',
- choices: [true, 'false'],
- },
- },
- });
- }, {message: 'Each value of the option `choices` must be of the same type as its flag. Invalid flags: (`--number`, type: \'number\'), (`--boolean`, type: \'boolean\')'});
-});
-
-test('choices - success when each value of default exist within the option choices', t => {
- t.notThrows(() => {
- meow({
- importMeta,
- flags: {
- number: {
- type: 'number',
- choices: [1, 2, 3],
- default: 1,
- },
- string: {
- type: 'string',
- choices: ['dog', 'cat', 'unicorn'],
- default: 'dog',
- },
- multiString: {
- type: 'string',
- choices: ['dog', 'cat', 'unicorn'],
- default: ['dog', 'cat'],
- isMultiple: true,
- },
- },
- });
- });
-});
-
-test('choices - throws when default does not only include valid choices', t => {
- t.throws(() => {
- meow({
- importMeta,
- flags: {
- number: {
- type: 'number',
- choices: [1, 2, 3],
- default: 8,
- },
- string: {
- type: 'string',
- choices: ['dog', 'cat'],
- default: 'unicorn',
- },
- multiString: {
- type: 'string',
- choices: ['dog', 'cat'],
- default: ['dog', 'unicorn'],
- isMultiple: true,
- },
- },
- });
- }, {message: 'Each value of the option `default` must exist within the option `choices`. Invalid flags: `--number`, `--string`, `--multiString`'});
-});
diff --git a/test/errors.js b/test/errors.js
deleted file mode 100644
index dd28aac..0000000
--- a/test/errors.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import test from 'ava';
-import meow from '../source/index.js';
-import {stripIndent} from './_utils.js';
-
-const importMeta = import.meta;
-
-/**
-A convenience-wrapper around `t.throws` with `meow`.
-
-@param {import('../index').Options} options `meow` options with `importMeta` set
-@param {string} message The thrown error message. Strips indentation, so template literals can be used.
-*/
-const meowThrows = test.macro((t, options, {message}) => {
- options = {
- importMeta,
- ...options,
- };
-
- message = stripIndent(message);
-
- t.throws(() => meow(options), {message});
-});
-
-test('invalid package url', meowThrows, {
- importMeta: '/path/to/package',
-}, {message: 'The `importMeta` option is required. Its value must be `import.meta`.'});
-
-test('supports `number` flag type - throws on incorrect default value', meowThrows, {
- argv: [],
- flags: {
- foo: {
- type: 'number',
- default: 'x',
- },
- },
-}, {message: 'Expected "foo" default value to be of type "number", got "string"'});
-
-test('flag declared in kebab-case is an error', meowThrows, {
- flags: {'kebab-case': 'boolean', test: 'boolean', 'another-one': 'boolean'},
-}, {message: 'Flag keys may not contain \'-\'. Invalid flags: `kebab-case`, `another-one`'});
-
-test('single flag set more than once is an error', meowThrows, {
- argv: ['--foo=bar', '--foo=baz'],
- flags: {
- foo: {
- type: 'string',
- },
- },
-}, {message: 'The flag --foo can only be set once.'});
-
-test('suggests renaming alias to shortFlag', meowThrows, {
- flags: {
- foo: {
- type: 'string',
- alias: 'f',
- },
- bar: {
- type: 'string',
- alias: 'b',
- },
- baz: {
- type: 'string',
- shortFlag: 'z',
- },
- },
-}, {message: 'The option `alias` has been renamed to `shortFlag`. The following flags need to be updated: `--foo`, `--bar`'});
-
-test('options - multiple validation errors', meowThrows, {
- flags: {
- animal: {
- type: 'string',
- choices: 'cat',
- },
- plant: {
- type: 'string',
- alias: 'p',
- },
- 'some-thing': {
- type: 'string',
- },
- },
-}, {message: `
- Flag keys may not contain '-'. Invalid flags: \`some-thing\`
- The option \`alias\` has been renamed to \`shortFlag\`. The following flags need to be updated: \`--plant\`
- The option \`choices\` must be an array. Invalid flags: \`--animal\`
-`});
diff --git a/test/fixtures/help/fixture.js b/test/fixtures/help/fixture.js
new file mode 100755
index 0000000..54dbf9a
--- /dev/null
+++ b/test/fixtures/help/fixture.js
@@ -0,0 +1,21 @@
+#!/usr/bin/env node
+import meow from '../../../source/index.js';
+
+const cli = meow({
+ importMeta: import.meta,
+ help: 'foo',
+ flags: {
+ showHelp: {type: 'boolean'},
+ code: {type: 'number'},
+ },
+});
+
+const {code} = cli.flags;
+
+if (cli.flags.showHelp) {
+ if (code !== undefined) {
+ cli.showHelp(code);
+ }
+
+ cli.showHelp();
+}
diff --git a/test/fixtures/help/package.json b/test/fixtures/help/package.json
new file mode 100644
index 0000000..7f75ec9
--- /dev/null
+++ b/test/fixtures/help/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "foo",
+ "version": "1.0.0",
+ "bin": "./fixture.js",
+ "type": "module"
+}
diff --git a/test/fixtures/version/fixture.js b/test/fixtures/version/fixture.js
new file mode 100755
index 0000000..b28b332
--- /dev/null
+++ b/test/fixtures/version/fixture.js
@@ -0,0 +1,24 @@
+#!/usr/bin/env node
+import process from 'node:process';
+import meow from '../../../source/index.js';
+
+const version = process.env.VERSION === 'false' ? false : process.env.VERSION;
+
+const options = {
+ importMeta: import.meta,
+ version,
+ autoVersion: !process.argv.includes('--no-auto-version'),
+ flags: {
+ showVersion: {type: 'boolean'},
+ },
+};
+
+if (options.version === undefined) {
+ delete options.version;
+}
+
+const cli = meow(options);
+
+if (cli.flags.showVersion) {
+ cli.showVersion();
+}
diff --git a/test/fixtures/version/package.json b/test/fixtures/version/package.json
new file mode 100644
index 0000000..7f75ec9
--- /dev/null
+++ b/test/fixtures/version/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "foo",
+ "version": "1.0.0",
+ "bin": "./fixture.js",
+ "type": "module"
+}
diff --git a/test/fixtures/with-package-json/fixture.js b/test/fixtures/with-package-json/custom-bin/fixture.js
similarity index 50%
rename from test/fixtures/with-package-json/fixture.js
rename to test/fixtures/with-package-json/custom-bin/fixture.js
index b68255c..a2fa84f 100755
--- a/test/fixtures/with-package-json/fixture.js
+++ b/test/fixtures/with-package-json/custom-bin/fixture.js
@@ -1,9 +1,6 @@
#!/usr/bin/env node
import process from 'node:process';
-import meow from '../../../source/index.js';
-
-meow({
- importMeta: import.meta,
-});
+import meow from '../../../../source/index.js';
+meow({importMeta: import.meta});
console.log(process.title);
diff --git a/test/fixtures/with-package-json/custom-bin/package.json b/test/fixtures/with-package-json/custom-bin/package.json
new file mode 100644
index 0000000..7a04580
--- /dev/null
+++ b/test/fixtures/with-package-json/custom-bin/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "foo",
+ "bin": {
+ "bar": "./fixture.js"
+ },
+ "type": "module"
+}
diff --git a/test/fixtures/with-package-json/default/fixture.js b/test/fixtures/with-package-json/default/fixture.js
new file mode 100755
index 0000000..a2fa84f
--- /dev/null
+++ b/test/fixtures/with-package-json/default/fixture.js
@@ -0,0 +1,6 @@
+#!/usr/bin/env node
+import process from 'node:process';
+import meow from '../../../../source/index.js';
+
+meow({importMeta: import.meta});
+console.log(process.title);
diff --git a/test/fixtures/with-package-json/package.json b/test/fixtures/with-package-json/default/package.json
similarity index 100%
rename from test/fixtures/with-package-json/package.json
rename to test/fixtures/with-package-json/default/package.json
diff --git a/test/fixtures/with-package-json/no-bin/fixture.js b/test/fixtures/with-package-json/no-bin/fixture.js
new file mode 100755
index 0000000..a2fa84f
--- /dev/null
+++ b/test/fixtures/with-package-json/no-bin/fixture.js
@@ -0,0 +1,6 @@
+#!/usr/bin/env node
+import process from 'node:process';
+import meow from '../../../../source/index.js';
+
+meow({importMeta: import.meta});
+console.log(process.title);
diff --git a/test/fixtures/with-package-json/no-bin/package.json b/test/fixtures/with-package-json/no-bin/package.json
new file mode 100644
index 0000000..b55415b
--- /dev/null
+++ b/test/fixtures/with-package-json/no-bin/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "foo",
+ "type": "module"
+}
diff --git a/test/flags/_utils.js b/test/flags/_utils.js
new file mode 100644
index 0000000..dde13de
--- /dev/null
+++ b/test/flags/_utils.js
@@ -0,0 +1,31 @@
+/* eslint-disable ava/no-ignored-test-files */
+import test from 'ava';
+import meow from '../../source/index.js';
+
+export const _verifyFlags = importMeta => test.macro(async (t, {flags = {}, args, expected, error}) => {
+ const assertions = await t.try(async tt => {
+ const arguments_ = args?.split(' ') ?? [];
+ const meowOptions = {importMeta, argv: arguments_, flags};
+
+ tt.log('arguments:', arguments_);
+
+ if (error) {
+ tt.throws(() => meow(meowOptions), {
+ message(message) {
+ tt.log('error:\n', message);
+ return tt.is(message, error);
+ },
+ });
+ } else {
+ const cli = meow(meowOptions);
+
+ if (expected) {
+ tt.like(cli.flags, expected);
+ } else {
+ tt.pass();
+ }
+ }
+ });
+
+ assertions.commit({retainLogs: !assertions.passed});
+});
diff --git a/test/aliases.js b/test/flags/aliases.js
similarity index 67%
rename from test/aliases.js
rename to test/flags/aliases.js
index 39972ad..3e362e6 100644
--- a/test/aliases.js
+++ b/test/flags/aliases.js
@@ -1,10 +1,10 @@
import test from 'ava';
-import meow from '../source/index.js';
+import meow from '../../source/index.js';
const importMeta = import.meta;
-test('aliases - accepts one', t => {
- t.deepEqual(meow({
+test('accepts one', t => {
+ const cli = meow({
importMeta,
argv: ['--foo=baz'],
flags: {
@@ -13,13 +13,15 @@ test('aliases - accepts one', t => {
aliases: ['foo'],
},
},
- }).flags, {
+ });
+
+ t.like(cli.flags, {
fooBar: 'baz',
});
});
-test('aliases - accepts multiple', t => {
- t.deepEqual(meow({
+test('accepts multiple', t => {
+ const cli = meow({
importMeta,
argv: ['--foo=baz1', '--bar=baz2'],
flags: {
@@ -29,13 +31,15 @@ test('aliases - accepts multiple', t => {
isMultiple: true,
},
},
- }).flags, {
+ });
+
+ t.like(cli.flags, {
fooBar: ['baz1', 'baz2'],
});
});
-test('aliases - can be a short flag', t => {
- t.deepEqual(meow({
+test('can be a short flag', t => {
+ const cli = meow({
importMeta,
argv: ['--f=baz'],
flags: {
@@ -44,13 +48,15 @@ test('aliases - can be a short flag', t => {
aliases: ['f'],
},
},
- }).flags, {
+ });
+
+ t.like(cli.flags, {
fooBar: 'baz',
});
});
-test('aliases - works with short flag', t => {
- t.deepEqual(meow({
+test('works with short flag', t => {
+ const cli = meow({
importMeta,
argv: ['--foo=baz1', '--bar=baz2', '-f=baz3'],
flags: {
@@ -61,13 +67,15 @@ test('aliases - works with short flag', t => {
isMultiple: true,
},
},
- }).flags, {
+ });
+
+ t.like(cli.flags, {
fooBar: ['baz1', 'baz2', 'baz3'],
});
});
-test('aliases - unnormalized flags', t => {
- t.deepEqual(meow({
+test('unnormalized flags', t => {
+ const cli = meow({
importMeta,
argv: ['--foo=baz'],
flags: {
@@ -77,7 +85,9 @@ test('aliases - unnormalized flags', t => {
shortFlag: 'f',
},
},
- }).unnormalizedFlags, {
+ });
+
+ t.like(cli.unnormalizedFlags, {
fooBar: 'baz',
foo: 'baz',
f: 'baz',
diff --git a/test/flags/allow-unknown-flags.js b/test/flags/allow-unknown-flags.js
new file mode 100644
index 0000000..4a459ba
--- /dev/null
+++ b/test/flags/allow-unknown-flags.js
@@ -0,0 +1,63 @@
+import test from 'ava';
+import indentString from 'indent-string';
+import {_verifyCli, stripIndentTrim, meowVersion} from '../_utils.js';
+
+const fixtureFolder = 'allow-unknown-flags';
+
+const allowUnknownFlags = `${fixtureFolder}/fixture.js`;
+const allowUnknownFlagsWithHelp = `${fixtureFolder}/fixture-with-help.js`;
+
+const verifyFlags = _verifyCli(allowUnknownFlags);
+
+test('specifying unknown flags', verifyFlags, {
+ args: '--foo bar --unspecified-a --unspecified-b input-is-allowed',
+ error: stripIndentTrim`
+ Unknown flags
+ --unspecified-a
+ --unspecified-b
+ `,
+
+});
+
+test('specifying known flags', verifyFlags, {
+ args: '--foo bar',
+ expected: 'bar',
+});
+
+test('help as a known flag', verifyFlags, {
+ args: '--help',
+ expected: indentString('\nCustom description\n\nUsage\n foo \n\n', 2),
+});
+
+test('version as a known flag', verifyFlags, {
+ args: '--version',
+ expected: meowVersion,
+});
+
+test('help as an unknown flag', verifyFlags, {
+ args: '--help --no-auto-help',
+ error: stripIndentTrim`
+ Unknown flag
+ --help
+ `,
+});
+
+test('version as an unknown flag', verifyFlags, {
+ args: '--version --no-auto-version',
+ error: stripIndentTrim`
+ Unknown flag
+ --version
+ `,
+});
+
+test('help with custom config', verifyFlags, {
+ fixture: allowUnknownFlagsWithHelp,
+ args: '-h',
+ expected: indentString('\nCustom description\n\nUsage\n foo \n\n', 2),
+});
+
+test('version with custom config', verifyFlags, {
+ fixture: allowUnknownFlagsWithHelp,
+ args: '-v',
+ expected: meowVersion,
+});
diff --git a/test/flags/boolean-default.js b/test/flags/boolean-default.js
new file mode 100644
index 0000000..70747b4
--- /dev/null
+++ b/test/flags/boolean-default.js
@@ -0,0 +1,55 @@
+import test from 'ava';
+import meow from '../../source/index.js';
+
+const importMeta = import.meta;
+
+test('undefined - filter out unset boolean args', t => {
+ const cli = meow({
+ importMeta,
+ argv: ['--foo'],
+ booleanDefault: undefined,
+ flags: {
+ foo: {
+ type: 'boolean',
+ },
+ bar: {
+ type: 'boolean',
+ },
+ baz: {
+ type: 'boolean',
+ default: false,
+ },
+ },
+ });
+
+ t.like(cli.flags, {
+ foo: true,
+ bar: undefined,
+ baz: false,
+ });
+});
+
+test('boolean args are false by default', t => {
+ const cli = meow({
+ importMeta,
+ argv: ['--foo'],
+ flags: {
+ foo: {
+ type: 'boolean',
+ },
+ bar: {
+ type: 'boolean',
+ default: true,
+ },
+ baz: {
+ type: 'boolean',
+ },
+ },
+ });
+
+ t.like(cli.flags, {
+ foo: true,
+ bar: true,
+ baz: false,
+ });
+});
diff --git a/test/flags/choices.js b/test/flags/choices.js
new file mode 100644
index 0000000..f210c2c
--- /dev/null
+++ b/test/flags/choices.js
@@ -0,0 +1,187 @@
+import test from 'ava';
+import {oneLine} from 'common-tags';
+import {stripIndentTrim} from '../_utils.js';
+import {_verifyFlags} from './_utils.js';
+
+const verifyChoices = _verifyFlags(import.meta);
+
+test('success case', verifyChoices, {
+ flags: {
+ animal: {
+ choices: ['dog', 'cat', 'unicorn'],
+ },
+ number: {
+ type: 'number',
+ choices: [1.1, 2.2, 3.3],
+ },
+ },
+ args: '--animal cat --number=2.2',
+ expected: {
+ animal: 'cat',
+ number: 2.2,
+ },
+});
+
+test('throws if input does not match choices', verifyChoices, {
+ flags: {
+ animal: {
+ choices: ['dog', 'cat', 'unicorn'],
+ },
+ number: {
+ choices: [1, 2, 3],
+ },
+ },
+ args: '--animal rainbow --number 5',
+ error: stripIndentTrim`
+ Unknown value for flag \`--animal\`: \`rainbow\`. Value must be one of: [\`dog\`, \`cat\`, \`unicorn\`]
+ Unknown value for flag \`--number\`: \`5\`. Value must be one of: [\`1\`, \`2\`, \`3\`]
+ `,
+});
+
+test('throws if choices is not array', verifyChoices, {
+ flags: {
+ animal: {
+ choices: 'cat',
+ },
+ },
+ args: '--animal cat',
+ error: 'The option `choices` must be an array. Invalid flags: `--animal`',
+});
+
+test('does not throw error when isRequired is false', verifyChoices, {
+ flags: {
+ animal: {
+ isRequired: false,
+ choices: ['dog', 'cat', 'unicorn'],
+ },
+ },
+});
+
+test('throw error when isRequired is true', verifyChoices, {
+ flags: {
+ animal: {
+ isRequired: true,
+ choices: ['dog', 'cat', 'unicorn'],
+ },
+ },
+ error: 'Flag `--animal` has no value. Value must be one of: [`dog`, `cat`, `unicorn`]',
+});
+
+test('success with isMultiple', verifyChoices, {
+ flags: {
+ animal: {
+ type: 'string',
+ isMultiple: true,
+ choices: ['dog', 'cat', 'unicorn'],
+ },
+ },
+ args: '--animal=dog --animal=unicorn',
+ expected: {
+ animal: ['dog', 'unicorn'],
+ },
+});
+
+test('throws with isMultiple, one unknown value', verifyChoices, {
+ flags: {
+ animal: {
+ type: 'string',
+ isMultiple: true,
+ choices: ['dog', 'cat', 'unicorn'],
+ },
+ },
+ args: '--animal=dog --animal=rabbit',
+ error: 'Unknown value for flag `--animal`: `rabbit`. Value must be one of: [`dog`, `cat`, `unicorn`]',
+});
+
+test('throws with isMultiple, multiple unknown value', verifyChoices, {
+ flags: {
+ animal: {
+ type: 'string',
+ isMultiple: true,
+ choices: ['cat', 'unicorn'],
+ },
+ },
+ args: '--animal=dog --animal=rabbit',
+ error: 'Unknown values for flag `--animal`: `dog`, `rabbit`. Value must be one of: [`cat`, `unicorn`]',
+});
+
+test('throws with multiple flags', verifyChoices, {
+ flags: {
+ animal: {
+ type: 'string',
+ choices: ['cat', 'unicorn'],
+ },
+ plant: {
+ type: 'string',
+ choices: ['tree', 'flower'],
+ },
+ },
+ args: '--animal=dog --plant=succulent',
+ error: stripIndentTrim`
+ Unknown value for flag \`--animal\`: \`dog\`. Value must be one of: [\`cat\`, \`unicorn\`]
+ Unknown value for flag \`--plant\`: \`succulent\`. Value must be one of: [\`tree\`, \`flower\`]
+ `,
+});
+
+test('choices must be of the same type', verifyChoices, {
+ flags: {
+ number: {
+ type: 'number',
+ choices: [1, '2'],
+ },
+ boolean: {
+ type: 'boolean',
+ choices: [true, 'false'],
+ },
+ },
+ error: oneLine`
+ Each value of the option \`choices\` must be of the same type as its flag.
+ Invalid flags: (\`--number\`, type: 'number'), (\`--boolean\`, type: 'boolean')
+ `,
+});
+
+test('success when each value of default exist within the option choices', verifyChoices, {
+ flags: {
+ number: {
+ type: 'number',
+ choices: [1, 2, 3],
+ default: 1,
+ },
+ string: {
+ type: 'string',
+ choices: ['dog', 'cat', 'unicorn'],
+ default: 'dog',
+ },
+ multiString: {
+ type: 'string',
+ choices: ['dog', 'cat', 'unicorn'],
+ default: ['dog', 'cat'],
+ isMultiple: true,
+ },
+ },
+});
+
+test('throws when default does not only include valid choices', verifyChoices, {
+ flags: {
+ number: {
+ type: 'number',
+ choices: [1, 2, 3],
+ default: 8,
+ },
+ string: {
+ type: 'string',
+ choices: ['dog', 'cat'],
+ default: 'unicorn',
+ },
+ multiString: {
+ type: 'string',
+ choices: ['dog', 'cat'],
+ default: ['dog', 'unicorn'],
+ isMultiple: true,
+ },
+ },
+ error: oneLine`
+ Each value of the option \`default\` must exist within the option \`choices\`.
+ Invalid flags: \`--number\`, \`--string\`, \`--multiString\`
+ `,
+});
diff --git a/test/flags/is-multiple.js b/test/flags/is-multiple.js
new file mode 100644
index 0000000..801affe
--- /dev/null
+++ b/test/flags/is-multiple.js
@@ -0,0 +1,205 @@
+import test from 'ava';
+import meow from '../../source/index.js';
+import {_verifyFlags} from './_utils.js';
+
+const importMeta = import.meta;
+const verifyFlags = _verifyFlags(importMeta);
+
+test('unset flag returns empty array', verifyFlags, {
+ flags: {
+ foo: {
+ type: 'string',
+ isMultiple: true,
+ },
+ },
+ expected: {
+ foo: [],
+ },
+});
+
+test('flag set once returns array', verifyFlags, {
+ flags: {
+ foo: {
+ type: 'string',
+ isMultiple: true,
+ },
+ },
+ args: '--foo=bar',
+ expected: {
+ foo: ['bar'],
+ },
+});
+
+test('flag set multiple times', verifyFlags, {
+ flags: {
+ foo: {
+ type: 'string',
+ isMultiple: true,
+ },
+ },
+ args: '--foo=bar --foo=baz',
+ expected: {
+ foo: ['bar', 'baz'],
+ },
+});
+
+test('flag with space separated values', t => {
+ const cli = meow({
+ importMeta,
+ argv: ['--foo', 'bar', 'baz'],
+ flags: {
+ foo: {
+ type: 'string',
+ isMultiple: true,
+ },
+ },
+ });
+
+ t.like(cli, {
+ input: ['baz'],
+ flags: {
+ foo: ['bar'],
+ },
+ });
+});
+
+test('does not support comma separated values', verifyFlags, {
+ flags: {
+ foo: {
+ type: 'string',
+ isMultiple: true,
+ },
+ },
+ args: '--foo bar,baz',
+ expected: {
+ foo: ['bar,baz'],
+ },
+});
+
+test('default to type string', verifyFlags, {
+ flags: {
+ foo: {
+ isMultiple: true,
+ },
+ },
+ args: '--foo=bar',
+ expected: {
+ foo: ['bar'],
+ },
+});
+
+test('boolean flag', verifyFlags, {
+ flags: {
+ foo: {
+ type: 'boolean',
+ isMultiple: true,
+ },
+ },
+ args: '--foo --foo=false',
+ expected: {
+ foo: [true, false],
+ },
+});
+
+test('boolean flag is false by default', verifyFlags, {
+ flags: {
+ foo: {
+ type: 'boolean',
+ isMultiple: true,
+ },
+ },
+ expected: {
+ foo: [false],
+ },
+});
+
+test('number flag', verifyFlags, {
+ flags: {
+ foo: {
+ type: 'number',
+ isMultiple: true,
+ },
+ },
+ args: '--foo=1.3 --foo=-1',
+ expected: {
+ foo: [1.3, -1],
+ },
+});
+
+test('flag default values', verifyFlags, {
+ flags: {
+ string: {
+ type: 'string',
+ isMultiple: true,
+ default: ['foo'],
+ },
+ boolean: {
+ type: 'boolean',
+ isMultiple: true,
+ default: [true],
+ },
+ number: {
+ type: 'number',
+ isMultiple: true,
+ default: [0.5],
+ },
+ },
+ expected: {
+ string: ['foo'],
+ boolean: [true],
+ number: [0.5],
+ },
+});
+
+test('multiple flag default values', verifyFlags, {
+ flags: {
+ string: {
+ type: 'string',
+ isMultiple: true,
+ default: ['foo', 'bar'],
+ },
+ boolean: {
+ type: 'boolean',
+ isMultiple: true,
+ default: [true, false],
+ },
+ number: {
+ type: 'number',
+ isMultiple: true,
+ default: [0.5, 1],
+ },
+ },
+ expected: {
+ string: ['foo', 'bar'],
+ boolean: [true, false],
+ number: [0.5, 1],
+ },
+});
+
+// Happened in production 2020-05-10: https://github.com/sindresorhus/meow/pull/143#issuecomment-626287226
+test('handles multi-word flag name', verifyFlags, {
+ flags: {
+ fooBar: {
+ type: 'string',
+ isMultiple: true,
+ },
+ },
+ args: '--foo-bar=baz',
+ expected: {
+ fooBar: ['baz'],
+ },
+});
+
+test('works with short flags', verifyFlags, {
+ flags: {
+ foo: {
+ type: 'string',
+ shortFlag: 'f',
+ isMultiple: true,
+ },
+ },
+ args: '-f bar -f baz',
+ expected: {
+ foo: ['bar', 'baz'],
+ },
+});
diff --git a/test/flags/is-required.js b/test/flags/is-required.js
new file mode 100644
index 0000000..1810585
--- /dev/null
+++ b/test/flags/is-required.js
@@ -0,0 +1,108 @@
+import test from 'ava';
+import {_verifyCli, stripIndentTrim} from '../_utils.js';
+
+const fixtureFolder = 'required';
+
+const required = `${fixtureFolder}/fixture.js`;
+const requiredFunction = `${fixtureFolder}/fixture-required-function.js`;
+const requiredMultiple = `${fixtureFolder}/fixture-required-multiple.js`;
+const conditionalRequiredMultiple = `${fixtureFolder}/fixture-conditional-required-multiple.js`;
+
+const verifyFlags = _verifyCli(required);
+
+test('not specifying required flags', verifyFlags, {
+ error: stripIndentTrim`
+ Missing required flags
+ --test, -t
+ --number
+ --kebab-case
+ `,
+});
+
+test('specifying all required flags', verifyFlags, {
+ args: '--test test --number 6 --kebab-case test',
+ expected: 'test,6',
+});
+
+test('specifying required string flag with an empty string as value', verifyFlags, {
+ args: '--test ',
+ error: stripIndentTrim`
+ Missing required flags
+ --number
+ --kebab-case
+ `,
+});
+
+test('specifying required number flag without a number', verifyFlags, {
+ args: '--number',
+ error: stripIndentTrim`
+ Missing required flags
+ --test, -t
+ --number
+ --kebab-case
+ `,
+});
+
+test('setting isRequired as a function and not specifying any flags', verifyFlags, {
+ fixture: requiredFunction,
+ expected: 'false,undefined',
+});
+
+test('setting isRequired as a function and specifying only the flag that activates the isRequired condition for the other flag', verifyFlags, {
+ fixture: requiredFunction,
+ args: '--trigger',
+ error: stripIndentTrim`
+ Missing required flag
+ --with-trigger
+ `,
+});
+
+test('setting isRequired as a function and specifying both the flags', verifyFlags, {
+ fixture: requiredFunction,
+ args: '--trigger --withTrigger specified',
+ expected: 'true,specified',
+});
+
+test('setting isRequired as a function and check if returning a non-boolean value throws an error', verifyFlags, {
+ fixture: requiredFunction,
+ args: '--allowError --shouldError specified',
+ error: {
+ clean: true,
+ message: 'TypeError: Return value for isRequired callback should be of type boolean, but string was returned.',
+ code: 1,
+ },
+});
+
+test('isRequired with isMultiple giving a single value', verifyFlags, {
+ fixture: requiredMultiple,
+ args: '--test 1',
+ expected: '[ 1 ]',
+});
+
+test('isRequired with isMultiple giving multiple values', verifyFlags, {
+ fixture: requiredMultiple,
+ args: '--test 1 --test 2',
+ expected: '[ 1, 2 ]',
+});
+
+test('isRequired with isMultiple giving no values, but flag is given', verifyFlags, {
+ fixture: requiredMultiple,
+ args: '--test',
+ error: stripIndentTrim`
+ Missing required flag
+ --test, -t
+ `,
+});
+
+test('isRequired with isMultiple giving no values, but flag is not given', verifyFlags, {
+ fixture: requiredMultiple,
+ error: stripIndentTrim`
+ Missing required flag
+ --test, -t
+ `,
+});
+
+test('isRequire function that returns false with isMultiple given no values, but flag is not given', verifyFlags, {
+ fixture: conditionalRequiredMultiple,
+ expected: '[]',
+});
diff --git a/test/flags/short-flag.js b/test/flags/short-flag.js
new file mode 100644
index 0000000..311de00
--- /dev/null
+++ b/test/flags/short-flag.js
@@ -0,0 +1,78 @@
+import test from 'ava';
+import meow from '../../source/index.js';
+import {_verifyFlags} from './_utils.js';
+
+const importMeta = import.meta;
+const verifyFlags = _verifyFlags(importMeta);
+
+test('can be used in place of long flags', t => {
+ const cli = meow({
+ importMeta,
+ argv: ['-f'],
+ flags: {
+ foo: {
+ type: 'boolean',
+ shortFlag: 'f',
+ },
+ },
+ });
+
+ t.like(cli.flags, {
+ foo: true,
+ f: undefined,
+ });
+
+ t.like(cli.unnormalizedFlags, {
+ foo: true,
+ f: true,
+ });
+});
+
+test('grouped flags work', t => {
+ const cli = meow({
+ importMeta,
+ argv: ['-cl'],
+ flags: {
+ coco: {
+ type: 'boolean',
+ shortFlag: 'c',
+ },
+ loco: {
+ type: 'boolean',
+ shortFlag: 'l',
+ },
+ },
+ });
+
+ t.like(cli.flags, {
+ coco: true,
+ loco: true,
+ c: undefined,
+ l: undefined,
+ });
+
+ t.like(cli.unnormalizedFlags, {
+ coco: true,
+ loco: true,
+ c: true,
+ l: true,
+ });
+});
+
+test('suggests renaming alias to shortFlag', verifyFlags, {
+ flags: {
+ foo: {
+ type: 'string',
+ alias: 'f',
+ },
+ bar: {
+ type: 'string',
+ alias: 'b',
+ },
+ baz: {
+ type: 'string',
+ shortFlag: 'z',
+ },
+ },
+ error: 'The option `alias` has been renamed to `shortFlag`. The following flags need to be updated: `--foo`, `--bar`',
+});
diff --git a/test/flags/test.js b/test/flags/test.js
new file mode 100644
index 0000000..ae57a1b
--- /dev/null
+++ b/test/flags/test.js
@@ -0,0 +1,125 @@
+import test from 'ava';
+import {stripIndentTrim} from '../_utils.js';
+import {_verifyFlags} from './_utils.js';
+
+const verifyFlags = _verifyFlags(import.meta);
+
+test('flag types', verifyFlags, {
+ flags: {
+ foo: {type: 'string'},
+ bar: {type: 'number'},
+ baz: {type: 'boolean'},
+ },
+ args: '--foo=bar --bar=1.3 --baz=false',
+ expected: {
+ foo: 'bar',
+ bar: 1.3,
+ baz: false,
+ },
+});
+
+test('supports negation via --no', verifyFlags, {
+ flags: {
+ foo: {
+ type: 'boolean',
+ default: true,
+ },
+ },
+ args: '--no-foo',
+ expected: {
+ foo: false,
+ },
+});
+
+test('throws if default value is not of the correct type', verifyFlags, {
+ flags: {
+ foo: {
+ type: 'number',
+ default: 'x',
+ },
+ },
+ error: 'Expected "foo" default value to be of type "number", got "string"',
+});
+
+test('flag, no value', verifyFlags, {
+ flags: {
+ foo: {type: 'string'},
+ bar: {type: 'number'},
+ },
+ args: '--foo --bar',
+ expected: {
+ foo: '',
+ bar: undefined,
+ },
+});
+
+test('default - flag, no value', verifyFlags, {
+ flags: {
+ foo: {type: 'string', default: 'bar'},
+ bar: {type: 'number', default: 1.3},
+ },
+ args: '--foo --bar',
+ expected: {
+ foo: 'bar',
+ bar: 1.3,
+ },
+});
+
+test('default - no flag', verifyFlags, {
+ flags: {
+ foo: {type: 'string', default: 'bar'},
+ bar: {type: 'number', default: 1.3},
+ },
+ args: '',
+ expected: {
+ foo: 'bar',
+ bar: 1.3,
+ },
+});
+
+test('single character flag casing should be preserved', verifyFlags, {
+ args: '-F',
+ expected: {
+ F: true,
+ },
+});
+
+test('flag declared in kebab-case is an error', verifyFlags, {
+ flags: {
+ 'kebab-case': {type: 'boolean'},
+ test: {type: 'boolean'},
+ 'another-one': {type: 'boolean'},
+ },
+ error: 'Flag keys may not contain \'-\'. Invalid flags: `kebab-case`, `another-one`',
+});
+
+test('single flag set more than once is an error', verifyFlags, {
+ flags: {
+ foo: {
+ type: 'string',
+ },
+ },
+ args: '--foo=bar --foo=baz',
+ error: 'The flag --foo can only be set once.',
+});
+
+test('options - multiple validation errors', verifyFlags, {
+ flags: {
+ animal: {
+ type: 'string',
+ choices: 'cat',
+ },
+ plant: {
+ type: 'string',
+ alias: 'p',
+ },
+ 'some-thing': {
+ type: 'string',
+ },
+ },
+ error: stripIndentTrim`
+ Flag keys may not contain '-'. Invalid flags: \`some-thing\`
+ The option \`alias\` has been renamed to \`shortFlag\`. The following flags need to be updated: \`--plant\`
+ The option \`choices\` must be an array. Invalid flags: \`--animal\`
+ `,
+});
diff --git a/test/help.js b/test/help.js
deleted file mode 100644
index c361c0a..0000000
--- a/test/help.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import test from 'ava';
-import indentString from 'indent-string';
-import meow from '../source/index.js';
-import {spawnFixture} from './_utils.js';
-
-const importMeta = import.meta;
-
-test('support help shortcut', t => {
- t.is(meow(`
- unicorn
- cat
- `, {
- importMeta,
- }).help, indentString('\nCLI app helper\n\nunicorn\ncat\n', 2));
-});
-
-test('spawn cli and show help screen', async t => {
- const {stdout} = await spawnFixture(['--help']);
- t.is(stdout, indentString('\nCustom description\n\nUsage\n foo \n\n', 2));
-});
-
-test('spawn cli and disabled autoHelp', async t => {
- const {stdout} = await spawnFixture(['--help', '--no-auto-help']);
- t.is(stdout, 'help\nautoHelp\nmeow\ncamelCaseOption');
-});
-
-test('spawn cli and not show help', async t => {
- const {stdout} = await spawnFixture(['--help=all']);
- t.is(stdout, 'help\nmeow\ncamelCaseOption');
-});
-
-test('single line help messages are not indented', t => {
- t.is(meow({
- importMeta,
- description: false,
- help: 'single line',
- }).help, '\nsingle line\n');
-});
-
-test('descriptions with no help are not indented', t => {
- t.is(meow({
- importMeta,
- help: false,
- description: 'single line',
- }).help, '\nsingle line\n');
-});
-
-test('support help shortcut with no indentation', t => {
- t.is(meow(`
- unicorn
- cat
- `, {
- helpIndent: 0,
- importMeta,
- }).help, indentString('\nCLI app helper\n\nunicorn\ncat\n', 0));
-});
-
-test('no description and no indentation', t => {
- t.is(meow(`
- unicorn
- cat
- `, {
- helpIndent: 0,
- description: false,
- importMeta,
- }).help, indentString('\nunicorn\ncat\n', 0));
-});
diff --git a/test/is-multiple.js b/test/is-multiple.js
deleted file mode 100644
index 4b5469d..0000000
--- a/test/is-multiple.js
+++ /dev/null
@@ -1,211 +0,0 @@
-import test from 'ava';
-import meow from '../source/index.js';
-
-const importMeta = import.meta;
-
-test('isMultiple - unset flag returns empty array', t => {
- t.deepEqual(meow({
- importMeta,
- argv: [],
- flags: {
- foo: {
- type: 'string',
- isMultiple: true,
- },
- },
- }).flags, {
- foo: [],
- });
-});
-
-test('isMultiple - flag set once returns array', t => {
- t.deepEqual(meow({
- importMeta,
- argv: ['--foo=bar'],
- flags: {
- foo: {
- type: 'string',
- isMultiple: true,
- },
- },
- }).flags, {
- foo: ['bar'],
- });
-});
-
-test('isMultiple - flag set multiple times', t => {
- t.deepEqual(meow({
- importMeta,
- argv: ['--foo=bar', '--foo=baz'],
- flags: {
- foo: {
- type: 'string',
- isMultiple: true,
- },
- },
- }).flags, {
- foo: ['bar', 'baz'],
- });
-});
-
-test('isMultiple - flag with space separated values', t => {
- const {input, flags} = meow({
- importMeta,
- argv: ['--foo', 'bar', 'baz'],
- flags: {
- foo: {
- type: 'string',
- isMultiple: true,
- },
- },
- });
-
- t.deepEqual(input, ['baz']);
- t.deepEqual(flags.foo, ['bar']);
-});
-
-test('isMultiple - flag with comma separated values', t => {
- t.deepEqual(meow({
- importMeta,
- argv: ['--foo', 'bar,baz'],
- flags: {
- foo: {
- type: 'string',
- isMultiple: true,
- },
- },
- }).flags, {
- foo: ['bar,baz'],
- });
-});
-
-test('isMultiple - default to type string', t => {
- t.deepEqual(meow({
- importMeta,
- argv: ['--foo=bar'],
- flags: {
- foo: {
- isMultiple: true,
- },
- },
- }).flags, {
- foo: ['bar'],
- });
-});
-
-test('isMultiple - boolean flag', t => {
- t.deepEqual(meow({
- importMeta,
- argv: ['--foo', '--foo=false'],
- flags: {
- foo: {
- type: 'boolean',
- isMultiple: true,
- },
- },
- }).flags, {
- foo: [true, false],
- });
-});
-
-test('isMultiple - boolean flag is false by default', t => {
- t.deepEqual(meow({
- importMeta,
- argv: [],
- flags: {
- foo: {
- type: 'boolean',
- isMultiple: true,
- },
- },
- }).flags, {
- foo: [false],
- });
-});
-
-test('isMultiple - number flag', t => {
- t.deepEqual(meow({
- importMeta,
- argv: ['--foo=1.3', '--foo=-1'],
- flags: {
- foo: {
- type: 'number',
- isMultiple: true,
- },
- },
- }).flags, {
- foo: [1.3, -1],
- });
-});
-
-test('isMultiple - flag default values', t => {
- t.deepEqual(meow({
- importMeta,
- argv: [],
- flags: {
- string: {
- type: 'string',
- isMultiple: true,
- default: ['foo'],
- },
- boolean: {
- type: 'boolean',
- isMultiple: true,
- default: [true],
- },
- number: {
- type: 'number',
- isMultiple: true,
- default: [0.5],
- },
- },
- }).flags, {
- string: ['foo'],
- boolean: [true],
- number: [0.5],
- });
-});
-
-test('isMultiple - multiple flag default values', t => {
- t.deepEqual(meow({
- importMeta,
- argv: [],
- flags: {
- string: {
- type: 'string',
- isMultiple: true,
- default: ['foo', 'bar'],
- },
- boolean: {
- type: 'boolean',
- isMultiple: true,
- default: [true, false],
- },
- number: {
- type: 'number',
- isMultiple: true,
- default: [0.5, 1],
- },
- },
- }).flags, {
- string: ['foo', 'bar'],
- boolean: [true, false],
- number: [0.5, 1],
- });
-});
-
-// Happened in production 2020-05-10: https://github.com/sindresorhus/meow/pull/143#issuecomment-626287226
-test('isMultiple - handles multi-word flag name', t => {
- t.deepEqual(meow({
- importMeta,
- argv: ['--foo-bar=baz'],
- flags: {
- fooBar: {
- type: 'string',
- isMultiple: true,
- },
- },
- }).flags, {
- fooBar: ['baz'],
- });
-});
diff --git a/test/is-required-flag.js b/test/is-required-flag.js
deleted file mode 100644
index f83337d..0000000
--- a/test/is-required-flag.js
+++ /dev/null
@@ -1,111 +0,0 @@
-import test from 'ava';
-import {spawnFixture} from './_utils.js';
-
-const fixtureFolder = 'required';
-
-const required = `${fixtureFolder}/fixture.js`;
-const requiredFunction = `${fixtureFolder}/fixture-required-function.js`;
-const requiredMultiple = `${fixtureFolder}/fixture-required-multiple.js`;
-const conditionalRequiredMultiple = `${fixtureFolder}/fixture-conditional-required-multiple.js`;
-
-test('spawn cli and test not specifying required flags', async t => {
- const {stderr} = await t.throwsAsync(
- spawnFixture(required),
- {message: /^Command failed with exit code 2/},
- );
-
- t.regex(stderr, /Missing required flag/);
- t.regex(stderr, /--test, -t/);
- t.regex(stderr, /--number/);
- t.regex(stderr, /--kebab-case/);
- t.notRegex(stderr, /--not-required/);
-});
-
-test('spawn cli and test specifying all required flags', async t => {
- const {stdout} = await spawnFixture(required, ['-t', 'test', '--number', '6', '--kebab-case', 'test']);
- t.is(stdout, 'test,6');
-});
-
-test('spawn cli and test specifying required string flag with an empty string as value', async t => {
- const {stderr} = await t.throwsAsync(
- spawnFixture(required, ['--test', '']),
- {message: /^Command failed with exit code 2/},
- );
-
- t.regex(stderr, /Missing required flag/);
- t.notRegex(stderr, /--test, -t/);
-});
-
-test('spawn cli and test specifying required number flag without a number', async t => {
- const {stderr} = await t.throwsAsync(
- spawnFixture(required, ['--number']),
- {message: /^Command failed with exit code 2/},
- );
-
- t.regex(stderr, /Missing required flag/);
- t.regex(stderr, /--number/);
-});
-
-test('spawn cli and test setting isRequired as a function and not specifying any flags', async t => {
- const {stdout} = await spawnFixture(requiredFunction);
- t.is(stdout, 'false,undefined');
-});
-
-test('spawn cli and test setting isRequired as a function and specifying only the flag that activates the isRequired condition for the other flag', async t => {
- const {stderr} = await t.throwsAsync(
- spawnFixture(requiredFunction, ['--trigger']),
- {message: /^Command failed with exit code 2/},
- );
-
- t.regex(stderr, /Missing required flag/);
- t.regex(stderr, /--with-trigger/);
-});
-
-test('spawn cli and test setting isRequired as a function and specifying both the flags', async t => {
- const {stdout} = await spawnFixture(requiredFunction, ['--trigger', '--withTrigger', 'specified']);
- t.is(stdout, 'true,specified');
-});
-
-test('spawn cli and test setting isRequired as a function and check if returning a non-boolean value throws an error', async t => {
- const {stderr} = await t.throwsAsync(
- spawnFixture(requiredFunction, ['--allowError', '--shouldError', 'specified']),
- {message: /^Command failed with exit code 1/},
- );
-
- t.regex(stderr, /Return value for isRequired callback should be of type boolean, but string was returned./);
-});
-
-test('spawn cli and test isRequired with isMultiple giving a single value', async t => {
- const {stdout} = await spawnFixture(requiredMultiple, ['--test', '1']);
- t.is(stdout, '[ 1 ]');
-});
-
-test('spawn cli and test isRequired with isMultiple giving multiple values', async t => {
- const {stdout} = await spawnFixture(requiredMultiple, ['--test', '1', '--test', '2']);
- t.is(stdout, '[ 1, 2 ]');
-});
-
-test('spawn cli and test isRequired with isMultiple giving no values, but flag is given', async t => {
- const {stderr} = await t.throwsAsync(
- spawnFixture(requiredMultiple, ['--test']),
- {message: /^Command failed with exit code 2/},
- );
-
- t.regex(stderr, /Missing required flag/);
- t.regex(stderr, /--test/);
-});
-
-test('spawn cli and test isRequired with isMultiple giving no values, but flag is not given', async t => {
- const {stderr} = await t.throwsAsync(
- spawnFixture(requiredMultiple),
- {message: /^Command failed with exit code 2/},
- );
-
- t.regex(stderr, /Missing required flag/);
- t.regex(stderr, /--test/);
-});
-
-test('spawn cli and test isRequire function that returns false with isMultiple given no values, but flag is not given', async t => {
- const {stdout} = await spawnFixture(conditionalRequiredMultiple);
- t.is(stdout, '[]');
-});
diff --git a/test/options/help.js b/test/options/help.js
new file mode 100644
index 0000000..1a1ad15
--- /dev/null
+++ b/test/options/help.js
@@ -0,0 +1,132 @@
+import test from 'ava';
+import indentString from 'indent-string';
+import meow from '../../source/index.js';
+import {_verifyCli, stripIndent, stripIndentTrim} from '../_utils.js';
+
+const importMeta = import.meta;
+
+const verifyCli = _verifyCli();
+
+const verifyHelp = test.macro(async (t, {cli: cliArguments, expected}) => {
+ const assertions = await t.try(async tt => {
+ const cli = Array.isArray(cliArguments)
+ ? meow(cliArguments.at(0), {importMeta, ...cliArguments.at(1)})
+ : meow({importMeta, ...cliArguments});
+
+ tt.log('help text:\n', cli.help);
+ tt.is(cli.help, expected);
+ });
+
+ assertions.commit({retainLogs: !assertions.passed});
+});
+
+test('support help shortcut', verifyHelp, {
+ cli: [`
+ unicorn
+ cat
+ `],
+ expected: indentString('\nCLI app helper\n\nunicorn\ncat\n', 2),
+});
+
+test('spawn cli and show help screen', verifyCli, {
+ args: '--help',
+ expected: indentString('\nCustom description\n\nUsage\n foo \n\n', 2),
+});
+
+test('spawn cli and disabled autoHelp', verifyCli, {
+ args: '--help --no-auto-help',
+ expected: stripIndentTrim`
+ help
+ autoHelp
+ meow
+ camelCaseOption
+ `,
+});
+
+test('spawn cli and not show help', verifyCli, {
+ args: '--help=all',
+ expected: stripIndentTrim`
+ help
+ meow
+ camelCaseOption
+ `,
+});
+
+test('single line help messages are not indented', verifyHelp, {
+ cli: {
+ description: false,
+ help: 'single line',
+ },
+ expected: stripIndent`
+
+ single line
+ `,
+});
+
+test('descriptions with no help are not indented', verifyHelp, {
+ cli: {
+ help: false,
+ description: 'single line',
+ },
+ expected: stripIndent`
+
+ single line
+ `,
+
+});
+
+test('support help shortcut with no indentation', verifyHelp, {
+ cli: [`
+ unicorn
+ cat
+ `, {
+ helpIndent: 0,
+ }],
+ expected: stripIndent`
+
+ CLI app helper
+
+ unicorn
+ cat
+ `,
+});
+
+test('no description and no indentation', verifyHelp, {
+ cli: [`
+ unicorn
+ cat
+ `, {
+ helpIndent: 0,
+ description: false,
+ }],
+ expected: stripIndent`
+
+ unicorn
+ cat
+ `,
+});
+
+test('exits with code 0 by default', verifyCli, {
+ args: '--help',
+});
+
+test('showHelp exits with code 2 by default', verifyCli, {
+ fixture: 'help/fixture.js',
+ args: '--show-help',
+ error: {
+ message: stripIndent`
+
+ foo
+ `,
+ code: 2,
+ },
+});
+
+test('showHelp exits with given code', verifyCli, {
+ fixture: 'help/fixture.js',
+ args: '--show-help --code=0',
+ expected: stripIndent`
+
+ foo
+ `,
+});
diff --git a/test/options/import-meta.js b/test/options/import-meta.js
new file mode 100644
index 0000000..421c3c1
--- /dev/null
+++ b/test/options/import-meta.js
@@ -0,0 +1,24 @@
+import test from 'ava';
+import meow from '../../source/index.js';
+
+test('main', t => {
+ t.notThrows(() => meow({importMeta: import.meta}));
+});
+
+test('with help shortcut', t => {
+ t.notThrows(() => (
+ meow(`
+ unicorns
+ rainbows
+ `, {
+ importMeta: import.meta,
+ })
+ ));
+});
+
+test('invalid package url', t => {
+ t.throws(
+ () => meow({importMeta: '/path/to/package'}),
+ {message: 'The `importMeta` option is required. Its value must be `import.meta`.'},
+ );
+});
diff --git a/test/options/infer-type.js b/test/options/infer-type.js
new file mode 100644
index 0000000..a96ffe5
--- /dev/null
+++ b/test/options/infer-type.js
@@ -0,0 +1,39 @@
+import test from 'ava';
+import meow from '../../source/index.js';
+
+const verifyTypeInference = test.macro((t, {cli: cliArguments, expected}) => {
+ const cli = meow({importMeta: import.meta, ...cliArguments});
+ t.like(cli.input, [expected]);
+});
+
+test('no inference by default', verifyTypeInference, {
+ cli: {
+ argv: ['5'],
+ },
+ expected: '5',
+});
+
+test('type inference', verifyTypeInference, {
+ cli: {
+ argv: ['5'],
+ inferType: true,
+ },
+ expected: 5,
+});
+
+test('with input type', verifyTypeInference, {
+ cli: {
+ argv: ['5'],
+ input: 'number',
+ },
+ expected: 5,
+});
+
+test('works with flags', verifyTypeInference, {
+ cli: {
+ argv: ['5'],
+ inferType: true,
+ flags: {foo: 'string'},
+ },
+ expected: 5,
+});
diff --git a/test/options/pkg.js b/test/options/pkg.js
new file mode 100644
index 0000000..3259a8f
--- /dev/null
+++ b/test/options/pkg.js
@@ -0,0 +1,57 @@
+import test from 'ava';
+import {_verifyCli, stripIndent} from '../_utils.js';
+import meow from '../../source/index.js';
+
+const importMeta = import.meta;
+
+const verifyPackage = _verifyCli('with-package-json/default/fixture.js');
+
+test('description', t => {
+ const cli = meow({
+ importMeta,
+ pkg: {
+ description: 'Unicorn and rainbow creator',
+ },
+ });
+
+ t.is(cli.help, stripIndent`
+
+ Unicorn and rainbow creator
+ `);
+});
+
+test.todo('version');
+
+test('overriding pkg still normalizes', t => {
+ const cli = meow({
+ importMeta,
+ pkg: {
+ name: 'browser-sync',
+ bin: './bin/browser-sync.js',
+ },
+ });
+
+ t.like(cli, {
+ pkg: {
+ name: 'browser-sync',
+ version: '',
+ },
+ });
+
+ // TODO: test that showVersion logs undefined
+});
+
+test('process title - bin default', verifyPackage, {
+ expected: 'foo',
+});
+
+test('process title - bin custom', verifyPackage, {
+ fixture: 'with-package-json/custom-bin/fixture.js',
+ expected: 'bar',
+});
+
+test('process title - name backup', verifyPackage, {
+ fixture: 'with-package-json/no-bin/fixture.js',
+ expected: 'foo',
+});
+
diff --git a/test/options/version.js b/test/options/version.js
new file mode 100644
index 0000000..29b5f28
--- /dev/null
+++ b/test/options/version.js
@@ -0,0 +1,47 @@
+import test from 'ava';
+import {_verifyCli, defaultFixture, stripIndentTrim} from '../_utils.js';
+
+const verifyVersion = _verifyCli('version/fixture.js');
+
+test('spawn cli and show version', verifyVersion, {
+ args: '--version',
+ expected: '1.0.0',
+});
+
+test('spawn cli and disabled autoVersion', verifyVersion, {
+ fixture: defaultFixture,
+ args: '--version --no-auto-version',
+ expected: stripIndentTrim`
+ version
+ autoVersion
+ meow
+ camelCaseOption
+ `,
+});
+
+test('spawn cli and not show version', verifyVersion, {
+ fixture: defaultFixture,
+ args: '--version=beta',
+ expected: stripIndentTrim`
+ version
+ meow
+ camelCaseOption
+ `,
+});
+
+test('custom version', verifyVersion, {
+ args: '--version',
+ execaOptions: {env: {VERSION: 'beta'}},
+ expected: 'beta',
+});
+
+test('version = false has no effect', verifyVersion, {
+ args: '--version',
+ execaOptions: {env: {VERSION: 'false'}},
+ expected: '1.0.0',
+});
+
+test('manual showVersion', verifyVersion, {
+ args: '--show-version',
+ expected: '1.0.0',
+});
diff --git a/test/test.js b/test/test.js
index 4d8c4ec..c589ad0 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1,14 +1,10 @@
-import path from 'node:path';
-import process from 'node:process';
import test from 'ava';
import indentString from 'indent-string';
-import {execa} from 'execa';
-import {readPackage} from 'read-pkg';
import meow from '../source/index.js';
-import {spawnFixture, __dirname} from './_utils.js';
+import {_verifyCli, stripIndentTrim} from './_utils.js';
const importMeta = import.meta;
-const NODE_MAJOR_VERSION = process.versions.node.split('.').at(0);
+const verifyCli = _verifyCli();
test('return object', t => {
const cli = meow({
@@ -25,219 +21,43 @@ test('return object', t => {
},
});
- t.is(cli.input.at(0), 'foo');
- t.true(cli.flags.fooBar);
- t.is(cli.flags.meow, 'dog');
- t.is(cli.flags.unicorn, 'cat');
- t.deepEqual(cli.flags['--'], ['unicorn', 'cake']);
- t.is(cli.pkg.name, 'meow');
- t.is(cli.help, indentString('\nCLI app helper\n\nUsage\n foo \n', 2));
-});
-
-test('spawn cli and show version', async t => {
- const pkg = await readPackage();
- const {stdout} = await spawnFixture(['--version']);
- t.is(stdout, pkg.version);
-});
-
-test('spawn cli and disabled autoVersion and autoHelp', async t => {
- const {stdout} = await spawnFixture(['--version', '--help']);
- t.is(stdout, 'version\nhelp\nmeow\ncamelCaseOption');
-});
-
-test('spawn cli and disabled autoVersion', async t => {
- const {stdout} = await spawnFixture(['--version', '--no-auto-version']);
- t.is(stdout, 'version\nautoVersion\nmeow\ncamelCaseOption');
-});
-
-test('spawn cli and not show version', async t => {
- const {stdout} = await spawnFixture(['--version=beta']);
- t.is(stdout, 'version\nmeow\ncamelCaseOption');
-});
-
-test('spawn cli and test input', async t => {
- const {stdout} = await spawnFixture(['-u', 'cat']);
- t.is(stdout, 'unicorn\nmeow\ncamelCaseOption');
-});
-
-test('spawn cli and test input flag', async t => {
- const {stdout} = await spawnFixture(['--camel-case-option', 'bar']);
- t.is(stdout, 'bar');
-});
-
-test('spawn cli and test process title', async t => {
- const {stdout} = await spawnFixture('with-package-json/fixture.js');
- t.is(stdout, 'foo');
-});
-
-test('setting pkg.bin should work', t => {
- const cli = meow({
- importMeta,
- pkg: {
- name: 'browser-sync',
- bin: './bin/browser-sync.js',
- },
- });
-
- t.is(cli.pkg.name, 'browser-sync');
- t.is(cli.pkg.version, '');
- t.is(cli.version, undefined);
-});
-
-test('single character flag casing should be preserved', t => {
- t.deepEqual(meow({
- importMeta,
- argv: ['-F'],
- }).flags, {F: true});
-});
-
-test('type inference', t => {
- t.is(meow({importMeta, argv: ['5']}).input.at(0), '5');
- t.is(meow({importMeta, argv: ['5']}, {input: 'string'}).input.at(0), '5');
- t.is(meow({
- importMeta,
- argv: ['5'],
- inferType: true,
- }).input.at(0), 5);
- t.is(meow({
- importMeta,
- argv: ['5'],
- inferType: true,
- flags: {foo: 'string'},
- }).input.at(0), 5);
- t.is(meow({
- importMeta,
- argv: ['5'],
- inferType: true,
- flags: {
- foo: 'string',
- },
- }).input.at(0), 5);
- t.is(meow({
- importMeta,
- argv: ['5'],
- input: 'number',
- }).input.at(0), 5);
-});
-
-test('booleanDefault: undefined, filter out unset boolean args', t => {
- t.deepEqual(meow({
- importMeta,
- argv: ['--foo'],
- booleanDefault: undefined,
+ t.like(cli, {
+ input: ['foo'],
flags: {
- foo: {
- type: 'boolean',
- },
- bar: {
- type: 'boolean',
- },
- baz: {
- type: 'boolean',
- default: false,
- },
+ fooBar: true,
+ meow: 'dog',
+ unicorn: 'cat',
+ '--': ['unicorn', 'cake'],
},
- }).flags, {
- foo: true,
- baz: false,
- });
-});
-
-test('boolean args are false by default', t => {
- t.deepEqual(meow({
- importMeta,
- argv: ['--foo'],
- flags: {
- foo: {
- type: 'boolean',
- },
- bar: {
- type: 'boolean',
- default: true,
- },
- baz: {
- type: 'boolean',
- },
- },
- }).flags, {
- foo: true,
- bar: true,
- baz: false,
- });
-});
-
-test('enforces boolean flag type', t => {
- const cli = meow({
- importMeta,
- argv: ['--cursor=false'],
- flags: {
- cursor: {
- type: 'boolean',
- },
+ pkg: {
+ name: 'meow',
},
+ help: indentString('\nCLI app helper\n\nUsage\n foo \n', 2),
});
- t.deepEqual(cli.flags, {cursor: false});
});
-test('accept help and options', t => {
- t.deepEqual(meow({
- importMeta,
- argv: ['-f'],
- flags: {
- foo: {
- type: 'boolean',
- shortFlag: 'f',
- },
- },
- }).flags, {
- foo: true,
- });
+test('spawn cli and disabled autoVersion and autoHelp', verifyCli, {
+ args: '--version --help',
+ expected: stripIndentTrim`
+ version
+ help
+ meow
+ camelCaseOption
+ `,
});
-test('grouped short-flags work', t => {
- const cli = meow({
- importMeta,
- argv: ['-cl'],
- flags: {
- coco: {
- type: 'boolean',
- shortFlag: 'c',
- },
- loco: {
- type: 'boolean',
- shortFlag: 'l',
- },
- },
- });
-
- const {unnormalizedFlags} = cli;
- t.true(unnormalizedFlags.coco);
- t.true(unnormalizedFlags.loco);
- t.true(unnormalizedFlags.c);
- t.true(unnormalizedFlags.l);
+test('spawn cli and test input', verifyCli, {
+ args: '-u cat',
+ expected: stripIndentTrim`
+ unicorn
+ meow
+ camelCaseOption
+ `,
});
-test('grouped flags work', t => {
- const cli = meow({
- importMeta,
- argv: ['-cl'],
- flags: {
- coco: {
- type: 'boolean',
- shortFlag: 'c',
- },
- loco: {
- type: 'boolean',
- shortFlag: 'l',
- },
- },
- });
-
- const {flags} = cli;
- t.true(flags.coco);
- t.true(flags.loco);
- t.is(flags.c, undefined);
- t.is(flags.l, undefined);
+test('spawn cli and test input flag', verifyCli, {
+ args: '--camel-case-option bar',
+ expected: 'bar',
});
test('disable autoVersion/autoHelp if `cli.input.length > 0`', t => {
@@ -245,74 +65,3 @@ test('disable autoVersion/autoHelp if `cli.input.length > 0`', t => {
t.is(meow({importMeta, argv: ['bar', '--help']}).input.at(0), 'bar');
t.is(meow({importMeta, argv: ['bar', '--version', '--help']}).input.at(0), 'bar');
});
-
-test('supports `number` flag type', t => {
- const cli = meow({
- importMeta,
- argv: ['--foo=1.3'],
- flags: {
- foo: {
- type: 'number',
- },
- },
- }).flags.foo;
-
- t.is(cli, 1.3);
-});
-
-test('supports `number` flag type - flag but no value', t => {
- const cli = meow({
- importMeta,
- argv: ['--foo'],
- flags: {
- foo: {
- type: 'number',
- },
- },
- }).flags.foo;
-
- t.is(cli, undefined);
-});
-
-test('supports `number` flag type - flag but no value but default', t => {
- const cli = meow({
- importMeta,
- argv: ['--foo'],
- flags: {
- foo: {
- type: 'number',
- default: 2,
- },
- },
- }).flags.foo;
-
- t.is(cli, 2);
-});
-
-test('supports `number` flag type - no flag but default', t => {
- const cli = meow({
- importMeta,
- argv: [],
- flags: {
- foo: {
- type: 'number',
- default: 2,
- },
- },
- }).flags.foo;
-
- t.is(cli, 2);
-});
-
-if (NODE_MAJOR_VERSION >= 14) {
- test('supports es modules', async t => {
- try {
- const {stdout} = await execa('node', ['estest/index.js', '--version'], {
- importMeta: path.join(__dirname, '..'),
- });
- t.regex(stdout, /1.2.3/);
- } catch (error) {
- t.is(error, undefined);
- }
- });
-}