From b2cab46c561281dbb490574fd2389369a025b889 Mon Sep 17 00:00:00 2001 From: Stseb Date: Mon, 24 Jul 2017 18:01:29 +0200 Subject: [PATCH 01/10] feat: pass argument to main function --- bin/create-webextension | 2 +- index.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/create-webextension b/bin/create-webextension index 9260a73..752e409 100755 --- a/bin/create-webextension +++ b/bin/create-webextension @@ -1,3 +1,3 @@ #!/usr/bin/env node -require("..").main(); \ No newline at end of file +require("..").main(process.argv[2]); diff --git a/index.js b/index.js index 6ec9528..cecd1fc 100644 --- a/index.js +++ b/index.js @@ -87,14 +87,14 @@ function getProjectManifest(projectDirName) { }; } -exports.main = function main() { - if (!process.argv[2]) { +exports.main = function main(dirPath) { + if (!dirPath) { console.error(`${chalk.red("Missing project dir name.")}\n`); console.log(USAGE_MSG); process.exit(1); } - const projectPath = path.resolve(process.argv[2]); + const projectPath = path.resolve(dirPath); const projectDirName = path.basename(projectPath); return fs.mkdir(projectPath).then(() => { From 12be9ae19974a48a1c4dc6cfcce441ae915dc13e Mon Sep 17 00:00:00 2001 From: saintsebastian Date: Tue, 25 Jul 2017 18:53:45 +0200 Subject: [PATCH 02/10] move checking for argument into wrapper --- bin/create-webextension | 9 +++++++++ index.js | 6 +----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/bin/create-webextension b/bin/create-webextension index 752e409..703d5fb 100755 --- a/bin/create-webextension +++ b/bin/create-webextension @@ -1,3 +1,12 @@ #!/usr/bin/env node +const chalk = require("chalk"); + +const USAGE_MSG = `Usage: create-webextension project_dir_name`; + +if (!process.argv[2]) { + console.error(`${chalk.red("Missing project dir name.")}\n`); + console.log(USAGE_MSG); + process.exit(1); +} require("..").main(process.argv[2]); diff --git a/index.js b/index.js index cecd1fc..a7bf280 100644 --- a/index.js +++ b/index.js @@ -5,8 +5,6 @@ const chalk = require("chalk"); const fs = require("mz/fs"); const stripAnsi = require("strip-ansi"); -const USAGE_MSG = `Usage: create-webextension project_dir_name`; - const README = ` This project contains a blank WebExtension addon, a "white canvas" for your new experiment of extending and remixing the Web. @@ -89,9 +87,7 @@ function getProjectManifest(projectDirName) { exports.main = function main(dirPath) { if (!dirPath) { - console.error(`${chalk.red("Missing project dir name.")}\n`); - console.log(USAGE_MSG); - process.exit(1); + throw new Error("Project directory name is a compulsory argument"); } const projectPath = path.resolve(dirPath); From 2e41028616232a10d4b7e00dcfdac2a7bf599ee7 Mon Sep 17 00:00:00 2001 From: saintsebastian Date: Thu, 27 Jul 2017 21:01:08 +0200 Subject: [PATCH 03/10] better handled errors --- bin/create-webextension | 4 +++- index.js | 11 ++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/bin/create-webextension b/bin/create-webextension index 703d5fb..0a018d7 100755 --- a/bin/create-webextension +++ b/bin/create-webextension @@ -9,4 +9,6 @@ if (!process.argv[2]) { process.exit(1); } -require("..").main(process.argv[2]); +require("..").main(process.argv[2]) + .then(console.log) + .catch(err => console.log(err.message)); diff --git a/index.js b/index.js index a7bf280..d78ce20 100644 --- a/index.js +++ b/index.js @@ -87,7 +87,7 @@ function getProjectManifest(projectDirName) { exports.main = function main(dirPath) { if (!dirPath) { - throw new Error("Project directory name is a compulsory argument"); + throw new Error("Project directory name is a mandatory argument"); } const projectPath = path.resolve(dirPath); @@ -106,16 +106,13 @@ exports.main = function main(dirPath) { .then(() => getProjectReadme(projectDirName)) .then(projectReadme => fs.writeFile(path.join(projectPath, "README.md"), stripAnsi(projectReadme))) - .then(() => getProjectCreatedMessage(projectPath)) - .then(console.log); + .then(() => getProjectCreatedMessage(projectPath)); }, error => { if (error.code === "EEXIST") { const msg = `Unable to create a new WebExtension: ${chalk.bold.underline(projectPath)} dir already exist.`; - console.error(`${chalk.red(msg)}\n`); - process.exit(1); + throw new Error(msg); } }).catch((error) => { - console.error(error); - process.exit(1); + throw error; }); }; From 63916af6a1b08df2fa38e2d711adb35c22975443 Mon Sep 17 00:00:00 2001 From: saintsebastian Date: Mon, 7 Aug 2017 13:21:05 +0200 Subject: [PATCH 04/10] feat: add expected erro class and its handling --- bin/create-webextension | 8 +++++++- errors.js | 18 ++++++++++++++++++ index.js | 3 ++- package.json | 1 + 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 errors.js diff --git a/bin/create-webextension b/bin/create-webextension index 0a018d7..5c163e9 100755 --- a/bin/create-webextension +++ b/bin/create-webextension @@ -1,5 +1,8 @@ #!/usr/bin/env node const chalk = require("chalk"); +const UsageError = require("../errors").UsageError; +const onlyInstancesOf = require("../errors").onlyInstancesOf; + const USAGE_MSG = `Usage: create-webextension project_dir_name`; @@ -11,4 +14,7 @@ if (!process.argv[2]) { require("..").main(process.argv[2]) .then(console.log) - .catch(err => console.log(err.message)); + .catch(onlyInstancesOf(UsageError, (error) => { + console.log(`${chalk.red(error.message)}\n`); + process.exit(1); + })); diff --git a/errors.js b/errors.js new file mode 100644 index 0000000..960fe27 --- /dev/null +++ b/errors.js @@ -0,0 +1,18 @@ +const ES6Error = require("es6-error"); + +exports.onlyInstancesOf = function(errorType, handler) { + return(error) => { + if (error instanceof errorType) { + return handler(error); + } else { + console.log(error.stack); + process.exit(1); + } + } +} + +exports.UsageError = class UsageError extends ES6Error { + constructor(message) { + super(message); + } +} diff --git a/index.js b/index.js index d78ce20..0105693 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ const path = require("path"); const chalk = require("chalk"); const fs = require("mz/fs"); const stripAnsi = require("strip-ansi"); +const UsageError = require("./errors").UsageError; const README = ` This project contains a blank WebExtension addon, a "white canvas" for your new experiment of @@ -110,7 +111,7 @@ exports.main = function main(dirPath) { }, error => { if (error.code === "EEXIST") { const msg = `Unable to create a new WebExtension: ${chalk.bold.underline(projectPath)} dir already exist.`; - throw new Error(msg); + throw new UsageError(msg); } }).catch((error) => { throw error; diff --git a/package.json b/package.json index 4783eae..3470614 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "homepage": "https://github.com/rpl/create-webextension#readme", "dependencies": { "chalk": "^1.1.3", + "es6-error": "^4.0.2", "mz": "^2.6.0", "strip-ansi": "^3.0.1" }, From 9b9e16c31d4ce5f97ff08ded3a93be0d14c5342e Mon Sep 17 00:00:00 2001 From: saintsebastian Date: Mon, 7 Aug 2017 13:43:41 +0200 Subject: [PATCH 05/10] test: initial test for onlyInstancesOf --- __tests__/errors.test.js | 13 +++++++++++++ errors.js | 12 +++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 __tests__/errors.test.js diff --git a/__tests__/errors.test.js b/__tests__/errors.test.js new file mode 100644 index 0000000..794ba43 --- /dev/null +++ b/__tests__/errors.test.js @@ -0,0 +1,13 @@ +"use strict"; + +const UsageError = require("../errors").UsageError; +const onlyInstancesOf = require("../errors").onlyInstancesOf; + +describe("onlyInstancesOf", () => { + test("catches specified error", () => { + return Promise.reject(new UsageError("fake usage error")) + .catch(onlyInstancesOf(UsageError, (error) => { + expect(error).toBeInstanceOf(UsageError); + })); + }); +}); diff --git a/errors.js b/errors.js index 960fe27..3c33c02 100644 --- a/errors.js +++ b/errors.js @@ -1,18 +1,20 @@ const ES6Error = require("es6-error"); -exports.onlyInstancesOf = function(errorType, handler) { - return(error) => { +exports.onlyInstancesOf = function (errorType, handler) { + return (error) => { if (error instanceof errorType) { return handler(error); + // eslint-disable-next-line no-else-return } else { console.log(error.stack); process.exit(1); } - } -} + }; +}; exports.UsageError = class UsageError extends ES6Error { + // eslint-disable-next-line no-useless-constructor constructor(message) { super(message); } -} +}; From 1457b194b20ec47198d0474b068311270ae0e14d Mon Sep 17 00:00:00 2001 From: saintsebastian Date: Thu, 10 Aug 2017 22:36:50 +0200 Subject: [PATCH 06/10] fix: cleanup and better syntax --- bin/create-webextension | 8 ++++++-- errors.js | 12 ++---------- index.js | 7 ++++--- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/bin/create-webextension b/bin/create-webextension index 5c163e9..e92dda6 100755 --- a/bin/create-webextension +++ b/bin/create-webextension @@ -15,6 +15,10 @@ if (!process.argv[2]) { require("..").main(process.argv[2]) .then(console.log) .catch(onlyInstancesOf(UsageError, (error) => { - console.log(`${chalk.red(error.message)}\n`); + console.error(`${chalk.red(error.message)}\n`); process.exit(1); - })); + })) + .catch((error) => { + console.error(`${chalk.red(error.message)}: ${error.stack}\n`); // or something similar that prints both the message and the stack + process.exit(1); + }); diff --git a/errors.js b/errors.js index 3c33c02..cf399b1 100644 --- a/errors.js +++ b/errors.js @@ -4,17 +4,9 @@ exports.onlyInstancesOf = function (errorType, handler) { return (error) => { if (error instanceof errorType) { return handler(error); - // eslint-disable-next-line no-else-return - } else { - console.log(error.stack); - process.exit(1); } + throw error; }; }; -exports.UsageError = class UsageError extends ES6Error { - // eslint-disable-next-line no-useless-constructor - constructor(message) { - super(message); - } -}; +exports.UsageError = class UsageError extends ES6Error {}; diff --git a/index.js b/index.js index 0105693..db0db11 100644 --- a/index.js +++ b/index.js @@ -107,13 +107,14 @@ exports.main = function main(dirPath) { .then(() => getProjectReadme(projectDirName)) .then(projectReadme => fs.writeFile(path.join(projectPath, "README.md"), stripAnsi(projectReadme))) - .then(() => getProjectCreatedMessage(projectPath)); + .then(() => { + const projectCreatedMessage = getProjectCreatedMessage(projectPath); + return {projectPath, projectCreatedMessage}; + }); }, error => { if (error.code === "EEXIST") { const msg = `Unable to create a new WebExtension: ${chalk.bold.underline(projectPath)} dir already exist.`; throw new UsageError(msg); } - }).catch((error) => { - throw error; }); }; From 441432b6aea2b712e6cb6610327d630714124f9e Mon Sep 17 00:00:00 2001 From: saintsebastian Date: Fri, 11 Aug 2017 16:40:22 +0200 Subject: [PATCH 07/10] test: initial test for main --- __tests__/errors.test.js | 13 ------ __tests__/{ => integration}/index.test.js | 6 +-- __tests__/unit/errors.test.js | 23 ++++++++++ __tests__/unit/index.test.js | 55 +++++++++++++++++++++++ bin/create-webextension | 4 +- index.js | 43 ++++++++++++------ 6 files changed, 112 insertions(+), 32 deletions(-) delete mode 100644 __tests__/errors.test.js rename __tests__/{ => integration}/index.test.js (92%) create mode 100644 __tests__/unit/errors.test.js create mode 100644 __tests__/unit/index.test.js diff --git a/__tests__/errors.test.js b/__tests__/errors.test.js deleted file mode 100644 index 794ba43..0000000 --- a/__tests__/errors.test.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -const UsageError = require("../errors").UsageError; -const onlyInstancesOf = require("../errors").onlyInstancesOf; - -describe("onlyInstancesOf", () => { - test("catches specified error", () => { - return Promise.reject(new UsageError("fake usage error")) - .catch(onlyInstancesOf(UsageError, (error) => { - expect(error).toBeInstanceOf(UsageError); - })); - }); -}); diff --git a/__tests__/index.test.js b/__tests__/integration/index.test.js similarity index 92% rename from __tests__/index.test.js rename to __tests__/integration/index.test.js index a57b92b..8ec1622 100644 --- a/__tests__/index.test.js +++ b/__tests__/integration/index.test.js @@ -3,10 +3,10 @@ const path = require("path"); const fs = require("mz/fs"); const linter = require("addons-linter"); -const withTmpDir = require("./helpers/tmp-dir"); -const cmdRunner = require("./helpers/cmd-runner"); +const withTmpDir = require("../helpers/tmp-dir"); +const cmdRunner = require("../helpers/cmd-runner"); -const execDirPath = path.join(__dirname, "..", "bin"); +const execDirPath = path.join(__dirname, "../..", "bin"); describe("main", () => { test("creates files including manifest with correct name", () => withTmpDir( diff --git a/__tests__/unit/errors.test.js b/__tests__/unit/errors.test.js new file mode 100644 index 0000000..f363ec8 --- /dev/null +++ b/__tests__/unit/errors.test.js @@ -0,0 +1,23 @@ +"use strict"; + +const UsageError = require("../../errors").UsageError; +const onlyInstancesOf = require("../../errors").onlyInstancesOf; + +describe("onlyInstancesOf", () => { + test("catches specified error", () => { + return Promise.reject(new UsageError("fake usage error")) + .catch(onlyInstancesOf(UsageError, (error) => { + expect(error).toBeInstanceOf(UsageError); + })); + }); + + test("throws other errors", () => { + return Promise.reject(new Error("fake error")) + .catch(onlyInstancesOf(UsageError, () => { + throw new Error("Unexpectedly caught the wrong error"); + })) + .catch((error) => { + expect(error.message).toMatch(/fake error/); + }); + }); +}); diff --git a/__tests__/unit/index.test.js b/__tests__/unit/index.test.js new file mode 100644 index 0000000..6de4c15 --- /dev/null +++ b/__tests__/unit/index.test.js @@ -0,0 +1,55 @@ +"use strict"; + +const path = require("path"); +const chalk = require("chalk"); +const fs = require("mz/fs"); +const withTmpDir = require("../helpers/tmp-dir"); +const main = require("../../index").main; +const MORE_INFO_MSG = require("../../index").MORE_INFO_MSG; +const asciiLogo = require("../../index").asciiLogo; + +describe("main", () => { + test("returns project path and creation message", () => withTmpDir( + async (tmpPath) => { + const projName = "target"; + const targetDir = path.join(tmpPath, projName); + const expectedMessage = + `${asciiLogo} \n` + ` + Congratulations!!! A new WebExtension has been created at: + + ${chalk.bold(chalk.green(targetDir))} ${MORE_INFO_MSG}`; + + const result = await main({ + dirPath: projName, + baseDir: tmpPath, + }); + expect(result.projectPath).toEqual(targetDir); + expect(result.projectCreatedMessage).toEqual(expectedMessage); + }) + ); + + test("creates files, readme and manifest with correct name", () => withTmpDir( + async (tmpPath) => { + const projName = "target"; + const targetDir = path.join(tmpPath, projName); + + const result = await main({ + dirPath: projName, + baseDir: tmpPath, + }); + + const contentStat = await fs.stat(path.join(targetDir, "content.js")); + expect(contentStat.isDirectory()).toBeFalsy(); + + const bgStat = await fs.stat(path.join(targetDir, "background.js")); + expect(bgStat.isDirectory()).toBeFalsy(); + + const rmStat = await fs.stat(path.join(targetDir, "README.md")); + expect(rmStat.isDirectory()).toBeFalsy(); + + const manifest = await fs.readFile(path.join(targetDir, "manifest.json"), "utf-8"); + const parsed = JSON.parse(manifest); + expect(parsed.name).toEqual(projName); + }) + ); +}); diff --git a/bin/create-webextension b/bin/create-webextension index e92dda6..b380c5e 100755 --- a/bin/create-webextension +++ b/bin/create-webextension @@ -12,8 +12,8 @@ if (!process.argv[2]) { process.exit(1); } -require("..").main(process.argv[2]) - .then(console.log) +require("..").main({dirPath: process.argv[2]}) + .then(({projectCreatedMessage}) => console.log(projectCreatedMessage)) .catch(onlyInstancesOf(UsageError, (error) => { console.error(`${chalk.red(error.message)}\n`); process.exit(1); diff --git a/index.js b/index.js index db0db11..8a09e37 100644 --- a/index.js +++ b/index.js @@ -35,16 +35,17 @@ a WebExtension from the command line: ${chalk.bold.blue("web-ext run -s /path/to/extension")} `; +const asciiLogo = fs.readFileSync( + path.join(__dirname, "assets", "webextension-logo.ascii") +); + function getProjectCreatedMessage(projectPath) { - return fs.readFile(path.join(__dirname, "assets", "webextension-logo.ascii")) - .then(asciiLogo => { - const PROJECT_CREATED_MSG = `\n -Congratulations!!! A new WebExtension has been created at: + const PROJECT_CREATED_MSG = `\n + Congratulations!!! A new WebExtension has been created at: - ${chalk.bold(chalk.green(projectPath))}`; + ${chalk.bold(chalk.green(projectPath))}`; - return `${asciiLogo} ${PROJECT_CREATED_MSG} ${MORE_INFO_MSG}`; - }); + return `${asciiLogo} ${PROJECT_CREATED_MSG} ${MORE_INFO_MSG}`; } function getProjectReadme(projectDirName) { @@ -86,29 +87,37 @@ function getProjectManifest(projectDirName) { }; } -exports.main = function main(dirPath) { +function main({ + dirPath, + baseDir = process.cwd(), + getProjectManifestFn = getProjectManifest, + getPlaceholderIconFn = getPlaceholderIcon, + getProjectReadmeFn = getProjectReadme, + getProjectCreatedMessageFn = getProjectCreatedMessage, +}) { if (!dirPath) { throw new Error("Project directory name is a mandatory argument"); } - const projectPath = path.resolve(dirPath); + const projectPath = path.resolve(baseDir, dirPath); + console.log(projectPath); const projectDirName = path.basename(projectPath); return fs.mkdir(projectPath).then(() => { return Promise.all([ fs.writeFile(path.join(projectPath, "manifest.json"), - JSON.stringify(getProjectManifest(projectDirName), null, 2)), + JSON.stringify(getProjectManifestFn(projectDirName), null, 2)), fs.writeFile(path.join(projectPath, "background.js"), `console.log("${projectDirName} - background page loaded");`), fs.writeFile(path.join(projectPath, "content.js"), `console.log("${projectDirName} - content script loaded");`), - ]).then(() => getPlaceholderIcon()) + ]).then(() => getPlaceholderIconFn()) .then(iconData => fs.writeFile(path.join(projectPath, "icon.png"), iconData)) - .then(() => getProjectReadme(projectDirName)) + .then(() => getProjectReadmeFn(projectDirName)) .then(projectReadme => fs.writeFile(path.join(projectPath, "README.md"), stripAnsi(projectReadme))) - .then(() => { - const projectCreatedMessage = getProjectCreatedMessage(projectPath); + .then(async () => { + const projectCreatedMessage = await getProjectCreatedMessageFn(projectPath); return {projectPath, projectCreatedMessage}; }); }, error => { @@ -117,4 +126,10 @@ exports.main = function main(dirPath) { throw new UsageError(msg); } }); +} + +module.exports = { + main, + MORE_INFO_MSG, + asciiLogo, }; From 1f936d1f654a084c5fc91d108d9edf8c1b2c2154 Mon Sep 17 00:00:00 2001 From: saintsebastian Date: Mon, 14 Aug 2017 20:27:31 +0200 Subject: [PATCH 08/10] test: unit tests for main --- .npmignore | 2 +- __tests__/unit/index.test.js | 76 +++++++++++++++++++++++++++++++++++- index.js | 5 +-- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/.npmignore b/.npmignore index 5399e9e..0a2b677 100644 --- a/.npmignore +++ b/.npmignore @@ -1,2 +1,2 @@ node_modules/ -assets/screenshot.png \ No newline at end of file +assets/screenshot.png diff --git a/__tests__/unit/index.test.js b/__tests__/unit/index.test.js index 6de4c15..30de180 100644 --- a/__tests__/unit/index.test.js +++ b/__tests__/unit/index.test.js @@ -4,6 +4,8 @@ const path = require("path"); const chalk = require("chalk"); const fs = require("mz/fs"); const withTmpDir = require("../helpers/tmp-dir"); +const onlyInstancesOf = require("../../errors").onlyInstancesOf; +const UsageError = require("../../errors").UsageError; const main = require("../../index").main; const MORE_INFO_MSG = require("../../index").MORE_INFO_MSG; const asciiLogo = require("../../index").asciiLogo; @@ -14,7 +16,7 @@ describe("main", () => { const projName = "target"; const targetDir = path.join(tmpPath, projName); const expectedMessage = - `${asciiLogo} \n` + ` + `${asciiLogo} \n Congratulations!!! A new WebExtension has been created at: ${chalk.bold(chalk.green(targetDir))} ${MORE_INFO_MSG}`; @@ -33,7 +35,7 @@ describe("main", () => { const projName = "target"; const targetDir = path.join(tmpPath, projName); - const result = await main({ + await main({ dirPath: projName, baseDir: tmpPath, }); @@ -52,4 +54,74 @@ describe("main", () => { expect(parsed.name).toEqual(projName); }) ); + + test("calls all of its necessary dependencies", () => withTmpDir( + async (tmpPath) => { + const projName = "target"; + const targetDir = path.join(tmpPath, projName); + + const getProjectManifestMock = jest.fn(); + const getPlaceholderIconMock = jest.fn(); + const getProjectReadmeMock = jest.fn(); + const getProjectCreatedMessageMock = jest.fn(); + + await main({ + dirPath: projName, + baseDir: tmpPath, + getProjectManifestFn: getProjectManifestMock, + getPlaceholderIconFn: getPlaceholderIconMock, + getProjectReadmeFn: getProjectReadmeMock, + getProjectCreatedMessageFn: getProjectCreatedMessageMock, + }); + + expect(getProjectManifestMock.mock.calls.length).toBe(1); + expect(getProjectManifestMock.mock.calls[0][0]).toBe(projName); + + expect(getPlaceholderIconMock.mock.calls.length).toBe(1); + + expect(getProjectReadmeMock.mock.calls.length).toBe(1); + expect(getProjectReadmeMock.mock.calls[0][0]).toBe(projName); + + expect(getProjectCreatedMessageMock.mock.calls.length).toBe(1); + expect(getProjectCreatedMessageMock.mock.calls[0][0]).toBe(targetDir); + }) + ); + + test("throws Usage Error when directory already exists", () => withTmpDir( + async (tmpPath) => { + const projName = "target"; + const targetDir = path.join(tmpPath, projName); + await fs.mkdir(targetDir); + + try { + await main({ + dirPath: projName, + baseDir: tmpPath, + }); + } catch (error) { + onlyInstancesOf(UsageError, () => { + expect(error.message).toMatch(/dir already exists/); + }); + } + }) + ); + + test("throws error when directory already exists", () => withTmpDir( + async (tmpPath) => { + const projName = "target"; + const getPlaceholderIconMock = jest.fn(() => { + throw new Error("error"); + }); + + try { + await main({ + dirPath: projName, + baseDir: tmpPath, + getPlaceholderIconFn: getPlaceholderIconMock, + }); + } catch (error) { + expect(error.message).toMatch(/error/); + } + }) + ); }); diff --git a/index.js b/index.js index 8a09e37..03be113 100644 --- a/index.js +++ b/index.js @@ -43,7 +43,7 @@ function getProjectCreatedMessage(projectPath) { const PROJECT_CREATED_MSG = `\n Congratulations!!! A new WebExtension has been created at: - ${chalk.bold(chalk.green(projectPath))}`; + ${chalk.bold(chalk.green(projectPath))}`; return `${asciiLogo} ${PROJECT_CREATED_MSG} ${MORE_INFO_MSG}`; } @@ -100,7 +100,6 @@ function main({ } const projectPath = path.resolve(baseDir, dirPath); - console.log(projectPath); const projectDirName = path.basename(projectPath); return fs.mkdir(projectPath).then(() => { @@ -122,7 +121,7 @@ function main({ }); }, error => { if (error.code === "EEXIST") { - const msg = `Unable to create a new WebExtension: ${chalk.bold.underline(projectPath)} dir already exist.`; + const msg = `Unable to create a new WebExtension: ${chalk.bold.underline(projectPath)} dir already exists.`; throw new UsageError(msg); } }); From 599396d5776db7e34b1f5890eef03192796ff9a2 Mon Sep 17 00:00:00 2001 From: saintsebastian Date: Sun, 3 Sep 2017 14:50:45 +0200 Subject: [PATCH 09/10] fix: reviewed tests for index --- __tests__/integration/index.test.js | 2 +- __tests__/unit/index.test.js | 76 ++++++++++++++--------------- index.js | 29 ++++------- 3 files changed, 49 insertions(+), 58 deletions(-) diff --git a/__tests__/integration/index.test.js b/__tests__/integration/index.test.js index 8ec1622..8a4f52a 100644 --- a/__tests__/integration/index.test.js +++ b/__tests__/integration/index.test.js @@ -6,7 +6,7 @@ const linter = require("addons-linter"); const withTmpDir = require("../helpers/tmp-dir"); const cmdRunner = require("../helpers/cmd-runner"); -const execDirPath = path.join(__dirname, "../..", "bin"); +const execDirPath = path.join(__dirname, "..", "..", "bin"); describe("main", () => { test("creates files including manifest with correct name", () => withTmpDir( diff --git a/__tests__/unit/index.test.js b/__tests__/unit/index.test.js index 30de180..166c684 100644 --- a/__tests__/unit/index.test.js +++ b/__tests__/unit/index.test.js @@ -1,25 +1,20 @@ "use strict"; const path = require("path"); -const chalk = require("chalk"); const fs = require("mz/fs"); const withTmpDir = require("../helpers/tmp-dir"); -const onlyInstancesOf = require("../../errors").onlyInstancesOf; const UsageError = require("../../errors").UsageError; const main = require("../../index").main; -const MORE_INFO_MSG = require("../../index").MORE_INFO_MSG; -const asciiLogo = require("../../index").asciiLogo; +const getProjectCreatedMessage = + require("../../index").getProjectCreatedMessage; describe("main", () => { test("returns project path and creation message", () => withTmpDir( async (tmpPath) => { const projName = "target"; const targetDir = path.join(tmpPath, projName); - const expectedMessage = - `${asciiLogo} \n - Congratulations!!! A new WebExtension has been created at: - - ${chalk.bold(chalk.green(targetDir))} ${MORE_INFO_MSG}`; + const dirname = path.join(__dirname, "..", ".."); + const expectedMessage = await getProjectCreatedMessage(targetDir, dirname); const result = await main({ dirPath: projName, @@ -41,13 +36,13 @@ describe("main", () => { }); const contentStat = await fs.stat(path.join(targetDir, "content.js")); - expect(contentStat.isDirectory()).toBeFalsy(); + expect(contentStat.isFile()).toBeTruthy(); const bgStat = await fs.stat(path.join(targetDir, "background.js")); - expect(bgStat.isDirectory()).toBeFalsy(); + expect(bgStat.isFile()).toBeTruthy(); const rmStat = await fs.stat(path.join(targetDir, "README.md")); - expect(rmStat.isDirectory()).toBeFalsy(); + expect(rmStat.isFile()).toBeTruthy(); const manifest = await fs.readFile(path.join(targetDir, "manifest.json"), "utf-8"); const parsed = JSON.parse(manifest); @@ -58,12 +53,10 @@ describe("main", () => { test("calls all of its necessary dependencies", () => withTmpDir( async (tmpPath) => { const projName = "target"; - const targetDir = path.join(tmpPath, projName); const getProjectManifestMock = jest.fn(); const getPlaceholderIconMock = jest.fn(); const getProjectReadmeMock = jest.fn(); - const getProjectCreatedMessageMock = jest.fn(); await main({ dirPath: projName, @@ -71,7 +64,6 @@ describe("main", () => { getProjectManifestFn: getProjectManifestMock, getPlaceholderIconFn: getPlaceholderIconMock, getProjectReadmeFn: getProjectReadmeMock, - getProjectCreatedMessageFn: getProjectCreatedMessageMock, }); expect(getProjectManifestMock.mock.calls.length).toBe(1); @@ -81,9 +73,6 @@ describe("main", () => { expect(getProjectReadmeMock.mock.calls.length).toBe(1); expect(getProjectReadmeMock.mock.calls[0][0]).toBe(projName); - - expect(getProjectCreatedMessageMock.mock.calls.length).toBe(1); - expect(getProjectCreatedMessageMock.mock.calls[0][0]).toBe(targetDir); }) ); @@ -93,35 +82,46 @@ describe("main", () => { const targetDir = path.join(tmpPath, projName); await fs.mkdir(targetDir); - try { - await main({ - dirPath: projName, - baseDir: tmpPath, - }); - } catch (error) { - onlyInstancesOf(UsageError, () => { - expect(error.message).toMatch(/dir already exists/); - }); - } + await expect(main({ + dirPath: projName, + baseDir: tmpPath, + })).rejects + .toBeInstanceOf(UsageError); + + await expect(main({ + dirPath: projName, + baseDir: tmpPath, + })).rejects + .toMatchObject({ + message: expect.stringMatching(/dir already exists/), + }); }) ); - test("throws error when directory already exists", () => withTmpDir( + test("throws error when one of dependencies throws", () => withTmpDir( async (tmpPath) => { const projName = "target"; const getPlaceholderIconMock = jest.fn(() => { throw new Error("error"); }); - try { - await main({ - dirPath: projName, - baseDir: tmpPath, - getPlaceholderIconFn: getPlaceholderIconMock, - }); - } catch (error) { - expect(error.message).toMatch(/error/); - } + await expect(main({ + dirPath: projName, + baseDir: tmpPath, + getPlaceholderIconFn: getPlaceholderIconMock, + })).rejects + .toMatchObject({ + message: expect.stringMatching(/error/), + }); }) ); }); + +describe("getProjectCreatedMessage", () => { + test("returns message with correct directory", async () => { + const targetDir = path.join("target"); + const returnedMessage = await getProjectCreatedMessage(targetDir); + + expect(returnedMessage).toMatch(new RegExp(targetDir)); + }); +}); diff --git a/index.js b/index.js index 03be113..a4198d2 100644 --- a/index.js +++ b/index.js @@ -35,18 +35,16 @@ a WebExtension from the command line: ${chalk.bold.blue("web-ext run -s /path/to/extension")} `; -const asciiLogo = fs.readFileSync( - path.join(__dirname, "assets", "webextension-logo.ascii") -); - -function getProjectCreatedMessage(projectPath) { - const PROJECT_CREATED_MSG = `\n - Congratulations!!! A new WebExtension has been created at: - +exports.getProjectCreatedMessage = function getProjectCreatedMessage(projectPath, dirname = __dirname) { + return fs.readFile(path.join(dirname, "assets", "webextension-logo.ascii")) + .then(asciiLogo => { + const PROJECT_CREATED_MSG = `\n +Congratulations!!! A new WebExtension has been created at: ${chalk.bold(chalk.green(projectPath))}`; - return `${asciiLogo} ${PROJECT_CREATED_MSG} ${MORE_INFO_MSG}`; -} + return `${asciiLogo} ${PROJECT_CREATED_MSG} ${MORE_INFO_MSG}`; + }); +}; function getProjectReadme(projectDirName) { return fs.readFile(path.join(__dirname, "assets", "webextension-logo.ascii")) @@ -87,13 +85,12 @@ function getProjectManifest(projectDirName) { }; } -function main({ +exports.main = function main({ dirPath, baseDir = process.cwd(), getProjectManifestFn = getProjectManifest, getPlaceholderIconFn = getPlaceholderIcon, getProjectReadmeFn = getProjectReadme, - getProjectCreatedMessageFn = getProjectCreatedMessage, }) { if (!dirPath) { throw new Error("Project directory name is a mandatory argument"); @@ -116,7 +113,7 @@ function main({ .then(projectReadme => fs.writeFile(path.join(projectPath, "README.md"), stripAnsi(projectReadme))) .then(async () => { - const projectCreatedMessage = await getProjectCreatedMessageFn(projectPath); + const projectCreatedMessage = await module.exports.getProjectCreatedMessage(projectPath); return {projectPath, projectCreatedMessage}; }); }, error => { @@ -125,10 +122,4 @@ function main({ throw new UsageError(msg); } }); -} - -module.exports = { - main, - MORE_INFO_MSG, - asciiLogo, }; From 2f52f911a47adfeb6dd5968bc2278b7c2c94ac7f Mon Sep 17 00:00:00 2001 From: saintsebastian Date: Sun, 3 Sep 2017 17:08:24 +0200 Subject: [PATCH 10/10] test: separate dependencies for main into module --- __tests__/unit/index.test.js | 26 ++++++++--------- dependencies-main.js | 49 ++++++++++++++++++++++++++++++++ index.js | 55 ++++-------------------------------- 3 files changed, 67 insertions(+), 63 deletions(-) create mode 100644 dependencies-main.js diff --git a/__tests__/unit/index.test.js b/__tests__/unit/index.test.js index 166c684..a6c2a33 100644 --- a/__tests__/unit/index.test.js +++ b/__tests__/unit/index.test.js @@ -54,25 +54,22 @@ describe("main", () => { async (tmpPath) => { const projName = "target"; - const getProjectManifestMock = jest.fn(); - const getPlaceholderIconMock = jest.fn(); - const getProjectReadmeMock = jest.fn(); + jest.mock("../../dependencies-main"); + const dependenciesMain = require("../../dependencies-main"); await main({ dirPath: projName, baseDir: tmpPath, - getProjectManifestFn: getProjectManifestMock, - getPlaceholderIconFn: getPlaceholderIconMock, - getProjectReadmeFn: getProjectReadmeMock, + dependencies: dependenciesMain, }); - expect(getProjectManifestMock.mock.calls.length).toBe(1); - expect(getProjectManifestMock.mock.calls[0][0]).toBe(projName); + expect(dependenciesMain.getProjectManifest.mock.calls.length).toBe(1); + expect(dependenciesMain.getProjectManifest.mock.calls[0][0]).toBe(projName); - expect(getPlaceholderIconMock.mock.calls.length).toBe(1); + expect(dependenciesMain.getPlaceholderIcon.mock.calls.length).toBe(1); - expect(getProjectReadmeMock.mock.calls.length).toBe(1); - expect(getProjectReadmeMock.mock.calls[0][0]).toBe(projName); + expect(dependenciesMain.getProjectReadme.mock.calls.length).toBe(1); + expect(dependenciesMain.getProjectReadme.mock.calls[0][0]).toBe(projName); }) ); @@ -101,14 +98,17 @@ describe("main", () => { test("throws error when one of dependencies throws", () => withTmpDir( async (tmpPath) => { const projName = "target"; - const getPlaceholderIconMock = jest.fn(() => { + + jest.mock("../../dependencies-main"); + const dependenciesMain = require("../../dependencies-main"); + dependenciesMain.getPlaceholderIcon = jest.fn(() => { throw new Error("error"); }); await expect(main({ dirPath: projName, baseDir: tmpPath, - getPlaceholderIconFn: getPlaceholderIconMock, + dependencies: dependenciesMain, })).rejects .toMatchObject({ message: expect.stringMatching(/error/), diff --git a/dependencies-main.js b/dependencies-main.js new file mode 100644 index 0000000..1ee6f29 --- /dev/null +++ b/dependencies-main.js @@ -0,0 +1,49 @@ +const path = require("path"); +const fs = require("mz/fs"); + +const README = ` +This project contains a blank WebExtension addon, a "white canvas" for your new experiment of +extending and remixing the Web. +`; + +exports.getProjectReadme = function getProjectReadme( + projectDirName, + MORE_INFO_MSG +) { + return fs.readFile(path.join(__dirname, "assets", "webextension-logo.ascii")) + .then(() => { + return `# ${projectDirName}\n${README}${MORE_INFO_MSG}`; + }); +}; + +exports.getPlaceholderIcon = function getPlaceholderIcon() { + return fs.readFile(path.join(__dirname, "assets", "icon.png")); +}; + +exports.getProjectManifest = function getProjectManifest(projectDirName) { + return { + manifest_version: 2, + name: projectDirName, + version: "0.1", + description: `${projectDirName} description`, + content_scripts: [ + { + matches: ["https://developer.mozilla.org/*"], + js: ["content.js"], + }, + ], + permissions: [], + icons: { + "64": "icon.png", + }, + browser_action: { + default_title: `${projectDirName} (browserAction)`, + default_icon: { + "64": "icon.png", + }, + }, + background: { + scripts: ["background.js"], + }, + }; +}; diff --git a/index.js b/index.js index a4198d2..185af72 100644 --- a/index.js +++ b/index.js @@ -5,11 +5,7 @@ const chalk = require("chalk"); const fs = require("mz/fs"); const stripAnsi = require("strip-ansi"); const UsageError = require("./errors").UsageError; - -const README = ` -This project contains a blank WebExtension addon, a "white canvas" for your new experiment of -extending and remixing the Web. -`; +const dependenciesMain = require("./dependencies-main"); const MORE_INFO_MSG = ` @@ -46,51 +42,10 @@ Congratulations!!! A new WebExtension has been created at: }); }; -function getProjectReadme(projectDirName) { - return fs.readFile(path.join(__dirname, "assets", "webextension-logo.ascii")) - .then(() => { - return `# ${projectDirName}\n${README}${MORE_INFO_MSG}`; - }); -} - -function getPlaceholderIcon() { - return fs.readFile(path.join(__dirname, "assets", "icon.png")); -} - -function getProjectManifest(projectDirName) { - return { - manifest_version: 2, - name: projectDirName, - version: "0.1", - description: `${projectDirName} description`, - content_scripts: [ - { - matches: ["https://developer.mozilla.org/*"], - js: ["content.js"], - }, - ], - permissions: [], - icons: { - "64": "icon.png", - }, - browser_action: { - default_title: `${projectDirName} (browserAction)`, - default_icon: { - "64": "icon.png", - }, - }, - background: { - scripts: ["background.js"], - }, - }; -} - exports.main = function main({ dirPath, baseDir = process.cwd(), - getProjectManifestFn = getProjectManifest, - getPlaceholderIconFn = getPlaceholderIcon, - getProjectReadmeFn = getProjectReadme, + dependencies = dependenciesMain, }) { if (!dirPath) { throw new Error("Project directory name is a mandatory argument"); @@ -102,14 +57,14 @@ exports.main = function main({ return fs.mkdir(projectPath).then(() => { return Promise.all([ fs.writeFile(path.join(projectPath, "manifest.json"), - JSON.stringify(getProjectManifestFn(projectDirName), null, 2)), + JSON.stringify(dependencies.getProjectManifest(projectDirName), null, 2)), fs.writeFile(path.join(projectPath, "background.js"), `console.log("${projectDirName} - background page loaded");`), fs.writeFile(path.join(projectPath, "content.js"), `console.log("${projectDirName} - content script loaded");`), - ]).then(() => getPlaceholderIconFn()) + ]).then(() => dependencies.getPlaceholderIcon()) .then(iconData => fs.writeFile(path.join(projectPath, "icon.png"), iconData)) - .then(() => getProjectReadmeFn(projectDirName)) + .then(() => dependencies.getProjectReadme(projectDirName, MORE_INFO_MSG)) .then(projectReadme => fs.writeFile(path.join(projectPath, "README.md"), stripAnsi(projectReadme))) .then(async () => {