Repository: ioBroker/create-adapter
Pull request #108 (2019-02-12 15:29:17)

Add unit tests for core modules
**PR Checklist:**  
- [x] Provide a meaningful description to this PR or mention which issues this fixes.
- [x] Add tests for your change. This includes negative tests (i.e. inputs that need to fail) as well as baseline tests (i.e. how should the directory structure look like?).
- [x] Run the test suite with `npm test`
- [ ] ~~If there are baseline changes, review them and make a separate commit for them with the comment "accept baselines" if they are desired changes~~
- [x] Ensure the project builds with `npm run build` and include those artifacts aswell
- [ ] ~~If you added a required option, also add it to the template creation (`travis/create_templates.ts`)~~
- [ ] Add your changes to `CHANGELOG.md` (referencing this PR or the issue you fixed)

**Description:**  
This PR adds unit tests for the core modules in `src/lib`. Progress:
- [ ] `actionsAndTransformers.ts`
- [ ] `createAdapter.ts`
- [x] `fetchVersions.ts`
- [x] ~~`licenses.ts`~~ generated, will not be tested
- [ ] `questions.ts`
- [ ] `tools.ts`



Patch:
diff --git a/package.json b/package.json
index a0d1db6..697cdcf 100644
--- a/package.json
+++ b/package.json
@@ -80,9 +80,11 @@
     "build": "tsc -p tsconfig.build.json",
     "postbuild": "copyfiles \"templates/**/*.raw.*\" build",
     "watch": "tsc -p tsconfig.build.json --watch",
-    "test:ts": "mocha",
+    "test:ts": "mocha src/**/*.test.ts",
+    "test:baselines": "mocha test/create-adapter.test.ts",
     "pretest": "npm run linkTemplates",
-    "test": "npm run test:ts",
+    "test": "npm run test:ts && npm run test:baselines",
+    "test:watch": "mocha src/**/*.test.ts --watch",
     "coverage": "node_modules/.bin/nyc npm run test:ts",
     "coveralls": "node_modules/.bin/nyc report --reporter=text-lcov | coveralls -v",
     "show-coverage": "npm run coverage && start ./coverage/index.html",
@@ -99,15 +101,13 @@
     ],
     "exclude": [
       "build/**",
-      "src/**/*.test.ts"
+      "src/**/*.test.ts",
+      "**/*.d.ts",
+      "test/**"
     ],
     "extension": [
       ".ts"
     ],
-    "require": [
-      "ts-node/register",
-      "source-map-support/register"
-    ],
     "reporter": [
       "text-summary",
       "html",
diff --git a/src/lib/licenses.ts b/src/lib/licenses.ts
index 6901c3e..191ecad 100644
--- a/src/lib/licenses.ts
+++ b/src/lib/licenses.ts
@@ -2,6 +2,9 @@
 // tslint:disable:object-literal-key-quotes
 // tslint:disable:trailing-comma
 
+// Since this is auto-generated, we will not unit-test this file
+// istanbul ignore
+
 export interface License {
 	id: string;
 	name: string;
diff --git a/src/lib/tools.ts b/src/lib/tools.ts
index 1d124cd..081f4ad 100644
--- a/src/lib/tools.ts
+++ b/src/lib/tools.ts
@@ -310,6 +310,7 @@ export function getOwnVersion(): string {
 			return require(jsonPath).version;
 		} catch (e) { /* OK */ }
 	}
+	/* istanbul ignore next */
 	return "unknown";
 }
 


