From 415bdefca092e60cacd867006bacddfda231ed94 Mon Sep 17 00:00:00 2001 From: Mariusz Nowak Date: Fri, 22 Oct 2021 12:37:26 +0200 Subject: [PATCH] perf(CLI): Integrate CLI triage into this package --- bin/serverless.js | 33 ++- lib/cli/triage.js | 190 ++++++++++++++++++ .../cli/js/component/serverless.js | 3 + .../@serverless/cli/js/function/serverless.js | 3 + .../cli/json/component/serverless.json | 3 + .../cli/yml/component/serverless.yml | 2 + .../js/template/foo/instance/serverless.js | 3 + .../components/js/template/foo/serverless.js | 3 + .../json/component/serverless.component.json | 1 + .../components/json/instance/serverless.json | 3 + .../json/template/foo/serverless.json | 1 + .../yml/component/serverless.component.yaml | 1 + .../components/yml/instance/serverless.yml | 1 + .../yml/template/foo/serverless.yml | 1 + .../js/component-in-custom/serverless.js | 3 + .../serverless/js/projects/bar/serverless.yml | 3 + .../serverless/js/projects/foo/serverless.js | 3 + .../json/component-in-custom/serverless.json | 1 + .../json/projects/bar/serverless.yml | 3 + .../json/projects/foo/serverless.json | 1 + .../fixtures/serverless/ts/any/serverless.ts | 3 + .../yml/component-in-custom/serverless.yml | 7 + .../yml/projects/bar/serverless.yml | 3 + .../yml/projects/foo/serverless.yml | 1 + test/unit/lib/cli/triage/index.test.js | 115 +++++++++++ 25 files changed, 373 insertions(+), 18 deletions(-) create mode 100644 lib/cli/triage.js create mode 100644 test/unit/lib/cli/triage/fixtures/@serverless/cli/js/component/serverless.js create mode 100644 test/unit/lib/cli/triage/fixtures/@serverless/cli/js/function/serverless.js create mode 100644 test/unit/lib/cli/triage/fixtures/@serverless/cli/json/component/serverless.json create mode 100644 test/unit/lib/cli/triage/fixtures/@serverless/cli/yml/component/serverless.yml create mode 100644 test/unit/lib/cli/triage/fixtures/@serverless/components/js/template/foo/instance/serverless.js create mode 100644 test/unit/lib/cli/triage/fixtures/@serverless/components/js/template/foo/serverless.js create mode 100644 test/unit/lib/cli/triage/fixtures/@serverless/components/json/component/serverless.component.json create mode 100644 test/unit/lib/cli/triage/fixtures/@serverless/components/json/instance/serverless.json create mode 100644 test/unit/lib/cli/triage/fixtures/@serverless/components/json/template/foo/serverless.json create mode 100644 test/unit/lib/cli/triage/fixtures/@serverless/components/yml/component/serverless.component.yaml create mode 100644 test/unit/lib/cli/triage/fixtures/@serverless/components/yml/instance/serverless.yml create mode 100644 test/unit/lib/cli/triage/fixtures/@serverless/components/yml/template/foo/serverless.yml create mode 100644 test/unit/lib/cli/triage/fixtures/serverless/js/component-in-custom/serverless.js create mode 100644 test/unit/lib/cli/triage/fixtures/serverless/js/projects/bar/serverless.yml create mode 100644 test/unit/lib/cli/triage/fixtures/serverless/js/projects/foo/serverless.js create mode 100644 test/unit/lib/cli/triage/fixtures/serverless/json/component-in-custom/serverless.json create mode 100644 test/unit/lib/cli/triage/fixtures/serverless/json/projects/bar/serverless.yml create mode 100644 test/unit/lib/cli/triage/fixtures/serverless/json/projects/foo/serverless.json create mode 100644 test/unit/lib/cli/triage/fixtures/serverless/ts/any/serverless.ts create mode 100644 test/unit/lib/cli/triage/fixtures/serverless/yml/component-in-custom/serverless.yml create mode 100644 test/unit/lib/cli/triage/fixtures/serverless/yml/projects/bar/serverless.yml create mode 100644 test/unit/lib/cli/triage/fixtures/serverless/yml/projects/foo/serverless.yml create mode 100644 test/unit/lib/cli/triage/index.test.js diff --git a/bin/serverless.js b/bin/serverless.js index 4fbbf6709..572e3f888 100755 --- a/bin/serverless.js +++ b/bin/serverless.js @@ -31,23 +31,20 @@ if (require('../lib/utils/isStandaloneExecutable')) { } } -// CLI Triage -(() => { - try { - const componentsV1 = require('@serverless/cli'); - const componentsV2 = require('@serverless/components'); +(async () => { + const cliName = await require('../lib/cli/triage')(); - // Serverless Components v1 CLI (deprecated) - if (componentsV1.runningComponents()) return () => componentsV1.runComponents(); - - // Serverless Components CLI - if (componentsV2.runningComponents()) return () => componentsV2.runComponents(); - } catch (error) { - if (process.env.SLS_DEBUG) { - require('../lib/classes/Error').logWarning(`CLI triage crashed with: ${error.stack}`); - } + switch (cliName) { + case 'serverless': + require('../scripts/serverless'); + return; + case '@serverless/components': + require('@serverless/components').runComponents(); + return; + case '@serverless/cli': + require('@serverless/cli').runComponents(); + return; + default: + throw new Error(`Unrecognized CLI name "${cliName}"`); } - - // Serverless Framework CLI - return () => require('../scripts/serverless'); -})()(); +})(); diff --git a/lib/cli/triage.js b/lib/cli/triage.js new file mode 100644 index 000000000..96c70fac8 --- /dev/null +++ b/lib/cli/triage.js @@ -0,0 +1,190 @@ +'use strict'; + +module.exports = async () => { + const cliArgs = new Set(process.argv.slice(2)); + + // Unconditionally favor "serverless" for version check + if (cliArgs.has('--version')) return 'serverless'; + if (cliArgs.has('-v')) return 'serverless'; + + // Unconditionally favor "@serverless/components" when component specific command or flag + const componentsCommands = new Set(['registry', 'init', 'publish']); + if (componentsCommands.has(process.argv[2])) return '@serverless/components'; + if (cliArgs.has('--help-components')) return '@serverless/components'; + if (cliArgs.has('--target')) return '@serverless/components'; + + // Detect eventual component configuration + const fsp = require('fs').promises; + if ( + ( + await Promise.all( + ['yml', 'yaml', 'json'].map(async (extension) => { + try { + await fsp.access(`serverless.component.${extension}`); + return true; + } catch { + return false; + } + }) + ) + ).some(Boolean) + ) { + return '@serverless/components'; + } + + // Detect eventual service configuration + const configurationExtension = + ( + await Promise.all( + ['yml', 'yaml', 'json', 'js', 'ts'].map(async (extension) => { + try { + await fsp.access(`serverless.${extension}`); + return extension; + } catch { + return null; + } + }) + ) + ).find(Boolean) || null; + + if (configurationExtension) { + // Found top level service configuration, recognize CLI by content + + const resolveByObjectConfiguration = (configuration) => { + if (configuration.provider) return 'serverless'; + if (configuration.component) return '@serverless/components'; + for (const value of Object.values(configuration)) { + if (value.component) return '@serverless/cli'; + } + return 'serverless'; + }; + switch (configurationExtension) { + case 'yml': + case 'yaml': { + const content = await fsp.readFile(`serverless.${configurationExtension}`, 'utf8'); + if (content.search(/(?:^|\n)provider\s*:/) !== -1) return 'serverless'; + if (content.search(/(?:^|\n)component\s*:/) !== -1) return '@serverless/components'; + if (content.search(/\n\s+component\s*:/) !== -1) return '@serverless/cli'; + return 'serverless'; + } + case 'json': { + const configuration = (() => { + try { + return require(`${process.cwd()}/serverless.json`); + } catch { + return null; + } + })(); + if (!configuration) return 'serverless'; + return resolveByObjectConfiguration(configuration); + } + case 'js': { + const configuration = (() => { + try { + return require(`${process.cwd()}/serverless.js`); + } catch { + return null; + } + })(); + if (!configuration) return 'serverless'; + if (typeof configuration === 'function') return '@serverless/cli'; + return resolveByObjectConfiguration(configuration); + } + case 'ts': + return 'serverless'; + default: + throw new Error(`Unrecognized extension "${configurationExtension}"`); + } + } + + // No top level service configuration + const isChinaUser = (() => { + if (process.env.SLS_GEO_LOCATION) return process.env.SLS_GEO_LOCATION === 'cn'; + return new Intl.DateTimeFormat('en', { timeZoneName: 'long' }) + .format() + .includes('China Standard Time'); + })(); + + // If "sls" or "sls deploy" command and in China, force "@serverless/components" + if ( + (process.argv.length === 2 || process.argv[2] === 'deploy') && + (isChinaUser || process.env.SERVERLESS_PLATFORM_VENDOR === 'tencent') + ) { + return '@serverless/components'; + } + + // Detect eventual component template + const nestedTemplateKeywords = new Set([ + 'deploy', + 'remove', + 'info', + 'help', + '--help', + 'dev', + 'logs', + 'invoke', + 'credentials', + ]); + if (!nestedTemplateKeywords.has(process.argv[2])) return 'serverless'; + + const path = require('path'); + const detectSubServiceType = async (subDirname) => { + const subConfigurationExtension = + ( + await Promise.all( + ['yml', 'yaml', 'json', 'js'].map(async (extension) => { + try { + await fsp.access(path.resolve(subDirname, `serverless.${extension}`)); + return extension; + } catch { + return null; + } + }) + ) + ).find(Boolean) || null; + if (!subConfigurationExtension) return null; + switch (subConfigurationExtension) { + case 'yml': + case 'yaml': { + const content = await fsp.readFile( + path.resolve(subDirname, `serverless.${subConfigurationExtension}`), + 'utf8' + ); + if (content.search(/(?:^|\n)component\s*:/) !== -1) return '@serverless/components'; + return 'other'; + } + case 'json': { + const configuration = (() => { + try { + return require(`${path.resolve(subDirname)}/serverless.json`); + } catch { + return null; + } + })(); + return configuration && configuration.component ? '@serverless/components' : 'other'; + } + case 'js': { + const configuration = (() => { + try { + return require(`${path.resolve(subDirname)}/serverless.js`); + } catch { + return null; + } + })(); + return configuration && configuration.component ? '@serverless/components' : 'other'; + } + default: + throw new Error(`Unrecognized extension "${subConfigurationExtension}"`); + } + }; + let hasComponent = false; + for (const subDirname of await fsp.readdir('.')) { + const stats = await fsp.lstat(subDirname); + if (!stats.isDirectory()) continue; + const subServiceType = await detectSubServiceType(subDirname); + if (!subServiceType) continue; + if (subServiceType === '@serverless/components') hasComponent = true; + else return 'serverless'; + } + return hasComponent ? '@serverless/components' : 'serverless'; +}; diff --git a/test/unit/lib/cli/triage/fixtures/@serverless/cli/js/component/serverless.js b/test/unit/lib/cli/triage/fixtures/@serverless/cli/js/component/serverless.js new file mode 100644 index 000000000..216349131 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/@serverless/cli/js/component/serverless.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = { foo: { component: 'foo' } }; diff --git a/test/unit/lib/cli/triage/fixtures/@serverless/cli/js/function/serverless.js b/test/unit/lib/cli/triage/fixtures/@serverless/cli/js/function/serverless.js new file mode 100644 index 000000000..529aa95af --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/@serverless/cli/js/function/serverless.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = () => {}; diff --git a/test/unit/lib/cli/triage/fixtures/@serverless/cli/json/component/serverless.json b/test/unit/lib/cli/triage/fixtures/@serverless/cli/json/component/serverless.json new file mode 100644 index 000000000..20e58a330 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/@serverless/cli/json/component/serverless.json @@ -0,0 +1,3 @@ +{ + "foo": { "component": "foo" } +} diff --git a/test/unit/lib/cli/triage/fixtures/@serverless/cli/yml/component/serverless.yml b/test/unit/lib/cli/triage/fixtures/@serverless/cli/yml/component/serverless.yml new file mode 100644 index 000000000..3f3c5a23c --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/@serverless/cli/yml/component/serverless.yml @@ -0,0 +1,2 @@ +foo: + component: foo diff --git a/test/unit/lib/cli/triage/fixtures/@serverless/components/js/template/foo/instance/serverless.js b/test/unit/lib/cli/triage/fixtures/@serverless/components/js/template/foo/instance/serverless.js new file mode 100644 index 000000000..b0676f300 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/@serverless/components/js/template/foo/instance/serverless.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = { component: 'foo' }; diff --git a/test/unit/lib/cli/triage/fixtures/@serverless/components/js/template/foo/serverless.js b/test/unit/lib/cli/triage/fixtures/@serverless/components/js/template/foo/serverless.js new file mode 100644 index 000000000..b0676f300 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/@serverless/components/js/template/foo/serverless.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = { component: 'foo' }; diff --git a/test/unit/lib/cli/triage/fixtures/@serverless/components/json/component/serverless.component.json b/test/unit/lib/cli/triage/fixtures/@serverless/components/json/component/serverless.component.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/@serverless/components/json/component/serverless.component.json @@ -0,0 +1 @@ +{} diff --git a/test/unit/lib/cli/triage/fixtures/@serverless/components/json/instance/serverless.json b/test/unit/lib/cli/triage/fixtures/@serverless/components/json/instance/serverless.json new file mode 100644 index 000000000..f3c7bc430 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/@serverless/components/json/instance/serverless.json @@ -0,0 +1,3 @@ +{ + "component": "foo" +} diff --git a/test/unit/lib/cli/triage/fixtures/@serverless/components/json/template/foo/serverless.json b/test/unit/lib/cli/triage/fixtures/@serverless/components/json/template/foo/serverless.json new file mode 100644 index 000000000..5f4c4daba --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/@serverless/components/json/template/foo/serverless.json @@ -0,0 +1 @@ +{ "component": "foo" } diff --git a/test/unit/lib/cli/triage/fixtures/@serverless/components/yml/component/serverless.component.yaml b/test/unit/lib/cli/triage/fixtures/@serverless/components/yml/component/serverless.component.yaml new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/@serverless/components/yml/component/serverless.component.yaml @@ -0,0 +1 @@ +{} diff --git a/test/unit/lib/cli/triage/fixtures/@serverless/components/yml/instance/serverless.yml b/test/unit/lib/cli/triage/fixtures/@serverless/components/yml/instance/serverless.yml new file mode 100644 index 000000000..e3e6ecdf9 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/@serverless/components/yml/instance/serverless.yml @@ -0,0 +1 @@ +component: foo diff --git a/test/unit/lib/cli/triage/fixtures/@serverless/components/yml/template/foo/serverless.yml b/test/unit/lib/cli/triage/fixtures/@serverless/components/yml/template/foo/serverless.yml new file mode 100644 index 000000000..e3e6ecdf9 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/@serverless/components/yml/template/foo/serverless.yml @@ -0,0 +1 @@ +component: foo diff --git a/test/unit/lib/cli/triage/fixtures/serverless/js/component-in-custom/serverless.js b/test/unit/lib/cli/triage/fixtures/serverless/js/component-in-custom/serverless.js new file mode 100644 index 000000000..1901b3270 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/serverless/js/component-in-custom/serverless.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = { service: 'foo', provider: { name: 'foo' }, custom: { component: 'foo' } }; diff --git a/test/unit/lib/cli/triage/fixtures/serverless/js/projects/bar/serverless.yml b/test/unit/lib/cli/triage/fixtures/serverless/js/projects/bar/serverless.yml new file mode 100644 index 000000000..27b360ba4 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/serverless/js/projects/bar/serverless.yml @@ -0,0 +1,3 @@ +service: foo +provider: + name: any diff --git a/test/unit/lib/cli/triage/fixtures/serverless/js/projects/foo/serverless.js b/test/unit/lib/cli/triage/fixtures/serverless/js/projects/foo/serverless.js new file mode 100644 index 000000000..b0676f300 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/serverless/js/projects/foo/serverless.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = { component: 'foo' }; diff --git a/test/unit/lib/cli/triage/fixtures/serverless/json/component-in-custom/serverless.json b/test/unit/lib/cli/triage/fixtures/serverless/json/component-in-custom/serverless.json new file mode 100644 index 000000000..97fa86a70 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/serverless/json/component-in-custom/serverless.json @@ -0,0 +1 @@ +{ "service": "foo", "provider": { "name": "foo" }, "custom": { "component": "foo" } } diff --git a/test/unit/lib/cli/triage/fixtures/serverless/json/projects/bar/serverless.yml b/test/unit/lib/cli/triage/fixtures/serverless/json/projects/bar/serverless.yml new file mode 100644 index 000000000..27b360ba4 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/serverless/json/projects/bar/serverless.yml @@ -0,0 +1,3 @@ +service: foo +provider: + name: any diff --git a/test/unit/lib/cli/triage/fixtures/serverless/json/projects/foo/serverless.json b/test/unit/lib/cli/triage/fixtures/serverless/json/projects/foo/serverless.json new file mode 100644 index 000000000..5f4c4daba --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/serverless/json/projects/foo/serverless.json @@ -0,0 +1 @@ +{ "component": "foo" } diff --git a/test/unit/lib/cli/triage/fixtures/serverless/ts/any/serverless.ts b/test/unit/lib/cli/triage/fixtures/serverless/ts/any/serverless.ts new file mode 100644 index 000000000..ee9b714f4 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/serverless/ts/any/serverless.ts @@ -0,0 +1,3 @@ +module.exports = { + foo: { component: 'foo' }, +}; diff --git a/test/unit/lib/cli/triage/fixtures/serverless/yml/component-in-custom/serverless.yml b/test/unit/lib/cli/triage/fixtures/serverless/yml/component-in-custom/serverless.yml new file mode 100644 index 000000000..c3e3f12ab --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/serverless/yml/component-in-custom/serverless.yml @@ -0,0 +1,7 @@ +service: foo + +provider: + name: aws + +custom: + component: foo diff --git a/test/unit/lib/cli/triage/fixtures/serverless/yml/projects/bar/serverless.yml b/test/unit/lib/cli/triage/fixtures/serverless/yml/projects/bar/serverless.yml new file mode 100644 index 000000000..27b360ba4 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/serverless/yml/projects/bar/serverless.yml @@ -0,0 +1,3 @@ +service: foo +provider: + name: any diff --git a/test/unit/lib/cli/triage/fixtures/serverless/yml/projects/foo/serverless.yml b/test/unit/lib/cli/triage/fixtures/serverless/yml/projects/foo/serverless.yml new file mode 100644 index 000000000..e3e6ecdf9 --- /dev/null +++ b/test/unit/lib/cli/triage/fixtures/serverless/yml/projects/foo/serverless.yml @@ -0,0 +1 @@ +component: foo diff --git a/test/unit/lib/cli/triage/index.test.js b/test/unit/lib/cli/triage/index.test.js new file mode 100644 index 000000000..94ff5e810 --- /dev/null +++ b/test/unit/lib/cli/triage/index.test.js @@ -0,0 +1,115 @@ +'use strict'; + +const { expect } = require('chai'); +const fs = require('fs'); +const overrideCwd = require('process-utils/override-cwd'); +const overrideEnv = require('process-utils/override-env'); +const overrideArgv = require('process-utils/override-argv'); +const path = require('path'); +const triage = require('../../../../../lib/cli/triage'); + +const fixturesDirname = path.resolve(__dirname, 'fixtures'); + +describe('test/unit/lib/cli/triage/index.test.js', () => { + before(() => overrideEnv({ variables: { SLS_GEO_LOCATION: 'us' } })); + + describe('CLI params', () => { + it('should recognize "registry" as "@serverless/components" command', async () => + overrideArgv({ args: ['sls', 'registry'] }, async () => { + expect(await triage()).to.equal('@serverless/components'); + })); + it('should recognize "init" as "@serverless/components" command', async () => + overrideArgv({ args: ['sls', 'init'] }, async () => { + expect(await triage()).to.equal('@serverless/components'); + })); + it('should recognize "publish" as "@serverless/components" command', async () => + overrideArgv({ args: ['sls', 'publish'] }, async () => { + expect(await triage()).to.equal('@serverless/components'); + })); + it('should recognize "--help-components" as "@serverless/components" exclusive flag', async () => { + await overrideArgv({ args: ['sls', '--help-components'] }, async () => { + expect(await triage()).to.equal('@serverless/components'); + }); + await overrideArgv({ args: ['sls', 'any', '--help-components'] }, async () => { + expect(await triage()).to.equal('@serverless/components'); + }); + }); + + it('should recognize "--target" as "@serverless/components" exclusive flag', async () => { + await overrideArgv({ args: ['sls', '--target'] }, async () => { + expect(await triage()).to.equal('@serverless/components'); + }); + await overrideArgv({ args: ['sls', 'any', '--target'] }, async () => { + expect(await triage()).to.equal('@serverless/components'); + }); + }); + + it('should favor "@serverless/components" for bare "sls" command when tencent platform set explicitly', async () => + overrideEnv({ variables: { SERVERLESS_PLATFORM_VENDOR: 'tencent' } }, async () => + overrideArgv({ args: ['sls'] }, async () => { + expect(await triage()).to.equal('@serverless/components'); + }) + )); + + it('should recognize bare "sls" command in China as "@serverless/components" command', async () => + overrideEnv({ variables: { SLS_GEO_LOCATION: 'cn' } }, async () => + overrideArgv({ args: ['sls'] }, async () => { + expect(await triage()).to.equal('@serverless/components'); + }) + )); + + it('should unconditionally favor "serverless" for version check', async () => + overrideEnv( + { variables: { SERVERLESS_PLATFORM_VENDOR: 'tencent', SLS_GEO_LOCATION: 'cn' } }, + async () => { + await overrideArgv({ args: ['sls', '-v'] }, async () => { + expect(await triage()).to.equal('serverless'); + }); + await overrideArgv({ args: ['sls', '--version'] }, async () => { + expect(await triage()).to.equal('serverless'); + }); + } + )); + + it('should favor "serverless" in other cases', async () => { + await overrideArgv({ args: ['sls', 'print'] }, async () => { + expect(await triage()).to.equal('serverless'); + }); + await overrideArgv({ args: ['sls', 'deploy'] }, async () => { + expect(await triage()).to.equal('serverless'); + }); + await overrideArgv({ args: ['sls'] }, async () => { + expect(await triage()).to.equal('serverless'); + }); + await overrideArgv({ args: ['sls', '--help'] }, async () => { + expect(await triage()).to.equal('serverless'); + }); + }); + }); + + describe('Service configuration', () => { + let restoreArgv; + before(() => { + ({ restoreArgv } = overrideArgv({ args: ['sls', 'deploy'] })); + }); + after(() => restoreArgv()); + + for (const cliName of ['serverless', '@serverless/components', '@serverless/cli']) { + for (const extension of fs.readdirSync(path.resolve(fixturesDirname, cliName))) { + for (const fixtureName of fs.readdirSync( + path.resolve(fixturesDirname, cliName, extension) + )) { + const testName = `should recognize "${cliName}" at "${cliName}/${extension}/${fixtureName}"`; + it(testName, async () => + overrideCwd( + path.resolve(fixturesDirname, cliName, extension, fixtureName), + async () => { + expect(await triage()).to.equal(cliName); + } + ) + ); + } + } + } + }); +});