From 506b35650204fed8e6a9add64badaa00ad16f64f Mon Sep 17 00:00:00 2001 From: Josh Spicer Date: Tue, 1 Nov 2022 22:45:22 +0000 Subject: [PATCH 1/9] checkpoint --- src/spec-node/featuresCLI/test.ts | 17 +++++++++++++---- src/spec-node/featuresCLI/testCommandImpl.ts | 10 +++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/spec-node/featuresCLI/test.ts b/src/spec-node/featuresCLI/test.ts index f730663dd..f014dc0ce 100644 --- a/src/spec-node/featuresCLI/test.ts +++ b/src/spec-node/featuresCLI/test.ts @@ -10,7 +10,7 @@ import { doFeaturesTestCommand } from './testCommandImpl'; export function featuresTestOptions(y: Argv) { return y .options({ - 'features': { type: 'array', alias: 'f', describe: 'Feature(s) to test as space-separated parameters. Omit to run all tests. Cannot be combined with \'--global-scenarios-only\'.', }, + 'features': { array: true, alias: 'f', describe: 'Feature(s) to test as space-separated parameters. Omit to run all tests. Cannot be combined with \'--global-scenarios-only\'.' }, 'global-scenarios-only': { type: 'boolean', default: false, description: 'Run only scenario tests under \'tests/_global\' . Cannot be combined with \'-f\'.' }, 'skip-scenarios': { type: 'boolean', default: false, description: 'Skip all \'scenario\' style tests. Cannot be combined with \'--global--scenarios-only\'.' }, 'skip-autogenerated': { type: 'boolean', default: false, description: 'Skip all \'autogenerated\' style tests.' }, @@ -18,8 +18,8 @@ export function featuresTestOptions(y: Argv) { 'remote-user': { type: 'string', alias: 'u', default: 'root', describe: 'Remote user. Not used for scenarios.', }, // TODO: Optionally replace 'scenario' configs with this value? 'log-level': { choices: ['info' as 'info', 'debug' as 'debug', 'trace' as 'trace'], default: 'info' as 'info', description: 'Log level.' }, 'quiet': { type: 'boolean', alias: 'q', default: false, description: 'Quiets output' }, + 'target-dir': { type: 'string', alias: 't', default: '.', description: 'Path to folder containing \'src\' and \'test\' sub-folders.' }, }) - .positional('target', { type: 'string', default: '.', description: 'Path to folder containing \'src\' and \'test\' sub-folders.' }) .check(argv => { if (argv['global-scenarios-only'] && argv['features']) { throw new Error('Cannot combine --global-scenarios-only and --features'); @@ -28,7 +28,12 @@ export function featuresTestOptions(y: Argv) { throw new Error('Cannot combine --skip-scenarios and --global-scenarios-only'); } return true; - }); + + }) + // DEPRECATED: Positional arguments don't play nice with the variadic/array --features option. + // Pass target directory with '--target-dir' instead. + .deprecateOption('target', 'Use --target-dir instead') + .positional('target', { type: 'string', default: '.', description: 'Path to folder containing \'src\' and \'test\' sub-folders.', }); } export type FeaturesTestArgs = UnpackArgv>; @@ -53,7 +58,8 @@ export function featuresTestHandler(args: FeaturesTestArgs) { async function featuresTest({ 'base-image': baseImage, - 'target': collectionFolder, + 'target': collectionFolder_deprecated, + 'target-dir': collectionFolder, features, 'global-scenarios-only': globalScenariosOnly, 'skip-scenarios': skipScenarios, @@ -73,6 +79,9 @@ async function featuresTest({ const logLevel = mapLogLevel(inputLogLevel); + // Prefer the new --target-dir option over the deprecated positional argument. + const targetDir = collectionFolder || collectionFolder_deprecated || '.'; + const args: FeaturesTestCommandInput = { baseImage, cliHost, diff --git a/src/spec-node/featuresCLI/testCommandImpl.ts b/src/spec-node/featuresCLI/testCommandImpl.ts index 8ecb31f61..ab5604be0 100644 --- a/src/spec-node/featuresCLI/testCommandImpl.ts +++ b/src/spec-node/featuresCLI/testCommandImpl.ts @@ -14,20 +14,20 @@ import { cpDirectoryLocal } from '../../spec-utils/pfs'; const TEST_LIBRARY_SCRIPT_NAME = 'dev-container-features-test-lib'; function fail(msg: string) { - log(msg, { prefix: '[-]', stderr: true }); + log(msg, { prefix: '[-]', error: true }); process.exit(1); } type Scenarios = { [key: string]: DevContainerConfig }; type TestResult = { testName: string; result: boolean }; -function log(msg: string, options?: { omitPrefix?: boolean; prefix?: string; info?: boolean; stderr?: boolean }) { +function log(msg: string, options?: { omitPrefix?: boolean; prefix?: string; info?: boolean; error?: boolean }) { const prefix = options?.prefix || '> '; const output = `${options?.omitPrefix ? '' : `${prefix} `}${msg}\n`; - if (options?.stderr) { - process.stderr.write(chalk.red(output)); + if (options?.error) { + process.stdout.write(chalk.red(output)); } else if (options?.info) { process.stdout.write(chalk.bold.blue(output)); } else { @@ -40,7 +40,7 @@ export async function doFeaturesTestCommand(args: FeaturesTestCommandInput): Pro process.stdout.write(` ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ -| dev container 'features' | +| dev container Features | │ v${pkg.version} │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘\n\n`); From e772a5d3f326b1431b130168477dbae67d14cd3e Mon Sep 17 00:00:00 2001 From: Josh Spicer Date: Thu, 3 Nov 2022 00:33:58 +0000 Subject: [PATCH 2/9] add --target-project and deprecate positional target --- src/spec-node/devContainersSpecCLI.ts | 2 +- src/spec-node/featuresCLI/test.ts | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/spec-node/devContainersSpecCLI.ts b/src/spec-node/devContainersSpecCLI.ts index e8763763e..a8e804a6c 100644 --- a/src/spec-node/devContainersSpecCLI.ts +++ b/src/spec-node/devContainersSpecCLI.ts @@ -63,7 +63,7 @@ const mountRegex = /^type=(bind|volume),source=([^,]+),target=([^,]+)(?:,externa y.command('run-user-commands', 'Run user commands', runUserCommandsOptions, runUserCommandsHandler); y.command('read-configuration', 'Read configuration', readConfigurationOptions, readConfigurationHandler); y.command('features', 'Features commands', (y: Argv) => { - y.command('test ', 'Test features', featuresTestOptions, featuresTestHandler); + y.command('test [target]', 'Test features', featuresTestOptions, featuresTestHandler); y.command('package ', 'Package features', featuresPackageOptions, featuresPackageHandler); y.command('publish ', 'Package and publish features', featuresPublishOptions, featuresPublishHandler); y.command('info ', 'Fetch info on a feature', featuresInfoOptions, featuresInfoHandler); diff --git a/src/spec-node/featuresCLI/test.ts b/src/spec-node/featuresCLI/test.ts index f014dc0ce..3a76e375b 100644 --- a/src/spec-node/featuresCLI/test.ts +++ b/src/spec-node/featuresCLI/test.ts @@ -10,6 +10,7 @@ import { doFeaturesTestCommand } from './testCommandImpl'; export function featuresTestOptions(y: Argv) { return y .options({ + 'target-project': { type: 'string', alias: 't', default: '.', description: 'Path to folder containing \'src\' and \'test\' sub-folders.' }, 'features': { array: true, alias: 'f', describe: 'Feature(s) to test as space-separated parameters. Omit to run all tests. Cannot be combined with \'--global-scenarios-only\'.' }, 'global-scenarios-only': { type: 'boolean', default: false, description: 'Run only scenario tests under \'tests/_global\' . Cannot be combined with \'-f\'.' }, 'skip-scenarios': { type: 'boolean', default: false, description: 'Skip all \'scenario\' style tests. Cannot be combined with \'--global--scenarios-only\'.' }, @@ -18,8 +19,14 @@ export function featuresTestOptions(y: Argv) { 'remote-user': { type: 'string', alias: 'u', default: 'root', describe: 'Remote user. Not used for scenarios.', }, // TODO: Optionally replace 'scenario' configs with this value? 'log-level': { choices: ['info' as 'info', 'debug' as 'debug', 'trace' as 'trace'], default: 'info' as 'info', description: 'Log level.' }, 'quiet': { type: 'boolean', alias: 'q', default: false, description: 'Quiets output' }, - 'target-dir': { type: 'string', alias: 't', default: '.', description: 'Path to folder containing \'src\' and \'test\' sub-folders.' }, }) + // DEPRECATED: Positional arguments don't play nice with the variadic/array --features option. + // Pass target directory with '--target-project' instead. + // This will still continue to work, but any value provided by --target-project will be preferred. + // Omitting both will default to the current working directory. + .deprecateOption('target', 'Use --target-project instead') + .positional('target', { type: 'string', default: '.', description: 'Path to folder containing \'src\' and \'test\' sub-folders.', }) + // Validation .check(argv => { if (argv['global-scenarios-only'] && argv['features']) { throw new Error('Cannot combine --global-scenarios-only and --features'); @@ -29,11 +36,7 @@ export function featuresTestOptions(y: Argv) { } return true; - }) - // DEPRECATED: Positional arguments don't play nice with the variadic/array --features option. - // Pass target directory with '--target-dir' instead. - .deprecateOption('target', 'Use --target-dir instead') - .positional('target', { type: 'string', default: '.', description: 'Path to folder containing \'src\' and \'test\' sub-folders.', }); + }); } export type FeaturesTestArgs = UnpackArgv>; @@ -59,7 +62,7 @@ export function featuresTestHandler(args: FeaturesTestArgs) { async function featuresTest({ 'base-image': baseImage, 'target': collectionFolder_deprecated, - 'target-dir': collectionFolder, + 'target-project': collectionFolder, features, 'global-scenarios-only': globalScenariosOnly, 'skip-scenarios': skipScenarios, @@ -79,8 +82,8 @@ async function featuresTest({ const logLevel = mapLogLevel(inputLogLevel); - // Prefer the new --target-dir option over the deprecated positional argument. - const targetDir = collectionFolder || collectionFolder_deprecated || '.'; + // Prefer the new --target-project option over the deprecated positional argument. + const targetProject = collectionFolder !== '.' ? collectionFolder : collectionFolder_deprecated; const args: FeaturesTestCommandInput = { baseImage, @@ -88,7 +91,7 @@ async function featuresTest({ logLevel, quiet, pkg, - collectionFolder: cliHost.path.resolve(collectionFolder), + collectionFolder: cliHost.path.resolve(targetProject), features: features ? (Array.isArray(features) ? features as string[] : [features]) : undefined, globalScenariosOnly, skipScenarios, From 033cab41997b8117c93140caada9c26ee2ef168b Mon Sep 17 00:00:00 2001 From: Josh Spicer Date: Thu, 3 Nov 2022 00:34:41 +0000 Subject: [PATCH 3/9] copy entire contents of test dir on test run (https://github.com/devcontainers/spec/discussions/114#discussioncomment-4031722) --- src/spec-node/featuresCLI/testCommandImpl.ts | 35 +++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/spec-node/featuresCLI/testCommandImpl.ts b/src/spec-node/featuresCLI/testCommandImpl.ts index ab5604be0..549178e5d 100644 --- a/src/spec-node/featuresCLI/testCommandImpl.ts +++ b/src/spec-node/featuresCLI/testCommandImpl.ts @@ -163,16 +163,14 @@ async function doRunAutoTest(feature: string, workspaceFolder: string, params: D fail(`Could not find test.sh script at ${testScriptPath}`); } - // Move the test script into the workspaceFolder - const testScript = await cliHost.readFile(testScriptPath); - const remoteTestScriptName = `${feature}-test-${Date.now()}.sh`; - await cliHost.writeFile(`${workspaceFolder}/${remoteTestScriptName}`, testScript); + // Move the entire test directory for the given Feature into the workspaceFolder + await cpDirectoryLocal(featureTestFolder, workspaceFolder); - // Move the test library script into the workspaceFolder - await cliHost.writeFile(`${workspaceFolder}/${TEST_LIBRARY_SCRIPT_NAME}`, Buffer.from(testLibraryScript)); + // Move the test library script into the workspaceFolder test scripts folder. + await cliHost.writeFile(path.join(workspaceFolder, TEST_LIBRARY_SCRIPT_NAME), Buffer.from(testLibraryScript)); // Execute Test - const result = await execTest(params, remoteTestScriptName, workspaceFolder); + const result = await execTest(params, 'test.sh', workspaceFolder); testResults.push({ testName: feature, result, @@ -206,9 +204,8 @@ async function doScenario(pathToTestDir: string, args: FeaturesTestCommandInput, log(`Running scenario: ${scenarioName}`); // Check if we have a scenario test script, otherwise skip. - const scenarioTestScript = path.join(pathToTestDir, `${scenarioName}.sh`); - if (!(await cliHost.isFile(scenarioTestScript))) { - fail(`No scenario test script found at path '${scenarioTestScript}'. Either add a script to the test folder, or remove from scenarios.json.`); + if (!(await cliHost.isFile(path.join(pathToTestDir, `${scenarioName}.sh`)))) { + fail(`No scenario test script found at path '${path.join(pathToTestDir, `${scenarioName}.sh`)}'. Either add a script to the test folder, or remove from scenarios.json.`); } // Create Container @@ -216,19 +213,16 @@ async function doScenario(pathToTestDir: string, args: FeaturesTestCommandInput, const params = await generateDockerParams(workspaceFolder, args); await createContainerFromWorkingDirectory(params, workspaceFolder, args); - // Execute test script - // Move the test script into the workspaceFolder - const testScript = await cliHost.readFile(scenarioTestScript); - const remoteTestScriptName = `${scenarioName}-test-${Date.now()}.sh`; - await cliHost.writeFile(`${workspaceFolder}/${remoteTestScriptName}`, testScript); + // Move the entire test directory for the given Feature into the workspaceFolder + await cpDirectoryLocal(pathToTestDir, workspaceFolder); // Move the test library script into the workspaceFolder - await cliHost.writeFile(`${workspaceFolder}/${TEST_LIBRARY_SCRIPT_NAME}`, Buffer.from(testLibraryScript)); + await cliHost.writeFile(path.join(workspaceFolder, TEST_LIBRARY_SCRIPT_NAME), Buffer.from(testLibraryScript)); // Execute Test testResults.push({ testName: scenarioName, - result: await execTest(params, remoteTestScriptName, workspaceFolder) + result: await execTest(params, `${scenarioName}.sh`, workspaceFolder) }); } return testResults; @@ -408,13 +402,14 @@ async function launchProject(params: DockerResolverParameters, workspaceFolder: } } -async function execTest(params: DockerResolverParameters, remoteTestScriptName: string, workspaceFolder: string) { +async function execTest(params: DockerResolverParameters, testFileName: string, workspaceFolder: string) { + // Ensure all the tests scripts in the workspace folder are executable let cmd = 'chmod'; - let args = ['777', `./${remoteTestScriptName}`, `./${TEST_LIBRARY_SCRIPT_NAME}`]; + let args = ['-R', '777', '.']; await exec(params, cmd, args, workspaceFolder); - cmd = `./${remoteTestScriptName}`; + cmd = `./${testFileName}`; args = []; return await exec(params, cmd, args, workspaceFolder); } From 85a0666be1635842934e462b4939f5af84c664d4 Mon Sep 17 00:00:00 2001 From: Josh Spicer Date: Thu, 3 Nov 2022 00:34:57 +0000 Subject: [PATCH 4/9] actually catch parse errors of scenarios.json --- src/spec-node/featuresCLI/testCommandImpl.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/spec-node/featuresCLI/testCommandImpl.ts b/src/spec-node/featuresCLI/testCommandImpl.ts index 549178e5d..c8a7b9fa0 100644 --- a/src/spec-node/featuresCLI/testCommandImpl.ts +++ b/src/spec-node/featuresCLI/testCommandImpl.ts @@ -192,11 +192,15 @@ async function doScenario(pathToTestDir: string, args: FeaturesTestCommandInput, const scenariosBuffer = await cliHost.readFile(scenariosPath); // Parse to json let scenarios: Scenarios = {}; - try { - scenarios = jsonc.parse(scenariosBuffer.toString()); - } catch (e) { - fail(`Failed to parse scenarios.json: ${e.message}`); - return []; // We never reach here, we exit via fail(). + let errors: jsonc.ParseError[] = []; + scenarios = jsonc.parse(scenariosBuffer.toString(), errors); + if (errors.length > 0) { + // Print each jsonc error + errors.forEach(error => { + log(`${jsonc.printParseErrorCode(error.error)}`, { prefix: '⚠️' }); + }); + fail(`Failed to parse scenarios.json at ${scenariosPath}`); + return []; // We never reach here, we exit via fail() } // For EACH scenario: Spin up a container and exec the scenario test script From bce2e3a2eda573b1c30a5f896c8508c343ee459c Mon Sep 17 00:00:00 2001 From: Josh Spicer Date: Thu, 3 Nov 2022 00:35:13 +0000 Subject: [PATCH 5/9] better organize test asserts --- .../featuresCLICommands.test.ts | 358 +++++++++--------- 1 file changed, 181 insertions(+), 177 deletions(-) diff --git a/src/test/container-features/featuresCLICommands.test.ts b/src/test/container-features/featuresCLICommands.test.ts index 3eed7e49e..1f0705a49 100644 --- a/src/test/container-features/featuresCLICommands.test.ts +++ b/src/test/container-features/featuresCLICommands.test.ts @@ -22,209 +22,213 @@ describe('CLI features subcommands', async function () { await shellExec(`npm --prefix ${tmp} install devcontainers-cli-${pkg.version}.tgz`); }); - it('features test subcommand with defaults', async function () { - const collectionFolder = `${__dirname}/example-v2-features-sets/simple`; - let success = false; - let result: ExecResult | undefined = undefined; - try { - result = await shellExec(`${cli} features test --base-image mcr.microsoft.com/devcontainers/base:ubuntu --log-level trace ${collectionFolder}`); - success = true; - - } catch (error) { - assert.fail('features test sub-command should not throw'); - } - - assert.isTrue(success); - assert.isDefined(result); - - const expectedTestReport = ` ================== TEST REPORT ================== + describe('features test', async function () { + + it('features test subcommand with defaults', async function () { + const collectionFolder = `${__dirname}/example-v2-features-sets/simple`; + let success = false; + let result: ExecResult | undefined = undefined; + try { + result = await shellExec(`${cli} features test --base-image mcr.microsoft.com/devcontainers/base:ubuntu --log-level trace ${collectionFolder}`); + success = true; + + } catch (error) { + assert.fail('features test sub-command should not throw'); + } + + assert.isTrue(success); + assert.isDefined(result); + + const expectedTestReport = ` ================== TEST REPORT ================== ✅ Passed: 'color' ✅ Passed: 'specific_color_scenario' ✅ Passed: 'hello' ✅ Passed: 'custom_options' ✅ Passed: 'with_external_feature'`; - const hasExpectedTestReport = result.stdout.includes(expectedTestReport); - assert.isTrue(hasExpectedTestReport); + const hasExpectedTestReport = result.stdout.includes(expectedTestReport); + assert.isTrue(hasExpectedTestReport); - assert.isTrue(result.stderr.includes('my favorite color is red')); - assert.isTrue(result.stderr.includes('hey, root?????')); + assert.isTrue(result.stderr.includes('my favorite color is red')); + assert.isTrue(result.stderr.includes('hey, root?????')); - assert.isTrue(result.stderr.includes('my favorite color is Magenta')); - assert.isTrue(result.stderr.includes('Ciao, root?????')); - }); + assert.isTrue(result.stderr.includes('my favorite color is Magenta')); + assert.isTrue(result.stderr.includes('Ciao, root?????')); + }); - it('features test subcommand with --skip-autogenerated and subset of features', async function () { - const collectionFolder = `${__dirname}/example-v2-features-sets/simple`; - let success = false; - let result: ExecResult | undefined = undefined; - try { - result = await shellExec(`${cli} features test -f color --skip-autogenerated --log-level trace ${collectionFolder}`); - success = true; + it('features test subcommand with --skip-autogenerated and subset of features', async function () { + const collectionFolder = `${__dirname}/example-v2-features-sets/simple`; + let success = false; + let result: ExecResult | undefined = undefined; + try { + result = await shellExec(`${cli} features test -f color --skip-autogenerated --log-level trace ${collectionFolder}`); + success = true; - } catch (error) { - assert.fail('features test sub-command should not throw'); - } + } catch (error) { + assert.fail('features test sub-command should not throw'); + } - assert.isTrue(success); - assert.isDefined(result); + assert.isTrue(success); + assert.isDefined(result); - const expectedTestReport = ` ================== TEST REPORT ================== + const expectedTestReport = ` ================== TEST REPORT ================== ✅ Passed: 'specific_color_scenario'`; - const hasExpectedTestReport = result.stdout.includes(expectedTestReport); - assert.isTrue(hasExpectedTestReport); - - assert.isTrue(result.stderr.includes('my favorite color is green')); - - // Given the '--skip-autogenerated' and '-f color' switches, these cases should not be exercised. - assert.isFalse(result.stderr.includes('my favorite color is red')); - assert.isFalse(result.stderr.includes('hey, root?????')); - assert.isFalse(result.stderr.includes('Ciao, root?????')); - }); - - it('features test subcommand with --global-scenarios-only', async function () { - const collectionFolder = `${__dirname}/example-v2-features-sets/simple`; - let success = false; - let result: ExecResult | undefined = undefined; - try { - result = await shellExec(`${cli} features test --global-scenarios-only --log-level trace ${collectionFolder}`); - success = true; - } catch (error) { - assert.fail('features test sub-command should not throw'); - } - - assert.isTrue(success); - assert.isDefined(result); - - const expectedTestReport = ` ================== TEST REPORT ================== + const hasExpectedTestReport = result.stdout.includes(expectedTestReport); + assert.isTrue(hasExpectedTestReport); + + assert.isTrue(result.stderr.includes('my favorite color is green')); + + // Given the '--skip-autogenerated' and '-f color' switches, these cases should not be exercised. + assert.isFalse(result.stderr.includes('my favorite color is red')); + assert.isFalse(result.stderr.includes('hey, root?????')); + assert.isFalse(result.stderr.includes('Ciao, root?????')); + }); + + it('features test subcommand with --global-scenarios-only', async function () { + const collectionFolder = `${__dirname}/example-v2-features-sets/simple`; + let success = false; + let result: ExecResult | undefined = undefined; + try { + result = await shellExec(`${cli} features test --global-scenarios-only --log-level trace ${collectionFolder}`); + success = true; + } catch (error) { + assert.fail('features test sub-command should not throw'); + } + + assert.isTrue(success); + assert.isDefined(result); + + const expectedTestReport = ` ================== TEST REPORT ================== ✅ Passed: 'custom_options' ✅ Passed: 'with_external_feature'`; - const hasExpectedTestReport = result.stdout.includes(expectedTestReport); - assert.isTrue(hasExpectedTestReport); + const hasExpectedTestReport = result.stdout.includes(expectedTestReport); + assert.isTrue(hasExpectedTestReport); - // With --global-scenarios-only, - // the default values should NOT be included in the test - // and therefore we should NOT see the following outputs. - assert.isFalse(result.stderr.includes('my favorite color is red')); - assert.isFalse(result.stderr.includes('hey, root?????!')); + // With --global-scenarios-only, + // the default values should NOT be included in the test + // and therefore we should NOT see the following outputs. + assert.isFalse(result.stderr.includes('my favorite color is red')); + assert.isFalse(result.stderr.includes('hey, root?????!')); - assert.isTrue(result.stderr.includes('my favorite color is Magenta')); - assert.isTrue(result.stderr.includes('Ciao, root?????')); + assert.isTrue(result.stderr.includes('my favorite color is Magenta')); + assert.isTrue(result.stderr.includes('Ciao, root?????')); - }); + }); - it('features test subcommand with a failing test', async function () { - const collectionFolder = `${__dirname}/example-v2-features-sets/failing-test`; - // shellExec's doNotThrow set to 'true' - const result = await shellExec(`${cli} features test --base-image mcr.microsoft.com/devcontainers/base:ubuntu --log-level trace ${collectionFolder}`, undefined, undefined, true); + it('features test subcommand with a failing test', async function () { + const collectionFolder = `${__dirname}/example-v2-features-sets/failing-test`; + // shellExec's doNotThrow set to 'true' + const result = await shellExec(`${cli} features test --base-image mcr.microsoft.com/devcontainers/base:ubuntu --log-level trace ${collectionFolder}`, undefined, undefined, true); - const expectedTestReport = ` ================== TEST REPORT ================== + const expectedTestReport = ` ================== TEST REPORT ================== ❌ Failed: 'hello'`; - const hasExpectedTestReport = result.stdout.includes(expectedTestReport); - assert.isTrue(hasExpectedTestReport); - - assert.isTrue(result.stderr.includes('❌ testThatShouldFail check failed.')); - assert.isDefined(result.error); - }); - - // Feature A will crash in its install.sh if B has not already run. - it('features test subcommand installsAfter B -> A', async function () { - const collectionFolder = `${__dirname}/example-v2-features-sets/a-installs-after-b`; - let success = false; - let result: ExecResult | undefined = undefined; - try { - result = await shellExec(`${cli} features test --log-level trace ${collectionFolder}`); - success = true; - - } catch (error) { - assert.fail('features test sub-command should not throw'); - } - - assert.isTrue(success); - assert.isDefined(result); - - const expectedTestReport = ` ================== TEST REPORT ================== + const hasExpectedTestReport = result.stdout.includes(expectedTestReport); + assert.isTrue(hasExpectedTestReport); + + assert.isTrue(result.stderr.includes('❌ testThatShouldFail check failed.')); + assert.isDefined(result.error); + }); + + // Feature A will crash in its install.sh if B has not already run. + it('features test subcommand installsAfter B -> A', async function () { + const collectionFolder = `${__dirname}/example-v2-features-sets/a-installs-after-b`; + let success = false; + let result: ExecResult | undefined = undefined; + try { + result = await shellExec(`${cli} features test --log-level trace ${collectionFolder}`); + success = true; + + } catch (error) { + assert.fail('features test sub-command should not throw'); + } + + assert.isTrue(success); + assert.isDefined(result); + + const expectedTestReport = ` ================== TEST REPORT ================== ✅ Passed: 'a' ✅ Passed: 'b'`; - const hasExpectedTestReport = result.stdout.includes(expectedTestReport); - assert.isTrue(hasExpectedTestReport); - }); - - // Feature B will crash in its install.sh if A has not already run. - it('features test subcommand installsAfter A -> B', async function () { - const collectionFolder = `${__dirname}/example-v2-features-sets/b-installs-after-a`; - let success = false; - let result: ExecResult | undefined = undefined; - try { - result = await shellExec(`${cli} features test --log-level trace ${collectionFolder}`); - success = true; - - } catch (error) { - assert.fail('features test sub-command should not throw'); - } - - assert.isTrue(success); - assert.isDefined(result); - - const expectedTestReport = ` ================== TEST REPORT ================== + const hasExpectedTestReport = result.stdout.includes(expectedTestReport); + assert.isTrue(hasExpectedTestReport); + }); + + // Feature B will crash in its install.sh if A has not already run. + it('features test subcommand installsAfter A -> B', async function () { + const collectionFolder = `${__dirname}/example-v2-features-sets/b-installs-after-a`; + let success = false; + let result: ExecResult | undefined = undefined; + try { + result = await shellExec(`${cli} features test --log-level trace ${collectionFolder}`); + success = true; + + } catch (error) { + assert.fail('features test sub-command should not throw'); + } + + assert.isTrue(success); + assert.isDefined(result); + + const expectedTestReport = ` ================== TEST REPORT ================== ✅ Passed: 'a' ✅ Passed: 'b'`; - const hasExpectedTestReport = result.stdout.includes(expectedTestReport); - assert.isTrue(hasExpectedTestReport); - }); - - // -- Packaging - - it('features package subcommand by collection', async function () { - const srcFolder = `${__dirname}/example-v2-features-sets/simple/src`; - let success = false; - try { - await shellExec(`${cli} features package -o ${tmp}/output/test01 -f --log-level trace ${srcFolder} `); - success = true; - } catch (error) { - assert.fail('features package sub-command should not throw'); - } - assert.isTrue(success); - - const colorTgzExists = await isLocalFile(`${tmp}/output/test01/devcontainer-feature-color.tgz`); - assert.isTrue(colorTgzExists); - const tgzArchiveContentsColor = await shellExec(`tar -tvf ${tmp}/output/test01/devcontainer-feature-color.tgz`); - assert.match(tgzArchiveContentsColor.stdout, /devcontainer-feature.json/); - assert.match(tgzArchiveContentsColor.stdout, /install.sh/); - - const helloTgzExists = await isLocalFile(`${tmp}/output/test01/devcontainer-feature-hello.tgz`); - assert.isTrue(helloTgzExists); - const tgzArchiveContentsHello = await shellExec(`tar -tvf ${tmp}/output/test01/devcontainer-feature-hello.tgz`); - assert.match(tgzArchiveContentsHello.stdout, /devcontainer-feature.json/); - assert.match(tgzArchiveContentsHello.stdout, /install.sh/); - - const collectionFileExists = await isLocalFile(`${tmp}/output/test01/devcontainer-collection.json`); - const json = JSON.parse((await readLocalFile(`${tmp}/output/test01/devcontainer-collection.json`)).toString()); - assert.strictEqual(json.features.length, 2); - assert.isTrue(collectionFileExists); + const hasExpectedTestReport = result.stdout.includes(expectedTestReport); + assert.isTrue(hasExpectedTestReport); + }); }); - it('features package subcommand by single feature', async function () { - const singleFeatureFolder = `${__dirname}/example-v2-features-sets/simple/src/color`; - let success = false; - try { - await shellExec(`${cli} features package -o ${tmp}/output/test02 -f --log-level trace ${singleFeatureFolder} `); - success = true; - } catch (error) { - assert.fail('features package sub-command should not throw'); - } - assert.isTrue(success); - - const colorTgzExists = await isLocalFile(`${tmp}/output/test02/devcontainer-feature-color.tgz`); - assert.isTrue(colorTgzExists); - const tgzArchiveContentsColor = await shellExec(`tar -tvf ${tmp}/output/test02/devcontainer-feature-color.tgz`); - assert.match(tgzArchiveContentsColor.stdout, /devcontainer-feature.json/); - assert.match(tgzArchiveContentsColor.stdout, /install.sh/); - - const collectionFileExists = await isLocalFile(`${tmp}/output/test02/devcontainer-collection.json`); - assert.isTrue(collectionFileExists); - const json = JSON.parse((await readLocalFile(`${tmp}/output/test02/devcontainer-collection.json`)).toString()); - assert.strictEqual(json.features.length, 1); - assert.isTrue(collectionFileExists); + describe('features package', function () { + + it('features package subcommand by collection', async function () { + const srcFolder = `${__dirname}/example-v2-features-sets/simple/src`; + let success = false; + try { + await shellExec(`${cli} features package -o ${tmp}/output/test01 -f --log-level trace ${srcFolder} `); + success = true; + } catch (error) { + assert.fail('features package sub-command should not throw'); + } + assert.isTrue(success); + + const colorTgzExists = await isLocalFile(`${tmp}/output/test01/devcontainer-feature-color.tgz`); + assert.isTrue(colorTgzExists); + const tgzArchiveContentsColor = await shellExec(`tar -tvf ${tmp}/output/test01/devcontainer-feature-color.tgz`); + assert.match(tgzArchiveContentsColor.stdout, /devcontainer-feature.json/); + assert.match(tgzArchiveContentsColor.stdout, /install.sh/); + + const helloTgzExists = await isLocalFile(`${tmp}/output/test01/devcontainer-feature-hello.tgz`); + assert.isTrue(helloTgzExists); + const tgzArchiveContentsHello = await shellExec(`tar -tvf ${tmp}/output/test01/devcontainer-feature-hello.tgz`); + assert.match(tgzArchiveContentsHello.stdout, /devcontainer-feature.json/); + assert.match(tgzArchiveContentsHello.stdout, /install.sh/); + + const collectionFileExists = await isLocalFile(`${tmp}/output/test01/devcontainer-collection.json`); + const json = JSON.parse((await readLocalFile(`${tmp}/output/test01/devcontainer-collection.json`)).toString()); + assert.strictEqual(json.features.length, 2); + assert.isTrue(collectionFileExists); + }); + + it('features package subcommand by single feature', async function () { + const singleFeatureFolder = `${__dirname}/example-v2-features-sets/simple/src/color`; + let success = false; + try { + await shellExec(`${cli} features package -o ${tmp}/output/test02 -f --log-level trace ${singleFeatureFolder} `); + success = true; + } catch (error) { + assert.fail('features package sub-command should not throw'); + } + assert.isTrue(success); + + const colorTgzExists = await isLocalFile(`${tmp}/output/test02/devcontainer-feature-color.tgz`); + assert.isTrue(colorTgzExists); + const tgzArchiveContentsColor = await shellExec(`tar -tvf ${tmp}/output/test02/devcontainer-feature-color.tgz`); + assert.match(tgzArchiveContentsColor.stdout, /devcontainer-feature.json/); + assert.match(tgzArchiveContentsColor.stdout, /install.sh/); + + const collectionFileExists = await isLocalFile(`${tmp}/output/test02/devcontainer-collection.json`); + assert.isTrue(collectionFileExists); + const json = JSON.parse((await readLocalFile(`${tmp}/output/test02/devcontainer-collection.json`)).toString()); + assert.strictEqual(json.features.length, 1); + assert.isTrue(collectionFileExists); + }); }); }); From df25f5f994bb8be436a8df263034acb5472141b5 Mon Sep 17 00:00:00 2001 From: Josh Spicer Date: Thu, 3 Nov 2022 00:43:10 +0000 Subject: [PATCH 6/9] add test exercising --target-project --- .../featuresCLICommands.test.ts | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/test/container-features/featuresCLICommands.test.ts b/src/test/container-features/featuresCLICommands.test.ts index 1f0705a49..3c2a302a8 100644 --- a/src/test/container-features/featuresCLICommands.test.ts +++ b/src/test/container-features/featuresCLICommands.test.ts @@ -24,7 +24,32 @@ describe('CLI features subcommands', async function () { describe('features test', async function () { - it('features test subcommand with defaults', async function () { + it('succeeds when using --target-project', async function () { + const collectionFolder = `${__dirname}/example-v2-features-sets/simple`; + let success = false; + let result: ExecResult | undefined = undefined; + try { + result = await shellExec(`${cli} features test --target-project ${collectionFolder} --base-image mcr.microsoft.com/devcontainers/base:ubuntu --log-level trace`); + success = true; + + } catch (error) { + assert.fail('features test sub-command should not throw'); + } + + assert.isTrue(success); + assert.isDefined(result); + + const expectedTestReport = ` ================== TEST REPORT ================== +✅ Passed: 'color' +✅ Passed: 'specific_color_scenario' +✅ Passed: 'hello' +✅ Passed: 'custom_options' +✅ Passed: 'with_external_feature'`; + const hasExpectedTestReport = result.stdout.includes(expectedTestReport); + assert.isTrue(hasExpectedTestReport); + }); + + it('succeeds with defaults', async function () { const collectionFolder = `${__dirname}/example-v2-features-sets/simple`; let success = false; let result: ExecResult | undefined = undefined; @@ -55,7 +80,7 @@ describe('CLI features subcommands', async function () { assert.isTrue(result.stderr.includes('Ciao, root?????')); }); - it('features test subcommand with --skip-autogenerated and subset of features', async function () { + it('succeeds --skip-autogenerated and subset of features', async function () { const collectionFolder = `${__dirname}/example-v2-features-sets/simple`; let success = false; let result: ExecResult | undefined = undefined; @@ -83,7 +108,7 @@ describe('CLI features subcommands', async function () { assert.isFalse(result.stderr.includes('Ciao, root?????')); }); - it('features test subcommand with --global-scenarios-only', async function () { + it('succeeds with --global-scenarios-only', async function () { const collectionFolder = `${__dirname}/example-v2-features-sets/simple`; let success = false; let result: ExecResult | undefined = undefined; @@ -114,7 +139,7 @@ describe('CLI features subcommands', async function () { }); - it('features test subcommand with a failing test', async function () { + it('successfully reports a failing test', async function () { const collectionFolder = `${__dirname}/example-v2-features-sets/failing-test`; // shellExec's doNotThrow set to 'true' const result = await shellExec(`${cli} features test --base-image mcr.microsoft.com/devcontainers/base:ubuntu --log-level trace ${collectionFolder}`, undefined, undefined, true); @@ -129,7 +154,7 @@ describe('CLI features subcommands', async function () { }); // Feature A will crash in its install.sh if B has not already run. - it('features test subcommand installsAfter B -> A', async function () { + it('installsAfter B -> A', async function () { const collectionFolder = `${__dirname}/example-v2-features-sets/a-installs-after-b`; let success = false; let result: ExecResult | undefined = undefined; @@ -152,7 +177,7 @@ describe('CLI features subcommands', async function () { }); // Feature B will crash in its install.sh if A has not already run. - it('features test subcommand installsAfter A -> B', async function () { + it('installsAfter A -> B', async function () { const collectionFolder = `${__dirname}/example-v2-features-sets/b-installs-after-a`; let success = false; let result: ExecResult | undefined = undefined; From 02806bc24b5eb93eab818ee4246865bf58f650fc Mon Sep 17 00:00:00 2001 From: Josh Spicer Date: Thu, 3 Nov 2022 01:06:49 +0000 Subject: [PATCH 7/9] add test for tests that share scripts --- .../src/util/devcontainer-feature.json | 6 +++++ .../sharing-test-scripts/src/util/install.sh | 12 +++++++++ .../test/util/a_different_script.sh | 3 +++ .../test/util/a_helper_script_for_scenario.sh | 3 +++ .../test/util/scenarios.json | 8 ++++++ .../test/util/some_scenario.sh | 12 +++++++++ .../sharing-test-scripts/test/util/test.sh | 12 +++++++++ .../featuresCLICommands.test.ts | 25 +++++++++++++++++++ 8 files changed, 81 insertions(+) create mode 100644 src/test/container-features/example-v2-features-sets/sharing-test-scripts/src/util/devcontainer-feature.json create mode 100644 src/test/container-features/example-v2-features-sets/sharing-test-scripts/src/util/install.sh create mode 100644 src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/a_different_script.sh create mode 100644 src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/a_helper_script_for_scenario.sh create mode 100644 src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/scenarios.json create mode 100644 src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/some_scenario.sh create mode 100644 src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/test.sh diff --git a/src/test/container-features/example-v2-features-sets/sharing-test-scripts/src/util/devcontainer-feature.json b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/src/util/devcontainer-feature.json new file mode 100644 index 000000000..da3e75d6c --- /dev/null +++ b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/src/util/devcontainer-feature.json @@ -0,0 +1,6 @@ +{ + "id": "util", + "version": "1.0.0", + "name": "A utility", + "options": {} +} \ No newline at end of file diff --git a/src/test/container-features/example-v2-features-sets/sharing-test-scripts/src/util/install.sh b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/src/util/install.sh new file mode 100644 index 000000000..f15f2333c --- /dev/null +++ b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/src/util/install.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -e + +echo "Activating feature 'util'" + +cat > /usr/local/bin/util \ +<< EOF +#!/bin/sh +echo "you did it" +EOF + +chmod +x /usr/local/bin/util \ No newline at end of file diff --git a/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/a_different_script.sh b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/a_different_script.sh new file mode 100644 index 000000000..4ce6b1ee1 --- /dev/null +++ b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/a_different_script.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "I AM A DIFFERENT SCRIPT" \ No newline at end of file diff --git a/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/a_helper_script_for_scenario.sh b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/a_helper_script_for_scenario.sh new file mode 100644 index 000000000..9d9bf9af9 --- /dev/null +++ b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/a_helper_script_for_scenario.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "I AM A HELPER SCRIPT FOR A SCENARIO" \ No newline at end of file diff --git a/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/scenarios.json b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/scenarios.json new file mode 100644 index 000000000..59c55a247 --- /dev/null +++ b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/scenarios.json @@ -0,0 +1,8 @@ +{ + "some_scenario": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "util": {} + } + } +} \ No newline at end of file diff --git a/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/some_scenario.sh b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/some_scenario.sh new file mode 100644 index 000000000..ef8e97c1f --- /dev/null +++ b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/some_scenario.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# Definition specific tests +check "run a helper script for this scenario" ./a_helper_script_for_scenario.sh + +# Report result +reportResults \ No newline at end of file diff --git a/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/test.sh b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/test.sh new file mode 100644 index 000000000..aa06fd33c --- /dev/null +++ b/src/test/container-features/example-v2-features-sets/sharing-test-scripts/test/util/test.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# Definition specific tests +check "run a different script" ./a_different_script.sh + +# Report result +reportResults \ No newline at end of file diff --git a/src/test/container-features/featuresCLICommands.test.ts b/src/test/container-features/featuresCLICommands.test.ts index 3c2a302a8..d67b802dd 100644 --- a/src/test/container-features/featuresCLICommands.test.ts +++ b/src/test/container-features/featuresCLICommands.test.ts @@ -49,6 +49,31 @@ describe('CLI features subcommands', async function () { assert.isTrue(hasExpectedTestReport); }); + it('succeeds when invoking another script from the same test folder', async function () { + const collectionFolder = `${__dirname}/example-v2-features-sets/sharing-test-scripts`; + let success = false; + let result: ExecResult | undefined = undefined; + try { + result = await shellExec(`${cli} features test --target-project ${collectionFolder} --base-image mcr.microsoft.com/devcontainers/base:ubuntu --log-level trace`); + success = true; + + } catch (error) { + assert.fail('features test sub-command should not throw'); + } + + assert.isTrue(success); + assert.isDefined(result); + + const expectedTestReport = ` ================== TEST REPORT ================== +✅ Passed: 'util' +✅ Passed: 'some_scenario'`; + const hasExpectedTestReport = result.stdout.includes(expectedTestReport); + assert.isTrue(hasExpectedTestReport); + + assert.isTrue(result.stderr.includes('I AM A DIFFERENT SCRIPT')); + assert.isTrue(result.stderr.includes('I AM A HELPER SCRIPT FOR A SCENARIO')); + }); + it('succeeds with defaults', async function () { const collectionFolder = `${__dirname}/example-v2-features-sets/simple`; let success = false; From 1855bcc9a58adf1f4c2fa74aa328e2951a9406dc Mon Sep 17 00:00:00 2001 From: Josh Spicer Date: Sun, 6 Nov 2022 20:21:48 +0000 Subject: [PATCH 8/9] capitalize --- src/spec-node/featuresCLI/testCommandImpl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spec-node/featuresCLI/testCommandImpl.ts b/src/spec-node/featuresCLI/testCommandImpl.ts index c8a7b9fa0..b0eb3591c 100644 --- a/src/spec-node/featuresCLI/testCommandImpl.ts +++ b/src/spec-node/featuresCLI/testCommandImpl.ts @@ -40,7 +40,7 @@ export async function doFeaturesTestCommand(args: FeaturesTestCommandInput): Pro process.stdout.write(` ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ -| dev container Features | +| Dev Container Features | │ v${pkg.version} │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘\n\n`); From 92c32576027e3cb0b1d6dddf691ca91e77484de0 Mon Sep 17 00:00:00 2001 From: Josh Spicer Date: Mon, 7 Nov 2022 16:55:05 +0000 Subject: [PATCH 9/9] change to --project-folder --- src/spec-node/featuresCLI/test.ts | 12 ++++++------ .../container-features/featuresCLICommands.test.ts | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/spec-node/featuresCLI/test.ts b/src/spec-node/featuresCLI/test.ts index 3a76e375b..5131d4a7c 100644 --- a/src/spec-node/featuresCLI/test.ts +++ b/src/spec-node/featuresCLI/test.ts @@ -10,7 +10,7 @@ import { doFeaturesTestCommand } from './testCommandImpl'; export function featuresTestOptions(y: Argv) { return y .options({ - 'target-project': { type: 'string', alias: 't', default: '.', description: 'Path to folder containing \'src\' and \'test\' sub-folders.' }, + 'project-folder': { type: 'string', alias: 'p', default: '.', description: 'Path to folder containing \'src\' and \'test\' sub-folders. This is likely the git root of the project.' }, 'features': { array: true, alias: 'f', describe: 'Feature(s) to test as space-separated parameters. Omit to run all tests. Cannot be combined with \'--global-scenarios-only\'.' }, 'global-scenarios-only': { type: 'boolean', default: false, description: 'Run only scenario tests under \'tests/_global\' . Cannot be combined with \'-f\'.' }, 'skip-scenarios': { type: 'boolean', default: false, description: 'Skip all \'scenario\' style tests. Cannot be combined with \'--global--scenarios-only\'.' }, @@ -21,10 +21,10 @@ export function featuresTestOptions(y: Argv) { 'quiet': { type: 'boolean', alias: 'q', default: false, description: 'Quiets output' }, }) // DEPRECATED: Positional arguments don't play nice with the variadic/array --features option. - // Pass target directory with '--target-project' instead. - // This will still continue to work, but any value provided by --target-project will be preferred. + // Pass target directory with '--project-folder' instead. + // This will still continue to work, but any value provided by --project-folder will be preferred. // Omitting both will default to the current working directory. - .deprecateOption('target', 'Use --target-project instead') + .deprecateOption('target', 'Use --project-folder instead') .positional('target', { type: 'string', default: '.', description: 'Path to folder containing \'src\' and \'test\' sub-folders.', }) // Validation .check(argv => { @@ -62,7 +62,7 @@ export function featuresTestHandler(args: FeaturesTestArgs) { async function featuresTest({ 'base-image': baseImage, 'target': collectionFolder_deprecated, - 'target-project': collectionFolder, + 'project-folder': collectionFolder, features, 'global-scenarios-only': globalScenariosOnly, 'skip-scenarios': skipScenarios, @@ -82,7 +82,7 @@ async function featuresTest({ const logLevel = mapLogLevel(inputLogLevel); - // Prefer the new --target-project option over the deprecated positional argument. + // Prefer the new --project-folder option over the deprecated positional argument. const targetProject = collectionFolder !== '.' ? collectionFolder : collectionFolder_deprecated; const args: FeaturesTestCommandInput = { diff --git a/src/test/container-features/featuresCLICommands.test.ts b/src/test/container-features/featuresCLICommands.test.ts index d67b802dd..ec68e1207 100644 --- a/src/test/container-features/featuresCLICommands.test.ts +++ b/src/test/container-features/featuresCLICommands.test.ts @@ -24,12 +24,12 @@ describe('CLI features subcommands', async function () { describe('features test', async function () { - it('succeeds when using --target-project', async function () { + it('succeeds when using --project-folder', async function () { const collectionFolder = `${__dirname}/example-v2-features-sets/simple`; let success = false; let result: ExecResult | undefined = undefined; try { - result = await shellExec(`${cli} features test --target-project ${collectionFolder} --base-image mcr.microsoft.com/devcontainers/base:ubuntu --log-level trace`); + result = await shellExec(`${cli} features test --project-folder ${collectionFolder} --base-image mcr.microsoft.com/devcontainers/base:ubuntu --log-level trace`); success = true; } catch (error) { @@ -54,7 +54,7 @@ describe('CLI features subcommands', async function () { let success = false; let result: ExecResult | undefined = undefined; try { - result = await shellExec(`${cli} features test --target-project ${collectionFolder} --base-image mcr.microsoft.com/devcontainers/base:ubuntu --log-level trace`); + result = await shellExec(`${cli} features test --project-folder ${collectionFolder} --base-image mcr.microsoft.com/devcontainers/base:ubuntu --log-level trace`); success = true; } catch (error) {