Test patch:
diff --git a/src/lib/actionsAndTransformers.test.ts b/src/lib/actionsAndTransformers.test.ts
new file mode 100644
index 0000000..6d13f11
--- /dev/null
+++ b/src/lib/actionsAndTransformers.test.ts
@@ -0,0 +1,88 @@
+// tslint:disable: no-unused-expression
+
+import { checkMinSelections } from "./actionsAndTransformers";
+
+import * as proxyquireModule from "proxyquire";
+import { stub } from "sinon";
+
+const fetchPackageVersion = stub();
+const proxyquire = proxyquireModule.noPreserveCache();
+
+const { checkAdapterName } = proxyquire<typeof import ("./actionsAndTransformers")>("./actionsAndTransformers", {
+	"./fetchVersions": {
+		fetchPackageVersion,
+	},
+});
+
+describe("actionsAndTransformers/checkMinSelections()", () => {
+	it("should return true if the answers array has at least <min> entries", async () => {
+		await checkMinSelections("foo", 3, [1, 2, 3]).should.become(true);
+		await checkMinSelections("foo", 2, [1, 2, 3, 4]).should.become(true);
+		await checkMinSelections("foo", 1, [1, 2]).should.become(true);
+	});
+
+	it("should return an error message otherwise", async () => {
+		let result;
+		result = await checkMinSelections("foo", 3, [1, 2]);
+		result.should.be.a("string").and.match(/at least 3 foo/);
+
+		result = await checkMinSelections("bar", 4, [1, 2]);
+		result.should.be.a("string").and.match(/at least 4 bar/);
+	});
+});
+
+describe("actionsAndTransformers/checkAdapterName()", () => {
+
+	beforeEach(() => fetchPackageVersion.reset());
+
+	it("should not accept empty names", async () => {
+		const forbiddenNames = [
+			"", " ", "\t",
+		];
+		for (const name of forbiddenNames) {
+			const result = await checkAdapterName(name);
+			result.should.be.a("string").and.match(/Please enter/);
+		}
+	});
+
+	it(`should only accept lowercase letters, numbers, "-" and "_"`, async () => {
+		const forbiddenNames = [
+			"ö", "$", "foo/bar", "FOO",
+		];
+		for (const name of forbiddenNames) {
+			const result = await checkAdapterName(name);
+			result.should.be.a("string").and.match(/may only consist/);
+		}
+	});
+
+	it(`should only accept names that start with a letter`, async () => {
+		const forbiddenNames = [
+			"1baz", "_foo", "---123b",
+		];
+		for (const name of forbiddenNames) {
+			const result = await checkAdapterName(name);
+			result.should.be.a("string").and.match(/must start with/);
+		}
+	});
+
+	it(`should only accept names that end with a letter or number`, async () => {
+		const forbiddenNames = [
+			"abc-", "foo-bar-_",
+		];
+		for (const name of forbiddenNames) {
+			const result = await checkAdapterName(name);
+			result.should.be.a("string").and.match(/must end with/);
+		}
+	});
+
+	it("should return an error if the adapter already exists", async () => {
+		fetchPackageVersion.resolves("1.2.3");
+		const result = await checkAdapterName("foo");
+		result.should.be.a("string").and.match(/already exists/);
+	});
+
+	it("should return true otherwise", async () => {
+		fetchPackageVersion.rejects("404");
+		await checkAdapterName("foo").should.become(true);
+	});
+});
diff --git a/src/lib/createAdapter.test.ts b/src/lib/createAdapter.test.ts
new file mode 100644
index 0000000..fda8de2
--- /dev/null
+++ b/src/lib/createAdapter.test.ts
@@ -0,0 +1,71 @@
+// tslint:disable: no-unused-expression
+
+import { testCondition } from "./createAdapter";
+
+describe("createAdapter/testCondition()", () => {
+	it("should return true if a question has no condition", () => {
+		testCondition(undefined, {}).should.be.true;
+		testCondition(null as any, {}).should.be.true;
+	});
+
+	it(`for a "value" condition, it should return whether the answer equals the desired value`, () => {
+		testCondition(
+			{ name: "prop", value: "foo" },
+			{ prop: "foo" },
+		).should.equal(true);
+
+		testCondition(
+			{ name: "prop", value: "bar" },
+			{ prop: "foo" },
+		).should.equal(false);
+	});
+
+	it(`for a "contains" condition, it should return whether the answer array contains the desired value`, () => {
+		testCondition(
+			{ name: "prop", contains: "foo" },
+			{ prop: ["bar", "foo"] },
+		).should.equal(true);
+
+		testCondition(
+			{ name: "prop", contains: "baz" },
+			{ prop: ["bar", "foo"] },
+		).should.equal(false);
+	});
+
+	it(`for a "doesNotContain" condition, it should return whether the answer array contains the desired value`, () => {
+		testCondition(
+			{ name: "prop", doesNotContain: "foo" },
+			{ prop: ["bar", "foo"] },
+		).should.equal(false);
+
+		testCondition(
+			{ name: "prop", doesNotContain: "baz" },
+			{ prop: ["bar", "foo"] },
+		).should.equal(true);
+	});
+
+	it("should return false for invalid conditions", () => {
+		testCondition(
+			{ name: "prop" } as any,
+			{ prop: ["bar", "foo"] },
+		).should.equal(false);
+	});
+
+	it("should combine a condition array using AND", () => {
+		testCondition(
+			[
+				{ name: "prop1", doesNotContain: "baz" },
+				{ name: "prop2", value: true },
+			],
+			{ prop1: ["bar", "foo"], prop2: true },
+		).should.equal(true);
+
+		testCondition(
+			[
+				{ name: "prop1", doesNotContain: "baz" },
+				{ name: "prop2", value: false },
+			],
+			{ prop1: ["bar", "foo"], prop2: true },
+		).should.equal(false);
+	});
+});
diff --git a/src/lib/fetchVersions.test.ts b/src/lib/fetchVersions.test.ts
new file mode 100644
index 0000000..72bfb0c
--- /dev/null
+++ b/src/lib/fetchVersions.test.ts
@@ -0,0 +1,150 @@
+// tslint:disable: no-unused-expression
+
+import { expect } from "chai";
+import * as proxyquireModule from "proxyquire";
+import { stub } from "sinon";
+
+const axiosMock = stub();
+const proxyquire = proxyquireModule.noPreserveCache();
+
+const { fetchPackageVersion } = proxyquire<typeof import ("./fetchVersions")>("./fetchVersions", {
+	axios: {
+		default: axiosMock,
+	},
+});
+
+function returnVersion(version: string) {
+	axiosMock.resolves({
+		status: 200,
+		data: { latest: version },
+	});
+}
+function returnStatus(status: number) {
+	axiosMock.resolves({ status });
+}
+function getRandomPackageName() {
+	let ret = "";
+	const digits = "abcdefghijklmnopqrstuvwxyz-_0123456789";
+	while (ret.length < 8) {
+		const index = Math.round(Math.random() * digits.length);
+		ret += digits[index];
+	}
+	return ret;
+}
+
+const changedProcessEnv = new Map<string, any>();
+function setProcessEnv(env: string, value: any) {
+	if (!changedProcessEnv.has(env)) {
+		changedProcessEnv.set(env, process.env[env]);
+	}
+	process.env[env] = value;
+}
+function resetProcessEnv() {
+	for (const [env, value] of changedProcessEnv.entries()) {
+		process.env[env] = value;
+	}
+}
+
+describe("fetchVersions()", () => {
+
+	beforeEach(() => {
+		axiosMock.reset();
+		resetProcessEnv();
+	});
+
+	it("for status codes other than 200, an error is thrown", async () => {
+		returnStatus(403);
+		await fetchPackageVersion(getRandomPackageName()).should.be.rejectedWith("403");
+	});
+
+	it("for status codes other than 200, the fallback version is returned if it is passed", async () => {
+		returnStatus(403);
+		const version = "1.2.3-beta";
+		await fetchPackageVersion(getRandomPackageName(), version).should.become(version);
+	});
+
+	it("if the status code is 200, the latest version is extracted from the response data", async () => {
+		const latest = "3.2.1";
+		returnVersion(latest);
+		await fetchPackageVersion(getRandomPackageName()).should.become(latest);
+	});
+
+	it("subsequent requests should return a cached version instead of issuing another request", async () => {
+
+		const packageName = getRandomPackageName();
+
+		const latest = "3.2.1";
+		returnVersion(latest);
+		await fetchPackageVersion(packageName).should.become(latest);
+
+		// Even if the request would fail, this should work
+		returnStatus(403);
+		await fetchPackageVersion(packageName).should.become(latest);
+
+		// Changes in the version should not be detected
+		returnVersion("1.2.3");
+		await fetchPackageVersion(packageName).should.become(latest);
+	});
+
+	it("the request object should contain an URL with the package name", async () => {
+		returnVersion("1.2.3");
+		await fetchPackageVersion("bar-baz");
+
+		axiosMock.should.have.been.called;
+		expect(axiosMock.getCall(0).args[0]).to.be.an("object");
+		expect(axiosMock.getCall(0).args[0].url).to.include(encodeURIComponent("bar-baz"));
+	});
+
+	it("the request object should contain a default timeout of 5000ms", async () => {
+		returnVersion("1.2.3");
+		setProcessEnv("REQUEST_TIMEOUT", undefined);
+		await fetchPackageVersion(getRandomPackageName());
+
+		axiosMock.should.have.been.called;
+		const callArg = axiosMock.getCall(0).args[0];
+		expect(callArg).to.be.an("object");
+		expect(callArg.timeout).to.equal(5000);
+	});
+
+	it("if the REQUEST_TIMEOUT env variable is set, that should be the request timeout", async () => {
+		returnVersion("1.2.3");
+		setProcessEnv("REQUEST_TIMEOUT", 7865);
+		await fetchPackageVersion(getRandomPackageName());
+
+		axiosMock.should.have.been.called;
+		const callArg = axiosMock.getCall(0).args[0];
+		expect(callArg).to.be.an("object");
+		expect(callArg.timeout).to.equal(7865);
+	});
+
+	it("if the HTTPS_PROXY env variable is set, the proxy param should be set appropriately", async () => {
+		returnVersion("1.2.3");
+		setProcessEnv("HTTPS_PROXY", "https://foo.bar:8961");
+		await fetchPackageVersion(getRandomPackageName());
+
+		const callArg = axiosMock.getCall(0).args[0];
+		expect(callArg.proxy).to.deep.equal({
+			host: "foo.bar",
+			port: 8961,
+		});
+	});
+
+	it("the proxy port should default to 443", async () => {
+		returnVersion("1.2.3");
+		setProcessEnv("HTTPS_PROXY", "https://foo.bar");
+		await fetchPackageVersion(getRandomPackageName());
+
+		const callArg = axiosMock.getCall(0).args[0];
+		expect(callArg.proxy.port).to.equal(443);
+	});
+
+	it("if the proxy has no valid hostname, it should not be used", async () => {
+		returnVersion("1.2.3");
+		setProcessEnv("HTTPS_PROXY", "/bar:80");
+		await fetchPackageVersion(getRandomPackageName());
+
+		const callArg = axiosMock.getCall(0).args[0];
+		expect(callArg.proxy).to.be.undefined;
+	});
+
+});
diff --git a/src/lib/questions.test.ts b/src/lib/questions.test.ts
new file mode 100644
index 0000000..9d44ae2
--- /dev/null
+++ b/src/lib/questions.test.ts
@@ -0,0 +1,51 @@
+// tslint:disable: no-unused-expression
+
+import { expect } from "chai";
+import { isQuestionGroup, questions } from "./questions";
+
+describe("questions/isQuestionGroup()", () => {
+	it("should return false if the argument is null or undefined", () => {
+		isQuestionGroup(undefined).should.be.false;
+		isQuestionGroup(null).should.be.false;
+	});
+
+	it(`should return false if the argument doesn't have a string property "headline"`, () => {
+		for (const arg of [
+			{}, [], "whatever", { foo: "bar" }, 1, true,
+			{ headline: 1 }, { headline: false },
+		]) isQuestionGroup(arg).should.be.false;
+	});
+
+	it(`should return false if the argument doesn't have an array property "questions"`, () => {
+		for (const arg of [
+			{ headline: "headline" },
+			{ headline: "headline", questions: {} },
+			{ headline: "headline", questions: 1 },
+			{ headline: "headline", questions: false },
+		]) isQuestionGroup(arg).should.be.false;
+	});
+
+	it("should return true otherwise", () => {
+		isQuestionGroup({ headline: "string", questions: [] }).should.be.true;
+	});
+});
+
+describe("questions/questions", () => {
+	it("should be an array of question objects", () => {
+		questions.should.be.an("array");
+		questions.forEach(q => {
+			q.should.have.property("type");
+			expect(typeof q.type).to.equal("string");
+		});
+	});
+});
+
+describe("questions/checkAnswers()", () => {
+	it.skip("TODO: This is already tested by test:baselines");
+});
+describe("questions/formatAnswers()", () => {
+	it.skip("TODO: This is already tested by test:baselines");
+});
+describe("questions/validateAnswers()", () => {
+	it.skip("TODO: This is already tested by test:baselines");
+});
diff --git a/src/lib/tools.test.ts b/src/lib/tools.test.ts
new file mode 100644
index 0000000..3fc9741
--- /dev/null
+++ b/src/lib/tools.test.ts
@@ -0,0 +1,185 @@
+// tslint:disable: no-unused-expression
+
+import { expect } from "chai";
+import { capitalize, formatLicense, getOwnVersion, indentWithSpaces, indentWithTabs, jsFixQuotes, kebabCaseToUpperCamelCase, tsFixQuotes } from "./tools";
+
+describe("tools/error()", () => {
+	it.skip("TODO: Cannot test this because console.log should not be stubbed");
+});
+
+const indentationTests = [
+	{
+		spaces: "",
+		tabs: "",
+	},
+	{
+		spaces: "no change",
+		tabs: "no change",
+	},
+	{
+		spaces: "    test 1",
+		tabs: "\ttest 1",
+	},
+	{
+		spaces: "        test 2",
+		tabs: "\t\ttest 2",
+	},
+	{
+		spaces: "       test 3",
+		tabs: "\t   test 3",
+	},
+	{
+		spaces: "test 4.1\n    test 4.2\n        test 4.3",
+		tabs: "test 4.1\n\ttest 4.2\n\t\ttest 4.3",
+	},
+];
+
+describe("tools/indentWithTabs()", () => {
+	it("should replace leading groups of 4 spaces with tabs", () => {
+		for (const { spaces, tabs } of indentationTests) {
+			expect(indentWithTabs(spaces)).to.equal(tabs);
+		}
+	});
+});
+
+describe("tools/indentWithSpaces()", () => {
+	it("should replace leading tabs with groups of 4 spaces", () => {
+		for (const { spaces, tabs } of indentationTests) {
+			expect(indentWithSpaces(tabs)).to.equal(spaces);
+		}
+	});
+});
+
+describe("tools/jsFixQuotes()", () => {
+	it("should change all quotes to single quotes if that is requested", () => {
+		const original = `const foo = "";
+const bar = "'";
+fn1(\`'\`);
+fn2('\\"');
+`;
+
+		const expected = `const foo = '';
+const bar = "'";
+fn1(\`'\`);
+fn2('\\"');
+`;
+
+		expect(jsFixQuotes(original, "single")).to.equal(expected);
+	});
+
+	it("should change all quotes to double quotes if that is requested", () => {
+		const original = `const foo = '';
+const bar = "'";
+fn1(\`'\`);
+fn2('\\'');
+`;
+
+		const expected = `const foo = "";
+const bar = "'";
+fn1(\`'\`);
+fn2("'");
+`;
+
+		expect(jsFixQuotes(original, "double")).to.equal(expected);
+	});
+});
+
+describe("tools/tsFixQuotes()", () => {
+	it("should change all quotes to single quotes if that is requested", () => {
+		const original = `const foo = "";
+const bar = "'";
+fn1(\`'\`);
+fn2('\\"');
+`;
+
+		// TS does not avoid escaping as opposed to ESLint
+		const expected = `const foo = '';
+const bar = '\\'';
+fn1(\`'\`);
+fn2('\\"');
+`;
+
+		expect(tsFixQuotes(original, "single")).to.equal(expected);
+	});
+
+	it("should change all quotes to double quotes if that is requested", () => {
+		const original = `const foo = '';
+const bar = "'";
+fn1(\`'\`);
+fn2('\\'');
+`;
+
+		const expected = `const foo = "";
+const bar = "'";
+fn1(\`'\`);
+fn2("'");
+`;
+
+		expect(tsFixQuotes(original, "double")).to.equal(expected);
+	});
+});
+
+describe("tools/capitalize()", () => {
+	it("should capitalize the first letter in every word", () => {
+		const tests = [
+			{ original: "abc", expected: "Abc" },
+			{ original: "b12", expected: "B12" },
+			{ original: "DEF", expected: "DEF" },
+			{ original: "123", expected: "123" },
+		];
+
+		for (const { original, expected } of tests) {
+			expect(capitalize(original)).to.equal(expected);
+		}
+	});
+});
+
+describe("tools/kebabCaseToUpperCamelCase()", () => {
+	it("should convert kebab-case to UpperCamelCase", () => {
+		const tests = [
+			{ original: "foo-bar-baz", expected: "FooBarBaz" },
+			{ original: "aaaaa", expected: "Aaaaa" },
+			{ original: "DEF", expected: "DEF" },
+			{ original: "foo_bar_baz", expected: "FooBarBaz" },
+		];
+
+		for (const { original, expected } of tests) {
+			expect(kebabCaseToUpperCamelCase(original)).to.equal(expected);
+		}
+	});
+});
+
+describe("tools/getOwnVersion()", () => {
+	it("should return the version defined in package.json", () => {
+		const expected = require("../../package.json").version;
+		expect(getOwnVersion()).to.equal(expected);
+	});
+});
+
+describe("tools/formatLicense()", () => {
+	const answers = {authorName: "John Doe"};
+
+	it("should replace [year] with the current year", () => {
+		const curYear = new Date().getFullYear().toString();
+		const tests = [
+			{original: "[year]", expected: curYear},
+			{original: "Copyright © [year]", expected: `Copyright © ${curYear}`},
+			{original: "[year] [year] [year]", expected: `${curYear} ${curYear} ${curYear}`},
+		];
+
+		for (const { original, expected } of tests) {
+			expect(formatLicense(original, answers as any)).to.equal(expected);
+		}
+	});
+
+	it("should replace [fullname] with the author's name", () => {
+		const tests = [
+			{original: "[fullname]", expected: answers.authorName},
+			{original: "[fullname] [fullname] [fullname]", expected: `${answers.authorName} ${answers.authorName} ${answers.authorName}`},
+		];
+
+		for (const { original, expected } of tests) {
+			expect(formatLicense(original, answers as any)).to.equal(expected);
+		}
+	});
+});
diff --git a/test/baselines/adapter_JS_ES6Class_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/package.json b/test/baselines/adapter_JS_ES6Class_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/package.json
index f4ee960..176ad6a 100644
--- a/test/baselines/adapter_JS_ES6Class_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/package.json
+++ b/test/baselines/adapter_JS_ES6Class_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/package.json
@@ -27,7 +27,7 @@
     "@types/chai-as-promised": "^7.1.0",
     "@types/gulp": "^4.0.5",
     "@types/mocha": "^5.2.5",
