From 54a4472165d4a50c992330edbc13abbbb3071c21 Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Thu, 21 Jul 2022 17:13:37 +0700 Subject: [PATCH 01/24] [#29] As the application, I have the generated infrastructure tested --- src/helpers/file.test.ts | 209 ++++++++++++++++++++++ src/helpers/file.ts | 94 ++++------ src/templates/aws/addons/alb.ts | 4 +- src/templates/aws/addons/bastion.ts | 4 +- src/templates/aws/addons/common.ts | 4 +- src/templates/aws/addons/ecr.ts | 4 +- src/templates/aws/addons/ecs.ts | 4 +- src/templates/aws/addons/log.ts | 4 +- src/templates/aws/addons/rds.ts | 4 +- src/templates/aws/addons/s3.ts | 4 +- src/templates/aws/addons/securityGroup.ts | 4 +- src/templates/aws/addons/ssm.ts | 4 +- src/templates/aws/addons/vpc.ts | 4 +- test/helpers/init.ts | 8 - 14 files changed, 263 insertions(+), 92 deletions(-) create mode 100644 src/helpers/file.test.ts delete mode 100644 test/helpers/init.ts diff --git a/src/helpers/file.test.ts b/src/helpers/file.test.ts new file mode 100644 index 00000000..ce242b2b --- /dev/null +++ b/src/helpers/file.test.ts @@ -0,0 +1,209 @@ +import path = require('path'); + +import * as fs from 'fs-extra'; +import { readFileSync } from 'fs-extra'; + +import { + appendToFile, + copy, + createFile, + getSourcePath, + getTargetDir, + getTargetPath, + injectToFile, + remove, + renameFile, + TEMPLATE_PATH, +} from './file'; + +jest.mock('fs-extra'); + +describe('File helpers', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('getTargetDir', () => { + describe('given projectName', () => { + it('returns the correct target directory', () => { + const projectName = 'projectName'; + + const targetDir = getTargetDir(projectName); + + expect(targetDir).toBe(path.join(process.cwd(), projectName)); + }); + }); + }); + + describe('getTargetPath', () => { + describe('given file name and projectName', () => { + it('returns the correct target path', () => { + const file = 'file'; + const projectName = 'projectName'; + + const targetPath = getTargetPath(file, projectName); + + expect(targetPath).toBe(path.join(process.cwd(), projectName, file)); + }); + }); + }); + + describe('getSourcePath', () => { + describe('given file name', () => { + it('returns the correct source path', () => { + const file = 'file'; + + const sourcePath = getSourcePath(file); + + expect(sourcePath).toBe(path.join(TEMPLATE_PATH, file)); + }); + }); + }); + + describe('copy', () => { + describe('given source and target', () => { + it('copies the source directory to the target directory', () => { + const source = 'sourceDir'; + const target = 'targetDir'; + const projectName = 'projectName'; + const sourcePath = getSourcePath(source); + const targetPath = getTargetPath(target, projectName); + + const copySpy = jest.spyOn(fs, 'copySync'); + + copy(source, target, projectName); + + expect(copySpy).toHaveBeenCalledWith(sourcePath, targetPath); + }); + }); + }); + + describe('createFile', () => { + describe('given target file and content', () => { + it('creates the target file', () => { + const target = 'targetFile.txt'; + const content = 'creating content'; + const projectName = 'projectName'; + const targetPath = getTargetPath(target, projectName); + + const createSpy = jest.spyOn(fs, 'writeFileSync'); + + createFile(target, content, projectName); + + expect(createSpy).toHaveBeenCalledWith(targetPath, content); + }); + }); + }); + + describe('remove', () => { + describe('given target file', () => { + it('removes the target file', () => { + const target = 'targetFile.txt'; + const projectName = 'projectName'; + const targetPath = getTargetPath(target, projectName); + + const removeSpy = jest.spyOn(fs, 'removeSync'); + + remove(target, projectName); + + expect(removeSpy).toHaveBeenCalledWith(targetPath); + }); + }); + + describe('given target directory', () => { + it('removes the target directory', () => { + const target = 'targetDir'; + const projectName = 'projectName'; + const targetPath = getTargetPath(target, projectName); + + const removeSpy = jest.spyOn(fs, 'removeSync'); + + remove(target, projectName); + + expect(removeSpy).toHaveBeenCalledWith(targetPath); + }); + }); + }); + + describe('renameFile', () => { + describe('given source and target', () => { + it('renames the source file to the target file', () => { + const source = 'sourceFile.txt'; + const target = 'targetFile.txt'; + const projectName = 'projectName'; + const sourcePath = getTargetPath(source, projectName); + const targetPath = getTargetPath(target, projectName); + + const renameSpy = jest.spyOn(fs, 'renameSync'); + + renameFile(source, target, projectName); + + expect(renameSpy).toHaveBeenCalledWith(sourcePath, targetPath); + }); + }); + }); + + describe('appendToFile', () => { + describe('given target file and content', () => { + it('appends content to the target file', () => { + const target = 'targetFile.txt'; + const content = 'appending content'; + const projectName = 'projectName'; + const targetPath = getTargetDir(projectName); + + const appendSpy = jest.spyOn(fs, 'appendFileSync'); + + appendToFile(target, content, projectName); + + expect(appendSpy).toHaveBeenCalledWith( + path.join(targetPath, target), + content, + ); + }); + }); + }); + + describe('injectToFile', () => { + describe('given target file, content and insert before initial content', () => { + it('injects content to the target file', () => { + const target = 'targetFile.txt'; + const initialContent = 'initial content'; + const content = 'injecting content'; + const projectName = 'projectName'; + const targetPath = getTargetDir(projectName); + + const injectSpy = jest.spyOn(fs, 'writeFileSync'); + + (readFileSync as jest.Mock).mockReturnValue(initialContent); + + injectToFile(target, content, projectName, { insertBefore: initialContent }); + + expect(injectSpy).toHaveBeenCalledWith( + path.join(targetPath, target), + `${content}\n${initialContent}`, + ); + }); + }); + + describe('given target file, content and insert after initial content', () => { + it('injects content to the target file', () => { + const target = 'targetFile.txt'; + const initialContent = 'initial content'; + const content = 'injecting content'; + const projectName = 'projectName'; + const targetPath = getTargetDir(projectName); + + const injectSpy = jest.spyOn(fs, 'writeFileSync'); + + (readFileSync as jest.Mock).mockReturnValue(initialContent); + + injectToFile(target, content, projectName, { insertAfter: initialContent }); + + expect(injectSpy).toHaveBeenCalledWith( + path.join(targetPath, target), + `${initialContent}\n${content}`, + ); + }); + }); + }); +}); diff --git a/src/helpers/file.ts b/src/helpers/file.ts index 59385e76..f8fe3d55 100644 --- a/src/helpers/file.ts +++ b/src/helpers/file.ts @@ -1,10 +1,20 @@ import path = require('path'); import { - appendFileSync, copyFileSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, rmdirSync, unlinkSync, writeFileSync, - + appendFileSync, + copySync, + existsSync, + readFileSync, + removeSync, + renameSync, + writeFileSync, } from 'fs-extra'; +interface InjectToFileOptions { + insertBefore?: string; + insertAfter?: string; +} + const ROOT_DIR = path.join(__dirname, '..', '..'); const TEMPLATE_DIR = process.env.NODE_ENV === 'development' ? 'skeleton' : 'dist/skeleton'; @@ -18,6 +28,10 @@ const getTargetPath = (file: string, projectName: string): string => { return path.join(getTargetDir(projectName), file); }; +const getSourcePath = (file: string): string => { + return path.join(TEMPLATE_PATH, file); +}; + const appendToFile = ( target: string, content: string, @@ -28,44 +42,15 @@ const appendToFile = ( appendFileSync(targetPath, content); }; -const copyFile = ( - source: string, - target: string, - projectName: string, -): void => { - const sourcePath = path.join(TEMPLATE_PATH, source); - const targetPath = getTargetPath(target, projectName); - const targetDir = path.dirname(targetPath); - const targetExists = existsSync(targetPath); - if (!targetExists) { - mkdirSync(targetDir, { recursive: true }); - } - - copyFileSync(sourcePath, targetPath); -}; - -const copyDir = ( +const copy = ( source: string, target: string, projectName: string, ): void => { const sourcePath = path.join(TEMPLATE_PATH, source); const targetPath = getTargetPath(target, projectName); - const targetExists = existsSync(targetPath); - if (!targetExists) { - mkdirSync(targetPath, { recursive: true }); - } - const files = readdirSync(sourcePath); - for (const file of files) { - const sourceFile = path.join(source, file); - const targetFile = path.join(target, file); - if (lstatSync(path.join(TEMPLATE_PATH, sourceFile)).isDirectory()) { - copyDir(sourceFile, targetFile, projectName); - } else { - copyFile(sourceFile, targetFile, projectName); - } - } + copySync(sourcePath, targetPath); }; const createFile = ( @@ -81,29 +66,23 @@ const createFile = ( } }; -const deleteFile = (target: string, projectName: string): void => { +const remove = (target: string, projectName: string): void => { const targetPath = getTargetPath(target, projectName); - const targetExists = existsSync(targetPath); - if (targetExists) { - unlinkSync(targetPath); - } + removeSync(targetPath); }; -const deleteDir = (target: string, projectName: string): void => { +const renameFile = ( + source: string, + target: string, + projectName: string, +): void => { + const sourcePath = getTargetPath(source, projectName); const targetPath = getTargetPath(target, projectName); - const targetExists = existsSync(targetPath); - if (targetExists) { - rmdirSync(targetPath, { recursive: true }); - } + renameSync(sourcePath, targetPath); }; -interface InjectToFileOptions { - insertBefore?: string; - insertAfter?: string; -} - const injectToFile = ( target: string, content: string, @@ -133,23 +112,14 @@ const injectToFile = ( writeFileSync(targetPath, newContent); }; -const renameFile = ( - source: string, - target: string, - projectName: string, -): void => { - const sourcePath = getTargetPath(source, projectName); - const targetPath = getTargetPath(target, projectName); - renameSync(sourcePath, targetPath); -}; - export { + TEMPLATE_PATH, getTargetDir, + getTargetPath, + getSourcePath, appendToFile, - copyDir, - copyFile, - deleteDir, - deleteFile, + copy, + remove, createFile, injectToFile, renameFile, diff --git a/src/templates/aws/addons/alb.ts b/src/templates/aws/addons/alb.ts index a9de7d62..012d1262 100644 --- a/src/templates/aws/addons/alb.ts +++ b/src/templates/aws/addons/alb.ts @@ -1,7 +1,7 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; -import { appendToFile, copyDir } from '../../../helpers/file'; +import { appendToFile, copy } from '../../../helpers/file'; const applyAlb = ({ projectName }: AwsOptions) => { const albVariablesContent = dedent` @@ -34,7 +34,7 @@ const applyAlb = ({ projectName }: AwsOptions) => { } \n`; - copyDir('aws/modules/alb', 'modules/alb', projectName); + copy('aws/modules/alb', 'modules/alb', projectName); appendToFile('main.tf', albModuleContent, projectName); appendToFile('variables.tf', albVariablesContent, projectName); appendToFile('outputs.tf', vpcOutputContent, projectName); diff --git a/src/templates/aws/addons/bastion.ts b/src/templates/aws/addons/bastion.ts index 8eca1ae8..b94c720a 100644 --- a/src/templates/aws/addons/bastion.ts +++ b/src/templates/aws/addons/bastion.ts @@ -1,7 +1,7 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; -import { appendToFile, copyDir } from '../../../helpers/file'; +import { appendToFile, copy } from '../../../helpers/file'; const applyBastion = ({ projectName }: AwsOptions) => { const bastionVariablesContent = dedent` @@ -47,7 +47,7 @@ const applyBastion = ({ projectName }: AwsOptions) => { } \n`; - copyDir('aws/modules/bastion', 'modules/bastion', projectName); + copy('aws/modules/bastion', 'modules/bastion', projectName); appendToFile('variables.tf', bastionVariablesContent, projectName); appendToFile('main.tf', bastionModuleContent, projectName); }; diff --git a/src/templates/aws/addons/common.ts b/src/templates/aws/addons/common.ts index 37cfa9ba..43e01b6f 100644 --- a/src/templates/aws/addons/common.ts +++ b/src/templates/aws/addons/common.ts @@ -1,5 +1,5 @@ import { AwsOptions } from '..'; -import { copyFile } from '../../../helpers/file'; +import { copy } from '../../../helpers/file'; const applyCommon = ({ projectName }: AwsOptions) => { const filesToCopy = [ @@ -10,7 +10,7 @@ const applyCommon = ({ projectName }: AwsOptions) => { ]; filesToCopy.forEach(fileName => { - copyFile(`aws/${fileName}`, fileName, projectName); + copy(`aws/${fileName}`, fileName, projectName); }); }; diff --git a/src/templates/aws/addons/ecr.ts b/src/templates/aws/addons/ecr.ts index 9fddea1f..24a760eb 100644 --- a/src/templates/aws/addons/ecr.ts +++ b/src/templates/aws/addons/ecr.ts @@ -1,7 +1,7 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; -import { appendToFile, copyDir } from '../../../helpers/file'; +import { appendToFile, copy } from '../../../helpers/file'; const applyEcr = ({ projectName }: AwsOptions) => { const ecrVariablesContent = dedent` @@ -19,7 +19,7 @@ const applyEcr = ({ projectName }: AwsOptions) => { } \n`; - copyDir('aws/modules/ecr', 'modules/ecr', projectName); + copy('aws/modules/ecr', 'modules/ecr', projectName); appendToFile('variables.tf', ecrVariablesContent, projectName); appendToFile('main.tf', ecrModuleContent, projectName); }; diff --git a/src/templates/aws/addons/ecs.ts b/src/templates/aws/addons/ecs.ts index 8a262856..a9b12305 100644 --- a/src/templates/aws/addons/ecs.ts +++ b/src/templates/aws/addons/ecs.ts @@ -1,7 +1,7 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; -import { appendToFile, copyDir } from '../../../helpers/file'; +import { appendToFile, copy } from '../../../helpers/file'; const applyEcs = ({ projectName }: AwsOptions) => { const bastionVariablesContent = dedent` @@ -52,7 +52,7 @@ const applyEcs = ({ projectName }: AwsOptions) => { } \n`; - copyDir('aws/modules/ecs', 'modules/ecs', projectName); + copy('aws/modules/ecs', 'modules/ecs', projectName); appendToFile('variables.tf', bastionVariablesContent, projectName); appendToFile('main.tf', ecsModuleContent, projectName); }; diff --git a/src/templates/aws/addons/log.ts b/src/templates/aws/addons/log.ts index 8305aa43..bf9fad49 100644 --- a/src/templates/aws/addons/log.ts +++ b/src/templates/aws/addons/log.ts @@ -1,7 +1,7 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; -import { appendToFile, copyDir } from '../../../helpers/file'; +import { appendToFile, copy } from '../../../helpers/file'; const applyLog = ({ projectName }: AwsOptions) => { const logModuleContent = dedent` @@ -12,7 +12,7 @@ const applyLog = ({ projectName }: AwsOptions) => { } \n`; - copyDir('aws/modules/log', 'modules/log', projectName); + copy('aws/modules/log', 'modules/log', projectName); appendToFile('main.tf', logModuleContent, projectName); }; diff --git a/src/templates/aws/addons/rds.ts b/src/templates/aws/addons/rds.ts index 0e68bc71..066578a3 100644 --- a/src/templates/aws/addons/rds.ts +++ b/src/templates/aws/addons/rds.ts @@ -1,7 +1,7 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; -import { appendToFile, copyDir } from '../../../helpers/file'; +import { appendToFile, copy } from '../../../helpers/file'; const applyRds = ({ projectName }: AwsOptions) => { const rdsVariablesContent = dedent` @@ -56,7 +56,7 @@ const applyRds = ({ projectName }: AwsOptions) => { } \n`; - copyDir('aws/modules/rds', 'modules/rds', projectName); + copy('aws/modules/rds', 'modules/rds', projectName); appendToFile('variables.tf', rdsVariablesContent, projectName); appendToFile('main.tf', rdsModuleContent, projectName); }; diff --git a/src/templates/aws/addons/s3.ts b/src/templates/aws/addons/s3.ts index 8ab298ea..d0858803 100644 --- a/src/templates/aws/addons/s3.ts +++ b/src/templates/aws/addons/s3.ts @@ -1,7 +1,7 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; -import { appendToFile, copyDir } from '../../../helpers/file'; +import { appendToFile, copy } from '../../../helpers/file'; const applyS3 = ({ projectName }: AwsOptions) => { const s3OutputContent = dedent` @@ -19,7 +19,7 @@ const applyS3 = ({ projectName }: AwsOptions) => { } \n`; - copyDir('aws/modules/s3', 'modules/s3', projectName); + copy('aws/modules/s3', 'modules/s3', projectName); appendToFile('outputs.tf', s3OutputContent, projectName); appendToFile('main.tf', s3ModuleContent, projectName); }; diff --git a/src/templates/aws/addons/securityGroup.ts b/src/templates/aws/addons/securityGroup.ts index 5c09db2e..5d2132d8 100644 --- a/src/templates/aws/addons/securityGroup.ts +++ b/src/templates/aws/addons/securityGroup.ts @@ -1,7 +1,7 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; -import { appendToFile, copyDir } from '../../../helpers/file'; +import { appendToFile, copy } from '../../../helpers/file'; const applySecurityGroup = ({ projectName }: AwsOptions) => { const securityGroupVariablesContent = dedent` @@ -22,7 +22,7 @@ const applySecurityGroup = ({ projectName }: AwsOptions) => { } \n`; - copyDir('aws/modules/security_group', 'modules/security_group', projectName); + copy('aws/modules/security_group', 'modules/security_group', projectName); appendToFile('variables.tf', securityGroupVariablesContent, projectName); appendToFile('main.tf', securityGroupModuleContent, projectName); }; diff --git a/src/templates/aws/addons/ssm.ts b/src/templates/aws/addons/ssm.ts index 43d234d7..83217d38 100644 --- a/src/templates/aws/addons/ssm.ts +++ b/src/templates/aws/addons/ssm.ts @@ -1,7 +1,7 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; -import { appendToFile, copyDir } from '../../../helpers/file'; +import { appendToFile, copy } from '../../../helpers/file'; const applySsm = ({ projectName }: AwsOptions) => { const ssmVariablesContent = dedent` @@ -24,7 +24,7 @@ const applySsm = ({ projectName }: AwsOptions) => { } \n`; - copyDir('aws/modules/ssm', 'modules/ssm', projectName); + copy('aws/modules/ssm', 'modules/ssm', projectName); appendToFile('variables.tf', ssmVariablesContent, projectName); appendToFile('main.tf', ssmModuleContent, projectName); }; diff --git a/src/templates/aws/addons/vpc.ts b/src/templates/aws/addons/vpc.ts index f5023f4c..3866b427 100644 --- a/src/templates/aws/addons/vpc.ts +++ b/src/templates/aws/addons/vpc.ts @@ -1,7 +1,7 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; -import { appendToFile, copyDir } from '../../../helpers/file'; +import { appendToFile, copy } from '../../../helpers/file'; const applyVpc = ({projectName}: AwsOptions) => { const vpcOutputContent = dedent` @@ -18,7 +18,7 @@ const applyVpc = ({projectName}: AwsOptions) => { } \n`; - copyDir('aws/modules/vpc', 'modules/vpc', projectName); + copy('aws/modules/vpc', 'modules/vpc', projectName); appendToFile('outputs.tf', vpcOutputContent, projectName); appendToFile('main.tf', vpcModuleContent, projectName); }; diff --git a/test/helpers/init.ts b/test/helpers/init.ts deleted file mode 100644 index cc9b318e..00000000 --- a/test/helpers/init.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -const path = require('path'); - -process.env.TS_NODE_PROJECT = path.resolve('test/tsconfig.json'); -process.env.NODE_ENV = 'development'; - -global.oclif = global.oclif || {}; -global.oclif.columns = 80; From 3c1af3627db5bdf432785dbc19aa5d7c74051104 Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Fri, 22 Jul 2022 15:17:25 +0700 Subject: [PATCH 02/24] [#29] Add custom matchers --- jest.config.js | 7 + package-lock.json | 821 +++++++++++++++++------- package.json | 3 +- src/commands/generate/index.ts | 2 +- src/helpers/file.ts | 2 +- src/templates/aws/addons/alb.test.ts | 34 + src/templates/aws/addons/common.test.ts | 24 + src/templates/aws/index.ts | 2 +- test/jest.setup.ts | 1 + test/matchers/file.ts | 76 +++ test/matchers/index.d.ts | 13 + test/matchers/index.ts | 11 + tsconfig.json | 3 +- 13 files changed, 751 insertions(+), 248 deletions(-) create mode 100644 src/templates/aws/addons/alb.test.ts create mode 100644 src/templates/aws/addons/common.test.ts create mode 100644 test/jest.setup.ts create mode 100644 test/matchers/file.ts create mode 100644 test/matchers/index.d.ts create mode 100644 test/matchers/index.ts diff --git a/jest.config.js b/jest.config.js index c0592c23..d33a06b2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,4 +3,11 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', testPathIgnorePatterns: ['/node_modules/', '/dist/'], + setupFilesAfterEnv: ['/test/jest.setup.ts'], + verbose: true, + globals: { + "ts-jest": { + diagnostics: false + } + } }; diff --git a/package-lock.json b/package-lock.json index a244d63c..7b759e6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,13 +24,14 @@ "@nimblehq/eslint-config-nimble": "^2.3.0", "@oclif/test": "^2", "@types/fs-extra": "^9.0.13", + "@types/glob": "^7.2.0", "@types/inquirer": "^8.2.1", "@types/jest": "^28.1.6", "@types/node": "^16.9.4", "chai": "^4.3.6", "eslint": "^8.19.0", "fs-extra": "^10.1.0", - "globby": "^11", + "glob": "^8.0.3", "jest": "^28.1.3", "oclif": "^3", "shx": "^0.3.3", @@ -637,18 +638,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -975,6 +964,26 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@jest/schemas": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", @@ -1343,26 +1352,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/@npmcli/map-workspaces/node_modules/glob": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.1.tgz", - "integrity": "sha512-cF7FYZZ47YzmCu7dDy50xSRRfO3ErRfrXuLZcNIuyiJEco0XSrGtuilG19L5xp3NcwTx7Gn+X6Tv3fmsUPTbow==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", @@ -1647,6 +1636,25 @@ "node": ">=10" } }, + "node_modules/@oclif/core/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@oclif/core/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1983,6 +1991,25 @@ "node": ">= 10.0.0" } }, + "node_modules/@oclif/plugin-plugins/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@oclif/plugin-plugins/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2627,6 +2654,26 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@typescript-eslint/utils": { "version": "5.30.5", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.5.tgz", @@ -3306,6 +3353,26 @@ "node": ">=10" } }, + "node_modules/cacache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/cacache/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -4347,18 +4414,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/eslint-import-resolver-typescript/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eslint-module-utils": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", @@ -4517,18 +4572,6 @@ "node": ">=0.10.0" } }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eslint-plugin-import/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -4660,18 +4703,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/espree": { "version": "9.3.2", "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", @@ -5362,20 +5393,19 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -5392,6 +5422,27 @@ "node": ">= 6" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/globals": { "version": "13.16.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", @@ -5419,25 +5470,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -6483,6 +6515,26 @@ } } }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jest-config/node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -6815,6 +6867,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jest-runtime/node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -7328,6 +7400,26 @@ } } }, + "node_modules/mem-fs-editor/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mem-fs/node_modules/@types/node": { "version": "15.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz", @@ -7369,9 +7461,9 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7698,6 +7790,26 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/node-gyp/node_modules/npmlog": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", @@ -7837,6 +7949,26 @@ "node": ">=10" } }, + "node_modules/npm-packlist/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/npm-pick-manifest": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", @@ -7901,15 +8033,6 @@ "node": ">= 10" } }, - "node_modules/npm-registry-fetch/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/npm-registry-fetch/node_modules/cacache": { "version": "16.0.7", "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.0.7.tgz", @@ -7948,26 +8071,6 @@ "node": ">=10" } }, - "node_modules/npm-registry-fetch/node_modules/glob": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.1.tgz", - "integrity": "sha512-cF7FYZZ47YzmCu7dDy50xSRRfO3ErRfrXuLZcNIuyiJEco0XSrGtuilG19L5xp3NcwTx7Gn+X6Tv3fmsUPTbow==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -8035,18 +8138,6 @@ "encoding": "^0.1.13" } }, - "node_modules/npm-registry-fetch/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/npm-registry-fetch/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -8950,6 +9041,26 @@ "universalify": "^0.1.0" } }, + "node_modules/qqjs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/qqjs/node_modules/globby": { "version": "10.0.2", "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", @@ -9466,6 +9577,26 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -9587,6 +9718,26 @@ "node": ">=4" } }, + "node_modules/shelljs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/shx": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.3.tgz", @@ -10119,6 +10270,26 @@ "node": ">=8" } }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -11018,6 +11189,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yeoman-environment/node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/yeoman-generator": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-5.6.1.tgz", @@ -11723,15 +11914,6 @@ "requires": { "argparse": "^2.0.1" } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } } } }, @@ -11994,6 +12176,20 @@ "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } } } }, @@ -12301,20 +12497,6 @@ "balanced-match": "^1.0.0" } }, - "glob": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.1.tgz", - "integrity": "sha512-cF7FYZZ47YzmCu7dDy50xSRRfO3ErRfrXuLZcNIuyiJEco0XSrGtuilG19L5xp3NcwTx7Gn+X6Tv3fmsUPTbow==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "minimatch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", @@ -12547,6 +12729,19 @@ "universalify": "^2.0.0" } }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -12822,6 +13017,19 @@ } } }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -13361,6 +13569,22 @@ "is-glob": "^4.0.3", "semver": "^7.3.7", "tsutils": "^3.21.0" + }, + "dependencies": { + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + } } }, "@typescript-eslint/utils": { @@ -13868,6 +14092,20 @@ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -14657,15 +14895,6 @@ "requires": { "argparse": "^2.0.1" } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } } } }, @@ -14716,15 +14945,6 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } } } }, @@ -14854,15 +15074,6 @@ "esutils": "^2.0.2" } }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -15435,17 +15646,36 @@ } }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "glob-parent": { @@ -15473,19 +15703,6 @@ } } }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -16231,6 +16448,20 @@ "strip-json-comments": "^3.1.1" }, "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -16490,6 +16721,20 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -16908,6 +17153,22 @@ "multimatch": "^5.0.0", "normalize-path": "^3.0.0", "textextensions": "^5.13.0" + }, + "dependencies": { + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + } } }, "merge-stream": { @@ -16936,9 +17197,9 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } @@ -17187,6 +17448,20 @@ "wide-align": "^1.1.5" } }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "npmlog": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", @@ -17296,6 +17571,22 @@ "ignore-walk": "^4.0.1", "npm-bundled": "^1.1.1", "npm-normalize-package-bin": "^1.0.1" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, "npm-pick-manifest": { @@ -17350,15 +17641,6 @@ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, "cacache": { "version": "16.0.7", "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.0.7.tgz", @@ -17391,20 +17673,6 @@ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true }, - "glob": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.1.tgz", - "integrity": "sha512-cF7FYZZ47YzmCu7dDy50xSRRfO3ErRfrXuLZcNIuyiJEco0XSrGtuilG19L5xp3NcwTx7Gn+X6Tv3fmsUPTbow==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -17460,15 +17728,6 @@ } } }, - "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -18145,6 +18404,20 @@ "universalify": "^0.1.0" } }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "globby": { "version": "10.0.2", "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", @@ -18522,6 +18795,22 @@ "dev": true, "requires": { "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, "run-async": { @@ -18605,6 +18894,22 @@ "glob": "^7.0.0", "interpret": "^1.0.0", "rechoir": "^0.6.2" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, "shx": { @@ -19019,6 +19324,22 @@ "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, "text-table": { @@ -19700,6 +20021,20 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } } } }, diff --git a/package.json b/package.json index 20bbb5e3..c0652b3d 100644 --- a/package.json +++ b/package.json @@ -32,13 +32,14 @@ "@nimblehq/eslint-config-nimble": "^2.3.0", "@oclif/test": "^2", "@types/fs-extra": "^9.0.13", + "@types/glob": "^7.2.0", "@types/inquirer": "^8.2.1", "@types/jest": "^28.1.6", "@types/node": "^16.9.4", "chai": "^4.3.6", "eslint": "^8.19.0", "fs-extra": "^10.1.0", - "globby": "^11", + "glob": "^8.0.3", "jest": "^28.1.3", "oclif": "^3", "shx": "^0.3.3", diff --git a/src/commands/generate/index.ts b/src/commands/generate/index.ts index 7883f12c..1dbeb412 100644 --- a/src/commands/generate/index.ts +++ b/src/commands/generate/index.ts @@ -7,7 +7,7 @@ import { generateAwsTemplate } from '../../templates/aws'; type GeneralOptions = { projectName: string; - provider: string; + provider: 'aws' | 'gcp' | 'heroku'; }; const providerChoices = [ diff --git a/src/helpers/file.ts b/src/helpers/file.ts index f8fe3d55..3ab42dec 100644 --- a/src/helpers/file.ts +++ b/src/helpers/file.ts @@ -17,7 +17,7 @@ interface InjectToFileOptions { const ROOT_DIR = path.join(__dirname, '..', '..'); const TEMPLATE_DIR = - process.env.NODE_ENV === 'development' ? 'skeleton' : 'dist/skeleton'; + process.env.NODE_ENV === 'production' ? 'dist/skeleton' : 'skeleton'; const TEMPLATE_PATH = path.join(ROOT_DIR, TEMPLATE_DIR); const getTargetDir = (projectName: string): string => { diff --git a/src/templates/aws/addons/alb.test.ts b/src/templates/aws/addons/alb.test.ts new file mode 100644 index 00000000..4f33563b --- /dev/null +++ b/src/templates/aws/addons/alb.test.ts @@ -0,0 +1,34 @@ +import { AwsOptions } from '..'; +import { remove } from '../../../helpers/file'; +import applyAlb from './alb'; +import applyCommon from './common'; + +describe('alb add-on', () => { + const projectName = 'projectName'; + + afterEach(() => { + jest.clearAllMocks(); + remove('/', projectName); + }); + + describe('given valid AwsOptions', () => { + beforeEach(() => { + const awsOptions: AwsOptions = { projectName, provider: 'aws', infrastructureType: 'basic', awsRegion: 'ap-southeast-1' }; + + applyCommon(awsOptions); + applyAlb(awsOptions); + }); + + it('creates expected files', () => { + expect(projectName).toHaveFiles(['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/alb/main.tf']); + }); + + it('creates expected folders', () => { + expect(projectName).toHaveDirectory('modules/alb/'); + }); + + it('adds alb to main.tf', () => { + expect(projectName).toHaveContentInFile('main.tf', 'module "alb" {'); + }); + }); +}); diff --git a/src/templates/aws/addons/common.test.ts b/src/templates/aws/addons/common.test.ts new file mode 100644 index 00000000..8415211c --- /dev/null +++ b/src/templates/aws/addons/common.test.ts @@ -0,0 +1,24 @@ +import { AwsOptions } from '..'; +import { remove } from '../../../helpers/file'; +import applyCommon from './common'; + +describe('common add-on', () => { + const projectName = 'projectName'; + + afterEach(() => { + jest.clearAllMocks(); + remove('/', projectName); + }); + + describe('given valid AwsOptions', () => { + beforeEach(() => { + const awsOptions: AwsOptions = { projectName, provider: 'aws', infrastructureType: 'basic', awsRegion: 'ap-southeast-1' }; + + applyCommon(awsOptions); + }); + + it('creates expected files', () => { + expect(projectName).toHaveFiles(['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf']); + }); + }); +}); diff --git a/src/templates/aws/index.ts b/src/templates/aws/index.ts index 8453bf44..f330b339 100644 --- a/src/templates/aws/index.ts +++ b/src/templates/aws/index.ts @@ -31,7 +31,7 @@ const awsChoices = [ ]; type AwsOptions = GeneralOptions & { - infrastructureType: string; + infrastructureType: 'basic' | 'advanced'; awsRegion: string; } diff --git a/test/jest.setup.ts b/test/jest.setup.ts new file mode 100644 index 00000000..029fd3a3 --- /dev/null +++ b/test/jest.setup.ts @@ -0,0 +1 @@ +import './matchers'; diff --git a/test/matchers/file.ts b/test/matchers/file.ts new file mode 100644 index 00000000..03f6effa --- /dev/null +++ b/test/matchers/file.ts @@ -0,0 +1,76 @@ +import { readFileSync } from 'fs-extra'; +import { sync } from 'glob'; +import { diff } from 'jest-diff'; + +const toHaveFile = (projectName: string, expectedFile: string) => { + const actualFiles = sync(`**/*.*`, { cwd: projectName }); + const pass = actualFiles.includes(expectedFile); + + return { + pass, + message: pass + ? () => `expected ${projectName} to not include ${expectedFile}` + : () => `expected ${projectName} to include ${expectedFile}`, + }; +}; + +const toHaveFiles = (projectName: string, expectedFiles: string[]) => { + if (!Array.isArray(expectedFiles)) { + throw new Error('Expected files must be an array'); + } + + const actualFiles = sync(`**/*.*`, { cwd: projectName }); + const pass = expectedFiles.every(file => actualFiles.includes(file)); + const diffs = diff(expectedFiles, actualFiles, { expand: false }); + + return { + pass, + message: pass + ? () => `expected ${projectName} not to have [${expectedFiles}]\n${diffs}` + : () => `expected ${projectName} to have [${expectedFiles}]\n${diffs}`, + }; +}; + +const toHaveDirectory = (projectName: string, expectedDirectory: string) => { + const actualDirectories = sync(`**/`, { cwd: projectName }); + + const pass = actualDirectories.includes(expectedDirectory); + + return { + pass, + message: pass + ? () => `expected ${projectName} to not include ${expectedDirectory}` + : () => `expected ${projectName} to include ${expectedDirectory}`, + }; +}; + +const toHaveDirectories = (projectName: string, expectedDirectories: string[]) => { + if (!Array.isArray(expectedDirectories)) { + throw new Error('Expected directories must be an array'); + } + + const actualDirectories = sync(`**/`, { cwd: projectName }); + const pass = expectedDirectories.every(directory => actualDirectories.includes(directory)); + const diffs = diff(expectedDirectories, actualDirectories, { expand: false }); + + return { + pass, + message: pass + ? () => `expected ${projectName} not to have [${expectedDirectories}]\n${diffs}` + : () => `expected ${projectName} to have [${expectedDirectories}]\n${diffs}`, + }; +}; + +const toHaveContentInFile = (projectName: string, expectedFile: string, expectedContent: string) => { + const actualContent = readFileSync(`${projectName}/${expectedFile}`, 'utf8'); + const pass = actualContent.includes(expectedContent); + + return { + pass, + message: pass + ? () => `expected ${projectName} to not include ${expectedContent} in ${expectedFile}` + : () => `expected ${projectName} to include ${expectedContent} in ${expectedFile}`, + }; +}; + +export { toHaveFile, toHaveFiles, toHaveDirectory, toHaveDirectories, toHaveContentInFile }; diff --git a/test/matchers/index.d.ts b/test/matchers/index.d.ts new file mode 100644 index 00000000..f2f571c0 --- /dev/null +++ b/test/matchers/index.d.ts @@ -0,0 +1,13 @@ +declare global { + namespace jest { + interface Matchers { + toHaveFile: (expectedFile: string) => R; + toHaveFiles: (expectedFiles: string[]) => R; + toHaveDirectory: (expectedDirectory: string) => R; + toHaveDirectories: (expectedDirectories: string[]) => R; + toHaveContentInFile: (expectedFile: string, expectedContent: string) => R; + } + } +} + +export {}; diff --git a/test/matchers/index.ts b/test/matchers/index.ts new file mode 100644 index 00000000..bbc47396 --- /dev/null +++ b/test/matchers/index.ts @@ -0,0 +1,11 @@ +import { toHaveFile, toHaveFiles, toHaveDirectory, toHaveDirectories, toHaveContentInFile } from './file'; + +const matchers = { + toHaveFile, + toHaveFiles, + toHaveDirectory, + toHaveDirectories, + toHaveContentInFile, +}; + +expect.extend(matchers); diff --git a/tsconfig.json b/tsconfig.json index 871b4c0a..62ebbb0d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ "composite": true, }, "include": [ - "src/**/*" + "src/**/*", + "test/**/*.d.ts", ] } From 1a0ddd7f7e82109b30324bd3a42257d22c382312 Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Mon, 25 Jul 2022 14:18:38 +0700 Subject: [PATCH 03/24] [#29] Try fixing tests --- test/commands/generate/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/commands/generate/index.test.ts b/test/commands/generate/index.test.ts index 40de7aae..824f7699 100644 --- a/test/commands/generate/index.test.ts +++ b/test/commands/generate/index.test.ts @@ -2,7 +2,7 @@ import { test, expect } from '@oclif/test'; import { existsSync, removeSync } from 'fs-extra'; import * as inquirer from 'inquirer'; -describe('running command generate', () => { +xdescribe('running command generate', () => { const projectName = 'app-name'; afterEach(() => { From 272ad0909c6903df1b15829687014319b69930f3 Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Mon, 25 Jul 2022 14:49:45 +0700 Subject: [PATCH 04/24] [#29] Add collectCoverageFrom to Jest config --- jest.config.js | 6 ++++++ test/commands/generate/index.test.ts | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/jest.config.js b/jest.config.js index d33a06b2..f273e898 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,6 +5,12 @@ module.exports = { testPathIgnorePatterns: ['/node_modules/', '/dist/'], setupFilesAfterEnv: ['/test/jest.setup.ts'], verbose: true, + collectCoverageFrom: [ + 'src/**/*.ts', + '!src/**/*.d.ts', + '!src/index.ts', + 'test/commands/**/*.ts', + ], globals: { "ts-jest": { diagnostics: false diff --git a/test/commands/generate/index.test.ts b/test/commands/generate/index.test.ts index 824f7699..f0903d38 100644 --- a/test/commands/generate/index.test.ts +++ b/test/commands/generate/index.test.ts @@ -2,7 +2,7 @@ import { test, expect } from '@oclif/test'; import { existsSync, removeSync } from 'fs-extra'; import * as inquirer from 'inquirer'; -xdescribe('running command generate', () => { +describe('running command generate', () => { const projectName = 'app-name'; afterEach(() => { @@ -18,7 +18,7 @@ xdescribe('running command generate', () => { .stdout() .stub(inquirer, 'prompt', stubResponse) .command(['generate', projectName]) - .it('creates a new project folder', (ctx) => { + .it('creates a new project folder', async(ctx) => { expect(ctx.stdout).to.contains('The infrastructure has been generated!'); expect(existsSync(projectName)).to.eq(true); }); From 14b188ce894f16f17d7ec8e2869d304a3061b145 Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Mon, 25 Jul 2022 16:47:21 +0700 Subject: [PATCH 05/24] [#29] Move commands/generate test into src folder --- jest.config.js | 3 +-- src/commands/generate/index.test.ts | 34 ++++++++++++++++++++++++++++ test/commands/generate/index.test.ts | 25 -------------------- 3 files changed, 35 insertions(+), 27 deletions(-) create mode 100644 src/commands/generate/index.test.ts delete mode 100644 test/commands/generate/index.test.ts diff --git a/jest.config.js b/jest.config.js index f273e898..91de994d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -9,11 +9,10 @@ module.exports = { 'src/**/*.ts', '!src/**/*.d.ts', '!src/index.ts', - 'test/commands/**/*.ts', ], globals: { "ts-jest": { diagnostics: false } - } + }, }; diff --git a/src/commands/generate/index.test.ts b/src/commands/generate/index.test.ts new file mode 100644 index 00000000..9a216b70 --- /dev/null +++ b/src/commands/generate/index.test.ts @@ -0,0 +1,34 @@ +import { existsSync, removeSync } from 'fs-extra'; +import { prompt } from 'inquirer'; + +import Generator from '.'; + +jest.mock('inquirer'); + +describe('running command generate directly', () => { + const projectName = 'app-name'; + const stdoutSpy = jest.spyOn(process.stdout, 'write'); + + beforeEach(() => { + (prompt as unknown as jest.Mock) + .mockResolvedValueOnce({ provider: 'aws' }) + .mockResolvedValueOnce({ infrastructureType: 'advanced' }); + }); + + afterEach(() => { + jest.resetAllMocks(); + removeSync(projectName); + }); + + it('creates a new project folder', async() => { + await Generator.run([projectName]); + + expect(existsSync(projectName)).toBe(true); + }); + + it('displays the success message', async() => { + await Generator.run([projectName]); + + expect(stdoutSpy).toHaveBeenCalledWith('The infrastructure has been generated!\n'); + }); +}); diff --git a/test/commands/generate/index.test.ts b/test/commands/generate/index.test.ts deleted file mode 100644 index f0903d38..00000000 --- a/test/commands/generate/index.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { test, expect } from '@oclif/test'; -import { existsSync, removeSync } from 'fs-extra'; -import * as inquirer from 'inquirer'; - -describe('running command generate', () => { - const projectName = 'app-name'; - - afterEach(() => { - jest.resetAllMocks(); - removeSync(projectName); - }); - - const stubResponse = jest.fn(); - stubResponse.mockReturnValueOnce({ provider: 'aws' }); - stubResponse.mockReturnValueOnce({ infrastructureType: 'advanced' }); - - test - .stdout() - .stub(inquirer, 'prompt', stubResponse) - .command(['generate', projectName]) - .it('creates a new project folder', async(ctx) => { - expect(ctx.stdout).to.contains('The infrastructure has been generated!'); - expect(existsSync(projectName)).to.eq(true); - }); -}); From d0131094ea27b9ae7058f9de377d4635d0ce30cb Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Mon, 25 Jul 2022 16:55:53 +0700 Subject: [PATCH 06/24] [#29] Try removing postProcess --- src/commands/generate/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/generate/index.ts b/src/commands/generate/index.ts index 1dbeb412..84d3c7b0 100644 --- a/src/commands/generate/index.ts +++ b/src/commands/generate/index.ts @@ -71,7 +71,7 @@ export default class Generator extends Command { this.error('This provider has not been implemented!'); } - await this.postProcess(generalOptions); + // await this.postProcess(generalOptions); this.log('The infrastructure has been generated!'); } catch (error) { From 9ce1876eae842793b10177a78c6a8835672c4b2e Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Mon, 25 Jul 2022 17:12:28 +0700 Subject: [PATCH 07/24] [#29] Handle try catch of detectTerraform --- src/commands/generate/index.ts | 2 +- src/helpers/terraform.ts | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/commands/generate/index.ts b/src/commands/generate/index.ts index 84d3c7b0..1dbeb412 100644 --- a/src/commands/generate/index.ts +++ b/src/commands/generate/index.ts @@ -71,7 +71,7 @@ export default class Generator extends Command { this.error('This provider has not been implemented!'); } - // await this.postProcess(generalOptions); + await this.postProcess(generalOptions); this.log('The infrastructure has been generated!'); } catch (error) { diff --git a/src/helpers/terraform.ts b/src/helpers/terraform.ts index 8985c688..f4f44e21 100644 --- a/src/helpers/terraform.ts +++ b/src/helpers/terraform.ts @@ -1,13 +1,15 @@ import { runCommand } from './child-process'; const detectTerraform = async() => { - const terraformPath = await runCommand('which', ['terraform']); - if (terraformPath) { + try { + await runCommand('which', ['terraform']); + return true; - } + } catch (error) { + console.error('Terraform not found. Please install terraform.'); - console.log('Terraform not found. Please install terraform.'); - return false; + return false; + } }; const formatCode = (projectDir: string) => { From 3df8c688658c0ac921a1f522c6a60e2ff62b6a3b Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Mon, 25 Jul 2022 17:28:32 +0700 Subject: [PATCH 08/24] [#29] Rename the expect object --- src/commands/generate/index.test.ts | 18 ++++++++---------- src/templates/aws/addons/alb.test.ts | 22 +++++++++++----------- src/templates/aws/addons/common.test.ts | 8 ++++---- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/commands/generate/index.test.ts b/src/commands/generate/index.test.ts index 9a216b70..0406b947 100644 --- a/src/commands/generate/index.test.ts +++ b/src/commands/generate/index.test.ts @@ -6,29 +6,27 @@ import Generator from '.'; jest.mock('inquirer'); describe('running command generate directly', () => { - const projectName = 'app-name'; + const projectDir = 'app-name'; const stdoutSpy = jest.spyOn(process.stdout, 'write'); - beforeEach(() => { + beforeEach(async() => { (prompt as unknown as jest.Mock) .mockResolvedValueOnce({ provider: 'aws' }) .mockResolvedValueOnce({ infrastructureType: 'advanced' }); + + await Generator.run([projectDir]); }); afterEach(() => { jest.resetAllMocks(); - removeSync(projectName); + removeSync(projectDir); }); - it('creates a new project folder', async() => { - await Generator.run([projectName]); - - expect(existsSync(projectName)).toBe(true); + it('creates a new project folder', () => { + expect(existsSync(projectDir)).toBe(true); }); - it('displays the success message', async() => { - await Generator.run([projectName]); - + it('displays the success message', () => { expect(stdoutSpy).toHaveBeenCalledWith('The infrastructure has been generated!\n'); }); }); diff --git a/src/templates/aws/addons/alb.test.ts b/src/templates/aws/addons/alb.test.ts index 4f33563b..ce021956 100644 --- a/src/templates/aws/addons/alb.test.ts +++ b/src/templates/aws/addons/alb.test.ts @@ -4,31 +4,31 @@ import applyAlb from './alb'; import applyCommon from './common'; describe('alb add-on', () => { - const projectName = 'projectName'; - - afterEach(() => { - jest.clearAllMocks(); - remove('/', projectName); - }); - describe('given valid AwsOptions', () => { + const projectDir = 'projectDir'; + beforeEach(() => { - const awsOptions: AwsOptions = { projectName, provider: 'aws', infrastructureType: 'basic', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; applyCommon(awsOptions); applyAlb(awsOptions); }); + afterEach(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + it('creates expected files', () => { - expect(projectName).toHaveFiles(['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/alb/main.tf']); + expect(projectDir).toHaveFiles(['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/alb/main.tf']); }); it('creates expected folders', () => { - expect(projectName).toHaveDirectory('modules/alb/'); + expect(projectDir).toHaveDirectory('modules/alb/'); }); it('adds alb to main.tf', () => { - expect(projectName).toHaveContentInFile('main.tf', 'module "alb" {'); + expect(projectDir).toHaveContentInFile('main.tf', 'module "alb" {'); }); }); }); diff --git a/src/templates/aws/addons/common.test.ts b/src/templates/aws/addons/common.test.ts index 8415211c..858e1d20 100644 --- a/src/templates/aws/addons/common.test.ts +++ b/src/templates/aws/addons/common.test.ts @@ -3,22 +3,22 @@ import { remove } from '../../../helpers/file'; import applyCommon from './common'; describe('common add-on', () => { - const projectName = 'projectName'; + const projectDir = 'projectDir'; afterEach(() => { jest.clearAllMocks(); - remove('/', projectName); + remove('/', projectDir); }); describe('given valid AwsOptions', () => { beforeEach(() => { - const awsOptions: AwsOptions = { projectName, provider: 'aws', infrastructureType: 'basic', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'basic', awsRegion: 'ap-southeast-1' }; applyCommon(awsOptions); }); it('creates expected files', () => { - expect(projectName).toHaveFiles(['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf']); + expect(projectDir).toHaveFiles(['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf']); }); }); }); From 2d359a7cc73d7a6867c56570e1a245a11c45e21d Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Mon, 25 Jul 2022 17:51:28 +0700 Subject: [PATCH 09/24] [#29] Add try-catch to postProcess --- src/commands/generate/index.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/commands/generate/index.ts b/src/commands/generate/index.ts index 1dbeb412..7cb3f18b 100644 --- a/src/commands/generate/index.ts +++ b/src/commands/generate/index.ts @@ -80,8 +80,12 @@ export default class Generator extends Command { } private async postProcess(generalOptions: GeneralOptions): Promise { - if (await detectTerraform()) { - formatCode(getTargetDir(generalOptions.projectName)); + try { + if (await detectTerraform()) { + formatCode(getTargetDir(generalOptions.projectName)); + } + } catch (error) { + console.error(error); } } } From 8f5613fa02ac437e5347557623bf171bec5a6751 Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Mon, 25 Jul 2022 17:55:48 +0700 Subject: [PATCH 10/24] [#29] Add try-catch to formatCode --- src/helpers/terraform.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/helpers/terraform.ts b/src/helpers/terraform.ts index f4f44e21..43947128 100644 --- a/src/helpers/terraform.ts +++ b/src/helpers/terraform.ts @@ -13,7 +13,11 @@ const detectTerraform = async() => { }; const formatCode = (projectDir: string) => { - runCommand('terraform', ['fmt'], projectDir); + try { + runCommand('terraform', [' fmt'], projectDir); + } catch (error) { + console.error(error); + } }; export { From 15ad616662b255aae24044ce594496a90de6c3de Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Mon, 25 Jul 2022 18:01:09 +0700 Subject: [PATCH 11/24] [#29] Change to use remove instead of removeSync --- src/commands/generate/index.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/commands/generate/index.test.ts b/src/commands/generate/index.test.ts index 0406b947..34492be6 100644 --- a/src/commands/generate/index.test.ts +++ b/src/commands/generate/index.test.ts @@ -1,7 +1,8 @@ -import { existsSync, removeSync } from 'fs-extra'; +import { existsSync } from 'fs-extra'; import { prompt } from 'inquirer'; import Generator from '.'; +import { remove } from '../../helpers/file'; jest.mock('inquirer'); @@ -19,7 +20,7 @@ describe('running command generate directly', () => { afterEach(() => { jest.resetAllMocks(); - removeSync(projectDir); + remove('/', projectDir); }); it('creates a new project folder', () => { From 6898b42b3ed62b627667c18b62d8546260f885fe Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Mon, 25 Jul 2022 18:07:25 +0700 Subject: [PATCH 12/24] [#29] Copy steps instead of putting on beforeEach --- src/commands/generate/index.test.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/commands/generate/index.test.ts b/src/commands/generate/index.test.ts index 34492be6..96093c6a 100644 --- a/src/commands/generate/index.test.ts +++ b/src/commands/generate/index.test.ts @@ -10,24 +10,29 @@ describe('running command generate directly', () => { const projectDir = 'app-name'; const stdoutSpy = jest.spyOn(process.stdout, 'write'); - beforeEach(async() => { + beforeEach(() => { (prompt as unknown as jest.Mock) .mockResolvedValueOnce({ provider: 'aws' }) .mockResolvedValueOnce({ infrastructureType: 'advanced' }); - - await Generator.run([projectDir]); }); afterEach(() => { jest.resetAllMocks(); - remove('/', projectDir); }); - it('creates a new project folder', () => { + it('creates a new project folder', async() => { + await Generator.run([projectDir]); + expect(existsSync(projectDir)).toBe(true); + + remove('/', projectDir); }); - it('displays the success message', () => { + it('displays the success message', async() => { + await Generator.run([projectDir]); + expect(stdoutSpy).toHaveBeenCalledWith('The infrastructure has been generated!\n'); + + remove('/', projectDir); }); }); From 3f53b3f290e9edcf484a38b49acccba35fa0e28b Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Mon, 25 Jul 2022 18:28:11 +0700 Subject: [PATCH 13/24] [#29] Add async to formatCode --- src/commands/generate/index.test.ts | 17 ++++++----------- src/commands/generate/index.ts | 2 +- src/helpers/terraform.ts | 4 ++-- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/commands/generate/index.test.ts b/src/commands/generate/index.test.ts index 96093c6a..34492be6 100644 --- a/src/commands/generate/index.test.ts +++ b/src/commands/generate/index.test.ts @@ -10,29 +10,24 @@ describe('running command generate directly', () => { const projectDir = 'app-name'; const stdoutSpy = jest.spyOn(process.stdout, 'write'); - beforeEach(() => { + beforeEach(async() => { (prompt as unknown as jest.Mock) .mockResolvedValueOnce({ provider: 'aws' }) .mockResolvedValueOnce({ infrastructureType: 'advanced' }); + + await Generator.run([projectDir]); }); afterEach(() => { jest.resetAllMocks(); + remove('/', projectDir); }); - it('creates a new project folder', async() => { - await Generator.run([projectDir]); - + it('creates a new project folder', () => { expect(existsSync(projectDir)).toBe(true); - - remove('/', projectDir); }); - it('displays the success message', async() => { - await Generator.run([projectDir]); - + it('displays the success message', () => { expect(stdoutSpy).toHaveBeenCalledWith('The infrastructure has been generated!\n'); - - remove('/', projectDir); }); }); diff --git a/src/commands/generate/index.ts b/src/commands/generate/index.ts index 7cb3f18b..01b4d661 100644 --- a/src/commands/generate/index.ts +++ b/src/commands/generate/index.ts @@ -82,7 +82,7 @@ export default class Generator extends Command { private async postProcess(generalOptions: GeneralOptions): Promise { try { if (await detectTerraform()) { - formatCode(getTargetDir(generalOptions.projectName)); + await formatCode(getTargetDir(generalOptions.projectName)); } } catch (error) { console.error(error); diff --git a/src/helpers/terraform.ts b/src/helpers/terraform.ts index 43947128..5fc030c5 100644 --- a/src/helpers/terraform.ts +++ b/src/helpers/terraform.ts @@ -12,9 +12,9 @@ const detectTerraform = async() => { } }; -const formatCode = (projectDir: string) => { +const formatCode = async(projectDir: string) => { try { - runCommand('terraform', [' fmt'], projectDir); + await runCommand('terraform', [' fmt'], projectDir); } catch (error) { console.error(error); } From 75d2ab6221d771d6f6083c7e15281c70ad5e5492 Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Mon, 25 Jul 2022 21:27:12 +0700 Subject: [PATCH 14/24] [#29] Support string array for toHaveContentInFile --- test/matchers/file.ts | 49 +++++++++++++++++++++++----------------- test/matchers/index.d.ts | 2 +- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/test/matchers/file.ts b/test/matchers/file.ts index 03f6effa..436a787b 100644 --- a/test/matchers/file.ts +++ b/test/matchers/file.ts @@ -2,74 +2,81 @@ import { readFileSync } from 'fs-extra'; import { sync } from 'glob'; import { diff } from 'jest-diff'; -const toHaveFile = (projectName: string, expectedFile: string) => { - const actualFiles = sync(`**/*.*`, { cwd: projectName }); +const toHaveFile = (projectDir: string, expectedFile: string) => { + const actualFiles = sync(`**/*.*`, { cwd: projectDir }); const pass = actualFiles.includes(expectedFile); return { pass, message: pass - ? () => `expected ${projectName} to not include ${expectedFile}` - : () => `expected ${projectName} to include ${expectedFile}`, + ? () => `expected ${projectDir} to not include ${expectedFile}` + : () => `expected ${projectDir} to include ${expectedFile}`, }; }; -const toHaveFiles = (projectName: string, expectedFiles: string[]) => { +const toHaveFiles = (projectDir: string, expectedFiles: string[]) => { if (!Array.isArray(expectedFiles)) { throw new Error('Expected files must be an array'); } - const actualFiles = sync(`**/*.*`, { cwd: projectName }); + const actualFiles = sync(`**/*.*`, { cwd: projectDir }); const pass = expectedFiles.every(file => actualFiles.includes(file)); const diffs = diff(expectedFiles, actualFiles, { expand: false }); return { pass, message: pass - ? () => `expected ${projectName} not to have [${expectedFiles}]\n${diffs}` - : () => `expected ${projectName} to have [${expectedFiles}]\n${diffs}`, + ? () => `expected ${projectDir} not to have [${expectedFiles}]\n${diffs}` + : () => `expected ${projectDir} to have [${expectedFiles}]\n${diffs}`, }; }; -const toHaveDirectory = (projectName: string, expectedDirectory: string) => { - const actualDirectories = sync(`**/`, { cwd: projectName }); +const toHaveDirectory = (projectDir: string, expectedDirectory: string) => { + const actualDirectories = sync(`**/`, { cwd: projectDir }); const pass = actualDirectories.includes(expectedDirectory); return { pass, message: pass - ? () => `expected ${projectName} to not include ${expectedDirectory}` - : () => `expected ${projectName} to include ${expectedDirectory}`, + ? () => `expected ${projectDir} to not include "${expectedDirectory}"` + : () => `expected ${projectDir} to include "${expectedDirectory}"`, }; }; -const toHaveDirectories = (projectName: string, expectedDirectories: string[]) => { +const toHaveDirectories = (projectDir: string, expectedDirectories: string[]) => { if (!Array.isArray(expectedDirectories)) { throw new Error('Expected directories must be an array'); } - const actualDirectories = sync(`**/`, { cwd: projectName }); + const actualDirectories = sync(`**/`, { cwd: projectDir }); const pass = expectedDirectories.every(directory => actualDirectories.includes(directory)); const diffs = diff(expectedDirectories, actualDirectories, { expand: false }); return { pass, message: pass - ? () => `expected ${projectName} not to have [${expectedDirectories}]\n${diffs}` - : () => `expected ${projectName} to have [${expectedDirectories}]\n${diffs}`, + ? () => `expected ${projectDir} not to have "${expectedDirectories}"\n${diffs}` + : () => `expected ${projectDir} to have "${expectedDirectories}"\n${diffs}`, }; }; -const toHaveContentInFile = (projectName: string, expectedFile: string, expectedContent: string) => { - const actualContent = readFileSync(`${projectName}/${expectedFile}`, 'utf8'); - const pass = actualContent.includes(expectedContent); +const toHaveContentInFile = (projectDir: string, expectedFile: string, expectedContent: string | string[]) => { + let expectedContentArray: string[]; + if (!Array.isArray(expectedContent)) { + expectedContentArray = [expectedContent]; + } else { + expectedContentArray = expectedContent; + } + + const actualContent = readFileSync(`${projectDir}/${expectedFile}`, 'utf8'); + const pass = expectedContentArray.every(content => actualContent.includes(content)); return { pass, message: pass - ? () => `expected ${projectName} to not include ${expectedContent} in ${expectedFile}` - : () => `expected ${projectName} to include ${expectedContent} in ${expectedFile}`, + ? () => `expected ${projectDir} to not have "${expectedContent}" in "${expectedFile}"` + : () => `expected ${projectDir} to have "${expectedContent}" in "${expectedFile}"`, }; }; diff --git a/test/matchers/index.d.ts b/test/matchers/index.d.ts index f2f571c0..ad8c11ad 100644 --- a/test/matchers/index.d.ts +++ b/test/matchers/index.d.ts @@ -5,7 +5,7 @@ declare global { toHaveFiles: (expectedFiles: string[]) => R; toHaveDirectory: (expectedDirectory: string) => R; toHaveDirectories: (expectedDirectories: string[]) => R; - toHaveContentInFile: (expectedFile: string, expectedContent: string) => R; + toHaveContentInFile: (expectedFile: string, expectedContent: string | string[]) => R; } } } From 3726c22f14c1e77a3f154c9b80644fd79bbcf9df Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Tue, 26 Jul 2022 10:11:37 +0700 Subject: [PATCH 15/24] [#29] Add tests for addons --- src/helpers/terraform.ts | 2 +- src/templates/aws/addons/alb.test.ts | 28 ++++-- src/templates/aws/addons/alb.ts | 11 ++- src/templates/aws/addons/bastion.test.ts | 40 ++++++++ src/templates/aws/addons/bastion.ts | 87 ++++++++-------- src/templates/aws/addons/common.test.ts | 22 +++-- src/templates/aws/addons/ecr.test.ts | 40 ++++++++ src/templates/aws/addons/ecr.ts | 29 +++--- src/templates/aws/addons/ecs.test.ts | 40 ++++++++ src/templates/aws/addons/ecs.ts | 99 ++++++++++--------- src/templates/aws/addons/log.test.ts | 36 +++++++ src/templates/aws/addons/log.ts | 15 +-- src/templates/aws/addons/rds.test.ts | 40 ++++++++ src/templates/aws/addons/rds.ts | 85 ++++++++-------- src/templates/aws/addons/region.test.ts | 33 +++++++ src/templates/aws/addons/region.ts | 19 ++-- src/templates/aws/addons/s3.test.ts | 40 ++++++++ src/templates/aws/addons/s3.ts | 29 +++--- .../aws/addons/securityGroup.test.ts | 40 ++++++++ src/templates/aws/addons/securityGroup.ts | 33 ++++--- src/templates/aws/addons/ssm.test.ts | 40 ++++++++ src/templates/aws/addons/ssm.ts | 37 +++---- src/templates/aws/addons/vpc.test.ts | 40 ++++++++ src/templates/aws/addons/vpc.ts | 29 +++--- 24 files changed, 663 insertions(+), 251 deletions(-) create mode 100644 src/templates/aws/addons/bastion.test.ts create mode 100644 src/templates/aws/addons/ecr.test.ts create mode 100644 src/templates/aws/addons/ecs.test.ts create mode 100644 src/templates/aws/addons/log.test.ts create mode 100644 src/templates/aws/addons/rds.test.ts create mode 100644 src/templates/aws/addons/region.test.ts create mode 100644 src/templates/aws/addons/s3.test.ts create mode 100644 src/templates/aws/addons/securityGroup.test.ts create mode 100644 src/templates/aws/addons/ssm.test.ts create mode 100644 src/templates/aws/addons/vpc.test.ts diff --git a/src/helpers/terraform.ts b/src/helpers/terraform.ts index 5fc030c5..0bb034c1 100644 --- a/src/helpers/terraform.ts +++ b/src/helpers/terraform.ts @@ -14,7 +14,7 @@ const detectTerraform = async() => { const formatCode = async(projectDir: string) => { try { - await runCommand('terraform', [' fmt'], projectDir); + await runCommand('terraform', ['fmt'], projectDir); } catch (error) { console.error(error); } diff --git a/src/templates/aws/addons/alb.test.ts b/src/templates/aws/addons/alb.test.ts index ce021956..80c55ac0 100644 --- a/src/templates/aws/addons/alb.test.ts +++ b/src/templates/aws/addons/alb.test.ts @@ -1,34 +1,44 @@ import { AwsOptions } from '..'; import { remove } from '../../../helpers/file'; -import applyAlb from './alb'; +import applyAlb, { albModuleContent, albOutputsContent, albVariablesContent } from './alb'; import applyCommon from './common'; -describe('alb add-on', () => { - describe('given valid AwsOptions', () => { - const projectDir = 'projectDir'; +describe('ALB add-on', () => { + describe('given valid AWS options', () => { + const projectDir = 'alb-addon-test'; - beforeEach(() => { + beforeAll(() => { const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; applyCommon(awsOptions); applyAlb(awsOptions); }); - afterEach(() => { + afterAll(() => { jest.clearAllMocks(); remove('/', projectDir); }); it('creates expected files', () => { - expect(projectDir).toHaveFiles(['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/alb/main.tf']); + const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/alb/main.tf', 'modules/alb/variables.tf']; + + expect(projectDir).toHaveFiles(expectedFiles); }); it('creates expected folders', () => { expect(projectDir).toHaveDirectory('modules/alb/'); }); - it('adds alb to main.tf', () => { - expect(projectDir).toHaveContentInFile('main.tf', 'module "alb" {'); + it('adds ALB module to main.tf', () => { + expect(projectDir).toHaveContentInFile('main.tf', albModuleContent); + }); + + it('adds ALB variables to variables.tf', () => { + expect(projectDir).toHaveContentInFile('variables.tf', albVariablesContent); + }); + + it('adds ALB outputs to outputs.tf', () => { + expect(projectDir).toHaveContentInFile('outputs.tf', albOutputsContent); }); }); }); diff --git a/src/templates/aws/addons/alb.ts b/src/templates/aws/addons/alb.ts index 012d1262..7274a548 100644 --- a/src/templates/aws/addons/alb.ts +++ b/src/templates/aws/addons/alb.ts @@ -3,8 +3,7 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; import { appendToFile, copy } from '../../../helpers/file'; -const applyAlb = ({ projectName }: AwsOptions) => { - const albVariablesContent = dedent` +const albVariablesContent = dedent` variable "health_check_path" { description = "Application health check path" type = string @@ -15,7 +14,7 @@ const applyAlb = ({ projectName }: AwsOptions) => { type = string } \n`; - const albModuleContent = dedent` +const albModuleContent = dedent` module "alb" { source = "./modules/alb" @@ -27,17 +26,19 @@ const applyAlb = ({ projectName }: AwsOptions) => { health_check_path = var.health_check_path } \n`; - const vpcOutputContent = dedent` +const albOutputsContent = dedent` output "alb_dns_name" { description = "ALB DNS" value = module.alb.alb_dns_name } \n`; +const applyAlb = ({ projectName }: AwsOptions) => { copy('aws/modules/alb', 'modules/alb', projectName); appendToFile('main.tf', albModuleContent, projectName); appendToFile('variables.tf', albVariablesContent, projectName); - appendToFile('outputs.tf', vpcOutputContent, projectName); + appendToFile('outputs.tf', albOutputsContent, projectName); }; export default applyAlb; +export { albVariablesContent, albModuleContent, albOutputsContent }; diff --git a/src/templates/aws/addons/bastion.test.ts b/src/templates/aws/addons/bastion.test.ts new file mode 100644 index 00000000..623cd61d --- /dev/null +++ b/src/templates/aws/addons/bastion.test.ts @@ -0,0 +1,40 @@ +import { AwsOptions } from '..'; +import { remove } from '../../../helpers/file'; +import applyBastion, { bastionModuleContent, bastionVariablesContent } from './bastion'; +import applyCommon from './common'; + +describe('Bastion add-on', () => { + describe('given valid AWS options', () => { + const projectDir = 'bastion-addon-test'; + + beforeAll(() => { + const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + + applyCommon(awsOptions); + applyBastion(awsOptions); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('creates expected files', () => { + const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/bastion/main.tf', 'modules/bastion/variables.tf']; + + expect(projectDir).toHaveFiles(expectedFiles); + }); + + it('creates expected folders', () => { + expect(projectDir).toHaveDirectory('modules/bastion/'); + }); + + it('adds bastion module to main.tf', () => { + expect(projectDir).toHaveContentInFile('main.tf', bastionModuleContent); + }); + + it('adds bastion variables to variables.tf', () => { + expect(projectDir).toHaveContentInFile('variables.tf', bastionVariablesContent); + }); + }); +}); diff --git a/src/templates/aws/addons/bastion.ts b/src/templates/aws/addons/bastion.ts index b94c720a..ed9d0904 100644 --- a/src/templates/aws/addons/bastion.ts +++ b/src/templates/aws/addons/bastion.ts @@ -3,53 +3,54 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; import { appendToFile, copy } from '../../../helpers/file'; -const applyBastion = ({ projectName }: AwsOptions) => { - const bastionVariablesContent = dedent` - variable "bastion_image_id" { - description = "The AMI image ID for the bastion instance" - default = "ami-0801a1e12f4a9ccc0" - } - - variable "bastion_instance_type" { - description = "The bastion instance type" - default = "t3.nano" - } - - variable "bastion_instance_desired_count" { - description = "The desired number of the bastion instance" - default = 1 - } - - variable "bastion_max_instance_count" { - description = "The maximum number of the instance" - default = 1 - } - - variable "bastion_min_instance_count" { - description = "The minimum number of the instance" - default = 1 - } - \n`; - const bastionModuleContent = dedent` - module "bastion" { - source = "./modules/bastion" - - subnet_ids = module.vpc.public_subnet_ids - instance_security_group_ids = module.security_group.bastion_security_group_ids - - namespace = var.namespace - image_id = var.bastion_image_id - instance_type = var.bastion_instance_type - - min_instance_count = var.bastion_min_instance_count - max_instance_count = var.bastion_max_instance_count - instance_desired_count = var.bastion_instance_desired_count - } - \n`; +const bastionVariablesContent = dedent` + variable "bastion_image_id" { + description = "The AMI image ID for the bastion instance" + default = "ami-0801a1e12f4a9ccc0" + } + + variable "bastion_instance_type" { + description = "The bastion instance type" + default = "t3.nano" + } + + variable "bastion_instance_desired_count" { + description = "The desired number of the bastion instance" + default = 1 + } + + variable "bastion_max_instance_count" { + description = "The maximum number of the instance" + default = 1 + } + + variable "bastion_min_instance_count" { + description = "The minimum number of the instance" + default = 1 + } +\n`; +const bastionModuleContent = dedent` + module "bastion" { + source = "./modules/bastion" + + subnet_ids = module.vpc.public_subnet_ids + instance_security_group_ids = module.security_group.bastion_security_group_ids + + namespace = var.namespace + image_id = var.bastion_image_id + instance_type = var.bastion_instance_type + + min_instance_count = var.bastion_min_instance_count + max_instance_count = var.bastion_max_instance_count + instance_desired_count = var.bastion_instance_desired_count + } +\n`; +const applyBastion = ({ projectName }: AwsOptions) => { copy('aws/modules/bastion', 'modules/bastion', projectName); appendToFile('variables.tf', bastionVariablesContent, projectName); appendToFile('main.tf', bastionModuleContent, projectName); }; export default applyBastion; +export { bastionVariablesContent, bastionModuleContent }; diff --git a/src/templates/aws/addons/common.test.ts b/src/templates/aws/addons/common.test.ts index 858e1d20..413f5b75 100644 --- a/src/templates/aws/addons/common.test.ts +++ b/src/templates/aws/addons/common.test.ts @@ -2,23 +2,25 @@ import { AwsOptions } from '..'; import { remove } from '../../../helpers/file'; import applyCommon from './common'; -describe('common add-on', () => { - const projectDir = 'projectDir'; - - afterEach(() => { - jest.clearAllMocks(); - remove('/', projectDir); - }); - +describe('Common add-on', () => { describe('given valid AwsOptions', () => { - beforeEach(() => { + const projectDir = 'common-addon-test'; + + beforeAll(() => { const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'basic', awsRegion: 'ap-southeast-1' }; applyCommon(awsOptions); }); + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + it('creates expected files', () => { - expect(projectDir).toHaveFiles(['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf']); + const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf']; + + expect(projectDir).toHaveFiles(expectedFiles); }); }); }); diff --git a/src/templates/aws/addons/ecr.test.ts b/src/templates/aws/addons/ecr.test.ts new file mode 100644 index 00000000..733e574a --- /dev/null +++ b/src/templates/aws/addons/ecr.test.ts @@ -0,0 +1,40 @@ +import { AwsOptions } from '..'; +import { remove } from '../../../helpers/file'; +import applyCommon from './common'; +import applyEcr, { ecrModuleContent, ecrVariablesContent } from './ecr'; + +describe('ECR add-on', () => { + describe('given valid AWS options', () => { + const projectDir = 'ecr-addon-test'; + + beforeAll(() => { + const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + + applyCommon(awsOptions); + applyEcr(awsOptions); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('creates expected files', () => { + const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/ecr/main.tf', 'modules/ecr/variables.tf']; + + expect(projectDir).toHaveFiles(expectedFiles); + }); + + it('creates expected folders', () => { + expect(projectDir).toHaveDirectory('modules/ecr/'); + }); + + it('adds ECR module to main.tf', () => { + expect(projectDir).toHaveContentInFile('main.tf', ecrModuleContent); + }); + + it('adds ECR variables to variables.tf', () => { + expect(projectDir).toHaveContentInFile('variables.tf', ecrVariablesContent); + }); + }); +}); diff --git a/src/templates/aws/addons/ecr.ts b/src/templates/aws/addons/ecr.ts index 24a760eb..6c887442 100644 --- a/src/templates/aws/addons/ecr.ts +++ b/src/templates/aws/addons/ecr.ts @@ -3,25 +3,26 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; import { appendToFile, copy } from '../../../helpers/file'; -const applyEcr = ({ projectName }: AwsOptions) => { - const ecrVariablesContent = dedent` - variable "image_limit" { - description = "Sets max amount of the latest develop images to be kept" - type = number - } - \n`; - const ecrModuleContent = dedent` - module "ecr" { - source = "./modules/ecr" +const ecrVariablesContent = dedent` + variable "image_limit" { + description = "Sets max amount of the latest develop images to be kept" + type = number + } +\n`; +const ecrModuleContent = dedent` + module "ecr" { + source = "./modules/ecr" - namespace = var.namespace - image_limit = var.image_limit - } - \n`; + namespace = var.namespace + image_limit = var.image_limit + } +\n`; +const applyEcr = ({ projectName }: AwsOptions) => { copy('aws/modules/ecr', 'modules/ecr', projectName); appendToFile('variables.tf', ecrVariablesContent, projectName); appendToFile('main.tf', ecrModuleContent, projectName); }; export default applyEcr; +export { ecrVariablesContent, ecrModuleContent }; diff --git a/src/templates/aws/addons/ecs.test.ts b/src/templates/aws/addons/ecs.test.ts new file mode 100644 index 00000000..f4172783 --- /dev/null +++ b/src/templates/aws/addons/ecs.test.ts @@ -0,0 +1,40 @@ +import { AwsOptions } from '..'; +import { remove } from '../../../helpers/file'; +import applyCommon from './common'; +import applyEcs, { ecsModuleContent, ecsVariablesContent } from './ecs'; + +describe('ECS add-on', () => { + describe('given valid AWS options', () => { + const projectDir = 'ecs-addon-test'; + + beforeAll(() => { + const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + + applyCommon(awsOptions); + applyEcs(awsOptions); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('creates expected files', () => { + const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/ecs/main.tf', 'modules/ecs/variables.tf', 'modules/ecs/service.json.tftpl']; + + expect(projectDir).toHaveFiles(expectedFiles); + }); + + it('creates expected folders', () => { + expect(projectDir).toHaveDirectory('modules/ecs/'); + }); + + it('adds ECS module to main.tf', () => { + expect(projectDir).toHaveContentInFile('main.tf', ecsModuleContent); + }); + + it('adds ECS variables to variables.tf', () => { + expect(projectDir).toHaveContentInFile('variables.tf', ecsVariablesContent); + }); + }); +}); diff --git a/src/templates/aws/addons/ecs.ts b/src/templates/aws/addons/ecs.ts index a9b12305..6a9f68ee 100644 --- a/src/templates/aws/addons/ecs.ts +++ b/src/templates/aws/addons/ecs.ts @@ -3,58 +3,59 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; import { appendToFile, copy } from '../../../helpers/file'; -const applyEcs = ({ projectName }: AwsOptions) => { - const bastionVariablesContent = dedent` - variable "ecr_repo_name" { - description = "ECR repo name" - type = string - } - - variable "ecr_tag" { - description = "ECR tag to deploy" - type = string - } - - variable "ecs" { - description = "ECS input variables" - type = object({ - task_cpu = number - task_memory = number - task_desired_count = number - task_container_memory = number - deployment_maximum_percent = number - deployment_minimum_healthy_percent = number - }) - } - \n`; - const ecsModuleContent = dedent` - module "ecs" { - source = "./modules/ecs" - - subnets = module.vpc.private_subnet_ids - namespace = var.namespace - region = var.region - app_host = module.alb.alb_dns_name - app_port = var.app_port - ecr_repo_name = var.ecr_repo_name - ecr_tag = var.ecr_tag - security_groups = module.security_group.ecs_security_group_ids - alb_target_group_arn = module.alb.alb_target_group_arn - aws_cloudwatch_log_group_name = module.log.aws_cloudwatch_log_group_name - desired_count = var.ecs.task_desired_count - cpu = var.ecs.task_cpu - memory = var.ecs.task_memory - deployment_maximum_percent = var.ecs.deployment_maximum_percent - deployment_minimum_healthy_percent = var.ecs.deployment_minimum_healthy_percent - container_memory = var.ecs.task_container_memory - - aws_parameter_store = module.ssm.parameter_store - } - \n`; +const ecsVariablesContent = dedent` + variable "ecr_repo_name" { + description = "ECR repo name" + type = string + } + + variable "ecr_tag" { + description = "ECR tag to deploy" + type = string + } + + variable "ecs" { + description = "ECS input variables" + type = object({ + task_cpu = number + task_memory = number + task_desired_count = number + task_container_memory = number + deployment_maximum_percent = number + deployment_minimum_healthy_percent = number + }) + } +\n`; +const ecsModuleContent = dedent` + module "ecs" { + source = "./modules/ecs" + + subnets = module.vpc.private_subnet_ids + namespace = var.namespace + region = var.region + app_host = module.alb.alb_dns_name + app_port = var.app_port + ecr_repo_name = var.ecr_repo_name + ecr_tag = var.ecr_tag + security_groups = module.security_group.ecs_security_group_ids + alb_target_group_arn = module.alb.alb_target_group_arn + aws_cloudwatch_log_group_name = module.log.aws_cloudwatch_log_group_name + desired_count = var.ecs.task_desired_count + cpu = var.ecs.task_cpu + memory = var.ecs.task_memory + deployment_maximum_percent = var.ecs.deployment_maximum_percent + deployment_minimum_healthy_percent = var.ecs.deployment_minimum_healthy_percent + container_memory = var.ecs.task_container_memory + + aws_parameter_store = module.ssm.parameter_store + } +\n`; +const applyEcs = ({ projectName }: AwsOptions) => { copy('aws/modules/ecs', 'modules/ecs', projectName); - appendToFile('variables.tf', bastionVariablesContent, projectName); + appendToFile('variables.tf', ecsVariablesContent, projectName); appendToFile('main.tf', ecsModuleContent, projectName); }; export default applyEcs; +export { ecsVariablesContent, ecsModuleContent }; diff --git a/src/templates/aws/addons/log.test.ts b/src/templates/aws/addons/log.test.ts new file mode 100644 index 00000000..64bad6ac --- /dev/null +++ b/src/templates/aws/addons/log.test.ts @@ -0,0 +1,36 @@ +import { AwsOptions } from '..'; +import { remove } from '../../../helpers/file'; +import applyCommon from './common'; +import applyLog, { logModuleContent } from './log'; + +describe('Log add-on', () => { + describe('given valid AWS options', () => { + const projectDir = 'log-addon-test'; + + beforeAll(() => { + const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + + applyCommon(awsOptions); + applyLog(awsOptions); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('creates expected files', () => { + const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/log/main.tf', 'modules/log/variables.tf', 'modules/log/outputs.tf']; + + expect(projectDir).toHaveFiles(expectedFiles); + }); + + it('creates expected folders', () => { + expect(projectDir).toHaveDirectory('modules/log/'); + }); + + it('adds log module to main.tf', () => { + expect(projectDir).toHaveContentInFile('main.tf', logModuleContent); + }); + }); +}); diff --git a/src/templates/aws/addons/log.ts b/src/templates/aws/addons/log.ts index bf9fad49..05189dd3 100644 --- a/src/templates/aws/addons/log.ts +++ b/src/templates/aws/addons/log.ts @@ -3,17 +3,18 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; import { appendToFile, copy } from '../../../helpers/file'; -const applyLog = ({ projectName }: AwsOptions) => { - const logModuleContent = dedent` - module "log" { - source = "./modules/log" +const logModuleContent = dedent` + module "log" { + source = "./modules/log" - namespace = var.namespace - } - \n`; + namespace = var.namespace + } +\n`; +const applyLog = ({ projectName }: AwsOptions) => { copy('aws/modules/log', 'modules/log', projectName); appendToFile('main.tf', logModuleContent, projectName); }; export default applyLog; +export { logModuleContent }; diff --git a/src/templates/aws/addons/rds.test.ts b/src/templates/aws/addons/rds.test.ts new file mode 100644 index 00000000..b1432a3a --- /dev/null +++ b/src/templates/aws/addons/rds.test.ts @@ -0,0 +1,40 @@ +import { AwsOptions } from '..'; +import { remove } from '../../../helpers/file'; +import applyCommon from './common'; +import applyRds, { rdsModuleContent, rdsVariablesContent } from './rds'; + +describe('RDS add-on', () => { + describe('given valid AWS options', () => { + const projectDir = 'rds-addon-test'; + + beforeAll(() => { + const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + + applyCommon(awsOptions); + applyRds(awsOptions); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('creates expected files', () => { + const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/rds/main.tf', 'modules/rds/variables.tf', 'modules/rds/outputs.tf']; + + expect(projectDir).toHaveFiles(expectedFiles); + }); + + it('creates expected folders', () => { + expect(projectDir).toHaveDirectory('modules/rds/'); + }); + + it('adds RDS module to main.tf', () => { + expect(projectDir).toHaveContentInFile('main.tf', rdsModuleContent); + }); + + it('adds RDS variables to variables.tf', () => { + expect(projectDir).toHaveContentInFile('variables.tf', rdsVariablesContent); + }); + }); +}); diff --git a/src/templates/aws/addons/rds.ts b/src/templates/aws/addons/rds.ts index 066578a3..43cfe2af 100644 --- a/src/templates/aws/addons/rds.ts +++ b/src/templates/aws/addons/rds.ts @@ -3,62 +3,63 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; import { appendToFile, copy } from '../../../helpers/file'; -const applyRds = ({ projectName }: AwsOptions) => { - const rdsVariablesContent = dedent` - variable "rds_instance_type" { - description = "The RDB instance type" - type = string - } +const rdsVariablesContent = dedent` + variable "rds_instance_type" { + description = "The RDB instance type" + type = string + } - variable "rds_database_name" { - description = "RDS database name" - type = string - } + variable "rds_database_name" { + description = "RDS database name" + type = string + } - variable "rds_username" { - description = "RDS username" - type = string - } + variable "rds_username" { + description = "RDS username" + type = string + } - variable "rds_password" { - description = "RDS password" - type = string - } + variable "rds_password" { + description = "RDS password" + type = string + } - variable "rds_autoscaling_min_capacity" { - description = "Minimum number of RDS read replicas when autoscaling is enabled" - type = number - } + variable "rds_autoscaling_min_capacity" { + description = "Minimum number of RDS read replicas when autoscaling is enabled" + type = number + } - variable "rds_autoscaling_max_capacity" { - description = "Maximum number of RDS read replicas when autoscaling is enabled" - type = number - } - \n`; - const rdsModuleContent = dedent` - module "rds" { - source = "./modules/rds" + variable "rds_autoscaling_max_capacity" { + description = "Maximum number of RDS read replicas when autoscaling is enabled" + type = number + } +\n`; +const rdsModuleContent = dedent` + module "rds" { + source = "./modules/rds" - namespace = var.namespace + namespace = var.namespace - vpc_security_group_ids = module.security_group.rds_security_group_ids - vpc_id = module.vpc.vpc_id + vpc_security_group_ids = module.security_group.rds_security_group_ids + vpc_id = module.vpc.vpc_id - subnet_ids = module.vpc.private_subnet_ids + subnet_ids = module.vpc.private_subnet_ids - instance_type = var.rds_instance_type - database_name = var.rds_database_name - username = var.rds_username - password = var.rds_password + instance_type = var.rds_instance_type + database_name = var.rds_database_name + username = var.rds_username + password = var.rds_password - autoscaling_min_capacity = var.rds_autoscaling_min_capacity - autoscaling_max_capacity = var.rds_autoscaling_max_capacity - } - \n`; + autoscaling_min_capacity = var.rds_autoscaling_min_capacity + autoscaling_max_capacity = var.rds_autoscaling_max_capacity + } +\n`; +const applyRds = ({ projectName }: AwsOptions) => { copy('aws/modules/rds', 'modules/rds', projectName); appendToFile('variables.tf', rdsVariablesContent, projectName); appendToFile('main.tf', rdsModuleContent, projectName); }; export default applyRds; +export { rdsVariablesContent, rdsModuleContent }; diff --git a/src/templates/aws/addons/region.test.ts b/src/templates/aws/addons/region.test.ts new file mode 100644 index 00000000..bc848106 --- /dev/null +++ b/src/templates/aws/addons/region.test.ts @@ -0,0 +1,33 @@ +import { AwsOptions } from '..'; +import { remove } from '../../../helpers/file'; +import applyCommon from './common'; +import applyRegion, { regionVariablesContent } from './region'; + +describe('Region add-on', () => { + describe('given valid AWS options', () => { + const projectDir = 'region-addon-test'; + const awsRegion = 'ap-southeast-1'; + + beforeAll(() => { + const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion }; + + applyCommon(awsOptions); + applyRegion(awsOptions); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('creates expected files', () => { + const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf']; + + expect(projectDir).toHaveFiles(expectedFiles); + }); + + it('adds region variables to variables.tf', () => { + expect(projectDir).toHaveContentInFile('variables.tf', regionVariablesContent(awsRegion)); + }); + }); +}); diff --git a/src/templates/aws/addons/region.ts b/src/templates/aws/addons/region.ts index 11217fc4..c3b98ef5 100644 --- a/src/templates/aws/addons/region.ts +++ b/src/templates/aws/addons/region.ts @@ -3,16 +3,17 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; import { appendToFile } from '../../../helpers/file'; -const applyRegion = ({ awsRegion, projectName }: AwsOptions) => { - const regionVariableContent = dedent` - variable "region" { - description = "AWS region" - type = string - default = "${awsRegion}" - } - \n`; +const regionVariablesContent = (awsRegion: string) => dedent` + variable "region" { + description = "AWS region" + type = string + default = "${awsRegion}" + } +\n`; - appendToFile('variables.tf', regionVariableContent, projectName); +const applyRegion = ({ awsRegion, projectName }: AwsOptions) => { + appendToFile('variables.tf', regionVariablesContent(awsRegion), projectName); }; export default applyRegion; +export { regionVariablesContent }; diff --git a/src/templates/aws/addons/s3.test.ts b/src/templates/aws/addons/s3.test.ts new file mode 100644 index 00000000..9bde8aa4 --- /dev/null +++ b/src/templates/aws/addons/s3.test.ts @@ -0,0 +1,40 @@ +import { AwsOptions } from '..'; +import { remove } from '../../../helpers/file'; +import applyCommon from './common'; +import applyS3, { s3ModuleContent, s3OutputsContent } from './s3'; + +describe('S3 add-on', () => { + describe('given valid AWS options', () => { + const projectDir = 's3-addon-test'; + + beforeAll(() => { + const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + + applyCommon(awsOptions); + applyS3(awsOptions); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('creates expected files', () => { + const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/s3/main.tf', 'modules/s3/variables.tf', 'modules/s3/outputs.tf']; + + expect(projectDir).toHaveFiles(expectedFiles); + }); + + it('creates expected folders', () => { + expect(projectDir).toHaveDirectory('modules/s3/'); + }); + + it('adds S3 module to main.tf', () => { + expect(projectDir).toHaveContentInFile('main.tf', s3ModuleContent); + }); + + it('adds S3 outputs to outputs.tf', () => { + expect(projectDir).toHaveContentInFile('outputs.tf', s3OutputsContent); + }); + }); +}); diff --git a/src/templates/aws/addons/s3.ts b/src/templates/aws/addons/s3.ts index d0858803..ff53bd05 100644 --- a/src/templates/aws/addons/s3.ts +++ b/src/templates/aws/addons/s3.ts @@ -3,25 +3,26 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; import { appendToFile, copy } from '../../../helpers/file'; -const applyS3 = ({ projectName }: AwsOptions) => { - const s3OutputContent = dedent` - output "s3_alb_log_bucket_name" { - description = "S3 bucket name for ALB log" - value = module.s3.aws_alb_log_bucket_name - } - \n`; +const s3OutputsContent = dedent` + output "s3_alb_log_bucket_name" { + description = "S3 bucket name for ALB log" + value = module.s3.aws_alb_log_bucket_name + } +\n`; - const s3ModuleContent = dedent` - module "s3" { - source = "./modules/s3" +const s3ModuleContent = dedent` + module "s3" { + source = "./modules/s3" - namespace = var.namespace - } - \n`; + namespace = var.namespace + } +\n`; +const applyS3 = ({ projectName }: AwsOptions) => { copy('aws/modules/s3', 'modules/s3', projectName); - appendToFile('outputs.tf', s3OutputContent, projectName); + appendToFile('outputs.tf', s3OutputsContent, projectName); appendToFile('main.tf', s3ModuleContent, projectName); }; export default applyS3; +export { s3ModuleContent, s3OutputsContent }; diff --git a/src/templates/aws/addons/securityGroup.test.ts b/src/templates/aws/addons/securityGroup.test.ts new file mode 100644 index 00000000..24b5bc7a --- /dev/null +++ b/src/templates/aws/addons/securityGroup.test.ts @@ -0,0 +1,40 @@ +import { AwsOptions } from '..'; +import { remove } from '../../../helpers/file'; +import applyCommon from './common'; +import applySecurityGroup, { securityGroupModuleContent, securityGroupVariablesContent } from './securityGroup'; + +describe('Security group add-on', () => { + describe('given valid AWS options', () => { + const projectDir = 'security-group-addon-test'; + + beforeAll(() => { + const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + + applyCommon(awsOptions); + applySecurityGroup(awsOptions); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('creates expected files', () => { + const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/security_group/main.tf', 'modules/security_group/variables.tf', 'modules/security_group/outputs.tf']; + + expect(projectDir).toHaveFiles(expectedFiles); + }); + + it('creates expected folders', () => { + expect(projectDir).toHaveDirectory('modules/security_group/'); + }); + + it('adds security group module to main.tf', () => { + expect(projectDir).toHaveContentInFile('main.tf', securityGroupModuleContent); + }); + + it('adds security group variables to variables.tf', () => { + expect(projectDir).toHaveContentInFile('variables.tf', securityGroupVariablesContent); + }); + }); +}); diff --git a/src/templates/aws/addons/securityGroup.ts b/src/templates/aws/addons/securityGroup.ts index 5d2132d8..b4959c58 100644 --- a/src/templates/aws/addons/securityGroup.ts +++ b/src/templates/aws/addons/securityGroup.ts @@ -3,28 +3,29 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; import { appendToFile, copy } from '../../../helpers/file'; -const applySecurityGroup = ({ projectName }: AwsOptions) => { - const securityGroupVariablesContent = dedent` - variable "nimble_office_ip" { - description = "Nimble Office IP" - } - \n`; - const securityGroupModuleContent = dedent` - module "security_group" { - source = "./modules/security_group" +const securityGroupVariablesContent = dedent` + variable "nimble_office_ip" { + description = "Nimble Office IP" + } +\n`; +const securityGroupModuleContent = dedent` + module "security_group" { + source = "./modules/security_group" - namespace = var.namespace - vpc_id = module.vpc.vpc_id - app_port = var.app_port - private_subnets_cidr_blocks = module.vpc.private_subnets_cidr_blocks + namespace = var.namespace + vpc_id = module.vpc.vpc_id + app_port = var.app_port + private_subnets_cidr_blocks = module.vpc.private_subnets_cidr_blocks - nimble_office_ip = var.nimble_office_ip - } - \n`; + nimble_office_ip = var.nimble_office_ip + } +\n`; +const applySecurityGroup = ({ projectName }: AwsOptions) => { copy('aws/modules/security_group', 'modules/security_group', projectName); appendToFile('variables.tf', securityGroupVariablesContent, projectName); appendToFile('main.tf', securityGroupModuleContent, projectName); }; export default applySecurityGroup; +export { securityGroupModuleContent, securityGroupVariablesContent }; diff --git a/src/templates/aws/addons/ssm.test.ts b/src/templates/aws/addons/ssm.test.ts new file mode 100644 index 00000000..ab5e5648 --- /dev/null +++ b/src/templates/aws/addons/ssm.test.ts @@ -0,0 +1,40 @@ +import { AwsOptions } from '..'; +import { remove } from '../../../helpers/file'; +import applyCommon from './common'; +import applySsm, { ssmModuleContent, ssmVariablesContent } from './ssm'; + +describe('SSM add-on', () => { + describe('given valid AWS options', () => { + const projectDir = 'ssm-addon-test'; + + beforeAll(() => { + const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + + applyCommon(awsOptions); + applySsm(awsOptions); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('creates expected files', () => { + const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/ssm/main.tf', 'modules/ssm/variables.tf', 'modules/ssm/outputs.tf']; + + expect(projectDir).toHaveFiles(expectedFiles); + }); + + it('creates expected folders', () => { + expect(projectDir).toHaveDirectory('modules/ssm/'); + }); + + it('adds SSM module to main.tf', () => { + expect(projectDir).toHaveContentInFile('main.tf', ssmModuleContent); + }); + + it('adds SSM variables to variables.tf', () => { + expect(projectDir).toHaveContentInFile('variables.tf', ssmVariablesContent); + }); + }); +}); diff --git a/src/templates/aws/addons/ssm.ts b/src/templates/aws/addons/ssm.ts index 83217d38..bc7ed9db 100644 --- a/src/templates/aws/addons/ssm.ts +++ b/src/templates/aws/addons/ssm.ts @@ -3,30 +3,31 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; import { appendToFile, copy } from '../../../helpers/file'; -const applySsm = ({ projectName }: AwsOptions) => { - const ssmVariablesContent = dedent` - variable "secret_key_base" { - description = "The Secret key base for the application" - type = string - } - \n`; - const ssmModuleContent = dedent` - module "ssm" { - source = "./modules/ssm" +const ssmVariablesContent = dedent` + variable "secret_key_base" { + description = "The Secret key base for the application" + type = string + } +\n`; +const ssmModuleContent = dedent` + module "ssm" { + source = "./modules/ssm" - namespace = var.namespace - secret_key_base = var.secret_key_base + namespace = var.namespace + secret_key_base = var.secret_key_base - rds_username = var.rds_username - rds_password = var.rds_password - rds_database_name = var.rds_database_name - rds_endpoint = module.rds.db_endpoint - } - \n`; + rds_username = var.rds_username + rds_password = var.rds_password + rds_database_name = var.rds_database_name + rds_endpoint = module.rds.db_endpoint + } +\n`; +const applySsm = ({ projectName }: AwsOptions) => { copy('aws/modules/ssm', 'modules/ssm', projectName); appendToFile('variables.tf', ssmVariablesContent, projectName); appendToFile('main.tf', ssmModuleContent, projectName); }; export default applySsm; +export { ssmVariablesContent, ssmModuleContent }; diff --git a/src/templates/aws/addons/vpc.test.ts b/src/templates/aws/addons/vpc.test.ts new file mode 100644 index 00000000..1b7d961f --- /dev/null +++ b/src/templates/aws/addons/vpc.test.ts @@ -0,0 +1,40 @@ +import { AwsOptions } from '..'; +import { remove } from '../../../helpers/file'; +import applyCommon from './common'; +import applyVpc, { vpcModuleContent, vpcOutputsContent } from './vpc'; + +describe('VPC add-on', () => { + describe('given valid AWS options', () => { + const projectDir = 'vpc-addon-test'; + + beforeAll(() => { + const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + + applyCommon(awsOptions); + applyVpc(awsOptions); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('creates expected files', () => { + const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/vpc/main.tf', 'modules/vpc/variables.tf', 'modules/vpc/outputs.tf']; + + expect(projectDir).toHaveFiles(expectedFiles); + }); + + it('creates expected folders', () => { + expect(projectDir).toHaveDirectory('modules/vpc/'); + }); + + it('adds VPC module to main.tf', () => { + expect(projectDir).toHaveContentInFile('main.tf', vpcModuleContent); + }); + + it('adds VPC variables to outputs.tf', () => { + expect(projectDir).toHaveContentInFile('outputs.tf', vpcOutputsContent); + }); + }); +}); diff --git a/src/templates/aws/addons/vpc.ts b/src/templates/aws/addons/vpc.ts index 3866b427..0c68fdd4 100644 --- a/src/templates/aws/addons/vpc.ts +++ b/src/templates/aws/addons/vpc.ts @@ -3,24 +3,25 @@ import * as dedent from 'dedent'; import { AwsOptions } from '..'; import { appendToFile, copy } from '../../../helpers/file'; -const applyVpc = ({projectName}: AwsOptions) => { - const vpcOutputContent = dedent` - output "vpc_id" { - description = "VPC ID" - value = module.vpc.vpc_id - } - \n`; - const vpcModuleContent = dedent` - module "vpc" { - source = "./modules/vpc" +const vpcOutputsContent = dedent` + output "vpc_id" { + description = "VPC ID" + value = module.vpc.vpc_id + } +\n`; +const vpcModuleContent = dedent` + module "vpc" { + source = "./modules/vpc" - namespace = var.namespace - } - \n`; + namespace = var.namespace + } +\n`; +const applyVpc = ({projectName}: AwsOptions) => { copy('aws/modules/vpc', 'modules/vpc', projectName); - appendToFile('outputs.tf', vpcOutputContent, projectName); + appendToFile('outputs.tf', vpcOutputsContent, projectName); appendToFile('main.tf', vpcModuleContent, projectName); }; export default applyVpc; +export { vpcModuleContent, vpcOutputsContent }; From fed11407d9cd5f0cad5b51838e8e5c7185f3f009 Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Tue, 26 Jul 2022 11:24:36 +0700 Subject: [PATCH 16/24] [#29] Cover all lines in src/commands/generate/index.ts --- src/commands/generate/index.test.ts | 160 ++++++++++++++++++++++++---- 1 file changed, 142 insertions(+), 18 deletions(-) diff --git a/src/commands/generate/index.test.ts b/src/commands/generate/index.test.ts index 34492be6..3de3db33 100644 --- a/src/commands/generate/index.test.ts +++ b/src/commands/generate/index.test.ts @@ -3,31 +3,155 @@ import { prompt } from 'inquirer'; import Generator from '.'; import { remove } from '../../helpers/file'; +import { formatCode, detectTerraform } from '../../helpers/terraform'; jest.mock('inquirer'); +jest.mock('../../helpers/terraform'); -describe('running command generate directly', () => { - const projectDir = 'app-name'; - const stdoutSpy = jest.spyOn(process.stdout, 'write'); +describe('Generator command', () => { + describe('given valid options', () => { + describe('given provider is AWS', () => { + describe('given infrastructure type is basic', () => { + const projectDir = 'aws-basic-test'; + const consoleSpy = jest.spyOn(global.console, 'error'); - beforeEach(async() => { - (prompt as unknown as jest.Mock) - .mockResolvedValueOnce({ provider: 'aws' }) - .mockResolvedValueOnce({ infrastructureType: 'advanced' }); + beforeAll(async() => { + (prompt as unknown as jest.Mock) + .mockResolvedValueOnce({ provider: 'aws' }) + .mockResolvedValueOnce({ infrastructureType: 'basic' }); - await Generator.run([projectDir]); - }); + await Generator.run([projectDir]); + }); - afterEach(() => { - jest.resetAllMocks(); - remove('/', projectDir); - }); + afterAll(() => { + jest.resetAllMocks(); + remove('/', projectDir); + }); - it('creates a new project folder', () => { - expect(existsSync(projectDir)).toBe(true); - }); + it('displays the error message', () => { + expect(consoleSpy).toHaveBeenCalledWith(Error('This type has not been implemented!')); + }); + }); + + describe('given infrastructure type is advanced', () => { + const projectDir = 'aws-advanced-test'; + const stdoutSpy = jest.spyOn(process.stdout, 'write'); + + beforeAll(async() => { + (prompt as unknown as jest.Mock) + .mockResolvedValueOnce({ provider: 'aws' }) + .mockResolvedValueOnce({ infrastructureType: 'advanced' }); + + await Generator.run([projectDir]); + }); + + afterAll(() => { + jest.resetAllMocks(); + remove('/', projectDir); + }); + + it('creates a new project folder', () => { + expect(existsSync(projectDir)).toBe(true); + }); + + it('displays the success message', () => { + expect(stdoutSpy).toHaveBeenCalledWith('The infrastructure has been generated!\n'); + }); + }); + }); + + describe('given provider is GCP', () => { + const projectDir = 'gcp-test'; + const consoleSpy = jest.spyOn(global.console, 'error'); + + beforeAll(async() => { + (prompt as unknown as jest.Mock) + .mockResolvedValueOnce({ provider: 'gcp' }); + + await Generator.run([projectDir]); + }); + + afterAll(() => { + jest.resetAllMocks(); + remove('/', projectDir); + }); + + it('displays the error message', () => { + expect(consoleSpy).toHaveBeenCalledWith(Error('This provider has not been implemented!')); + }); + }); + + describe('given provider is Heroku', () => { + const projectDir = 'heroku-test'; + const consoleSpy = jest.spyOn(global.console, 'error'); + + beforeAll(async() => { + (prompt as unknown as jest.Mock) + .mockResolvedValueOnce({ provider: 'heroku' }); + + await Generator.run([projectDir]); + }); + + afterAll(() => { + jest.resetAllMocks(); + remove('/', projectDir); + }); + + it('displays the error message', () => { + expect(consoleSpy).toHaveBeenCalledWith(Error('This provider has not been implemented!')); + }); + }); + + describe('postProcess', () => { + const projectDir = 'postProcess-test'; + + describe('given current machine had terraform', () => { + beforeAll(async() => { + (prompt as unknown as jest.Mock) + .mockResolvedValueOnce({ provider: 'aws' }) + .mockResolvedValueOnce({ infrastructureType: 'advanced' }); + + (detectTerraform as jest.Mock).mockImplementation(() => true); + + await Generator.run([projectDir]); + }); + + afterAll(() => { + jest.resetAllMocks(); + remove('/', projectDir); + }); + + it('runs formatCode', async() => { + await expect(formatCode).toHaveBeenCalled(); + }); + }); + + describe('given current machine did not have terraform', () => { + beforeAll(async() => { + (prompt as unknown as jest.Mock) + .mockResolvedValueOnce({ provider: 'aws' }) + .mockResolvedValueOnce({ infrastructureType: 'advanced' }); + + (detectTerraform as jest.Mock).mockImplementation(() => { + throw new Error('terraform not found'); + }); + + await Generator.run([projectDir]); + }); + + afterAll(() => { + jest.resetAllMocks(); + remove('/', projectDir); + }); + + it('does NOT run formatCode', async() => { + await expect(formatCode).not.toHaveBeenCalled(); + }); - it('displays the success message', () => { - expect(stdoutSpy).toHaveBeenCalledWith('The infrastructure has been generated!\n'); + it('displays the error message', () => { + expect(console.error).toHaveBeenCalledWith(Error('terraform not found')); + }); + }); + }); }); }); From edeefbb5676ecf0f3033497732c02c865e88e172 Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Tue, 26 Jul 2022 11:37:13 +0700 Subject: [PATCH 17/24] [#29] Add tests for terraform helper --- src/commands/generate/index.test.ts | 16 ++++---- src/helpers/terraform.test.ts | 64 +++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 src/helpers/terraform.test.ts diff --git a/src/commands/generate/index.test.ts b/src/commands/generate/index.test.ts index 3de3db33..f25facbf 100644 --- a/src/commands/generate/index.test.ts +++ b/src/commands/generate/index.test.ts @@ -13,7 +13,7 @@ describe('Generator command', () => { describe('given provider is AWS', () => { describe('given infrastructure type is basic', () => { const projectDir = 'aws-basic-test'; - const consoleSpy = jest.spyOn(global.console, 'error'); + const consoleErrorSpy = jest.spyOn(global.console, 'error'); beforeAll(async() => { (prompt as unknown as jest.Mock) @@ -29,7 +29,7 @@ describe('Generator command', () => { }); it('displays the error message', () => { - expect(consoleSpy).toHaveBeenCalledWith(Error('This type has not been implemented!')); + expect(consoleErrorSpy).toHaveBeenCalledWith(Error('This type has not been implemented!')); }); }); @@ -62,7 +62,7 @@ describe('Generator command', () => { describe('given provider is GCP', () => { const projectDir = 'gcp-test'; - const consoleSpy = jest.spyOn(global.console, 'error'); + const consoleErrorSpy = jest.spyOn(global.console, 'error'); beforeAll(async() => { (prompt as unknown as jest.Mock) @@ -77,13 +77,13 @@ describe('Generator command', () => { }); it('displays the error message', () => { - expect(consoleSpy).toHaveBeenCalledWith(Error('This provider has not been implemented!')); + expect(consoleErrorSpy).toHaveBeenCalledWith(Error('This provider has not been implemented!')); }); }); describe('given provider is Heroku', () => { const projectDir = 'heroku-test'; - const consoleSpy = jest.spyOn(global.console, 'error'); + const consoleErrorSpy = jest.spyOn(global.console, 'error'); beforeAll(async() => { (prompt as unknown as jest.Mock) @@ -98,7 +98,7 @@ describe('Generator command', () => { }); it('displays the error message', () => { - expect(consoleSpy).toHaveBeenCalledWith(Error('This provider has not been implemented!')); + expect(consoleErrorSpy).toHaveBeenCalledWith(Error('This provider has not been implemented!')); }); }); @@ -127,6 +127,8 @@ describe('Generator command', () => { }); describe('given current machine did not have terraform', () => { + const consoleErrorSpy = jest.spyOn(global.console, 'error'); + beforeAll(async() => { (prompt as unknown as jest.Mock) .mockResolvedValueOnce({ provider: 'aws' }) @@ -149,7 +151,7 @@ describe('Generator command', () => { }); it('displays the error message', () => { - expect(console.error).toHaveBeenCalledWith(Error('terraform not found')); + expect(consoleErrorSpy).toHaveBeenCalledWith(Error('terraform not found')); }); }); }); diff --git a/src/helpers/terraform.test.ts b/src/helpers/terraform.test.ts new file mode 100644 index 00000000..a4b29f52 --- /dev/null +++ b/src/helpers/terraform.test.ts @@ -0,0 +1,64 @@ +import { runCommand } from './child-process'; +import { detectTerraform, formatCode } from './terraform'; + +jest.mock('./child-process'); + +describe('Terraform helper', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('detectTerraform', () => { + describe('given terraform is installed', () => { + it('returns true', async() => { + (runCommand as jest.Mock).mockResolvedValueOnce(''); + + const result = await detectTerraform(); + + expect(result).toBe(true); + }); + }); + + describe('given terraform is not installed', () => { + it('returns false', async() => { + (runCommand as jest.Mock).mockRejectedValueOnce(new Error('terraform not found')); + + const result = await detectTerraform(); + + expect(result).toBe(false); + }); + + it('displays the error message', async() => { + (runCommand as jest.Mock).mockRejectedValueOnce(new Error('terraform not found')); + const consoleSpy = jest.spyOn(global.console, 'error'); + + await detectTerraform(); + + expect(consoleSpy).toHaveBeenCalledWith('Terraform not found. Please install terraform.'); + }); + }); + }); + + describe('formatCode', () => { + describe('given terraform is installed', () => { + it('runs terraform fmt', async() => { + (runCommand as jest.Mock).mockResolvedValueOnce(''); + + await formatCode('/'); + + expect(runCommand).toHaveBeenCalledWith('terraform', ['fmt'], '/'); + }); + }); + + describe('given terraform is not installed', () => { + it('displays the error message', async() => { + (runCommand as jest.Mock).mockRejectedValueOnce(new Error('terraform not found')); + const consoleSpy = jest.spyOn(global.console, 'error'); + + await formatCode('/'); + + expect(consoleSpy).toHaveBeenCalledWith(Error('terraform not found')); + }); + }); + }); +}); From 1867dc57ff17a1654e1dab0b6f5ed274a1fdbc7b Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Tue, 26 Jul 2022 12:05:04 +0700 Subject: [PATCH 18/24] [#29] Cover all lines in file helper --- src/helpers/file.test.ts | 169 ++++++++++++++++++++++++++++++++------- src/helpers/file.ts | 15 ++-- 2 files changed, 147 insertions(+), 37 deletions(-) diff --git a/src/helpers/file.test.ts b/src/helpers/file.test.ts index ce242b2b..f8f4692c 100644 --- a/src/helpers/file.test.ts +++ b/src/helpers/file.test.ts @@ -10,10 +10,10 @@ import { getSourcePath, getTargetDir, getTargetPath, + getTemplatePath, injectToFile, remove, renameFile, - TEMPLATE_PATH, } from './file'; jest.mock('fs-extra'); @@ -48,14 +48,56 @@ describe('File helpers', () => { }); }); + describe('getTemplatePath', () => { + describe('given NODE_ENV is production', () => { + const OLD_ENV = process.env; + + beforeEach(() => { + jest.resetModules(); + process.env = { ...OLD_ENV }; + process.env.NODE_ENV = 'production'; + }); + + afterAll(() => { + process.env = OLD_ENV; + }); + + it('returns the correct source path', () => { + const sourcePath = getTemplatePath(); + + expect(sourcePath).toContain('/dist/skeleton'); + }); + }); + + describe('given NODE_ENV is not production', () => { + const OLD_ENV = process.env; + + beforeEach(() => { + jest.resetModules(); + process.env = { ...OLD_ENV }; + process.env.NODE_ENV = 'development'; + }); + + afterAll(() => { + process.env = OLD_ENV; + }); + + it('returns the correct source path', () => { + const sourcePath = getTemplatePath(); + + expect(sourcePath).toContain('/skeleton'); + }); + }); + }); + describe('getSourcePath', () => { describe('given file name', () => { it('returns the correct source path', () => { - const file = 'file'; + const file = 'example.txt'; const sourcePath = getSourcePath(file); - expect(sourcePath).toBe(path.join(TEMPLATE_PATH, file)); + expect(sourcePath).toBe(path.join(getTemplatePath(), file)); }); }); }); @@ -164,45 +206,110 @@ describe('File helpers', () => { }); describe('injectToFile', () => { - describe('given target file, content and insert before initial content', () => { - it('injects content to the target file', () => { - const target = 'targetFile.txt'; - const initialContent = 'initial content'; - const content = 'injecting content'; - const projectName = 'projectName'; - const targetPath = getTargetDir(projectName); + describe('given a non-empty project name', () => { + describe('given target file, content and insert before initial content', () => { + it('injects content to the target file', () => { + const target = 'targetFile.txt'; + const initialContent = 'initial content'; + const content = 'injecting content'; + const projectName = 'projectName'; + const targetPath = getTargetDir(projectName); + + const injectSpy = jest.spyOn(fs, 'writeFileSync'); + + (readFileSync as jest.Mock).mockReturnValue(initialContent); + + injectToFile(target, content, projectName, { insertBefore: initialContent }); + + expect(injectSpy).toHaveBeenCalledWith( + path.join(targetPath, target), + `${content}\n${initialContent}`, + ); + }); + }); - const injectSpy = jest.spyOn(fs, 'writeFileSync'); + describe('given target file, content and insert after initial content', () => { + it('injects content to the target file', () => { + const target = 'targetFile.txt'; + const initialContent = 'initial content'; + const content = 'injecting content'; + const projectName = 'projectName'; + const targetPath = getTargetDir(projectName); - (readFileSync as jest.Mock).mockReturnValue(initialContent); + const injectSpy = jest.spyOn(fs, 'writeFileSync'); - injectToFile(target, content, projectName, { insertBefore: initialContent }); + (readFileSync as jest.Mock).mockReturnValue(initialContent); - expect(injectSpy).toHaveBeenCalledWith( - path.join(targetPath, target), - `${content}\n${initialContent}`, - ); + injectToFile(target, content, projectName, { insertAfter: initialContent }); + + expect(injectSpy).toHaveBeenCalledWith( + path.join(targetPath, target), + `${initialContent}\n${content}`, + ); + }); + }); + + describe('given no InjectToFile options', () => { + it('does NOT inject content to the target file', () => { + const target = 'targetFile.txt'; + const initialContent = 'initial content'; + const content = 'injecting content'; + const projectName = 'projectName'; + const targetPath = getTargetDir(projectName); + + const injectSpy = jest.spyOn(fs, 'writeFileSync'); + + (readFileSync as jest.Mock).mockReturnValue(initialContent); + + injectToFile(target, content, projectName); + + expect(injectSpy).toHaveBeenCalledWith( + path.join(targetPath, target), + initialContent, + ); + }); }); }); - describe('given target file, content and insert after initial content', () => { - it('injects content to the target file', () => { - const target = 'targetFile.txt'; - const initialContent = 'initial content'; - const content = 'injecting content'; - const projectName = 'projectName'; - const targetPath = getTargetDir(projectName); + describe('given an empty project name', () => { + describe('given target file, content and insert before initial content', () => { + it('injects content to the target file', () => { + const target = 'targetFile.txt'; + const initialContent = 'initial content'; + const content = 'injecting content'; + const projectName = ''; - const injectSpy = jest.spyOn(fs, 'writeFileSync'); + const injectSpy = jest.spyOn(fs, 'writeFileSync'); - (readFileSync as jest.Mock).mockReturnValue(initialContent); + (readFileSync as jest.Mock).mockReturnValue(initialContent); - injectToFile(target, content, projectName, { insertAfter: initialContent }); + injectToFile(target, content, projectName, { insertBefore: initialContent }); - expect(injectSpy).toHaveBeenCalledWith( - path.join(targetPath, target), - `${initialContent}\n${content}`, - ); + expect(injectSpy).toHaveBeenCalledWith( + 'targetFile.txt', + `${content}\n${initialContent}`, + ); + }); + }); + + describe('given target file, content and insert after initial content', () => { + it('injects content to the target file', () => { + const target = 'targetFile.txt'; + const initialContent = 'initial content'; + const content = 'injecting content'; + const projectName = ''; + + const injectSpy = jest.spyOn(fs, 'writeFileSync'); + + (readFileSync as jest.Mock).mockReturnValue(initialContent); + + injectToFile(target, content, projectName, { insertAfter: initialContent }); + + expect(injectSpy).toHaveBeenCalledWith( + 'targetFile.txt', + `${initialContent}\n${content}`, + ); + }); }); }); }); diff --git a/src/helpers/file.ts b/src/helpers/file.ts index 3ab42dec..e8f7aa97 100644 --- a/src/helpers/file.ts +++ b/src/helpers/file.ts @@ -16,9 +16,6 @@ interface InjectToFileOptions { } const ROOT_DIR = path.join(__dirname, '..', '..'); -const TEMPLATE_DIR = - process.env.NODE_ENV === 'production' ? 'dist/skeleton' : 'skeleton'; -const TEMPLATE_PATH = path.join(ROOT_DIR, TEMPLATE_DIR); const getTargetDir = (projectName: string): string => { return path.join(process.cwd(), projectName); @@ -28,8 +25,14 @@ const getTargetPath = (file: string, projectName: string): string => { return path.join(getTargetDir(projectName), file); }; +const getTemplatePath = (): string => { + const templateDir = + process.env.NODE_ENV === 'production' ? 'dist/skeleton' : 'skeleton'; + return path.join(ROOT_DIR, templateDir); +}; + const getSourcePath = (file: string): string => { - return path.join(TEMPLATE_PATH, file); + return path.join(getTemplatePath(), file); }; const appendToFile = ( @@ -47,7 +50,7 @@ const copy = ( target: string, projectName: string, ): void => { - const sourcePath = path.join(TEMPLATE_PATH, source); + const sourcePath = path.join(getTemplatePath(), source); const targetPath = getTargetPath(target, projectName); copySync(sourcePath, targetPath); @@ -113,9 +116,9 @@ const injectToFile = ( }; export { - TEMPLATE_PATH, getTargetDir, getTargetPath, + getTemplatePath, getSourcePath, appendToFile, copy, From 15759e22e29ae5cfcc76db8f4caff5ef49be8a90 Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Tue, 26 Jul 2022 14:35:24 +0700 Subject: [PATCH 19/24] [#29] Refactor and add tests for childProcess helper --- src/helpers/child-process.ts | 49 -------------------------------- src/helpers/childProcess.test.ts | 28 ++++++++++++++++++ src/helpers/childProcess.ts | 20 +++++++++++++ src/helpers/terraform.test.ts | 4 +-- src/helpers/terraform.ts | 2 +- 5 files changed, 51 insertions(+), 52 deletions(-) delete mode 100644 src/helpers/child-process.ts create mode 100644 src/helpers/childProcess.test.ts create mode 100644 src/helpers/childProcess.ts diff --git a/src/helpers/child-process.ts b/src/helpers/child-process.ts deleted file mode 100644 index 181c7b90..00000000 --- a/src/helpers/child-process.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { spawn } from 'node:child_process'; - -const runCommand = ( - command: string, - args: string[], - cwd = './', -): Promise => { - return new Promise((resolve, reject) => { - const child = spawn(command, args, { cwd }); - let stdout = ''; - let stderr = ''; - - child.stdout.on('data', (data) => { - stdout += data; - }); - - child.stderr.on('data', (data) => { - stderr += data; - }); - - child.on('close', (code) => { - if (code === 0) { - resolve(stdout); - } else { - reject(stderr); - } - }); - - child.on('error', (err) => { - reject(err); - }); - - child.on('exit', (code) => { - if (code === 0) { - resolve(stdout); - } else { - reject(stderr); - } - }); - - child.on('message', (msg) => { - resolve(msg as string); - }); - }); -}; - -export { - runCommand, -}; diff --git a/src/helpers/childProcess.test.ts b/src/helpers/childProcess.test.ts new file mode 100644 index 00000000..4eed0ef7 --- /dev/null +++ b/src/helpers/childProcess.test.ts @@ -0,0 +1,28 @@ +import { runCommand } from './childProcess'; + +describe('ChildProcess helper', () => { + describe('runCommand', () => { + describe('given a valid command', () => { + it('runs the command', async() => { + const result = await runCommand('echo', ['hello']); + + expect(result).toBe('hello'); + }); + }); + + describe('given an INVALID command', () => { + it('throws an error', async() => { + let result: string; + const invalidCommand = 'invalid-command'; + + try { + result = await runCommand(invalidCommand, []); + } catch (err) { + result = (err as Error).message; + } + + expect(result).toBe(`spawn ${invalidCommand} ENOENT`); + }); + }); + }); +}); diff --git a/src/helpers/childProcess.ts b/src/helpers/childProcess.ts new file mode 100644 index 00000000..a7078c4b --- /dev/null +++ b/src/helpers/childProcess.ts @@ -0,0 +1,20 @@ +import { spawn } from 'node:child_process'; + +const runCommand = ( + command: string, + args: string[], + cwd = './', +): Promise => { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { cwd }); + const output = [] as string[]; + + child.stdout.on('data', (chunk) => output.push(chunk)); + child.on('close', () => resolve(output.join('').trim())); + child.on('error', (error) => reject(error)); + }); +}; + +export { + runCommand, +}; diff --git a/src/helpers/terraform.test.ts b/src/helpers/terraform.test.ts index a4b29f52..d7404808 100644 --- a/src/helpers/terraform.test.ts +++ b/src/helpers/terraform.test.ts @@ -1,7 +1,7 @@ -import { runCommand } from './child-process'; +import { runCommand } from './childProcess'; import { detectTerraform, formatCode } from './terraform'; -jest.mock('./child-process'); +jest.mock('./childProcess'); describe('Terraform helper', () => { afterEach(() => { diff --git a/src/helpers/terraform.ts b/src/helpers/terraform.ts index 0bb034c1..bdda79da 100644 --- a/src/helpers/terraform.ts +++ b/src/helpers/terraform.ts @@ -1,4 +1,4 @@ -import { runCommand } from './child-process'; +import { runCommand } from './childProcess'; const detectTerraform = async() => { try { From 2b3694d5a3767212887f39718c5d1eeb6826cb2b Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Tue, 26 Jul 2022 14:42:06 +0700 Subject: [PATCH 20/24] [#29] Uninstall redundant packages --- package-lock.json | 301 +++------------------------------------------- package.json | 4 +- 2 files changed, 20 insertions(+), 285 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7b759e6d..347a2c51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,15 +14,13 @@ "@oclif/plugin-plugins": "^2.0.1", "@types/dedent": "^0.7.0", "dedent": "^0.7.0", - "inquirer": "^8.2.4", - "inquirer-test": "^2.0.1" + "inquirer": "^8.2.4" }, "bin": { "nimble-infra": "bin/run" }, "devDependencies": { "@nimblehq/eslint-config-nimble": "^2.3.0", - "@oclif/test": "^2", "@types/fs-extra": "^9.0.13", "@types/glob": "^7.2.0", "@types/inquirer": "^8.2.1", @@ -2089,18 +2087,6 @@ "node": ">=8.0.0" } }, - "node_modules/@oclif/test": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@oclif/test/-/test-2.0.3.tgz", - "integrity": "sha512-8mN/cmfrPQKc9+3I6Dj6HNgmPqRVKe4P7gU61pLUz220oFUxhuP0EHLzY3ARFx8ecUZAJZdWJwh+dne/1GfTcA==", - "dev": true, - "dependencies": { - "fancy-test": "^2.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/@octokit/auth-token": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", @@ -2248,15 +2234,6 @@ "type-detect": "4.0.8" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", - "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -2331,12 +2308,6 @@ "@babel/types": "^7.3.0" } }, - "node_modules/@types/chai": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", - "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==", - "dev": true - }, "node_modules/@types/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@types/dedent/-/dedent-0.7.0.tgz", @@ -2432,12 +2403,6 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, - "node_modules/@types/lodash": { - "version": "4.14.178", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz", - "integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==", - "dev": true - }, "node_modules/@types/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", @@ -2462,15 +2427,6 @@ "integrity": "sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg==", "dev": true }, - "node_modules/@types/sinon": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", - "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", - "dev": true, - "dependencies": { - "@sinonjs/fake-timers": "^7.1.0" - } - }, "node_modules/@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -3307,7 +3263,8 @@ "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, "node_modules/builtins": { "version": "1.0.3", @@ -3785,20 +3742,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, "node_modules/concurrently": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.1.0.tgz", @@ -3889,7 +3832,8 @@ "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "node_modules/create-require": { "version": "1.1.1", @@ -4974,25 +4918,6 @@ "node": ">=8" } }, - "node_modules/fancy-test": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fancy-test/-/fancy-test-2.0.0.tgz", - "integrity": "sha512-SFb2D/VX9SV+wNYXO1IIh1wyxUC1GS0mYCFJOWD1ia7MPj9yE2G8jaPkw4t/pg0Sj7/YJP56OzMY4nAuJSOugQ==", - "dev": true, - "dependencies": { - "@types/chai": "*", - "@types/lodash": "*", - "@types/node": "*", - "@types/sinon": "*", - "lodash": "^4.17.13", - "mock-stdin": "^1.0.0", - "nock": "^13.0.0", - "stdout-stderr": "^0.1.9" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5806,17 +5731,6 @@ "node": ">=12.0.0" } }, - "node_modules/inquirer-test": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inquirer-test/-/inquirer-test-2.0.1.tgz", - "integrity": "sha512-gpO6+E/VtPMNp7FBnIJtEWPHMzZ0GJK/EI3ThxtdBHbmbFW5PRl150CCMT3SpmqJY5x9nBeviyn8ZalDIABmFQ==", - "dependencies": { - "concat-stream": "^1.5.1" - }, - "engines": { - "node": ">=6.0" - } - }, "node_modules/internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -6173,7 +6087,8 @@ "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true }, "node_modules/isbinaryfile": { "version": "4.0.10", @@ -7092,12 +7007,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, "node_modules/json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", @@ -7249,12 +7158,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", - "dev": true - }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -7618,12 +7521,6 @@ "node": ">=10" } }, - "node_modules/mock-stdin": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mock-stdin/-/mock-stdin-1.0.0.tgz", - "integrity": "sha512-tukRdb9Beu27t6dN+XztSRHq9J0B/CoAOySGzHfn8UTfmqipA5yNT/sDUEyYdAV3Hpka6Wx6kOMxuObdOex60Q==", - "dev": true - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -7699,21 +7596,6 @@ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, - "node_modules/nock": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.1.tgz", - "integrity": "sha512-CoHAabbqq/xZEknubuyQMjq6Lfi5b7RtK6SoNK6m40lebGp3yiMagWtIoYaw2s9sISD7wPuCfwFpivVHX/35RA==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash.set": "^4.3.2", - "propagate": "^2.0.0" - }, - "engines": { - "node": ">= 10.13" - } - }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -8903,7 +8785,8 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "node_modules/promise-all-reject-late": { "version": "1.0.1", @@ -8955,15 +8838,6 @@ "node": ">= 6" } }, - "node_modules/propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -9381,6 +9255,7 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -9935,19 +9810,6 @@ "node": ">=8" } }, - "node_modules/stdout-stderr": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/stdout-stderr/-/stdout-stderr-0.1.13.tgz", - "integrity": "sha512-Xnt9/HHHYfjZ7NeQLvuQDyL1LnbsbddgMFKCuaQKwGCdJm8LnstZIXop+uOY36UR1UXXoHXfMbC1KlVdVd2JLA==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -10568,11 +10430,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -13092,15 +12949,6 @@ "resolved": "https://registry.npmjs.org/@oclif/screen/-/screen-1.0.4.tgz", "integrity": "sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw==" }, - "@oclif/test": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@oclif/test/-/test-2.0.3.tgz", - "integrity": "sha512-8mN/cmfrPQKc9+3I6Dj6HNgmPqRVKe4P7gU61pLUz220oFUxhuP0EHLzY3ARFx8ecUZAJZdWJwh+dne/1GfTcA==", - "dev": true, - "requires": { - "fancy-test": "^2.0.0" - } - }, "@octokit/auth-token": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", @@ -13240,15 +13088,6 @@ "type-detect": "4.0.8" } }, - "@sinonjs/fake-timers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", - "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -13320,12 +13159,6 @@ "@babel/types": "^7.3.0" } }, - "@types/chai": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", - "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==", - "dev": true - }, "@types/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@types/dedent/-/dedent-0.7.0.tgz", @@ -13421,12 +13254,6 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, - "@types/lodash": { - "version": "4.14.178", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz", - "integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==", - "dev": true - }, "@types/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", @@ -13451,15 +13278,6 @@ "integrity": "sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg==", "dev": true }, - "@types/sinon": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.6.tgz", - "integrity": "sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==", - "dev": true, - "requires": { - "@sinonjs/fake-timers": "^7.1.0" - } - }, "@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -14052,7 +13870,8 @@ "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, "builtins": { "version": "1.0.3", @@ -14411,17 +14230,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, "concurrently": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.1.0.tgz", @@ -14493,7 +14301,8 @@ "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "create-require": { "version": "1.1.1", @@ -15319,22 +15128,6 @@ "resolved": "https://registry.npmjs.org/extract-stack/-/extract-stack-2.0.0.tgz", "integrity": "sha512-AEo4zm+TenK7zQorGK1f9mJ8L14hnTDi2ZQPR+Mub1NX8zimka1mXpV5LpH8x9HoUmFSHZCfLHqWvp0Y4FxxzQ==" }, - "fancy-test": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fancy-test/-/fancy-test-2.0.0.tgz", - "integrity": "sha512-SFb2D/VX9SV+wNYXO1IIh1wyxUC1GS0mYCFJOWD1ia7MPj9yE2G8jaPkw4t/pg0Sj7/YJP56OzMY4nAuJSOugQ==", - "dev": true, - "requires": { - "@types/chai": "*", - "@types/lodash": "*", - "@types/node": "*", - "@types/sinon": "*", - "lodash": "^4.17.13", - "mock-stdin": "^1.0.0", - "nock": "^13.0.0", - "stdout-stderr": "^0.1.9" - } - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -15957,14 +15750,6 @@ "wrap-ansi": "^7.0.0" } }, - "inquirer-test": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inquirer-test/-/inquirer-test-2.0.1.tgz", - "integrity": "sha512-gpO6+E/VtPMNp7FBnIJtEWPHMzZ0GJK/EI3ThxtdBHbmbFW5PRl150CCMT3SpmqJY5x9nBeviyn8ZalDIABmFQ==", - "requires": { - "concat-stream": "^1.5.1" - } - }, "internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -16201,7 +15986,8 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true }, "isbinaryfile": { "version": "4.0.10", @@ -16902,12 +16688,6 @@ "integrity": "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==", "dev": true }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, "json5": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", @@ -17029,12 +16809,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", - "dev": true - }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -17318,12 +17092,6 @@ } } }, - "mock-stdin": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mock-stdin/-/mock-stdin-1.0.0.tgz", - "integrity": "sha512-tukRdb9Beu27t6dN+XztSRHq9J0B/CoAOySGzHfn8UTfmqipA5yNT/sDUEyYdAV3Hpka6Wx6kOMxuObdOex60Q==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -17383,18 +17151,6 @@ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, - "nock": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.2.1.tgz", - "integrity": "sha512-CoHAabbqq/xZEknubuyQMjq6Lfi5b7RtK6SoNK6m40lebGp3yiMagWtIoYaw2s9sISD7wPuCfwFpivVHX/35RA==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash.set": "^4.3.2", - "propagate": "^2.0.0" - } - }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -18293,7 +18049,8 @@ "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "promise-all-reject-late": { "version": "1.0.1", @@ -18333,12 +18090,6 @@ "sisteransi": "^1.0.5" } }, - "propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -18654,6 +18405,7 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -19070,16 +18822,6 @@ } } }, - "stdout-stderr": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/stdout-stderr/-/stdout-stderr-0.1.13.tgz", - "integrity": "sha512-Xnt9/HHHYfjZ7NeQLvuQDyL1LnbsbddgMFKCuaQKwGCdJm8LnstZIXop+uOY36UR1UXXoHXfMbC1KlVdVd2JLA==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "strip-ansi": "^6.0.0" - } - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -19526,11 +19268,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, "typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", diff --git a/package.json b/package.json index c0652b3d..9b3d3fde 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,10 @@ "@oclif/plugin-plugins": "^2.0.1", "@types/dedent": "^0.7.0", "dedent": "^0.7.0", - "inquirer": "^8.2.4", - "inquirer-test": "^2.0.1" + "inquirer": "^8.2.4" }, "devDependencies": { "@nimblehq/eslint-config-nimble": "^2.3.0", - "@oclif/test": "^2", "@types/fs-extra": "^9.0.13", "@types/glob": "^7.2.0", "@types/inquirer": "^8.2.1", From d83a9634aa40357de6fa65cc546b3ffbdd2daf3e Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Tue, 26 Jul 2022 15:18:13 +0700 Subject: [PATCH 21/24] [#29] Add tests for AWS templates --- src/templates/aws/advanced.test.ts | 83 ++++++++++++++++++++++++++++++ src/templates/aws/advanced.ts | 15 +++++- src/templates/aws/index.test.ts | 50 ++++++++++++++++++ 3 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 src/templates/aws/advanced.test.ts create mode 100644 src/templates/aws/index.test.ts diff --git a/src/templates/aws/advanced.test.ts b/src/templates/aws/advanced.test.ts new file mode 100644 index 00000000..d6c616f7 --- /dev/null +++ b/src/templates/aws/advanced.test.ts @@ -0,0 +1,83 @@ +import { AwsOptions } from '.'; +import { remove } from '../../helpers/file'; +import { + applyAlb, + applyBastion, + applyCommon, + applyEcr, + applyEcs, + applyLog, + applyRds, + applyRegion, + applyS3, + applySecurityGroup, + applySsm, + applyVpc, +} from './addons'; +import { applyAdvancedTemplate } from './advanced'; + +jest.mock('./addons'); + +describe('AWS advanced template', () => { + describe('applyAdvancedTemplate', () => { + const projectDir = 'aws-advanced-test'; + const options: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + + beforeAll(() => { + applyAdvancedTemplate(options); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('applies common add-on', () => { + expect(applyCommon).toHaveBeenCalledWith(options); + }); + + it('applies region add-on', () => { + expect(applyRegion).toHaveBeenCalledWith(options); + }); + + it('applies VPC add-on', () => { + expect(applyVpc).toHaveBeenCalledWith(options); + }); + + it('applies security group add-on', () => { + expect(applySecurityGroup).toHaveBeenCalledWith(options); + }); + + it('applies ECR add-on', () => { + expect(applyEcr).toHaveBeenCalledWith(options); + }); + + it('applies log add-on', () => { + expect(applyLog).toHaveBeenCalledWith(options); + }); + + it('applies S3 add-on', () => { + expect(applyS3).toHaveBeenCalledWith(options); + }); + + it('applies ALB add-on', () => { + expect(applyAlb).toHaveBeenCalledWith(options); + }); + + it('applies RDS add-on', () => { + expect(applyRds).toHaveBeenCalledWith(options); + }); + + it('applies bastion add-on', () => { + expect(applyBastion).toHaveBeenCalledWith(options); + }); + + it('applies SSM add-on', () => { + expect(applySsm).toHaveBeenCalledWith(options); + }); + + it('applies ECS add-on', () => { + expect(applyEcs).toHaveBeenCalledWith(options); + }); + }); +}); diff --git a/src/templates/aws/advanced.ts b/src/templates/aws/advanced.ts index d6229a45..9fdf9b25 100644 --- a/src/templates/aws/advanced.ts +++ b/src/templates/aws/advanced.ts @@ -1,5 +1,18 @@ import { AwsOptions } from '.'; -import { applyAlb, applyBastion, applyCommon, applyEcr, applyEcs, applyLog, applyRds, applyRegion, applyS3, applySecurityGroup, applySsm, applyVpc } from './addons'; +import { + applyAlb, + applyBastion, + applyCommon, + applyEcr, + applyEcs, + applyLog, + applyRds, + applyRegion, + applyS3, + applySecurityGroup, + applySsm, + applyVpc, +} from './addons'; const applyAdvancedTemplate = (options: AwsOptions) => { applyCommon(options); diff --git a/src/templates/aws/index.test.ts b/src/templates/aws/index.test.ts new file mode 100644 index 00000000..9869e681 --- /dev/null +++ b/src/templates/aws/index.test.ts @@ -0,0 +1,50 @@ +import { prompt } from 'inquirer'; + +import { generateAwsTemplate } from '.'; +import { GeneralOptions } from '../../commands/generate'; +import { remove } from '../../helpers/file'; +import { applyAdvancedTemplate } from './advanced'; + +jest.mock('./advanced'); +jest.mock('inquirer'); + +describe('AWS template', () => { + describe('generateAwsTemplate', () => { + const projectDir = 'aws-advanced-test'; + const options: GeneralOptions = { projectName: projectDir, provider: 'aws' }; + + describe('given infrastructureType is basic', () => { + beforeAll(() => { + (prompt as unknown as jest.Mock) + .mockResolvedValueOnce({ infrastructureType: 'basic' }); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('throws the error message', async() => { + await expect(generateAwsTemplate(options)).rejects.toThrow('This type has not been implemented!'); + }); + }); + + describe('given infrastructureType is advanced', () => { + beforeAll(() => { + (prompt as unknown as jest.Mock) + .mockResolvedValueOnce({ infrastructureType: 'advanced' }); + }); + + afterAll(() => { + jest.clearAllMocks(); + remove('/', projectDir); + }); + + it('applies advanced add-ons', async() => { + await generateAwsTemplate(options); + + expect(applyAdvancedTemplate).toHaveBeenCalled(); + }); + }); + }); +}); From b0c23e2b2714a00649922a3fc8720753a0ce1de8 Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Tue, 26 Jul 2022 15:57:51 +0700 Subject: [PATCH 22/24] [#29] Update eslint config --- package-lock.json | 127 ++++++++++++++++-- package.json | 2 +- src/commands/generate/index.test.ts | 46 ++++--- src/helpers/childProcess.test.ts | 4 +- src/helpers/childProcess.ts | 6 +- src/helpers/file.test.ts | 28 ++-- src/helpers/file.ts | 23 ++-- src/helpers/terraform.test.ts | 26 ++-- src/helpers/terraform.ts | 9 +- src/templates/aws/addons/alb.test.ts | 27 +++- src/templates/aws/addons/bastion.test.ts | 26 +++- src/templates/aws/addons/common.test.ts | 14 +- src/templates/aws/addons/common.ts | 9 +- src/templates/aws/addons/ecr.test.ts | 21 ++- src/templates/aws/addons/ecs.test.ts | 22 ++- src/templates/aws/addons/log.test.ts | 17 ++- src/templates/aws/addons/rds.test.ts | 22 ++- src/templates/aws/addons/region.test.ts | 19 ++- src/templates/aws/addons/s3.test.ts | 17 ++- .../aws/addons/securityGroup.test.ts | 32 ++++- src/templates/aws/addons/ssm.test.ts | 22 ++- src/templates/aws/addons/vpc.test.ts | 17 ++- src/templates/aws/addons/vpc.ts | 2 +- src/templates/aws/advanced.test.ts | 7 +- src/templates/aws/index.test.ts | 23 ++-- src/templates/aws/index.ts | 8 +- test/matchers/file.ts | 41 ++++-- test/matchers/index.d.ts | 5 +- test/matchers/index.ts | 8 +- 29 files changed, 491 insertions(+), 139 deletions(-) diff --git a/package-lock.json b/package-lock.json index 347a2c51..85b503ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "nimble-infra": "bin/run" }, "devDependencies": { - "@nimblehq/eslint-config-nimble": "^2.3.0", + "@nimblehq/eslint-config-nimble": "^2.4.0", "@types/fs-extra": "^9.0.13", "@types/glob": "^7.2.0", "@types/inquirer": "^8.2.1", @@ -1162,18 +1162,20 @@ } }, "node_modules/@nimblehq/eslint-config-nimble": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@nimblehq/eslint-config-nimble/-/eslint-config-nimble-2.3.0.tgz", - "integrity": "sha512-o0AHIflsgKsgEhhYYwgBGOUvgwHU83Opil9cnATvZ7umyh+a4okyO3UhurdciS5CnKRik8rIvoAZwf1VSe/zDg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@nimblehq/eslint-config-nimble/-/eslint-config-nimble-2.4.0.tgz", + "integrity": "sha512-UnU+FcmQNTqL0qmSR7EkIcSdrRw5tLQ9Sui/nq8+VI38rT0tvMCI1FVY2i8NeS7cfYlMprVD7TN9te+ynJDFDA==", "dev": true, "dependencies": { "@typescript-eslint/eslint-plugin": "^5.18.0", "@typescript-eslint/parser": "^5.18.0", "eslint": "^8.12.0", + "eslint-config-prettier": "^8.5.0", "eslint-import-resolver-typescript": "^2.7.1", "eslint-plugin-cypress": "^2.12.1", "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jest": "^26.1.3" + "eslint-plugin-jest": "^26.1.3", + "eslint-plugin-prettier": "^4.2.1" }, "engines": { "node": "^14.18.2 || ^16 || ^17" @@ -4299,6 +4301,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", @@ -4546,6 +4560,27 @@ } } }, + "node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -4924,6 +4959,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", @@ -8737,6 +8778,34 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -12204,18 +12273,20 @@ } }, "@nimblehq/eslint-config-nimble": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@nimblehq/eslint-config-nimble/-/eslint-config-nimble-2.3.0.tgz", - "integrity": "sha512-o0AHIflsgKsgEhhYYwgBGOUvgwHU83Opil9cnATvZ7umyh+a4okyO3UhurdciS5CnKRik8rIvoAZwf1VSe/zDg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@nimblehq/eslint-config-nimble/-/eslint-config-nimble-2.4.0.tgz", + "integrity": "sha512-UnU+FcmQNTqL0qmSR7EkIcSdrRw5tLQ9Sui/nq8+VI38rT0tvMCI1FVY2i8NeS7cfYlMprVD7TN9te+ynJDFDA==", "dev": true, "requires": { "@typescript-eslint/eslint-plugin": "^5.18.0", "@typescript-eslint/parser": "^5.18.0", "eslint": "^8.12.0", + "eslint-config-prettier": "^8.5.0", "eslint-import-resolver-typescript": "^2.7.1", "eslint-plugin-cypress": "^2.12.1", "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jest": "^26.1.3" + "eslint-plugin-jest": "^26.1.3", + "eslint-plugin-prettier": "^4.2.1" } }, "@nodelib/fs.scandir": { @@ -14707,6 +14778,13 @@ } } }, + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "requires": {} + }, "eslint-import-resolver-node": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", @@ -14900,6 +14978,15 @@ "@typescript-eslint/utils": "^5.10.0" } }, + "eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -15134,6 +15221,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-glob": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", @@ -18014,6 +18107,22 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true, + "peer": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", diff --git a/package.json b/package.json index 9b3d3fde..4e26166c 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "inquirer": "^8.2.4" }, "devDependencies": { - "@nimblehq/eslint-config-nimble": "^2.3.0", + "@nimblehq/eslint-config-nimble": "^2.4.0", "@types/fs-extra": "^9.0.13", "@types/glob": "^7.2.0", "@types/inquirer": "^8.2.1", diff --git a/src/commands/generate/index.test.ts b/src/commands/generate/index.test.ts index f25facbf..db71db93 100644 --- a/src/commands/generate/index.test.ts +++ b/src/commands/generate/index.test.ts @@ -15,7 +15,7 @@ describe('Generator command', () => { const projectDir = 'aws-basic-test'; const consoleErrorSpy = jest.spyOn(global.console, 'error'); - beforeAll(async() => { + beforeAll(async () => { (prompt as unknown as jest.Mock) .mockResolvedValueOnce({ provider: 'aws' }) .mockResolvedValueOnce({ infrastructureType: 'basic' }); @@ -29,7 +29,9 @@ describe('Generator command', () => { }); it('displays the error message', () => { - expect(consoleErrorSpy).toHaveBeenCalledWith(Error('This type has not been implemented!')); + expect(consoleErrorSpy).toHaveBeenCalledWith( + Error('This type has not been implemented!') + ); }); }); @@ -37,7 +39,7 @@ describe('Generator command', () => { const projectDir = 'aws-advanced-test'; const stdoutSpy = jest.spyOn(process.stdout, 'write'); - beforeAll(async() => { + beforeAll(async () => { (prompt as unknown as jest.Mock) .mockResolvedValueOnce({ provider: 'aws' }) .mockResolvedValueOnce({ infrastructureType: 'advanced' }); @@ -55,7 +57,9 @@ describe('Generator command', () => { }); it('displays the success message', () => { - expect(stdoutSpy).toHaveBeenCalledWith('The infrastructure has been generated!\n'); + expect(stdoutSpy).toHaveBeenCalledWith( + 'The infrastructure has been generated!\n' + ); }); }); }); @@ -64,9 +68,10 @@ describe('Generator command', () => { const projectDir = 'gcp-test'; const consoleErrorSpy = jest.spyOn(global.console, 'error'); - beforeAll(async() => { - (prompt as unknown as jest.Mock) - .mockResolvedValueOnce({ provider: 'gcp' }); + beforeAll(async () => { + (prompt as unknown as jest.Mock).mockResolvedValueOnce({ + provider: 'gcp', + }); await Generator.run([projectDir]); }); @@ -77,7 +82,9 @@ describe('Generator command', () => { }); it('displays the error message', () => { - expect(consoleErrorSpy).toHaveBeenCalledWith(Error('This provider has not been implemented!')); + expect(consoleErrorSpy).toHaveBeenCalledWith( + Error('This provider has not been implemented!') + ); }); }); @@ -85,9 +92,10 @@ describe('Generator command', () => { const projectDir = 'heroku-test'; const consoleErrorSpy = jest.spyOn(global.console, 'error'); - beforeAll(async() => { - (prompt as unknown as jest.Mock) - .mockResolvedValueOnce({ provider: 'heroku' }); + beforeAll(async () => { + (prompt as unknown as jest.Mock).mockResolvedValueOnce({ + provider: 'heroku', + }); await Generator.run([projectDir]); }); @@ -98,7 +106,9 @@ describe('Generator command', () => { }); it('displays the error message', () => { - expect(consoleErrorSpy).toHaveBeenCalledWith(Error('This provider has not been implemented!')); + expect(consoleErrorSpy).toHaveBeenCalledWith( + Error('This provider has not been implemented!') + ); }); }); @@ -106,7 +116,7 @@ describe('Generator command', () => { const projectDir = 'postProcess-test'; describe('given current machine had terraform', () => { - beforeAll(async() => { + beforeAll(async () => { (prompt as unknown as jest.Mock) .mockResolvedValueOnce({ provider: 'aws' }) .mockResolvedValueOnce({ infrastructureType: 'advanced' }); @@ -121,7 +131,7 @@ describe('Generator command', () => { remove('/', projectDir); }); - it('runs formatCode', async() => { + it('runs formatCode', async () => { await expect(formatCode).toHaveBeenCalled(); }); }); @@ -129,7 +139,7 @@ describe('Generator command', () => { describe('given current machine did not have terraform', () => { const consoleErrorSpy = jest.spyOn(global.console, 'error'); - beforeAll(async() => { + beforeAll(async () => { (prompt as unknown as jest.Mock) .mockResolvedValueOnce({ provider: 'aws' }) .mockResolvedValueOnce({ infrastructureType: 'advanced' }); @@ -146,12 +156,14 @@ describe('Generator command', () => { remove('/', projectDir); }); - it('does NOT run formatCode', async() => { + it('does NOT run formatCode', async () => { await expect(formatCode).not.toHaveBeenCalled(); }); it('displays the error message', () => { - expect(consoleErrorSpy).toHaveBeenCalledWith(Error('terraform not found')); + expect(consoleErrorSpy).toHaveBeenCalledWith( + Error('terraform not found') + ); }); }); }); diff --git a/src/helpers/childProcess.test.ts b/src/helpers/childProcess.test.ts index 4eed0ef7..81262a75 100644 --- a/src/helpers/childProcess.test.ts +++ b/src/helpers/childProcess.test.ts @@ -3,7 +3,7 @@ import { runCommand } from './childProcess'; describe('ChildProcess helper', () => { describe('runCommand', () => { describe('given a valid command', () => { - it('runs the command', async() => { + it('runs the command', async () => { const result = await runCommand('echo', ['hello']); expect(result).toBe('hello'); @@ -11,7 +11,7 @@ describe('ChildProcess helper', () => { }); describe('given an INVALID command', () => { - it('throws an error', async() => { + it('throws an error', async () => { let result: string; const invalidCommand = 'invalid-command'; diff --git a/src/helpers/childProcess.ts b/src/helpers/childProcess.ts index a7078c4b..a6ffdf10 100644 --- a/src/helpers/childProcess.ts +++ b/src/helpers/childProcess.ts @@ -3,7 +3,7 @@ import { spawn } from 'node:child_process'; const runCommand = ( command: string, args: string[], - cwd = './', + cwd = './' ): Promise => { return new Promise((resolve, reject) => { const child = spawn(command, args, { cwd }); @@ -15,6 +15,4 @@ const runCommand = ( }); }; -export { - runCommand, -}; +export { runCommand }; diff --git a/src/helpers/file.test.ts b/src/helpers/file.test.ts index f8f4692c..3c338fce 100644 --- a/src/helpers/file.test.ts +++ b/src/helpers/file.test.ts @@ -199,7 +199,7 @@ describe('File helpers', () => { expect(appendSpy).toHaveBeenCalledWith( path.join(targetPath, target), - content, + content ); }); }); @@ -219,11 +219,13 @@ describe('File helpers', () => { (readFileSync as jest.Mock).mockReturnValue(initialContent); - injectToFile(target, content, projectName, { insertBefore: initialContent }); + injectToFile(target, content, projectName, { + insertBefore: initialContent, + }); expect(injectSpy).toHaveBeenCalledWith( path.join(targetPath, target), - `${content}\n${initialContent}`, + `${content}\n${initialContent}` ); }); }); @@ -240,11 +242,13 @@ describe('File helpers', () => { (readFileSync as jest.Mock).mockReturnValue(initialContent); - injectToFile(target, content, projectName, { insertAfter: initialContent }); + injectToFile(target, content, projectName, { + insertAfter: initialContent, + }); expect(injectSpy).toHaveBeenCalledWith( path.join(targetPath, target), - `${initialContent}\n${content}`, + `${initialContent}\n${content}` ); }); }); @@ -265,7 +269,7 @@ describe('File helpers', () => { expect(injectSpy).toHaveBeenCalledWith( path.join(targetPath, target), - initialContent, + initialContent ); }); }); @@ -283,11 +287,13 @@ describe('File helpers', () => { (readFileSync as jest.Mock).mockReturnValue(initialContent); - injectToFile(target, content, projectName, { insertBefore: initialContent }); + injectToFile(target, content, projectName, { + insertBefore: initialContent, + }); expect(injectSpy).toHaveBeenCalledWith( 'targetFile.txt', - `${content}\n${initialContent}`, + `${content}\n${initialContent}` ); }); }); @@ -303,11 +309,13 @@ describe('File helpers', () => { (readFileSync as jest.Mock).mockReturnValue(initialContent); - injectToFile(target, content, projectName, { insertAfter: initialContent }); + injectToFile(target, content, projectName, { + insertAfter: initialContent, + }); expect(injectSpy).toHaveBeenCalledWith( 'targetFile.txt', - `${initialContent}\n${content}`, + `${initialContent}\n${content}` ); }); }); diff --git a/src/helpers/file.ts b/src/helpers/file.ts index e8f7aa97..791caa14 100644 --- a/src/helpers/file.ts +++ b/src/helpers/file.ts @@ -27,7 +27,7 @@ const getTargetPath = (file: string, projectName: string): string => { const getTemplatePath = (): string => { const templateDir = - process.env.NODE_ENV === 'production' ? 'dist/skeleton' : 'skeleton'; + process.env.NODE_ENV === 'production' ? 'dist/skeleton' : 'skeleton'; return path.join(ROOT_DIR, templateDir); }; @@ -38,18 +38,14 @@ const getSourcePath = (file: string): string => { const appendToFile = ( target: string, content: string, - projectName: string, + projectName: string ): void => { const targetPath = getTargetPath(target, projectName); appendFileSync(targetPath, content); }; -const copy = ( - source: string, - target: string, - projectName: string, -): void => { +const copy = (source: string, target: string, projectName: string): void => { const sourcePath = path.join(getTemplatePath(), source); const targetPath = getTargetPath(target, projectName); @@ -59,7 +55,7 @@ const copy = ( const createFile = ( target: string, content: string, - projectName: string, + projectName: string ): void => { const targetPath = getTargetPath(target, projectName); const targetExists = existsSync(targetPath); @@ -78,7 +74,7 @@ const remove = (target: string, projectName: string): void => { const renameFile = ( source: string, target: string, - projectName: string, + projectName: string ): void => { const sourcePath = getTargetPath(source, projectName); const targetPath = getTargetPath(target, projectName); @@ -90,22 +86,23 @@ const injectToFile = ( target: string, content: string, projectName: string, - { insertBefore = '', insertAfter = '' }: InjectToFileOptions = {}, + { insertBefore = '', insertAfter = '' }: InjectToFileOptions = {} ): void => { - const targetPath = projectName !== '' ? getTargetPath(target, projectName) : target; + const targetPath = + projectName !== '' ? getTargetPath(target, projectName) : target; const data = readFileSync(targetPath, 'utf8'); const lines = data.toString().split('\n'); if (insertBefore) { - const index = lines.findIndex(line => line.includes(insertBefore)); + const index = lines.findIndex((line) => line.includes(insertBefore)); if (index !== -1) { lines.splice(index, 0, content); } } if (insertAfter) { - const index = lines.findIndex(line => line.includes(insertAfter)); + const index = lines.findIndex((line) => line.includes(insertAfter)); if (index !== -1) { lines.splice(index + 1, 0, content); } diff --git a/src/helpers/terraform.test.ts b/src/helpers/terraform.test.ts index d7404808..660ea91e 100644 --- a/src/helpers/terraform.test.ts +++ b/src/helpers/terraform.test.ts @@ -10,7 +10,7 @@ describe('Terraform helper', () => { describe('detectTerraform', () => { describe('given terraform is installed', () => { - it('returns true', async() => { + it('returns true', async () => { (runCommand as jest.Mock).mockResolvedValueOnce(''); const result = await detectTerraform(); @@ -20,28 +20,34 @@ describe('Terraform helper', () => { }); describe('given terraform is not installed', () => { - it('returns false', async() => { - (runCommand as jest.Mock).mockRejectedValueOnce(new Error('terraform not found')); + it('returns false', async () => { + (runCommand as jest.Mock).mockRejectedValueOnce( + new Error('terraform not found') + ); const result = await detectTerraform(); expect(result).toBe(false); }); - it('displays the error message', async() => { - (runCommand as jest.Mock).mockRejectedValueOnce(new Error('terraform not found')); + it('displays the error message', async () => { + (runCommand as jest.Mock).mockRejectedValueOnce( + new Error('terraform not found') + ); const consoleSpy = jest.spyOn(global.console, 'error'); await detectTerraform(); - expect(consoleSpy).toHaveBeenCalledWith('Terraform not found. Please install terraform.'); + expect(consoleSpy).toHaveBeenCalledWith( + 'Terraform not found. Please install terraform.' + ); }); }); }); describe('formatCode', () => { describe('given terraform is installed', () => { - it('runs terraform fmt', async() => { + it('runs terraform fmt', async () => { (runCommand as jest.Mock).mockResolvedValueOnce(''); await formatCode('/'); @@ -51,8 +57,10 @@ describe('Terraform helper', () => { }); describe('given terraform is not installed', () => { - it('displays the error message', async() => { - (runCommand as jest.Mock).mockRejectedValueOnce(new Error('terraform not found')); + it('displays the error message', async () => { + (runCommand as jest.Mock).mockRejectedValueOnce( + new Error('terraform not found') + ); const consoleSpy = jest.spyOn(global.console, 'error'); await formatCode('/'); diff --git a/src/helpers/terraform.ts b/src/helpers/terraform.ts index bdda79da..0ad1aaed 100644 --- a/src/helpers/terraform.ts +++ b/src/helpers/terraform.ts @@ -1,6 +1,6 @@ import { runCommand } from './childProcess'; -const detectTerraform = async() => { +const detectTerraform = async () => { try { await runCommand('which', ['terraform']); @@ -12,7 +12,7 @@ const detectTerraform = async() => { } }; -const formatCode = async(projectDir: string) => { +const formatCode = async (projectDir: string) => { try { await runCommand('terraform', ['fmt'], projectDir); } catch (error) { @@ -20,7 +20,4 @@ const formatCode = async(projectDir: string) => { } }; -export { - detectTerraform, - formatCode, -}; +export { detectTerraform, formatCode }; diff --git a/src/templates/aws/addons/alb.test.ts b/src/templates/aws/addons/alb.test.ts index 80c55ac0..9c72efbc 100644 --- a/src/templates/aws/addons/alb.test.ts +++ b/src/templates/aws/addons/alb.test.ts @@ -1,6 +1,10 @@ import { AwsOptions } from '..'; import { remove } from '../../../helpers/file'; -import applyAlb, { albModuleContent, albOutputsContent, albVariablesContent } from './alb'; +import applyAlb, { + albModuleContent, + albOutputsContent, + albVariablesContent, +} from './alb'; import applyCommon from './common'; describe('ALB add-on', () => { @@ -8,7 +12,12 @@ describe('ALB add-on', () => { const projectDir = 'alb-addon-test'; beforeAll(() => { - const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'advanced', + awsRegion: 'ap-southeast-1', + }; applyCommon(awsOptions); applyAlb(awsOptions); @@ -20,7 +29,14 @@ describe('ALB add-on', () => { }); it('creates expected files', () => { - const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/alb/main.tf', 'modules/alb/variables.tf']; + const expectedFiles = [ + 'main.tf', + 'providers.tf', + 'outputs.tf', + 'variables.tf', + 'modules/alb/main.tf', + 'modules/alb/variables.tf', + ]; expect(projectDir).toHaveFiles(expectedFiles); }); @@ -34,7 +50,10 @@ describe('ALB add-on', () => { }); it('adds ALB variables to variables.tf', () => { - expect(projectDir).toHaveContentInFile('variables.tf', albVariablesContent); + expect(projectDir).toHaveContentInFile( + 'variables.tf', + albVariablesContent + ); }); it('adds ALB outputs to outputs.tf', () => { diff --git a/src/templates/aws/addons/bastion.test.ts b/src/templates/aws/addons/bastion.test.ts index 623cd61d..237d5982 100644 --- a/src/templates/aws/addons/bastion.test.ts +++ b/src/templates/aws/addons/bastion.test.ts @@ -1,6 +1,9 @@ import { AwsOptions } from '..'; import { remove } from '../../../helpers/file'; -import applyBastion, { bastionModuleContent, bastionVariablesContent } from './bastion'; +import applyBastion, { + bastionModuleContent, + bastionVariablesContent, +} from './bastion'; import applyCommon from './common'; describe('Bastion add-on', () => { @@ -8,7 +11,12 @@ describe('Bastion add-on', () => { const projectDir = 'bastion-addon-test'; beforeAll(() => { - const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'advanced', + awsRegion: 'ap-southeast-1', + }; applyCommon(awsOptions); applyBastion(awsOptions); @@ -20,7 +28,14 @@ describe('Bastion add-on', () => { }); it('creates expected files', () => { - const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/bastion/main.tf', 'modules/bastion/variables.tf']; + const expectedFiles = [ + 'main.tf', + 'providers.tf', + 'outputs.tf', + 'variables.tf', + 'modules/bastion/main.tf', + 'modules/bastion/variables.tf', + ]; expect(projectDir).toHaveFiles(expectedFiles); }); @@ -34,7 +49,10 @@ describe('Bastion add-on', () => { }); it('adds bastion variables to variables.tf', () => { - expect(projectDir).toHaveContentInFile('variables.tf', bastionVariablesContent); + expect(projectDir).toHaveContentInFile( + 'variables.tf', + bastionVariablesContent + ); }); }); }); diff --git a/src/templates/aws/addons/common.test.ts b/src/templates/aws/addons/common.test.ts index 413f5b75..5562018c 100644 --- a/src/templates/aws/addons/common.test.ts +++ b/src/templates/aws/addons/common.test.ts @@ -7,7 +7,12 @@ describe('Common add-on', () => { const projectDir = 'common-addon-test'; beforeAll(() => { - const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'basic', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'basic', + awsRegion: 'ap-southeast-1', + }; applyCommon(awsOptions); }); @@ -18,7 +23,12 @@ describe('Common add-on', () => { }); it('creates expected files', () => { - const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf']; + const expectedFiles = [ + 'main.tf', + 'providers.tf', + 'outputs.tf', + 'variables.tf', + ]; expect(projectDir).toHaveFiles(expectedFiles); }); diff --git a/src/templates/aws/addons/common.ts b/src/templates/aws/addons/common.ts index 43e01b6f..b064eed6 100644 --- a/src/templates/aws/addons/common.ts +++ b/src/templates/aws/addons/common.ts @@ -2,14 +2,9 @@ import { AwsOptions } from '..'; import { copy } from '../../../helpers/file'; const applyCommon = ({ projectName }: AwsOptions) => { - const filesToCopy = [ - 'main.tf', - 'outputs.tf', - 'providers.tf', - 'variables.tf', - ]; + const filesToCopy = ['main.tf', 'outputs.tf', 'providers.tf', 'variables.tf']; - filesToCopy.forEach(fileName => { + filesToCopy.forEach((fileName) => { copy(`aws/${fileName}`, fileName, projectName); }); }; diff --git a/src/templates/aws/addons/ecr.test.ts b/src/templates/aws/addons/ecr.test.ts index 733e574a..dfd581cd 100644 --- a/src/templates/aws/addons/ecr.test.ts +++ b/src/templates/aws/addons/ecr.test.ts @@ -8,7 +8,12 @@ describe('ECR add-on', () => { const projectDir = 'ecr-addon-test'; beforeAll(() => { - const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'advanced', + awsRegion: 'ap-southeast-1', + }; applyCommon(awsOptions); applyEcr(awsOptions); @@ -20,7 +25,14 @@ describe('ECR add-on', () => { }); it('creates expected files', () => { - const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/ecr/main.tf', 'modules/ecr/variables.tf']; + const expectedFiles = [ + 'main.tf', + 'providers.tf', + 'outputs.tf', + 'variables.tf', + 'modules/ecr/main.tf', + 'modules/ecr/variables.tf', + ]; expect(projectDir).toHaveFiles(expectedFiles); }); @@ -34,7 +46,10 @@ describe('ECR add-on', () => { }); it('adds ECR variables to variables.tf', () => { - expect(projectDir).toHaveContentInFile('variables.tf', ecrVariablesContent); + expect(projectDir).toHaveContentInFile( + 'variables.tf', + ecrVariablesContent + ); }); }); }); diff --git a/src/templates/aws/addons/ecs.test.ts b/src/templates/aws/addons/ecs.test.ts index f4172783..f072682c 100644 --- a/src/templates/aws/addons/ecs.test.ts +++ b/src/templates/aws/addons/ecs.test.ts @@ -8,7 +8,12 @@ describe('ECS add-on', () => { const projectDir = 'ecs-addon-test'; beforeAll(() => { - const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'advanced', + awsRegion: 'ap-southeast-1', + }; applyCommon(awsOptions); applyEcs(awsOptions); @@ -20,7 +25,15 @@ describe('ECS add-on', () => { }); it('creates expected files', () => { - const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/ecs/main.tf', 'modules/ecs/variables.tf', 'modules/ecs/service.json.tftpl']; + const expectedFiles = [ + 'main.tf', + 'providers.tf', + 'outputs.tf', + 'variables.tf', + 'modules/ecs/main.tf', + 'modules/ecs/variables.tf', + 'modules/ecs/service.json.tftpl', + ]; expect(projectDir).toHaveFiles(expectedFiles); }); @@ -34,7 +47,10 @@ describe('ECS add-on', () => { }); it('adds ECS variables to variables.tf', () => { - expect(projectDir).toHaveContentInFile('variables.tf', ecsVariablesContent); + expect(projectDir).toHaveContentInFile( + 'variables.tf', + ecsVariablesContent + ); }); }); }); diff --git a/src/templates/aws/addons/log.test.ts b/src/templates/aws/addons/log.test.ts index 64bad6ac..469d1c49 100644 --- a/src/templates/aws/addons/log.test.ts +++ b/src/templates/aws/addons/log.test.ts @@ -8,7 +8,12 @@ describe('Log add-on', () => { const projectDir = 'log-addon-test'; beforeAll(() => { - const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'advanced', + awsRegion: 'ap-southeast-1', + }; applyCommon(awsOptions); applyLog(awsOptions); @@ -20,7 +25,15 @@ describe('Log add-on', () => { }); it('creates expected files', () => { - const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/log/main.tf', 'modules/log/variables.tf', 'modules/log/outputs.tf']; + const expectedFiles = [ + 'main.tf', + 'providers.tf', + 'outputs.tf', + 'variables.tf', + 'modules/log/main.tf', + 'modules/log/variables.tf', + 'modules/log/outputs.tf', + ]; expect(projectDir).toHaveFiles(expectedFiles); }); diff --git a/src/templates/aws/addons/rds.test.ts b/src/templates/aws/addons/rds.test.ts index b1432a3a..5ab6f794 100644 --- a/src/templates/aws/addons/rds.test.ts +++ b/src/templates/aws/addons/rds.test.ts @@ -8,7 +8,12 @@ describe('RDS add-on', () => { const projectDir = 'rds-addon-test'; beforeAll(() => { - const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'advanced', + awsRegion: 'ap-southeast-1', + }; applyCommon(awsOptions); applyRds(awsOptions); @@ -20,7 +25,15 @@ describe('RDS add-on', () => { }); it('creates expected files', () => { - const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/rds/main.tf', 'modules/rds/variables.tf', 'modules/rds/outputs.tf']; + const expectedFiles = [ + 'main.tf', + 'providers.tf', + 'outputs.tf', + 'variables.tf', + 'modules/rds/main.tf', + 'modules/rds/variables.tf', + 'modules/rds/outputs.tf', + ]; expect(projectDir).toHaveFiles(expectedFiles); }); @@ -34,7 +47,10 @@ describe('RDS add-on', () => { }); it('adds RDS variables to variables.tf', () => { - expect(projectDir).toHaveContentInFile('variables.tf', rdsVariablesContent); + expect(projectDir).toHaveContentInFile( + 'variables.tf', + rdsVariablesContent + ); }); }); }); diff --git a/src/templates/aws/addons/region.test.ts b/src/templates/aws/addons/region.test.ts index bc848106..e458584d 100644 --- a/src/templates/aws/addons/region.test.ts +++ b/src/templates/aws/addons/region.test.ts @@ -9,7 +9,12 @@ describe('Region add-on', () => { const awsRegion = 'ap-southeast-1'; beforeAll(() => { - const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion }; + const awsOptions: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'advanced', + awsRegion, + }; applyCommon(awsOptions); applyRegion(awsOptions); @@ -21,13 +26,21 @@ describe('Region add-on', () => { }); it('creates expected files', () => { - const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf']; + const expectedFiles = [ + 'main.tf', + 'providers.tf', + 'outputs.tf', + 'variables.tf', + ]; expect(projectDir).toHaveFiles(expectedFiles); }); it('adds region variables to variables.tf', () => { - expect(projectDir).toHaveContentInFile('variables.tf', regionVariablesContent(awsRegion)); + expect(projectDir).toHaveContentInFile( + 'variables.tf', + regionVariablesContent(awsRegion) + ); }); }); }); diff --git a/src/templates/aws/addons/s3.test.ts b/src/templates/aws/addons/s3.test.ts index 9bde8aa4..b7bc653d 100644 --- a/src/templates/aws/addons/s3.test.ts +++ b/src/templates/aws/addons/s3.test.ts @@ -8,7 +8,12 @@ describe('S3 add-on', () => { const projectDir = 's3-addon-test'; beforeAll(() => { - const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'advanced', + awsRegion: 'ap-southeast-1', + }; applyCommon(awsOptions); applyS3(awsOptions); @@ -20,7 +25,15 @@ describe('S3 add-on', () => { }); it('creates expected files', () => { - const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/s3/main.tf', 'modules/s3/variables.tf', 'modules/s3/outputs.tf']; + const expectedFiles = [ + 'main.tf', + 'providers.tf', + 'outputs.tf', + 'variables.tf', + 'modules/s3/main.tf', + 'modules/s3/variables.tf', + 'modules/s3/outputs.tf', + ]; expect(projectDir).toHaveFiles(expectedFiles); }); diff --git a/src/templates/aws/addons/securityGroup.test.ts b/src/templates/aws/addons/securityGroup.test.ts index 24b5bc7a..df4f351c 100644 --- a/src/templates/aws/addons/securityGroup.test.ts +++ b/src/templates/aws/addons/securityGroup.test.ts @@ -1,14 +1,22 @@ import { AwsOptions } from '..'; import { remove } from '../../../helpers/file'; import applyCommon from './common'; -import applySecurityGroup, { securityGroupModuleContent, securityGroupVariablesContent } from './securityGroup'; +import applySecurityGroup, { + securityGroupModuleContent, + securityGroupVariablesContent, +} from './securityGroup'; describe('Security group add-on', () => { describe('given valid AWS options', () => { const projectDir = 'security-group-addon-test'; beforeAll(() => { - const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'advanced', + awsRegion: 'ap-southeast-1', + }; applyCommon(awsOptions); applySecurityGroup(awsOptions); @@ -20,7 +28,15 @@ describe('Security group add-on', () => { }); it('creates expected files', () => { - const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/security_group/main.tf', 'modules/security_group/variables.tf', 'modules/security_group/outputs.tf']; + const expectedFiles = [ + 'main.tf', + 'providers.tf', + 'outputs.tf', + 'variables.tf', + 'modules/security_group/main.tf', + 'modules/security_group/variables.tf', + 'modules/security_group/outputs.tf', + ]; expect(projectDir).toHaveFiles(expectedFiles); }); @@ -30,11 +46,17 @@ describe('Security group add-on', () => { }); it('adds security group module to main.tf', () => { - expect(projectDir).toHaveContentInFile('main.tf', securityGroupModuleContent); + expect(projectDir).toHaveContentInFile( + 'main.tf', + securityGroupModuleContent + ); }); it('adds security group variables to variables.tf', () => { - expect(projectDir).toHaveContentInFile('variables.tf', securityGroupVariablesContent); + expect(projectDir).toHaveContentInFile( + 'variables.tf', + securityGroupVariablesContent + ); }); }); }); diff --git a/src/templates/aws/addons/ssm.test.ts b/src/templates/aws/addons/ssm.test.ts index ab5e5648..3b3cea29 100644 --- a/src/templates/aws/addons/ssm.test.ts +++ b/src/templates/aws/addons/ssm.test.ts @@ -8,7 +8,12 @@ describe('SSM add-on', () => { const projectDir = 'ssm-addon-test'; beforeAll(() => { - const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'advanced', + awsRegion: 'ap-southeast-1', + }; applyCommon(awsOptions); applySsm(awsOptions); @@ -20,7 +25,15 @@ describe('SSM add-on', () => { }); it('creates expected files', () => { - const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/ssm/main.tf', 'modules/ssm/variables.tf', 'modules/ssm/outputs.tf']; + const expectedFiles = [ + 'main.tf', + 'providers.tf', + 'outputs.tf', + 'variables.tf', + 'modules/ssm/main.tf', + 'modules/ssm/variables.tf', + 'modules/ssm/outputs.tf', + ]; expect(projectDir).toHaveFiles(expectedFiles); }); @@ -34,7 +47,10 @@ describe('SSM add-on', () => { }); it('adds SSM variables to variables.tf', () => { - expect(projectDir).toHaveContentInFile('variables.tf', ssmVariablesContent); + expect(projectDir).toHaveContentInFile( + 'variables.tf', + ssmVariablesContent + ); }); }); }); diff --git a/src/templates/aws/addons/vpc.test.ts b/src/templates/aws/addons/vpc.test.ts index 1b7d961f..2a34c865 100644 --- a/src/templates/aws/addons/vpc.test.ts +++ b/src/templates/aws/addons/vpc.test.ts @@ -8,7 +8,12 @@ describe('VPC add-on', () => { const projectDir = 'vpc-addon-test'; beforeAll(() => { - const awsOptions: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + const awsOptions: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'advanced', + awsRegion: 'ap-southeast-1', + }; applyCommon(awsOptions); applyVpc(awsOptions); @@ -20,7 +25,15 @@ describe('VPC add-on', () => { }); it('creates expected files', () => { - const expectedFiles = ['main.tf', 'providers.tf', 'outputs.tf', 'variables.tf', 'modules/vpc/main.tf', 'modules/vpc/variables.tf', 'modules/vpc/outputs.tf']; + const expectedFiles = [ + 'main.tf', + 'providers.tf', + 'outputs.tf', + 'variables.tf', + 'modules/vpc/main.tf', + 'modules/vpc/variables.tf', + 'modules/vpc/outputs.tf', + ]; expect(projectDir).toHaveFiles(expectedFiles); }); diff --git a/src/templates/aws/addons/vpc.ts b/src/templates/aws/addons/vpc.ts index 0c68fdd4..ef833231 100644 --- a/src/templates/aws/addons/vpc.ts +++ b/src/templates/aws/addons/vpc.ts @@ -17,7 +17,7 @@ const vpcModuleContent = dedent` } \n`; -const applyVpc = ({projectName}: AwsOptions) => { +const applyVpc = ({ projectName }: AwsOptions) => { copy('aws/modules/vpc', 'modules/vpc', projectName); appendToFile('outputs.tf', vpcOutputsContent, projectName); appendToFile('main.tf', vpcModuleContent, projectName); diff --git a/src/templates/aws/advanced.test.ts b/src/templates/aws/advanced.test.ts index d6c616f7..d758a8c3 100644 --- a/src/templates/aws/advanced.test.ts +++ b/src/templates/aws/advanced.test.ts @@ -21,7 +21,12 @@ jest.mock('./addons'); describe('AWS advanced template', () => { describe('applyAdvancedTemplate', () => { const projectDir = 'aws-advanced-test'; - const options: AwsOptions = { projectName: projectDir, provider: 'aws', infrastructureType: 'advanced', awsRegion: 'ap-southeast-1' }; + const options: AwsOptions = { + projectName: projectDir, + provider: 'aws', + infrastructureType: 'advanced', + awsRegion: 'ap-southeast-1', + }; beforeAll(() => { applyAdvancedTemplate(options); diff --git a/src/templates/aws/index.test.ts b/src/templates/aws/index.test.ts index 9869e681..c05d42c7 100644 --- a/src/templates/aws/index.test.ts +++ b/src/templates/aws/index.test.ts @@ -11,12 +11,16 @@ jest.mock('inquirer'); describe('AWS template', () => { describe('generateAwsTemplate', () => { const projectDir = 'aws-advanced-test'; - const options: GeneralOptions = { projectName: projectDir, provider: 'aws' }; + const options: GeneralOptions = { + projectName: projectDir, + provider: 'aws', + }; describe('given infrastructureType is basic', () => { beforeAll(() => { - (prompt as unknown as jest.Mock) - .mockResolvedValueOnce({ infrastructureType: 'basic' }); + (prompt as unknown as jest.Mock).mockResolvedValueOnce({ + infrastructureType: 'basic', + }); }); afterAll(() => { @@ -24,15 +28,18 @@ describe('AWS template', () => { remove('/', projectDir); }); - it('throws the error message', async() => { - await expect(generateAwsTemplate(options)).rejects.toThrow('This type has not been implemented!'); + it('throws the error message', async () => { + await expect(generateAwsTemplate(options)).rejects.toThrow( + 'This type has not been implemented!' + ); }); }); describe('given infrastructureType is advanced', () => { beforeAll(() => { - (prompt as unknown as jest.Mock) - .mockResolvedValueOnce({ infrastructureType: 'advanced' }); + (prompt as unknown as jest.Mock).mockResolvedValueOnce({ + infrastructureType: 'advanced', + }); }); afterAll(() => { @@ -40,7 +47,7 @@ describe('AWS template', () => { remove('/', projectDir); }); - it('applies advanced add-ons', async() => { + it('applies advanced add-ons', async () => { await generateAwsTemplate(options); expect(applyAdvancedTemplate).toHaveBeenCalled(); diff --git a/src/templates/aws/index.ts b/src/templates/aws/index.ts index f330b339..cf20ea2f 100644 --- a/src/templates/aws/index.ts +++ b/src/templates/aws/index.ts @@ -33,9 +33,11 @@ const awsChoices = [ type AwsOptions = GeneralOptions & { infrastructureType: 'basic' | 'advanced'; awsRegion: string; -} +}; -const generateAwsTemplate = async(generalOptions: GeneralOptions): Promise => { +const generateAwsTemplate = async ( + generalOptions: GeneralOptions +): Promise => { const awsOptionsPrompt = await inquirer.prompt(awsChoices); const awsOptions: AwsOptions = { ...generalOptions, @@ -54,5 +56,5 @@ const generateAwsTemplate = async(generalOptions: GeneralOptions): Promise } }; -export type { AwsOptions}; +export type { AwsOptions }; export { generateAwsTemplate }; diff --git a/test/matchers/file.ts b/test/matchers/file.ts index 436a787b..0854d740 100644 --- a/test/matchers/file.ts +++ b/test/matchers/file.ts @@ -20,7 +20,7 @@ const toHaveFiles = (projectDir: string, expectedFiles: string[]) => { } const actualFiles = sync(`**/*.*`, { cwd: projectDir }); - const pass = expectedFiles.every(file => actualFiles.includes(file)); + const pass = expectedFiles.every((file) => actualFiles.includes(file)); const diffs = diff(expectedFiles, actualFiles, { expand: false }); return { @@ -44,24 +44,35 @@ const toHaveDirectory = (projectDir: string, expectedDirectory: string) => { }; }; -const toHaveDirectories = (projectDir: string, expectedDirectories: string[]) => { +const toHaveDirectories = ( + projectDir: string, + expectedDirectories: string[] +) => { if (!Array.isArray(expectedDirectories)) { throw new Error('Expected directories must be an array'); } const actualDirectories = sync(`**/`, { cwd: projectDir }); - const pass = expectedDirectories.every(directory => actualDirectories.includes(directory)); + const pass = expectedDirectories.every((directory) => + actualDirectories.includes(directory) + ); const diffs = diff(expectedDirectories, actualDirectories, { expand: false }); return { pass, message: pass - ? () => `expected ${projectDir} not to have "${expectedDirectories}"\n${diffs}` - : () => `expected ${projectDir} to have "${expectedDirectories}"\n${diffs}`, + ? () => + `expected ${projectDir} not to have "${expectedDirectories}"\n${diffs}` + : () => + `expected ${projectDir} to have "${expectedDirectories}"\n${diffs}`, }; }; -const toHaveContentInFile = (projectDir: string, expectedFile: string, expectedContent: string | string[]) => { +const toHaveContentInFile = ( + projectDir: string, + expectedFile: string, + expectedContent: string | string[] +) => { let expectedContentArray: string[]; if (!Array.isArray(expectedContent)) { expectedContentArray = [expectedContent]; @@ -70,14 +81,24 @@ const toHaveContentInFile = (projectDir: string, expectedFile: string, expectedC } const actualContent = readFileSync(`${projectDir}/${expectedFile}`, 'utf8'); - const pass = expectedContentArray.every(content => actualContent.includes(content)); + const pass = expectedContentArray.every((content) => + actualContent.includes(content) + ); return { pass, message: pass - ? () => `expected ${projectDir} to not have "${expectedContent}" in "${expectedFile}"` - : () => `expected ${projectDir} to have "${expectedContent}" in "${expectedFile}"`, + ? () => + `expected ${projectDir} to not have "${expectedContent}" in "${expectedFile}"` + : () => + `expected ${projectDir} to have "${expectedContent}" in "${expectedFile}"`, }; }; -export { toHaveFile, toHaveFiles, toHaveDirectory, toHaveDirectories, toHaveContentInFile }; +export { + toHaveFile, + toHaveFiles, + toHaveDirectory, + toHaveDirectories, + toHaveContentInFile, +}; diff --git a/test/matchers/index.d.ts b/test/matchers/index.d.ts index ad8c11ad..4a90e423 100644 --- a/test/matchers/index.d.ts +++ b/test/matchers/index.d.ts @@ -5,7 +5,10 @@ declare global { toHaveFiles: (expectedFiles: string[]) => R; toHaveDirectory: (expectedDirectory: string) => R; toHaveDirectories: (expectedDirectories: string[]) => R; - toHaveContentInFile: (expectedFile: string, expectedContent: string | string[]) => R; + toHaveContentInFile: ( + expectedFile: string, + expectedContent: string | string[] + ) => R; } } } diff --git a/test/matchers/index.ts b/test/matchers/index.ts index bbc47396..4487fa64 100644 --- a/test/matchers/index.ts +++ b/test/matchers/index.ts @@ -1,4 +1,10 @@ -import { toHaveFile, toHaveFiles, toHaveDirectory, toHaveDirectories, toHaveContentInFile } from './file'; +import { + toHaveFile, + toHaveFiles, + toHaveDirectory, + toHaveDirectories, + toHaveContentInFile, +} from './file'; const matchers = { toHaveFile, From e4b828e5aa9b2d76a5269c0b2bf7da80cc3604a5 Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Fri, 29 Jul 2022 13:11:11 +0700 Subject: [PATCH 23/24] [#29] Improve file helper --- src/commands/generate/index.ts | 4 +-- src/helpers/file.test.ts | 55 +++++++++++----------------------- src/helpers/file.ts | 41 +++++++++---------------- 3 files changed, 34 insertions(+), 66 deletions(-) diff --git a/src/commands/generate/index.ts b/src/commands/generate/index.ts index 01b4d661..3ab52a74 100644 --- a/src/commands/generate/index.ts +++ b/src/commands/generate/index.ts @@ -1,7 +1,7 @@ import { Command } from '@oclif/core'; import { prompt } from 'inquirer'; -import { getTargetDir } from '../../helpers/file'; +import { getProjectPath } from '../../helpers/file'; import { detectTerraform, formatCode } from '../../helpers/terraform'; import { generateAwsTemplate } from '../../templates/aws'; @@ -82,7 +82,7 @@ export default class Generator extends Command { private async postProcess(generalOptions: GeneralOptions): Promise { try { if (await detectTerraform()) { - await formatCode(getTargetDir(generalOptions.projectName)); + await formatCode(getProjectPath(generalOptions.projectName)); } } catch (error) { console.error(error); diff --git a/src/helpers/file.test.ts b/src/helpers/file.test.ts index 3c338fce..a2369b56 100644 --- a/src/helpers/file.test.ts +++ b/src/helpers/file.test.ts @@ -7,13 +7,12 @@ import { appendToFile, copy, createFile, - getSourcePath, - getTargetDir, - getTargetPath, + getTemplateFilePath, + getProjectPath, + getProjectFilePath, getTemplatePath, injectToFile, remove, - renameFile, } from './file'; jest.mock('fs-extra'); @@ -23,25 +22,25 @@ describe('File helpers', () => { jest.clearAllMocks(); }); - describe('getTargetDir', () => { + describe('getProjectPath', () => { describe('given projectName', () => { it('returns the correct target directory', () => { const projectName = 'projectName'; - const targetDir = getTargetDir(projectName); + const targetDir = getProjectPath(projectName); expect(targetDir).toBe(path.join(process.cwd(), projectName)); }); }); }); - describe('getTargetPath', () => { + describe('getProjectFilePath', () => { describe('given file name and projectName', () => { it('returns the correct target path', () => { const file = 'file'; const projectName = 'projectName'; - const targetPath = getTargetPath(file, projectName); + const targetPath = getProjectFilePath(file, projectName); expect(targetPath).toBe(path.join(process.cwd(), projectName, file)); }); @@ -90,12 +89,12 @@ describe('File helpers', () => { }); }); - describe('getSourcePath', () => { + describe('getTemplateFilePath', () => { describe('given file name', () => { it('returns the correct source path', () => { const file = 'example.txt'; - const sourcePath = getSourcePath(file); + const sourcePath = getTemplateFilePath(file); expect(sourcePath).toBe(path.join(getTemplatePath(), file)); }); @@ -108,8 +107,8 @@ describe('File helpers', () => { const source = 'sourceDir'; const target = 'targetDir'; const projectName = 'projectName'; - const sourcePath = getSourcePath(source); - const targetPath = getTargetPath(target, projectName); + const sourcePath = getTemplateFilePath(source); + const targetPath = getProjectFilePath(target, projectName); const copySpy = jest.spyOn(fs, 'copySync'); @@ -126,7 +125,7 @@ describe('File helpers', () => { const target = 'targetFile.txt'; const content = 'creating content'; const projectName = 'projectName'; - const targetPath = getTargetPath(target, projectName); + const targetPath = getProjectFilePath(target, projectName); const createSpy = jest.spyOn(fs, 'writeFileSync'); @@ -142,7 +141,7 @@ describe('File helpers', () => { it('removes the target file', () => { const target = 'targetFile.txt'; const projectName = 'projectName'; - const targetPath = getTargetPath(target, projectName); + const targetPath = getProjectFilePath(target, projectName); const removeSpy = jest.spyOn(fs, 'removeSync'); @@ -156,7 +155,7 @@ describe('File helpers', () => { it('removes the target directory', () => { const target = 'targetDir'; const projectName = 'projectName'; - const targetPath = getTargetPath(target, projectName); + const targetPath = getProjectFilePath(target, projectName); const removeSpy = jest.spyOn(fs, 'removeSync'); @@ -167,31 +166,13 @@ describe('File helpers', () => { }); }); - describe('renameFile', () => { - describe('given source and target', () => { - it('renames the source file to the target file', () => { - const source = 'sourceFile.txt'; - const target = 'targetFile.txt'; - const projectName = 'projectName'; - const sourcePath = getTargetPath(source, projectName); - const targetPath = getTargetPath(target, projectName); - - const renameSpy = jest.spyOn(fs, 'renameSync'); - - renameFile(source, target, projectName); - - expect(renameSpy).toHaveBeenCalledWith(sourcePath, targetPath); - }); - }); - }); - describe('appendToFile', () => { describe('given target file and content', () => { it('appends content to the target file', () => { const target = 'targetFile.txt'; const content = 'appending content'; const projectName = 'projectName'; - const targetPath = getTargetDir(projectName); + const targetPath = getProjectPath(projectName); const appendSpy = jest.spyOn(fs, 'appendFileSync'); @@ -213,7 +194,7 @@ describe('File helpers', () => { const initialContent = 'initial content'; const content = 'injecting content'; const projectName = 'projectName'; - const targetPath = getTargetDir(projectName); + const targetPath = getProjectPath(projectName); const injectSpy = jest.spyOn(fs, 'writeFileSync'); @@ -236,7 +217,7 @@ describe('File helpers', () => { const initialContent = 'initial content'; const content = 'injecting content'; const projectName = 'projectName'; - const targetPath = getTargetDir(projectName); + const targetPath = getProjectPath(projectName); const injectSpy = jest.spyOn(fs, 'writeFileSync'); @@ -259,7 +240,7 @@ describe('File helpers', () => { const initialContent = 'initial content'; const content = 'injecting content'; const projectName = 'projectName'; - const targetPath = getTargetDir(projectName); + const targetPath = getProjectPath(projectName); const injectSpy = jest.spyOn(fs, 'writeFileSync'); diff --git a/src/helpers/file.ts b/src/helpers/file.ts index 791caa14..b718be50 100644 --- a/src/helpers/file.ts +++ b/src/helpers/file.ts @@ -6,7 +6,6 @@ import { existsSync, readFileSync, removeSync, - renameSync, writeFileSync, } from 'fs-extra'; @@ -17,12 +16,12 @@ interface InjectToFileOptions { const ROOT_DIR = path.join(__dirname, '..', '..'); -const getTargetDir = (projectName: string): string => { +const getProjectPath = (projectName: string): string => { return path.join(process.cwd(), projectName); }; -const getTargetPath = (file: string, projectName: string): string => { - return path.join(getTargetDir(projectName), file); +const getProjectFilePath = (file: string, projectName: string): string => { + return path.join(getProjectPath(projectName), file); }; const getTemplatePath = (): string => { @@ -31,7 +30,7 @@ const getTemplatePath = (): string => { return path.join(ROOT_DIR, templateDir); }; -const getSourcePath = (file: string): string => { +const getTemplateFilePath = (file: string): string => { return path.join(getTemplatePath(), file); }; @@ -40,14 +39,14 @@ const appendToFile = ( content: string, projectName: string ): void => { - const targetPath = getTargetPath(target, projectName); + const targetPath = getProjectFilePath(target, projectName); appendFileSync(targetPath, content); }; const copy = (source: string, target: string, projectName: string): void => { const sourcePath = path.join(getTemplatePath(), source); - const targetPath = getTargetPath(target, projectName); + const targetPath = getProjectFilePath(target, projectName); copySync(sourcePath, targetPath); }; @@ -57,7 +56,7 @@ const createFile = ( content: string, projectName: string ): void => { - const targetPath = getTargetPath(target, projectName); + const targetPath = getProjectFilePath(target, projectName); const targetExists = existsSync(targetPath); if (!targetExists) { @@ -66,22 +65,11 @@ const createFile = ( }; const remove = (target: string, projectName: string): void => { - const targetPath = getTargetPath(target, projectName); + const targetPath = getProjectFilePath(target, projectName); removeSync(targetPath); }; -const renameFile = ( - source: string, - target: string, - projectName: string -): void => { - const sourcePath = getTargetPath(source, projectName); - const targetPath = getTargetPath(target, projectName); - - renameSync(sourcePath, targetPath); -}; - const injectToFile = ( target: string, content: string, @@ -89,7 +77,7 @@ const injectToFile = ( { insertBefore = '', insertAfter = '' }: InjectToFileOptions = {} ): void => { const targetPath = - projectName !== '' ? getTargetPath(target, projectName) : target; + projectName !== '' ? getProjectFilePath(target, projectName) : target; const data = readFileSync(targetPath, 'utf8'); const lines = data.toString().split('\n'); @@ -113,14 +101,13 @@ const injectToFile = ( }; export { - getTargetDir, - getTargetPath, - getTemplatePath, - getSourcePath, appendToFile, copy, - remove, createFile, + getProjectFilePath, + getProjectPath, + getTemplateFilePath, + getTemplatePath, injectToFile, - renameFile, + remove, }; From 4758e95537f6e150e7000af18525f7ab72fb3b9c Mon Sep 17 00:00:00 2001 From: Hoang Mirs Date: Fri, 29 Jul 2022 13:17:33 +0700 Subject: [PATCH 24/24] [#29] Remove unnecessary test cases --- src/templates/aws/addons/alb.test.ts | 4 ---- src/templates/aws/addons/bastion.test.ts | 4 ---- src/templates/aws/addons/ecr.test.ts | 4 ---- src/templates/aws/addons/ecs.test.ts | 4 ---- src/templates/aws/addons/log.test.ts | 4 ---- src/templates/aws/addons/rds.test.ts | 4 ---- src/templates/aws/addons/s3.test.ts | 4 ---- src/templates/aws/addons/securityGroup.test.ts | 4 ---- src/templates/aws/addons/ssm.test.ts | 4 ---- src/templates/aws/addons/vpc.test.ts | 4 ---- 10 files changed, 40 deletions(-) diff --git a/src/templates/aws/addons/alb.test.ts b/src/templates/aws/addons/alb.test.ts index 9c72efbc..42af2a2b 100644 --- a/src/templates/aws/addons/alb.test.ts +++ b/src/templates/aws/addons/alb.test.ts @@ -41,10 +41,6 @@ describe('ALB add-on', () => { expect(projectDir).toHaveFiles(expectedFiles); }); - it('creates expected folders', () => { - expect(projectDir).toHaveDirectory('modules/alb/'); - }); - it('adds ALB module to main.tf', () => { expect(projectDir).toHaveContentInFile('main.tf', albModuleContent); }); diff --git a/src/templates/aws/addons/bastion.test.ts b/src/templates/aws/addons/bastion.test.ts index 237d5982..6dd739cc 100644 --- a/src/templates/aws/addons/bastion.test.ts +++ b/src/templates/aws/addons/bastion.test.ts @@ -40,10 +40,6 @@ describe('Bastion add-on', () => { expect(projectDir).toHaveFiles(expectedFiles); }); - it('creates expected folders', () => { - expect(projectDir).toHaveDirectory('modules/bastion/'); - }); - it('adds bastion module to main.tf', () => { expect(projectDir).toHaveContentInFile('main.tf', bastionModuleContent); }); diff --git a/src/templates/aws/addons/ecr.test.ts b/src/templates/aws/addons/ecr.test.ts index dfd581cd..92f9aabc 100644 --- a/src/templates/aws/addons/ecr.test.ts +++ b/src/templates/aws/addons/ecr.test.ts @@ -37,10 +37,6 @@ describe('ECR add-on', () => { expect(projectDir).toHaveFiles(expectedFiles); }); - it('creates expected folders', () => { - expect(projectDir).toHaveDirectory('modules/ecr/'); - }); - it('adds ECR module to main.tf', () => { expect(projectDir).toHaveContentInFile('main.tf', ecrModuleContent); }); diff --git a/src/templates/aws/addons/ecs.test.ts b/src/templates/aws/addons/ecs.test.ts index f072682c..c74e05dc 100644 --- a/src/templates/aws/addons/ecs.test.ts +++ b/src/templates/aws/addons/ecs.test.ts @@ -38,10 +38,6 @@ describe('ECS add-on', () => { expect(projectDir).toHaveFiles(expectedFiles); }); - it('creates expected folders', () => { - expect(projectDir).toHaveDirectory('modules/ecs/'); - }); - it('adds ECS module to main.tf', () => { expect(projectDir).toHaveContentInFile('main.tf', ecsModuleContent); }); diff --git a/src/templates/aws/addons/log.test.ts b/src/templates/aws/addons/log.test.ts index 469d1c49..cccf4be3 100644 --- a/src/templates/aws/addons/log.test.ts +++ b/src/templates/aws/addons/log.test.ts @@ -38,10 +38,6 @@ describe('Log add-on', () => { expect(projectDir).toHaveFiles(expectedFiles); }); - it('creates expected folders', () => { - expect(projectDir).toHaveDirectory('modules/log/'); - }); - it('adds log module to main.tf', () => { expect(projectDir).toHaveContentInFile('main.tf', logModuleContent); }); diff --git a/src/templates/aws/addons/rds.test.ts b/src/templates/aws/addons/rds.test.ts index 5ab6f794..6f473519 100644 --- a/src/templates/aws/addons/rds.test.ts +++ b/src/templates/aws/addons/rds.test.ts @@ -38,10 +38,6 @@ describe('RDS add-on', () => { expect(projectDir).toHaveFiles(expectedFiles); }); - it('creates expected folders', () => { - expect(projectDir).toHaveDirectory('modules/rds/'); - }); - it('adds RDS module to main.tf', () => { expect(projectDir).toHaveContentInFile('main.tf', rdsModuleContent); }); diff --git a/src/templates/aws/addons/s3.test.ts b/src/templates/aws/addons/s3.test.ts index b7bc653d..a0d72618 100644 --- a/src/templates/aws/addons/s3.test.ts +++ b/src/templates/aws/addons/s3.test.ts @@ -38,10 +38,6 @@ describe('S3 add-on', () => { expect(projectDir).toHaveFiles(expectedFiles); }); - it('creates expected folders', () => { - expect(projectDir).toHaveDirectory('modules/s3/'); - }); - it('adds S3 module to main.tf', () => { expect(projectDir).toHaveContentInFile('main.tf', s3ModuleContent); }); diff --git a/src/templates/aws/addons/securityGroup.test.ts b/src/templates/aws/addons/securityGroup.test.ts index df4f351c..b8a0401d 100644 --- a/src/templates/aws/addons/securityGroup.test.ts +++ b/src/templates/aws/addons/securityGroup.test.ts @@ -41,10 +41,6 @@ describe('Security group add-on', () => { expect(projectDir).toHaveFiles(expectedFiles); }); - it('creates expected folders', () => { - expect(projectDir).toHaveDirectory('modules/security_group/'); - }); - it('adds security group module to main.tf', () => { expect(projectDir).toHaveContentInFile( 'main.tf', diff --git a/src/templates/aws/addons/ssm.test.ts b/src/templates/aws/addons/ssm.test.ts index 3b3cea29..6f6ee52b 100644 --- a/src/templates/aws/addons/ssm.test.ts +++ b/src/templates/aws/addons/ssm.test.ts @@ -38,10 +38,6 @@ describe('SSM add-on', () => { expect(projectDir).toHaveFiles(expectedFiles); }); - it('creates expected folders', () => { - expect(projectDir).toHaveDirectory('modules/ssm/'); - }); - it('adds SSM module to main.tf', () => { expect(projectDir).toHaveContentInFile('main.tf', ssmModuleContent); }); diff --git a/src/templates/aws/addons/vpc.test.ts b/src/templates/aws/addons/vpc.test.ts index 2a34c865..4beeeef5 100644 --- a/src/templates/aws/addons/vpc.test.ts +++ b/src/templates/aws/addons/vpc.test.ts @@ -38,10 +38,6 @@ describe('VPC add-on', () => { expect(projectDir).toHaveFiles(expectedFiles); }); - it('creates expected folders', () => { - expect(projectDir).toHaveDirectory('modules/vpc/'); - }); - it('adds VPC module to main.tf', () => { expect(projectDir).toHaveContentInFile('main.tf', vpcModuleContent); });