@iobroker/testing 3.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,6 +43,10 @@ tests.integration(path.join(__dirname, ".."), {
43
43
  // By default, termination during startup is not allowed.
44
44
  allowedExitCodes: [11],
45
45
 
46
+ // To test against a different version of JS-Controller, you can change the version or dist-tag here.
47
+ // Make sure to remove this setting when you're done testing.
48
+ controllerVersion: "latest", // or a specific version like "4.0.1"
49
+
46
50
  // Define your own tests inside defineAdditionalTests
47
51
  defineAdditionalTests({ suite }) {
48
52
  // All tests (it, describe) must be grouped in one or more suites. Each suite sets up a fresh environment for the adapter tests.
@@ -50,7 +54,13 @@ tests.integration(path.join(__dirname, ".."), {
50
54
  // The adapter will run until the end of each suite.
51
55
 
52
56
  // Since the tests are heavily instrumented, each suite gives access to a so called "harness" to control the tests.
53
- suite("Test sendTo()", (harness) => {
57
+ suite("Test sendTo()", (getHarness) => {
58
+ // For convenience, get the current suite's harness before all tests
59
+ let harness;
60
+ before(() => {
61
+ harness = getHarness();
62
+ });
63
+
54
64
  it("Should work", () => {
55
65
  return new Promise(async (resolve) => {
56
66
  // Start the adapter and wait until it has started
@@ -64,6 +74,15 @@ tests.integration(path.join(__dirname, ".."), {
64
74
  });
65
75
  });
66
76
  });
77
+
78
+ // While developing the tests, you can run only a single suite using `suite.only`...
79
+ suite.only("Only this will run", (getHarness) => {
80
+ // ...
81
+ });
82
+ // ...or prevent a suite from running using `suite.skip`:
83
+ suite.skip("This will never run", (getHarness) => {
84
+ // ...
85
+ });
67
86
  },
68
87
  });
69
88
  ```
@@ -46,8 +46,8 @@ function executeCommand(command, argsOrOptions, options) {
46
46
  let bufferedStderr;
47
47
  const cmd = (0, child_process_1.spawn)(command, args, spawnOptions).on("close", (code, signal) => {
48
48
  resolve({
49
- exitCode: code !== null && code !== void 0 ? code : undefined,
50
- signal: signal !== null && signal !== void 0 ? signal : undefined,
49
+ exitCode: code ?? undefined,
50
+ signal: signal ?? undefined,
51
51
  stdout: bufferedStdout,
52
52
  stderr: bufferedStderr,
53
53
  });
@@ -7,17 +7,31 @@ export interface TestAdapterOptions {
7
7
  loglevel?: ioBroker.LogLevel;
8
8
  /** How long to wait before the adapter startup is considered successful */
9
9
  waitBeforeStartupSuccess?: number;
10
+ /**
11
+ * Which JS-Controller version or dist-tag should be used for the tests. Default: dev
12
+ * This should only be changed during active development.
13
+ */
14
+ controllerVersion?: string;
10
15
  /** Allows you to define additional tests */
11
16
  defineAdditionalTests?: (args: TestContext) => void;
12
17
  }
18
+ export interface TestSuiteFn {
19
+ (name: string, fn: (getHarness: () => TestHarness) => void): void;
20
+ }
21
+ export interface TestSuite extends TestSuiteFn {
22
+ /** Only runs the tests inside this `suite` for the current file */
23
+ only: TestSuiteFn;
24
+ /** Skips running the tests inside this `suite` for the current file */
25
+ skip: TestSuiteFn;
26
+ }
13
27
  export interface TestContext {
14
28
  /**
15
29
  * Defines a test suite. At the start of each suite, the adapter will be started with a fresh environment.
16
30
  * To define tests in each suite, use describe and it as usual.
17
31
  *
18
- * Each suite has its own test harness, which gets passed as an argument.
32
+ * Each suite has its own test harness, which can be retrieved using the function that is passed to the suite callback.
19
33
  */
20
- suite: (name: string, fn: (harness: TestHarness) => void) => void;
34
+ suite: TestSuite;
21
35
  describe: Mocha.SuiteFunction;
22
36
  it: Mocha.TestFunction;
23
37
  }
@@ -48,7 +48,6 @@ function testAdapter(adapterDir, options = {}) {
48
48
  console.log(`Running tests in ${testDir}`);
49
49
  console.log();
50
50
  async function prepareTests() {
51
- var _a;
52
51
  // Installation may take a while - especially if rsa-compat needs to be installed
53
52
  const oneMinute = 60000;
54
53
  this.timeout(30 * oneMinute);
@@ -59,11 +58,11 @@ function testAdapter(adapterDir, options = {}) {
59
58
  // Installation happens in two steps:
60
59
  // First we need to set up JS Controller, so the databases etc. can be created
61
60
  // First we need to copy all files and execute an npm install
62
- await controllerSetup.prepareTestDir();
61
+ await controllerSetup.prepareTestDir(options.controllerVersion);
63
62
  // Only then we can install the adapter, because some (including VIS) try to access
64
63
  // the databases if JS Controller is installed
65
64
  await adapterSetup.installAdapterInTestDir();
66
- const dbConnection = new dbConnection_1.DBConnection(appName, testDir, (0, logger_1.createLogger)((_a = options.loglevel) !== null && _a !== void 0 ? _a : "debug"));
65
+ const dbConnection = new dbConnection_1.DBConnection(appName, testDir, (0, logger_1.createLogger)(options.loglevel ?? "debug"));
67
66
  await dbConnection.start();
68
67
  controllerSetup.setupSystemConfig(dbConnection);
69
68
  await controllerSetup.disableAdminInstances(dbConnection);
@@ -82,9 +81,8 @@ function testAdapter(adapterDir, options = {}) {
82
81
  harness.removeAllListeners();
83
82
  }
84
83
  async function resetDbAndStartHarness() {
85
- var _a, _b;
86
84
  this.timeout(30000);
87
- dbConnection = new dbConnection_1.DBConnection(appName, testDir, (0, logger_1.createLogger)((_a = options.loglevel) !== null && _a !== void 0 ? _a : "debug"));
85
+ dbConnection = new dbConnection_1.DBConnection(appName, testDir, (0, logger_1.createLogger)(options.loglevel ?? "debug"));
88
86
  // Clean up before every single test
89
87
  await Promise.all([
90
88
  controllerSetup.clearDBDir(),
@@ -98,7 +96,7 @@ function testAdapter(adapterDir, options = {}) {
98
96
  await harness.changeAdapterConfig(adapterName, {
99
97
  common: {
100
98
  enabled: true,
101
- loglevel: (_b = options.loglevel) !== null && _b !== void 0 ? _b : "debug",
99
+ loglevel: options.loglevel ?? "debug",
102
100
  },
103
101
  });
104
102
  // And enable the sendTo emulation
@@ -110,9 +108,8 @@ function testAdapter(adapterDir, options = {}) {
110
108
  beforeEach(resetDbAndStartHarness);
111
109
  afterEach(shutdownTests);
112
110
  it("The adapter starts", function () {
113
- var _a;
114
111
  this.timeout(60000);
115
- const allowedExitCodes = new Set((_a = options.allowedExitCodes) !== null && _a !== void 0 ? _a : []);
112
+ const allowedExitCodes = new Set(options.allowedExitCodes ?? []);
116
113
  // Adapters with these modes are allowed to "immediately" exit with code 0
117
114
  switch (harness.getAdapterExecutionMode()) {
118
115
  case "schedule":
@@ -175,22 +172,26 @@ function testAdapter(adapterDir, options = {}) {
175
172
  describe("User-defined tests", () => {
176
173
  // patch the global it() function so nobody can bypass the checks
177
174
  global.it = patchedIt;
178
- const lazyHarness = new Proxy({}, {
179
- get(target, propKey) {
180
- return harness[propKey];
181
- },
175
+ // a test suite is a special describe which sets up and tears down the test environment before and after ALL tests
176
+ const suiteBody = (fn) => {
177
+ isInSuite = true;
178
+ before(resetDbAndStartHarness);
179
+ fn(() => harness);
180
+ after(shutdownTests);
181
+ isInSuite = false;
182
+ };
183
+ const suite = ((name, fn) => {
184
+ describe(name, () => suiteBody(fn));
182
185
  });
186
+ // Support .skip and .only
187
+ suite.skip = (name, fn) => {
188
+ describe.skip(name, () => suiteBody(fn));
189
+ };
190
+ suite.only = (name, fn) => {
191
+ describe.only(name, () => suiteBody(fn));
192
+ };
183
193
  const args = {
184
- // a test suite is a special describe which sets up and tears down the test environment before and after ALL tests
185
- suite: (name, fn) => {
186
- describe(name, () => {
187
- isInSuite = true;
188
- before(resetDbAndStartHarness);
189
- fn(lazyHarness);
190
- after(shutdownTests);
191
- isInSuite = false;
192
- });
193
- },
194
+ suite,
194
195
  describe,
195
196
  it: patchedIt,
196
197
  };
@@ -8,7 +8,7 @@ export declare class ControllerSetup {
8
8
  private testAdapterDir;
9
9
  private testControllerDir;
10
10
  private testDataDir;
11
- prepareTestDir(): Promise<void>;
11
+ prepareTestDir(controllerVersion?: string): Promise<void>;
12
12
  /**
13
13
  * Tests if JS-Controller is already installed
14
14
  * @param appName The branded name of "iobroker"
@@ -53,8 +53,8 @@ class ControllerSetup {
53
53
  debug(` appName: ${this.appName}`);
54
54
  debug(` adapterName: ${this.adapterName}`);
55
55
  }
56
- async prepareTestDir() {
57
- debug("Preparing the test directory...");
56
+ async prepareTestDir(controllerVersion = "dev") {
57
+ debug(`Preparing the test directory. JS-Controller version: "${controllerVersion}"...`);
58
58
  // Make sure the test dir exists
59
59
  await (0, fs_extra_1.ensureDir)(this.testDir);
60
60
  // Write the package.json
@@ -69,7 +69,7 @@ class ControllerSetup {
69
69
  author: "",
70
70
  license: "ISC",
71
71
  dependencies: {
72
- [`${this.appName}.js-controller`]: "dev",
72
+ [`${this.appName}.js-controller`]: controllerVersion,
73
73
  },
74
74
  description: "",
75
75
  };
@@ -1,4 +1,6 @@
1
1
  /// <reference types="iobroker" />
2
+ /// <reference types="iobroker" />
3
+ /// <reference types="node" />
2
4
  /// <reference types="node" />
3
5
  import EventEmitter from "events";
4
6
  export declare type ObjectsDB = Record<string, ioBroker.Object>;
@@ -152,17 +152,16 @@ class DBConnection extends events_1.default {
152
152
  debug("DB instances started");
153
153
  }
154
154
  async stop() {
155
- var _a, _b, _c, _d;
156
155
  if (!this._isRunning) {
157
156
  debug("No DB instance is running, nothing to stop...");
158
157
  return;
159
158
  }
160
159
  debug("Stopping DB instances...");
161
160
  // Stop clients before servers
162
- await ((_a = this._objectsClient) === null || _a === void 0 ? void 0 : _a.destroy());
163
- await ((_b = this._objectsServer) === null || _b === void 0 ? void 0 : _b.destroy());
164
- await ((_c = this._statesClient) === null || _c === void 0 ? void 0 : _c.destroy());
165
- await ((_d = this._statesServer) === null || _d === void 0 ? void 0 : _d.destroy());
161
+ await this._objectsClient?.destroy();
162
+ await this._objectsServer?.destroy();
163
+ await this._statesClient?.destroy();
164
+ await this._statesServer?.destroy();
166
165
  this._objectsClient = null;
167
166
  this._objectsServer = null;
168
167
  this._statesClient = null;
@@ -279,20 +278,18 @@ class DBConnection extends events_1.default {
279
278
  this._statesClient.pushMessage(instanceId, msg, callback);
280
279
  }
281
280
  async getStateIDs(pattern = "*") {
282
- var _a, _b, _c, _d;
283
281
  if (!this._statesClient) {
284
282
  throw new Error("States DB is not running");
285
283
  }
286
- return (((_b = (_a = this._statesClient).getKeysAsync) === null || _b === void 0 ? void 0 : _b.call(_a, pattern)) ||
287
- ((_d = (_c = this._statesClient).getKeys) === null || _d === void 0 ? void 0 : _d.call(_c, pattern)));
284
+ return (this._statesClient.getKeysAsync?.(pattern) ||
285
+ this._statesClient.getKeys?.(pattern));
288
286
  }
289
287
  async getObjectIDs(pattern = "*") {
290
- var _a, _b, _c, _d;
291
288
  if (!this._objectsClient) {
292
289
  throw new Error("Objects DB is not running");
293
290
  }
294
- return (((_b = (_a = this._objectsClient).getKeysAsync) === null || _b === void 0 ? void 0 : _b.call(_a, pattern)) ||
295
- ((_d = (_c = this._objectsClient).getKeys) === null || _d === void 0 ? void 0 : _d.call(_c, pattern)));
291
+ return (this._objectsClient.getKeysAsync?.(pattern) ||
292
+ this._objectsClient.getKeys?.(pattern));
296
293
  }
297
294
  }
298
295
  exports.DBConnection = DBConnection;
@@ -1,5 +1,8 @@
1
1
  /// <reference types="iobroker" />
2
2
  /// <reference types="node" />
3
+ /// <reference types="node" />
4
+ /// <reference types="iobroker" />
5
+ /// <reference types="node" />
3
6
  import { ChildProcess } from "child_process";
4
7
  import { EventEmitter } from "events";
5
8
  import type { DBConnection } from "./dbConnection";
@@ -190,7 +190,6 @@ class TestHarness extends events_1.EventEmitter {
190
190
  if (!this.isAdapterRunning())
191
191
  return;
192
192
  return new Promise(async (resolve) => {
193
- var _a;
194
193
  const onClose = (code, signal) => {
195
194
  if (!this._adapterProcess)
196
195
  return;
@@ -214,7 +213,7 @@ class TestHarness extends events_1.EventEmitter {
214
213
  }
215
214
  catch {
216
215
  // DB connection may be closed already, kill the process
217
- (_a = this._adapterProcess) === null || _a === void 0 ? void 0 : _a.kill("SIGTERM");
216
+ this._adapterProcess?.kill("SIGTERM");
218
217
  }
219
218
  });
220
219
  }
@@ -10,8 +10,7 @@ var LoglevelOrder;
10
10
  LoglevelOrder[LoglevelOrder["silly"] = 4] = "silly";
11
11
  })(LoglevelOrder || (LoglevelOrder = {}));
12
12
  function createLogger(loglevel) {
13
- var _a;
14
- const loglevelNumeric = (_a = LoglevelOrder[loglevel !== null && loglevel !== void 0 ? loglevel : "debug"]) !== null && _a !== void 0 ? _a : LoglevelOrder.debug;
13
+ const loglevelNumeric = LoglevelOrder[loglevel ?? "debug"] ?? LoglevelOrder.debug;
15
14
  // eslint-disable-next-line @typescript-eslint/no-empty-function
16
15
  const ignore = () => { };
17
16
  return {
@@ -130,7 +130,6 @@ function validatePackageFiles(adapterDir) {
130
130
  });
131
131
  });
132
132
  describe(`Check contents of io-package.json`, () => {
133
- var _a;
134
133
  beforeEach(function () {
135
134
  skipIfInvalid.call(this, "io-package.json");
136
135
  });
@@ -178,13 +177,12 @@ function validatePackageFiles(adapterDir) {
178
177
  // If the adapter has a configuration page, check that a supported admin UI is used
179
178
  const hasNoConfigPage = iopackContent.common.noConfig === true ||
180
179
  iopackContent.common.noConfig === "true" ||
181
- ((_a = iopackContent.common.adminUI) === null || _a === void 0 ? void 0 : _a.config) === "none";
180
+ iopackContent.common.adminUI?.config === "none";
182
181
  if (!hasNoConfigPage) {
183
182
  it("The adapter uses Material UI or JSON Config for the admin UI", () => {
184
- var _a, _b;
185
183
  const hasSupportedUI = !!iopackContent.common.materialize ||
186
- ((_a = iopackContent.common.adminUI) === null || _a === void 0 ? void 0 : _a.config) === "json" ||
187
- ((_b = iopackContent.common.adminUI) === null || _b === void 0 ? void 0 : _b.config) === "materialize";
184
+ iopackContent.common.adminUI?.config === "json" ||
185
+ iopackContent.common.adminUI?.config === "materialize";
188
186
  (0, chai_1.expect)(hasSupportedUI, "Unsupported Admin UI, must be materialize or json config!").to.be.true;
189
187
  });
190
188
  }
@@ -1,4 +1,5 @@
1
1
  /// <reference types="iobroker" />
2
+ /// <reference types="iobroker" />
2
3
  import type { MockAdapter } from "./mockAdapter";
3
4
  /**
4
5
  * A minimalistic version of ioBroker's Objects and States DB that just operates on a Map
@@ -4,7 +4,7 @@ export declare type IsAny<T> = Equals<T extends never ? false : true, boolean>;
4
4
  export declare type MockableMethods<T, All = Required<T>, NoAny = {
5
5
  [K in keyof All]: IsAny<All[K]> extends true ? never : All[K] extends (...args: any[]) => void ? K : never;
6
6
  }> = NoAny[keyof NoAny];
7
- export declare type Mock<T> = Overwrite<T, {
7
+ export declare type Mock<T extends {}> = Overwrite<T, {
8
8
  [K in MockableMethods<T>]: sinon.SinonStub;
9
9
  }>;
10
10
  export declare function doResetHistory(parent: Record<string, any>): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iobroker/testing",
3
- "version": "3.0.1",
3
+ "version": "4.1.0",
4
4
  "description": "Shared utilities for adapter and module testing in ioBroker",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -41,35 +41,35 @@
41
41
  "@alcalzone/release-script": "^3.5.9",
42
42
  "@alcalzone/release-script-plugin-license": "^3.5.9",
43
43
  "@iobroker/adapter-core": "^2.6.0",
44
- "@tsconfig/node12": "^1.0.9",
45
- "@types/chai": "^4.3.1",
44
+ "@tsconfig/node14": "^1.0.3",
45
+ "@types/chai": "^4.3.3",
46
46
  "@types/chai-as-promised": "^7.1.5",
47
47
  "@types/debug": "4.1.7",
48
48
  "@types/fs-extra": "^9.0.13",
49
- "@types/iobroker": "^4.0.2",
49
+ "@types/iobroker": "^4.0.4",
50
50
  "@types/mocha": "^9.1.1",
51
- "@types/node": "^12.20.50",
52
- "@types/sinon": "^10.0.11",
51
+ "@types/node": "^14.18.26",
52
+ "@types/sinon": "^10.0.13",
53
53
  "@types/sinon-chai": "^3.2.8",
54
- "@typescript-eslint/eslint-plugin": "^5.22.0",
55
- "@typescript-eslint/parser": "^5.22.0",
56
- "eslint": "^8.15.0",
54
+ "@typescript-eslint/eslint-plugin": "^5.35.1",
55
+ "@typescript-eslint/parser": "^5.35.1",
56
+ "eslint": "^8.23.0",
57
57
  "eslint-config-prettier": "^8.5.0",
58
- "eslint-plugin-prettier": "^4.0.0",
59
- "prettier": "^2.6.2",
58
+ "eslint-plugin-prettier": "^4.2.1",
59
+ "prettier": "^2.7.1",
60
60
  "rimraf": "^3.0.2",
61
61
  "source-map-support": "^0.5.21",
62
- "ts-node": "^10.7.0",
63
- "typescript": "~4.6.4"
62
+ "ts-node": "^10.9.1",
63
+ "typescript": "~4.8.2"
64
64
  },
65
65
  "dependencies": {
66
- "alcalzone-shared": "~4.0.1",
66
+ "alcalzone-shared": "~4.0.3",
67
67
  "chai": "^4.3.6",
68
68
  "chai-as-promised": "^7.1.1",
69
69
  "debug": "^4.3.4",
70
70
  "fs-extra": "^10.1.0",
71
- "mocha": "^9.2.2",
72
- "sinon": "^13.0.1",
71
+ "mocha": "^10.0.0",
72
+ "sinon": "^14.0.0",
73
73
  "sinon-chai": "^3.7.0"
74
74
  }
75
75
  }
package/CHANGELOG.md DELETED
@@ -1,66 +0,0 @@
1
- ## Changelog
2
-
3
- <!--
4
- PLACEHOLDER for next version:
5
- ## __WORK IN PROGRESS__
6
- -->
7
- ## 3.0.1 (2022-05-09)
8
- * BREAKING: The function signature of `defineAdditionalTests` in integration tests has changed. All user-defined integration tests must now be grouped in one or more `suite` blocks. The adapter will now only be started at the beginning of each suite. See the documentation for details.
9
- * BREAKING: The function signature of `harness.startAdapterAndWait` has changed. It now accepts a boolean as the first parameter which controls whether to wait for the `alive` state (`false`) or the `info.connection` state (`true`).
10
-
11
- ## 2.6.0 (2022-04-18)
12
- * The loglevel for the adapter and DB instances is now configurable and defaults to `"debug"` in both cases
13
-
14
- ## 2.5.6 (2022-03-05)
15
- * Allow immediate exit with code `0` for `once` and `subscribe` adapters too
16
-
17
- ## 2.5.5 (2022-03-04)
18
- * Allow immediate exit with code `0` for `schedule` adapters
19
- * Check that `npm` is not listed as a local dependency in `package.json`
20
- * Updated dependencies
21
-
22
- ## 2.5.4 (2022-02-02)
23
- * Modifying ioBroker databases now uses the same methods that JS-Controller uses internally. This ensures that the testing is compatible with the `jsonl` database format.
24
- * Testing adapters with adapter dependencies that try to access the databases during installation now works.
25
-
26
- ## 2.5.2 (2021-09-18)
27
- * Fix: `adminUI.config` is now respected for the config UI check and JSON config is allowed too
28
- * Updated dependencies
29
- * Modernized build process
30
-
31
- ## 2.5.1 (2021-09-05)
32
- * We now use the nightly js-controller dev builds instead of GitHub installation
33
-
34
- ## 2.4.4 (2021-03-14)
35
- * Fix error: `iopackContent.common.titleLang` is not iterable
36
-
37
- ## 2.4.3 (2021-03-12)
38
- * Fix: The adapter main file now correctly gets located when it is only defined in `package.json`, not `io-package.json`
39
-
40
- ## 2.4.2 (2021-01-06)
41
- * Fixed compatibility with the reworked database classes
42
- * Improved shutdown behavior of the adapter
43
-
44
- ## 2.4.1 (2021-01-01)
45
- * Fixed a bug where the wrong `js-controller` dependency would be installed
46
-
47
- ## 2.4.0 (2020-12-07)
48
- * Unit tests for adapter startup were removed and only log a warning that you can remove them
49
- * Upgrade many packages
50
-
51
- ## 2.3.0 (2020-08-20)
52
- * Added missing async functions to adapter mock
53
- * Fixed: `TypeError "Cannot redefine property readyHandler"` when using `createMocks` more than once
54
- * Upgrade to `@types/iobroker` v3.0.12
55
-
56
- ## 2.2.0 (2020-04-15)
57
- * Upgrade to `@types/iobroker` v3.0.2
58
- * Added mocks for `supportsFeature`, `getPluginInstance`, `getPluginConfig`
59
-
60
- ## 2.1.0 (2020-03-01)
61
- * **Integration tests:** For Node.js >= 10, the `engine-strict` flag is now set to `true` to be in line with newer ioBroker installations
62
-
63
- ## v2.0.2
64
- * **Unit tests:** added mocks for `getAbsoluteDefaultDataDir` and `getAbsoluteInstanceDataDir`
65
-
66
- Sorry, there isn't more yet.