-    "@types/node": "^11.9.2",
+    "@types/node": "^11.9.3",
     "@types/proxyquire": "^1.3.28",
     "@types/sinon": "^7.0.5",
     "@types/sinon-chai": "^3.2.2",
diff --git a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/package.json b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/package.json
index f4ee960..176ad6a 100644
--- a/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/package.json
+++ b/test/baselines/adapter_JS_ESLint_TypeChecking_Spaces_SingleQuotes_LGPLv3/package.json
@@ -27,7 +27,7 @@
     "@types/chai-as-promised": "^7.1.0",
     "@types/gulp": "^4.0.5",
     "@types/mocha": "^5.2.5",
-    "@types/node": "^11.9.2",
+    "@types/node": "^11.9.3",
     "@types/proxyquire": "^1.3.28",
     "@types/sinon": "^7.0.5",
     "@types/sinon-chai": "^3.2.2",
diff --git a/test/baselines/adapter_TS_ES6Class_TSLint_Tabs_DoubleQuotes_MIT/package.json b/test/baselines/adapter_TS_ES6Class_TSLint_Tabs_DoubleQuotes_MIT/package.json
index 2df7c91..91601b9 100644
--- a/test/baselines/adapter_TS_ES6Class_TSLint_Tabs_DoubleQuotes_MIT/package.json
+++ b/test/baselines/adapter_TS_ES6Class_TSLint_Tabs_DoubleQuotes_MIT/package.json
@@ -27,7 +27,7 @@
     "@types/chai-as-promised": "^7.1.0",
     "@types/gulp": "^4.0.5",
     "@types/mocha": "^5.2.5",
-    "@types/node": "^11.9.2",
+    "@types/node": "^11.9.3",
     "@types/proxyquire": "^1.3.28",
     "@types/sinon": "^7.0.5",
     "@types/sinon-chai": "^3.2.2",
diff --git a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/package.json b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/package.json
index 2df7c91..91601b9 100644
--- a/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/package.json
+++ b/test/baselines/adapter_TS_TSLint_Tabs_DoubleQuotes_MIT/package.json
@@ -27,7 +27,7 @@
     "@types/chai-as-promised": "^7.1.0",
     "@types/gulp": "^4.0.5",
     "@types/mocha": "^5.2.5",
-    "@types/node": "^11.9.2",
+    "@types/node": "^11.9.3",
     "@types/proxyquire": "^1.3.28",
     "@types/sinon": "^7.0.5",
     "@types/sinon-chai": "^3.2.2",
diff --git a/src/index.test.ts b/test/create-adapter.test.ts
similarity index 98%
rename from src/index.test.ts
rename to test/create-adapter.test.ts
index b6f26d9..e82e55f 100644
--- a/src/index.test.ts
+++ b/test/create-adapter.test.ts
@@ -5,9 +5,9 @@ process.env.TESTING = "true";
 
 import * as fs from "fs-extra";
 import * as path from "path";
-import { createAdapter } from "./index";
-import { File, writeFiles } from "./lib/createAdapter";
-import { Answers } from "./lib/questions";
+import { createAdapter } from "../src/index";
+import { File, writeFiles } from "../src/lib/createAdapter";
+import { Answers } from "../src/lib/questions";
 
 const baselineDir = path.join(__dirname, "../test/baselines");
 
diff --git a/test/mocha.opts b/test/mocha.opts
index 15a4046..5803be9 100644
--- a/test/mocha.opts
+++ b/test/mocha.opts
@@ -2,4 +2,3 @@
 --watch-extensions ts
 --require ts-node/register
 --require source-map-support/register
-src/**/*.test.ts