From 6beae7595d41ab57bbccaf5d5fe114f2a5074489 Mon Sep 17 00:00:00 2001 From: GermanBluefox Date: Thu, 23 Jan 2025 14:56:37 +0000 Subject: [PATCH] Corrected removed functions --- CHANGELOG.md | 2 +- build/lib/adapterTools.d.ts | 6 + build/lib/adapterTools.js | 14 +- build/lib/executeCommand.d.ts | 1 + build/lib/executeCommand.js | 20 ++- build/lib/testAdapter.d.ts | 1 + build/lib/testAdapter.js | 1 + build/tests/index.d.ts | 4 +- build/tests/integration/index.js | 7 +- build/tests/integration/lib/adapterSetup.js | 18 +-- .../integration/lib/controllerSetup.d.ts | 9 -- .../tests/integration/lib/controllerSetup.js | 18 +-- build/tests/integration/lib/dbConnection.js | 26 ++-- build/tests/integration/lib/harness.d.ts | 4 +- build/tests/integration/lib/harness.js | 28 ++-- build/tests/integration/lib/tools.d.ts | 7 +- build/tests/integration/lib/tools.js | 7 +- build/tests/packageFiles/index.js | 30 ++-- build/tests/unit/harness/createMocks.d.ts | 3 +- build/tests/unit/harness/createMocks.js | 1 - build/tests/unit/index.js | 3 +- build/tests/unit/mocks/mockAdapter.d.ts | 4 +- build/tests/unit/mocks/mockAdapter.js | 130 +++++++++++------- build/tests/unit/mocks/mockAdapterCore.d.ts | 11 +- build/tests/unit/mocks/mockAdapterCore.js | 7 +- build/tests/unit/mocks/mockDatabase.d.ts | 23 ++-- build/tests/unit/mocks/mockDatabase.js | 37 ++--- build/tests/unit/mocks/mockLogger.d.ts | 2 +- build/tests/unit/mocks/tools.js | 21 +-- src/tests/integration/lib/adapterSetup.ts | 4 +- src/tests/unit/mocks/mockAdapter.ts | 7 +- src/tests/unit/mocks/mockDatabase.ts | 7 +- tsconfig.build.json | 3 +- 33 files changed, 259 insertions(+), 207 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6b80fe0..7879001c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ PLACEHOLDER for the next version: ## **WORK IN PROGRESS** --> -## 5.0.2 (2025-01-21) +## **WORK IN PROGRESS** * Packages were updated ## 5.0.0 (2024-09-14) diff --git a/build/lib/adapterTools.d.ts b/build/lib/adapterTools.d.ts index 8df727a8..9d5b06dc 100644 --- a/build/lib/adapterTools.d.ts +++ b/build/lib/adapterTools.d.ts @@ -1,31 +1,37 @@ /** * Loads an adapter's package.json + * * @param adapterDir The directory the adapter resides in */ export declare function loadNpmPackage(adapterDir: string): Record; /** * Loads an adapter's io-package.json + * * @param adapterDir The directory the adapter resides in */ export declare function loadIoPackage(adapterDir: string): Record; export declare function getAdapterExecutionMode(adapterDir: string): ioBroker.AdapterCommon['mode']; /** * Locates an adapter's main file + * * @param adapterDir The directory the adapter resides in */ export declare function locateAdapterMainFile(adapterDir: string): Promise; /** * Locates an adapter's config to populate the `adapter.config` object with + * * @param adapterDir The directory the adapter resides in */ export declare function loadAdapterConfig(adapterDir: string): Record; /** * Loads the adapter's common configuration from `io-package.json` + * * @param adapterDir The directory the adapter resides in */ export declare function loadAdapterCommon(adapterDir: string): Record; /** * Loads the instanceObjects for an adapter from its `io-package.json` + * * @param adapterDir The directory the adapter resides in */ export declare function loadInstanceObjects(adapterDir: string): ioBroker.Object[]; diff --git a/build/lib/adapterTools.js b/build/lib/adapterTools.js index 2d57a847..17146b4c 100644 --- a/build/lib/adapterTools.js +++ b/build/lib/adapterTools.js @@ -48,7 +48,6 @@ exports.getAdapterName = getAdapterName; exports.getAdapterFullName = getAdapterFullName; exports.getAdapterDependencies = getAdapterDependencies; // Add debug logging for tests -// @ts-expect-error no types const typeguards_1 = require("alcalzone-shared/typeguards"); const debug_1 = __importDefault(require("debug")); const fs_extra_1 = require("fs-extra"); @@ -56,18 +55,18 @@ const path = __importStar(require("path")); const debug = (0, debug_1.default)('testing:unit:adapterTools'); /** * Loads an adapter's package.json + * * @param adapterDir The directory the adapter resides in */ function loadNpmPackage(adapterDir) { - // eslint-disable-next-line @typescript-eslint/no-require-imports return require(path.join(adapterDir, 'package.json')); } /** * Loads an adapter's io-package.json + * * @param adapterDir The directory the adapter resides in */ function loadIoPackage(adapterDir) { - // eslint-disable-next-line @typescript-eslint/no-require-imports return require(path.join(adapterDir, 'io-package.json')); } function getAdapterExecutionMode(adapterDir) { @@ -76,6 +75,7 @@ function getAdapterExecutionMode(adapterDir) { } /** * Locates an adapter's main file + * * @param adapterDir The directory the adapter resides in */ async function locateAdapterMainFile(adapterDir) { @@ -95,7 +95,7 @@ async function locateAdapterMainFile(adapterDir) { return ret; } // If both don't exist, JS-Controller uses .js as another fallback - ret = path.join(adapterDir, ioPackage.common.name + '.js'); + ret = path.join(adapterDir, `${ioPackage.common.name}.js`); debug(` => trying ${ret}`); if (await (0, fs_extra_1.pathExists)(ret)) { debug(` => found ${mainFile}`); @@ -105,6 +105,7 @@ async function locateAdapterMainFile(adapterDir) { } /** * Locates an adapter's config to populate the `adapter.config` object with + * * @param adapterDir The directory the adapter resides in */ function loadAdapterConfig(adapterDir) { @@ -113,6 +114,7 @@ function loadAdapterConfig(adapterDir) { } /** * Loads the adapter's common configuration from `io-package.json` + * * @param adapterDir The directory the adapter resides in */ function loadAdapterCommon(adapterDir) { @@ -121,6 +123,7 @@ function loadAdapterCommon(adapterDir) { } /** * Loads the instanceObjects for an adapter from its `io-package.json` + * * @param adapterDir The directory the adapter resides in */ function loadInstanceObjects(adapterDir) { @@ -153,8 +156,9 @@ function getAdapterDependencies(adapterDir) { } else if ((0, typeguards_1.isObject)(dep)) { const key = Object.keys(dep)[0]; - if (key) + if (key) { ret[key] = dep[key] || 'latest'; + } } } } diff --git a/build/lib/executeCommand.d.ts b/build/lib/executeCommand.d.ts index bb30d2cc..d4821d09 100644 --- a/build/lib/executeCommand.d.ts +++ b/build/lib/executeCommand.d.ts @@ -23,6 +23,7 @@ export interface ExecuteCommandResult { export declare function executeCommand(command: string, options?: Partial): Promise; /** * Executes a command and returns the exit code and (if requested) the stdout + * * @param command The command to execute * @param args The command line arguments for the command * @param options (optional) Some options for the command execution diff --git a/build/lib/executeCommand.js b/build/lib/executeCommand.js index 970f8530..402368fb 100644 --- a/build/lib/executeCommand.js +++ b/build/lib/executeCommand.js @@ -13,16 +13,19 @@ function executeCommand(command, argsOrOptions, options) { // no args were given options = argsOrOptions; } - if (options == null) + if (options == null) { options = {}; - if (args == null) + } + if (args == null) { args = []; + } const spawnOptions = { stdio: [options.stdin || process.stdin, options.stdout || process.stdout, options.stderr || process.stderr], windowsHide: true, }; - if (options.cwd != null) + if (options.cwd != null) { spawnOptions.cwd = options.cwd; + } // Fix npm / node executable paths on Windows if (isWindows) { if (command === 'npm') { @@ -35,10 +38,11 @@ function executeCommand(command, argsOrOptions, options) { command += '.exe'; } } - if (options.logCommandExecution == null) + if (options.logCommandExecution == null) { options.logCommandExecution = false; + } if (options.logCommandExecution) { - console.log('executing: ' + `${command} ${args.join(' ')}`); + console.log(`executing: ${command} ${args.join(' ')}`); } // Now execute the npm process and avoid throwing errors try { @@ -56,16 +60,18 @@ function executeCommand(command, argsOrOptions, options) { if (options.stdout === 'pipe') { bufferedStdout = ''; cmd.stdout.on('data', (chunk) => { - if (Buffer.isBuffer(chunk)) + if (Buffer.isBuffer(chunk)) { chunk = chunk.toString('utf8'); + } bufferedStdout += chunk; }); } if (options.stderr === 'pipe') { bufferedStderr = ''; cmd.stderr.on('data', (chunk) => { - if (Buffer.isBuffer(chunk)) + if (Buffer.isBuffer(chunk)) { chunk = chunk.toString('utf8'); + } bufferedStderr += chunk; }); } diff --git a/build/lib/testAdapter.d.ts b/build/lib/testAdapter.d.ts index e69de29b..cb0ff5c3 100644 --- a/build/lib/testAdapter.d.ts +++ b/build/lib/testAdapter.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/build/lib/testAdapter.js b/build/lib/testAdapter.js index b20cb7e9..79eb1d54 100644 --- a/build/lib/testAdapter.js +++ b/build/lib/testAdapter.js @@ -1,6 +1,7 @@ "use strict"; // TODO: Do we need this file? // import * as utils from "@iobroker/adapter-core"; +Object.defineProperty(exports, "__esModule", { value: true }); // const adapter = utils.adapter({ // name: "foo", // ready() { diff --git a/build/tests/index.d.ts b/build/tests/index.d.ts index 25f4322f..17e49641 100644 --- a/build/tests/index.d.ts +++ b/build/tests/index.d.ts @@ -4,7 +4,7 @@ import { testAdapterWithMocks } from './unit'; import { createMocks } from './unit/harness/createMocks'; import { createAsserts } from './unit/mocks/mockDatabase'; export { TestHarness as IntegrationTestHarness } from './integration/lib/harness'; -export { MockAdapter } from './unit/mocks/mockAdapter'; +export type { MockAdapter } from './unit/mocks/mockAdapter'; export { MockDatabase } from './unit/mocks/mockDatabase'; /** Predefined test sets */ export declare const tests: { @@ -19,6 +19,6 @@ export declare const utils: { createMocks: typeof createMocks; createAsserts: typeof createAsserts; /** @deprecated Adapter startup unit tests are no longer supported */ - startMockAdapter: () => {}; + startMockAdapter: () => any; }; }; diff --git a/build/tests/integration/index.js b/build/tests/integration/index.js index bbcf2b11..a853821d 100644 --- a/build/tests/integration/index.js +++ b/build/tests/integration/index.js @@ -121,10 +121,15 @@ function testAdapter(adapterDir, options = {}) { // Adapters with these modes are allowed to "immediately" exit with code 0 switch (harness.getAdapterExecutionMode()) { case 'schedule': + allowedExitCodes.add(0); + break; case 'once': + allowedExitCodes.add(0); + break; // @ts-expect-error subscribe was deprecated case 'subscribe': allowedExitCodes.add(0); + break; } return new Promise((resolve, reject) => { // Register a handler to check the alive state and exit codes @@ -147,7 +152,7 @@ function testAdapter(adapterDir, options = {}) { resolve(`The expected ${typeof code === 'number' ? 'exit code' : 'signal'} ${code} was received.`); } }); - harness.startAdapter(); + void harness.startAdapter(); }).then(msg => console.log(msg)); }); }); diff --git a/build/tests/integration/lib/adapterSetup.js b/build/tests/integration/lib/adapterSetup.js index d0ae977d..70c1febe 100644 --- a/build/tests/integration/lib/adapterSetup.js +++ b/build/tests/integration/lib/adapterSetup.js @@ -38,8 +38,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) { Object.defineProperty(exports, "__esModule", { value: true }); exports.AdapterSetup = void 0; // Add debug logging for tests -// @ts-expect-error no types -const objects_1 = require("alcalzone-shared/objects"); const debug_1 = __importDefault(require("debug")); const fs_extra_1 = require("fs-extra"); const path = __importStar(require("path")); @@ -78,8 +76,9 @@ class AdapterSetup { const packResult = await (0, executeCommand_1.executeCommand)('npm', ['pack', '--loglevel', 'silent'], { stdout: 'pipe', }); - if (packResult.exitCode !== 0 || typeof packResult.stdout !== 'string') + if (packResult.exitCode !== 0 || typeof packResult.stdout !== 'string') { throw new Error(`Packing the adapter tarball failed!`); + } // The last non-empty line of `npm pack`s STDOUT contains the tarball path const stdoutLines = packResult.stdout.trim().split(/[\r\n]+/); const tarballName = stdoutLines[stdoutLines.length - 1].trim(); @@ -99,16 +98,18 @@ class AdapterSetup { const packageJsonPath = path.join(this.testDir, 'package.json'); const packageJson = await (0, fs_extra_1.readJSON)(packageJsonPath); packageJson.dependencies[this.adapterFullName] = `file:./${tarballName}`; - for (const [dep, version] of (0, objects_1.entries)((0, adapterTools_1.getAdapterDependencies)(this.adapterDir))) { + for (const [dep, version] of Object.entries((0, adapterTools_1.getAdapterDependencies)(this.adapterDir))) { // Don't overwrite the js-controller GitHub dependency with a probably lower one - if (dep === 'js-controller') + if (dep === 'js-controller') { continue; + } packageJson.dependencies[`${this.appName}.${dep}`] = version; } await (0, fs_extra_1.writeJSON)(packageJsonPath, packageJson, { spaces: 2 }); debug('Deleting old remains of this adapter'); - if (await (0, fs_extra_1.pathExists)(this.testAdapterDir)) + if (await (0, fs_extra_1.pathExists)(this.testAdapterDir)) { await (0, fs_extra_1.remove)(this.testAdapterDir); + } debug('Installing adapter'); // Defer to npm to install the controller (if it wasn't already) await (0, executeCommand_1.executeCommand)('npm', ['i', '--omit=dev'], { @@ -126,15 +127,16 @@ class AdapterSetup { cwd: this.testControllerDir, stdout: 'ignore', }); - if (addResult.exitCode !== 0) + if (addResult.exitCode !== 0) { throw new Error(`Adding the adapter instance failed!`); + } debug(' => done!'); } async deleteOldInstances(dbConnection) { debug('Removing old adapter instances...'); const allKeys = new Set([...(await dbConnection.getObjectIDs()), ...(await dbConnection.getStateIDs())]); const instanceRegex = new RegExp(`^system\\.adapter\\.${this.adapterName}\\.\\d+`); - const instanceObjsRegex = new RegExp(`^${this.adapterName}\\.\\d+\.`); + const instanceObjsRegex = new RegExp(`^${this.adapterName}\\.\\d+\\.`); const belongsToAdapter = (id) => { return (instanceRegex.test(id) || instanceObjsRegex.test(id) || diff --git a/build/tests/integration/lib/controllerSetup.d.ts b/build/tests/integration/lib/controllerSetup.d.ts index 1b2d8e99..aead29cf 100644 --- a/build/tests/integration/lib/controllerSetup.d.ts +++ b/build/tests/integration/lib/controllerSetup.d.ts @@ -11,8 +11,6 @@ export declare class ControllerSetup { prepareTestDir(controllerVersion?: string): Promise; /** * Tests if JS-Controller is already installed - * @param appName The branded name of "iobroker" - * @param testDir The directory the integration tests are executed in */ isJsControllerInstalled(): Promise; /** @@ -25,25 +23,18 @@ export declare class ControllerSetup { setupJsController(): Promise; /** * Changes the objects and states db to use alternative ports - * @param appName The branded name of "iobroker" - * @param testDir The directory the integration tests are executed in */ setupSystemConfig(dbConnection: DBConnection): void; /** * Clears the log dir for integration tests (and creates it if it doesn't exist) - * @param appName The branded name of "iobroker" - * @param testDir The directory the integration tests are executed in */ clearLogDir(): Promise; /** * Clears the sqlite DB dir for integration tests (and creates it if it doesn't exist) - * @param appName The branded name of "iobroker" - * @param testDir The directory the integration tests are executed in */ clearDBDir(): Promise; /** * Disables all admin instances in the objects DB - * @param objects The contents of objects.json */ disableAdminInstances(dbConnection: DBConnection): Promise; } diff --git a/build/tests/integration/lib/controllerSetup.js b/build/tests/integration/lib/controllerSetup.js index be55cf2a..e8a7ddfc 100644 --- a/build/tests/integration/lib/controllerSetup.js +++ b/build/tests/integration/lib/controllerSetup.js @@ -88,8 +88,9 @@ class ControllerSetup { }); // Delete a possible package-lock.json as it can mess with future installations const pckLockPath = path.join(this.testDir, 'package-lock.json'); - if (await (0, fs_extra_1.pathExists)(pckLockPath)) + if (await (0, fs_extra_1.pathExists)(pckLockPath)) { await (0, fs_extra_1.unlink)(pckLockPath); + } // Set the engineStrict flag on new Node.js versions to be in line with newer ioBroker installations const nodeMajorVersion = parseInt(process.versions.node.split('.')[0], 10); if (nodeMajorVersion >= 10) { @@ -103,14 +104,13 @@ class ControllerSetup { cwd: this.testDir, }); // Prepare/clean the databases and config - if (wasJsControllerInstalled) + if (wasJsControllerInstalled) { await this.setupJsController(); + } debug(' => done!'); } /** * Tests if JS-Controller is already installed - * @param appName The branded name of "iobroker" - * @param testDir The directory the integration tests are executed in */ async isJsControllerInstalled() { debug('Testing if JS-Controller is installed...'); @@ -187,14 +187,13 @@ class ControllerSetup { cwd: this.testControllerDir, stdout: 'ignore', }); - if (setupResult.exitCode !== 0) + if (setupResult.exitCode !== 0) { throw new Error(`${this.appName} setup first failed!`); + } debug(' => done!'); } /** * Changes the objects and states db to use alternative ports - * @param appName The branded name of "iobroker" - * @param testDir The directory the integration tests are executed in */ setupSystemConfig(dbConnection) { debug(`Moving databases to different ports...`); @@ -206,8 +205,6 @@ class ControllerSetup { } /** * Clears the log dir for integration tests (and creates it if it doesn't exist) - * @param appName The branded name of "iobroker" - * @param testDir The directory the integration tests are executed in */ clearLogDir() { debug('Cleaning log directory...'); @@ -215,8 +212,6 @@ class ControllerSetup { } /** * Clears the sqlite DB dir for integration tests (and creates it if it doesn't exist) - * @param appName The branded name of "iobroker" - * @param testDir The directory the integration tests are executed in */ clearDBDir() { debug('Cleaning SQLite directory...'); @@ -224,7 +219,6 @@ class ControllerSetup { } /** * Disables all admin instances in the objects DB - * @param objects The contents of objects.json */ async disableAdminInstances(dbConnection) { debug('Disabling admin instances...'); diff --git a/build/tests/integration/lib/dbConnection.js b/build/tests/integration/lib/dbConnection.js index 503de8d9..ce20bbb2 100644 --- a/build/tests/integration/lib/dbConnection.js +++ b/build/tests/integration/lib/dbConnection.js @@ -55,43 +55,43 @@ class DBConnection extends events_1.default { this.testDir = testDir; this.logger = logger; this._isRunning = false; - this.getObject = async (id) => { + this.getObject = id => { if (!this._objectsClient) { throw new Error('Objects DB is not running'); } return this._objectsClient.getObjectAsync(id); }; - this.setObject = async (...args) => { + this.setObject = (...args) => { if (!this._objectsClient) { throw new Error('Objects DB is not running'); } return this._objectsClient.setObjectAsync(...args); }; - this.delObject = async (...args) => { + this.delObject = (...args) => { if (!this._objectsClient) { throw new Error('Objects DB is not running'); } return this._objectsClient.delObjectAsync(...args); }; - this.getState = async (id) => { + this.getState = (id) => { if (!this._statesClient) { throw new Error('States DB is not running'); } return this._statesClient.getStateAsync(id); }; - this.setState = (async (...args) => { + this.setState = ((...args) => { if (!this._statesClient) { throw new Error('States DB is not running'); } return this._statesClient.setStateAsync(...args); }); - this.delState = async (...args) => { + this.delState = (...args) => { if (!this._statesClient) { throw new Error('States DB is not running'); } return this._statesClient.delStateAsync(...args); }; - this.getObjectViewAsync = async (...args) => { + this.getObjectViewAsync = (...args) => { if (!this._objectsClient) { throw new Error('Objects DB is not running'); } @@ -130,8 +130,9 @@ class DBConnection extends events_1.default { await this.stop(); const objects = await (0, fs_extra_1.readFile)(this.objectsPath); const states = await (0, fs_extra_1.readFile)(this.statesPath); - if (wasRunning) + if (wasRunning) { await this.start(); + } return { objects, states }; } async restore(objects, states) { @@ -140,8 +141,9 @@ class DBConnection extends events_1.default { await this.stop(); await (0, fs_extra_1.writeFile)(this.objectsPath, objects); await (0, fs_extra_1.writeFile)(this.statesPath, states); - if (wasRunning) + if (wasRunning) { await this.start(); + } } setSystemConfig(systemConfig) { const systemFilename = path.join(this.testDataDir, `${this.appName}.json`); @@ -200,7 +202,6 @@ class DBConnection extends events_1.default { paths: [path.join(this.testDir, 'node_modules'), path.join(this.testControllerDir, 'node_modules')], }); debug(` => objects DB lib found at ${objectsDbPath}`); - // eslint-disable-next-line @typescript-eslint/no-require-imports const { Server, Client } = require(objectsDbPath); // First create the server await new Promise(resolve => { @@ -245,7 +246,6 @@ class DBConnection extends events_1.default { paths: [path.join(this.testDir, 'node_modules'), path.join(this.testControllerDir, 'node_modules')], }); debug(` => states DB lib found at ${statesDbPath}`); - // eslint-disable-next-line @typescript-eslint/no-require-imports const { Server, Client } = require(statesDbPath); // First create the server await new Promise(resolve => { @@ -281,13 +281,13 @@ class DBConnection extends events_1.default { } this._statesClient.pushMessage(instanceId, msg, callback); } - async getStateIDs(pattern = '*') { + getStateIDs(pattern = '*') { if (!this._statesClient) { throw new Error('States DB is not running'); } return this._statesClient.getKeysAsync?.(pattern) || this._statesClient.getKeys?.(pattern); } - async getObjectIDs(pattern = '*') { + getObjectIDs(pattern = '*') { if (!this._objectsClient) { throw new Error('Objects DB is not running'); } diff --git a/build/tests/integration/lib/harness.d.ts b/build/tests/integration/lib/harness.d.ts index 8c4bd083..b0316096 100644 --- a/build/tests/integration/lib/harness.d.ts +++ b/build/tests/integration/lib/harness.d.ts @@ -1,4 +1,4 @@ -import { ChildProcess } from 'child_process'; +import { type ChildProcess } from 'child_process'; import { EventEmitter } from 'events'; import type { DBConnection } from './dbConnection'; export interface TestHarness { @@ -41,11 +41,13 @@ export declare class TestHarness extends EventEmitter { stopController(): Promise; /** * Starts the adapter in a separate process and monitors its status + * * @param env Additional environment variables to set */ startAdapter(env?: NodeJS.ProcessEnv): Promise; /** * Starts the adapter in a separate process and resolves after it has started + * * @param waitForConnection By default, the test will wait for the adapter's `alive` state to become true. Set this to `true` to wait for the `info.connection` state instead. * @param env Additional environment variables to set */ diff --git a/build/tests/integration/lib/harness.js b/build/tests/integration/lib/harness.js index f9e1c78e..110ef62c 100644 --- a/build/tests/integration/lib/harness.js +++ b/build/tests/integration/lib/harness.js @@ -37,9 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TestHarness = void 0; -// @ts-expect-error no types const async_1 = require("alcalzone-shared/async"); -// @ts-expect-error no types const objects_1 = require("alcalzone-shared/objects"); const child_process_1 = require("child_process"); const debug_1 = __importDefault(require("debug")); @@ -116,8 +114,9 @@ class TestHarness extends events_1.EventEmitter { } /** Stops the controller instance (and the adapter if it is running) */ async stopController() { - if (!this.isControllerRunning()) + if (!this.isControllerRunning()) { return; + } if (!this.didAdapterStop()) { debug('Stopping adapter instance...'); // Give the adapter time to stop (as long as configured in the io-package.json) @@ -127,7 +126,9 @@ class TestHarness extends events_1.EventEmitter { .common.stopTimeout; stopTimeout += 1000; } - catch { } + catch { + // ignore + } stopTimeout ||= 5000; // default 5s debug(` => giving it ${stopTimeout}ms to terminate`); await Promise.race([this.stopAdapter(), (0, async_1.wait)(stopTimeout)]); @@ -146,13 +147,16 @@ class TestHarness extends events_1.EventEmitter { } /** * Starts the adapter in a separate process and monitors its status + * * @param env Additional environment variables to set */ async startAdapter(env = {}) { - if (this.isAdapterRunning()) + if (this.isAdapterRunning()) { throw new Error('The adapter is already running!'); - else if (this.didAdapterStop()) + } + else if (this.didAdapterStop()) { throw new Error('This test harness has already been used. Please create a new one for each test!'); + } const mainFileAbsolute = await (0, adapterTools_1.locateAdapterMainFile)(this.testAdapterDir); const mainFileRelative = path.relative(this.testAdapterDir, mainFileAbsolute); const onClose = (code, signal) => { @@ -170,6 +174,7 @@ class TestHarness extends events_1.EventEmitter { } /** * Starts the adapter in a separate process and resolves after it has started + * * @param waitForConnection By default, the test will wait for the adapter's `alive` state to become true. Set this to `true` to wait for the `info.connection` state instead. * @param env Additional environment variables to set */ @@ -178,7 +183,7 @@ class TestHarness extends events_1.EventEmitter { const waitForStateId = waitForConnection ? `${this.adapterName}.0.info.connection` : `system.adapter.${this.adapterName}.0.alive`; - this.on('stateChange', async (id, state) => { + void this.on('stateChange', (id, state) => { if (id === waitForStateId && state && state.val === true) { resolve(); } @@ -199,12 +204,15 @@ class TestHarness extends events_1.EventEmitter { } /** Stops the adapter process */ stopAdapter() { - if (!this.isAdapterRunning()) + if (!this.isAdapterRunning()) { return; + } + // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve) => { const onClose = (code, signal) => { - if (!this._adapterProcess) + if (!this._adapterProcess) { return; + } this._adapterProcess.removeAllListeners(); this._adapterExit = code != undefined ? code : signal; this._adapterProcess = undefined; @@ -271,7 +279,7 @@ class TestHarness extends events_1.EventEmitter { ack: false, time: Date.now(), }, - }, (err, id) => console.log('published message ' + id)); + }, (err, id) => console.log(`published message ${id}`)); } } exports.TestHarness = TestHarness; diff --git a/build/tests/integration/lib/tools.d.ts b/build/tests/integration/lib/tools.d.ts index 6076cbcf..8c7c5b2a 100644 --- a/build/tests/integration/lib/tools.d.ts +++ b/build/tests/integration/lib/tools.d.ts @@ -1,29 +1,34 @@ /** * Locates the directory where JS-Controller is installed for integration tests + * * @param appName The branded name of "iobroker" * @param testDir The directory the integration tests are executed in */ export declare function getTestControllerDir(appName: string, testDir: string): string; /** * Locates the directory where JS-Controller stores its data for integration tests + * * @param appName The branded name of "iobroker" * @param testDir The directory the integration tests are executed in */ export declare function getTestDataDir(appName: string, testDir: string): string; /** * Locates the directory where JS-Controller stores its logs for integration tests + * * @param appName The branded name of "iobroker" * @param testDir The directory the integration tests are executed in */ export declare function getTestLogDir(appName: string, testDir: string): string; /** * Locates the directory where JS-Controller stores its sqlite db during integration tests + * * @param appName The branded name of "iobroker" * @param testDir The directory the integration tests are executed in */ export declare function getTestDBDir(appName: string, testDir: string): string; /** - * Locates the directory where the adapter will be be stored for integration tests + * Locates the directory where the adapter will be stored for integration tests + * * @param adapterDir The root directory of the adapter * @param testDir The directory the integration tests are executed in */ diff --git a/build/tests/integration/lib/tools.js b/build/tests/integration/lib/tools.js index 25923a49..13cfd1be 100644 --- a/build/tests/integration/lib/tools.js +++ b/build/tests/integration/lib/tools.js @@ -42,6 +42,7 @@ const path = __importStar(require("path")); const adapterTools_1 = require("../../../lib/adapterTools"); /** * Locates the directory where JS-Controller is installed for integration tests + * * @param appName The branded name of "iobroker" * @param testDir The directory the integration tests are executed in */ @@ -50,6 +51,7 @@ function getTestControllerDir(appName, testDir) { } /** * Locates the directory where JS-Controller stores its data for integration tests + * * @param appName The branded name of "iobroker" * @param testDir The directory the integration tests are executed in */ @@ -58,6 +60,7 @@ function getTestDataDir(appName, testDir) { } /** * Locates the directory where JS-Controller stores its logs for integration tests + * * @param appName The branded name of "iobroker" * @param testDir The directory the integration tests are executed in */ @@ -66,6 +69,7 @@ function getTestLogDir(appName, testDir) { } /** * Locates the directory where JS-Controller stores its sqlite db during integration tests + * * @param appName The branded name of "iobroker" * @param testDir The directory the integration tests are executed in */ @@ -73,7 +77,8 @@ function getTestDBDir(appName, testDir) { return path.resolve(getTestDataDir(appName, testDir), 'sqlite'); } /** - * Locates the directory where the adapter will be be stored for integration tests + * Locates the directory where the adapter will be stored for integration tests + * * @param adapterDir The root directory of the adapter * @param testDir The directory the integration tests are executed in */ diff --git a/build/tests/packageFiles/index.js b/build/tests/packageFiles/index.js index f0c06286..4a96c789 100644 --- a/build/tests/packageFiles/index.js +++ b/build/tests/packageFiles/index.js @@ -34,7 +34,6 @@ var __importStar = (this && this.__importStar) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.validatePackageFiles = validatePackageFiles; -// @ts-expect-error no types const typeguards_1 = require("alcalzone-shared/typeguards"); const chai_1 = require("chai"); const fs = __importStar(require("fs")); @@ -52,8 +51,9 @@ function validatePackageFiles(adapterDir) { 'io-package.json': false, }; function skipIfInvalid(...filenames) { - if (filenames.some(f => invalidFiles[f])) + if (filenames.some(f => invalidFiles[f])) { return this.skip(); + } } function markAsInvalid(filename) { if (this.currentTest.state === 'failed' && invalidFiles[filename] === false) { @@ -67,7 +67,6 @@ function validatePackageFiles(adapterDir) { it(`The property "${propertyPath}" exists`, () => { let prev = targetObj; for (const part of propertyParts) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions (0, chai_1.expect)(prev[part]).to.not.be.undefined; prev = prev[part]; } @@ -85,7 +84,6 @@ function validatePackageFiles(adapterDir) { skipIfInvalid.call(this, filename); }); it('exists', () => { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions (0, chai_1.expect)(fs.existsSync(packagePath), `${filename} is missing in the adapter dir. Please create it!`).to.be.true; }); it('contains valid JSON', () => { @@ -94,9 +92,7 @@ function validatePackageFiles(adapterDir) { }, `${filename} contains invalid JSON!`).not.to.throw(); }); it('is an object', () => { - (0, chai_1.expect)( - // eslint-disable-next-line @typescript-eslint/no-require-imports - require(packagePath), `${filename} must contain an object!`).to.be.an('object'); + (0, chai_1.expect)(require(packagePath), `${filename} must contain an object!`).to.be.an('object'); }); }); } @@ -105,9 +101,7 @@ function validatePackageFiles(adapterDir) { beforeEach(function () { skipIfInvalid.call(this, 'package.json'); }); - // eslint-disable-next-line @typescript-eslint/no-require-imports const packageContent = require(packageJsonPath); - // eslint-disable-next-line @typescript-eslint/no-require-imports const iopackContent = require(ioPackageJsonPath); const requiredProperties = [ 'name', @@ -123,13 +117,12 @@ function validatePackageFiles(adapterDir) { let name = packageContent.name; (0, chai_1.expect)(name).to.match(/^iobroker\./, `The npm package name must start with lowercase "iobroker."!`); name = name.replace(/^iobroker\./, ''); - (0, chai_1.expect)(name).to.match(/[a-z0-9_\-]+/, `The adapter name must only contain lowercase letters, numbers, "-" and "_"!`); + (0, chai_1.expect)(name).to.match(/[-a-z0-9_]+/, `The adapter name must only contain lowercase letters, numbers, "-" and "_"!`); (0, chai_1.expect)(name).to.match(/^[a-z]/, `The adapter name must start with a letter!`); (0, chai_1.expect)(name).to.match(/[a-z0-9]$/, `The adapter name must end with a letter or number!`); }); if (!iopackContent.common.onlyWWW) { it(`property main is defined for non onlyWWW adapters`, () => { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions (0, chai_1.expect)(packageContent.main).to.not.be.undefined; }); } @@ -144,6 +137,7 @@ function validatePackageFiles(adapterDir) { 'peerDependencies', ]) { if ((0, typeguards_1.isObject)(packageContent[depType]) && 'npm' in packageContent[depType]) { + // eslint-disable-next-line @typescript-eslint/only-throw-error throw new chai_1.AssertionError(`npm must not be listed in ${depType}, found "${packageContent[depType].npm}"!`); } } @@ -153,7 +147,6 @@ function validatePackageFiles(adapterDir) { beforeEach(function () { skipIfInvalid.call(this, 'io-package.json'); }); - // eslint-disable-next-line @typescript-eslint/no-require-imports const iopackContent = require(ioPackageJsonPath); const requiredProperties = [ 'common.name', @@ -169,8 +162,9 @@ function validatePackageFiles(adapterDir) { ]; requiredProperties.forEach(prop => ensurePropertyExists(prop, iopackContent)); it(`The title does not contain "adapter" or "iobroker"`, () => { - if (!iopackContent.title) + if (!iopackContent.title) { return; + } (0, chai_1.expect)(iopackContent.common.title).not.to.match(/iobroker|adapter/i); }); it(`titleLang is an object to support multiple languages`, () => { @@ -186,13 +180,11 @@ function validatePackageFiles(adapterDir) { }); it(`common.authors is an array that is not empty`, () => { const authors = iopackContent.common.authors; - // eslint-disable-next-line @typescript-eslint/no-unused-expressions (0, chai_1.expect)((0, typeguards_1.isArray)(authors)).to.be.true; (0, chai_1.expect)(authors.length).to.be.at.least(1); }); it(`common.news is an object that contains maximum 20 entries`, () => { const news = iopackContent.common.news; - // eslint-disable-next-line @typescript-eslint/no-unused-expressions (0, chai_1.expect)((0, typeguards_1.isObject)(news)).to.be.true; (0, chai_1.expect)(Object.keys(news).length).to.be.at.most(20); }); @@ -206,19 +198,16 @@ function validatePackageFiles(adapterDir) { 'limited', ]); if (iopackContent.common.licenseInformation.type !== 'free') { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions (0, chai_1.expect)(iopackContent.common.licenseInformation.link, 'License link is missing').to.not.be .undefined; } }); it(`common.license should not exist together with common.licenseInformation`, () => { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions (0, chai_1.expect)(iopackContent.common.license, 'common.license must be removed').to.be.undefined; }); } else { it(`common.license must exist without common.licenseInformation`, () => { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions (0, chai_1.expect)(iopackContent.common.license, 'common.licenseInformation (preferred) or common.license (deprecated) must exist').to.not.be.undefined; }); } @@ -238,7 +227,6 @@ function validatePackageFiles(adapterDir) { iopackContent.common.adminUI?.config === 'html' || iopackContent.common.adminUI?.config === 'json' || iopackContent.common.adminUI?.config === 'materialize'; - // eslint-disable-next-line @typescript-eslint/no-unused-expressions (0, chai_1.expect)(hasSupportedUI, 'Unsupported Admin UI, must be html, materialize or JSON config!').to.be .true; }); @@ -248,12 +236,10 @@ function validatePackageFiles(adapterDir) { beforeEach(function () { skipIfInvalid.call(this, 'package.json', 'io-package.json'); }); - // eslint-disable-next-line @typescript-eslint/no-require-imports const packageContent = require(packageJsonPath); - // eslint-disable-next-line @typescript-eslint/no-require-imports const iopackContent = require(ioPackageJsonPath); it('The name matches', () => { - (0, chai_1.expect)('iobroker.' + iopackContent.common.name).to.equal(packageContent.name); + (0, chai_1.expect)(`iobroker.${iopackContent.common.name}`).to.equal(packageContent.name); }); it('The version matches', () => { (0, chai_1.expect)(iopackContent.common.version).to.equal(packageContent.version); diff --git a/build/tests/unit/harness/createMocks.d.ts b/build/tests/unit/harness/createMocks.d.ts index 9c228c3a..5fb3a9ba 100644 --- a/build/tests/unit/harness/createMocks.d.ts +++ b/build/tests/unit/harness/createMocks.d.ts @@ -1,3 +1,4 @@ +import { type MockAdapter } from '../mocks/mockAdapter'; import { MockDatabase } from '../mocks/mockDatabase'; /** * Creates a new set of mocks, including a mock database and a mock adapter. @@ -5,5 +6,5 @@ import { MockDatabase } from '../mocks/mockDatabase'; */ export declare function createMocks(adapterOptions: Partial): { database: MockDatabase; - adapter: any; + adapter: MockAdapter; }; diff --git a/build/tests/unit/harness/createMocks.js b/build/tests/unit/harness/createMocks.js index adfafe52..efa53dfa 100644 --- a/build/tests/unit/harness/createMocks.js +++ b/build/tests/unit/harness/createMocks.js @@ -7,7 +7,6 @@ const mockDatabase_1 = require("../mocks/mockDatabase"); * Creates a new set of mocks, including a mock database and a mock adapter. * To test the startup of an adapter, use `startMockAdapter` instead. */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type function createMocks(adapterOptions) { const databaseMock = new mockDatabase_1.MockDatabase(); const adapterMock = mockAdapter_1.createAdapterMock.bind(undefined)(databaseMock, adapterOptions); diff --git a/build/tests/unit/index.js b/build/tests/unit/index.js index d8240357..44def725 100644 --- a/build/tests/unit/index.js +++ b/build/tests/unit/index.js @@ -7,7 +7,7 @@ exports.testAdapterWithMocks = testAdapterWithMocks; * This is meant to be executed in a mocha context. */ function testAdapterWithMocks(_adapterDir, options = {}) { - describe(`Unit tests`, async () => { + describe(`Unit tests`, () => { // Call the user's tests if (typeof options.defineAdditionalTests === 'function') { options.defineAdditionalTests(); @@ -19,5 +19,6 @@ function testAdapterWithMocks(_adapterDir, options = {}) { console.warn(`from package.json and from your Travis/Github Actions workflow.\u001b[0m`); }); } + return Promise.resolve(); }); } diff --git a/build/tests/unit/mocks/mockAdapter.d.ts b/build/tests/unit/mocks/mockAdapter.d.ts index 7e5eb0e4..07abe903 100644 --- a/build/tests/unit/mocks/mockAdapter.d.ts +++ b/build/tests/unit/mocks/mockAdapter.d.ts @@ -1,6 +1,6 @@ import type { MockDatabase } from './mockDatabase'; -import { MockLogger } from './mockLogger'; -import { Mock } from './tools'; +import { type MockLogger } from './mockLogger'; +import { type Mock } from './tools'; export type MockAdapter = Mock & { readyHandler: ioBroker.ReadyHandler | undefined; objectChangeHandler: ioBroker.ObjectChangeHandler | undefined; diff --git a/build/tests/unit/mocks/mockAdapter.js b/build/tests/unit/mocks/mockAdapter.js index 55afba82..60a82fdc 100644 --- a/build/tests/unit/mocks/mockAdapter.js +++ b/build/tests/unit/mocks/mockAdapter.js @@ -1,7 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createAdapterMock = createAdapterMock; -// @ts-expect-error no types const objects_1 = require("alcalzone-shared/objects"); const sinon_1 = require("sinon"); const mockLogger_1 = require("./mockLogger"); @@ -75,14 +74,16 @@ const implementedMethods = { }; function getCallback(...args) { const lastArg = args[args.length - 1]; - if (typeof lastArg === 'function') + if (typeof lastArg === 'function') { return lastArg; + } } /** Stub implementation which can be promisified */ const asyncEnabledStub = ((...args) => { const callback = getCallback(...args); - if (typeof callback === 'function') + if (typeof callback === 'function') { callback(); + } }); /** * Creates an adapter mock that is connected to a given database mock @@ -115,28 +116,34 @@ function createAdapterMock(db, options = {}) { sendToHost: asyncEnabledStub, idToDCS: (0, sinon_1.stub)(), getObject: ((id, ...args) => { - if (!id.startsWith(ret.namespace)) - id = ret.namespace + '.' + id; + if (!id.startsWith(ret.namespace)) { + id = `${ret.namespace}.${id}`; + } const callback = getCallback(...args); - if (callback) + if (callback) { callback(null, db.getObject(id)); + } }), setObject: ((id, obj, ...args) => { - if (!id.startsWith(ret.namespace)) - id = ret.namespace + '.' + id; + if (!id.startsWith(ret.namespace)) { + id = `${ret.namespace}.${id}`; + } obj._id = id; db.publishObject(obj); const callback = getCallback(...args); - if (callback) + if (callback) { callback(null, { id }); + } }), setObjectNotExists: ((id, obj, ...args) => { - if (!id.startsWith(ret.namespace)) - id = ret.namespace + '.' + id; + if (!id.startsWith(ret.namespace)) { + id = `${ret.namespace}.${id}`; + } const callback = getCallback(...args); if (db.hasObject(id)) { - if (callback) + if (callback) { callback(null, { id }); + } } else { ret.setObject(id, obj, callback); @@ -151,12 +158,14 @@ function createAdapterMock(db, options = {}) { } const callback = getCallback(...args); if (typeof callback === 'function') { - let objects = (0, objects_1.values)(db.getObjects('*')); + let objects = Object.values(db.getObjects('*')); objects = objects.filter(obj => obj.type === search); - if (startkey) + if (startkey) { objects = objects.filter(obj => obj._id >= startkey); - if (endkey) + } + if (endkey) { objects = objects.filter(obj => obj._id <= endkey); + } callback(null, { rows: objects.map(obj => ({ id: obj._id, @@ -168,7 +177,7 @@ function createAdapterMock(db, options = {}) { getObjectList: (({ startkey, endkey, include_docs, }, ...args) => { const callback = getCallback(...args); if (typeof callback === 'function') { - let objects = (0, objects_1.values)(db.getObjects('*')); + let objects = Object.values(db.getObjects('*')); if (startkey) { objects = objects.filter(obj => obj._id >= startkey); } @@ -188,47 +197,55 @@ function createAdapterMock(db, options = {}) { } }), extendObject: ((id, obj, ...args) => { - if (!id.startsWith(ret.namespace)) - id = ret.namespace + '.' + id; + if (!id.startsWith(ret.namespace)) { + id = `${ret.namespace}.${id}`; + } const existing = db.getObject(id) || {}; const target = (0, objects_1.extend)({}, existing, obj); target._id = id; db.publishObject(target); const callback = getCallback(...args); - if (callback) + if (callback) { callback(null, { id: target._id, value: target }, id); + } }), delObject: ((id, ...args) => { - if (!id.startsWith(ret.namespace)) - id = ret.namespace + '.' + id; + if (!id.startsWith(ret.namespace)) { + id = `${ret.namespace}.${id}`; + } db.deleteObject(id); const callback = getCallback(...args); - if (callback) + if (callback) { callback(undefined); + } }), getForeignObject: ((id, ...args) => { const callback = getCallback(...args); - if (callback) + if (callback) { callback(null, db.getObject(id)); + } }), getForeignObjects: ((pattern, ...args) => { const type = typeof args[0] === 'string' ? args[0] : undefined; const callback = getCallback(...args); - if (callback) + if (callback) { callback(null, db.getObjects(pattern, type)); + } }), setForeignObject: ((id, obj, ...args) => { obj._id = id; db.publishObject(obj); const callback = getCallback(...args); - if (callback) + if (callback) { callback(null, { id }); + } }), setForeignObjectNotExists: ((id, obj, ...args) => { const callback = getCallback(...args); if (db.hasObject(id)) { - if (callback) + if (callback) { callback(null, { id }); + } } else { ret.setObject(id, obj, callback); @@ -240,20 +257,23 @@ function createAdapterMock(db, options = {}) { target._id = id; db.publishObject(target); const callback = getCallback(...args); - if (callback) + if (callback) { callback(null, { id: target._id, value: target }, id); + } }), findForeignObject: (0, sinon_1.stub)(), delForeignObject: ((id, ...args) => { db.deleteObject(id); const callback = getCallback(...args); - if (callback) + if (callback) { callback(undefined); + } }), setState: ((id, state, ...args) => { const callback = getCallback(...args); - if (!id.startsWith(ret.namespace)) - id = ret.namespace + '.' + id; + if (!id.startsWith(ret.namespace)) { + id = `${ret.namespace}.${id}`; + } let ack; if (state != null && typeof state === 'object') { ack = !!state.ack; @@ -263,8 +283,9 @@ function createAdapterMock(db, options = {}) { ack = typeof args[0] === 'boolean' ? args[0] : false; } db.publishState(id, { val: state, ack }); - if (callback) + if (callback) { callback(null, id); + } }), setStateChanged: ((id, state, ...args) => { const callback = getCallback(...args); @@ -276,13 +297,15 @@ function createAdapterMock(db, options = {}) { else { ack = typeof args[0] === 'boolean' ? args[0] : false; } - if (!id.startsWith(ret.namespace)) - id = ret.namespace + '.' + id; + if (!id.startsWith(ret.namespace)) { + id = `${ret.namespace}.${id}`; + } if (!db.hasState(id) || db.getState(id).val !== state) { db.publishState(id, { val: state, ack }); } - if (callback) + if (callback) { callback(null, id); + } }), setForeignState: ((id, state, ...args) => { const callback = getCallback(...args); @@ -295,8 +318,9 @@ function createAdapterMock(db, options = {}) { ack = typeof args[0] === 'boolean' ? args[0] : false; } db.publishState(id, { val: state, ack }); - if (callback) + if (callback) { callback(null, id); + } }), setForeignStateChanged: ((id, state, ...args) => { const callback = getCallback(...args); @@ -311,46 +335,56 @@ function createAdapterMock(db, options = {}) { if (!db.hasState(id) || db.getState(id).val !== state) { db.publishState(id, { val: state, ack }); } - if (callback) + if (callback) { callback(null, id); + } }), getState: ((id, ...args) => { - if (!id.startsWith(ret.namespace)) - id = ret.namespace + '.' + id; + if (!id.startsWith(ret.namespace)) { + id = `${ret.namespace}.${id}`; + } const callback = getCallback(...args); - if (callback) + if (callback) { callback(null, db.getState(id)); + } }), getForeignState: ((id, ...args) => { const callback = getCallback(...args); - if (callback) + if (callback) { callback(null, db.getState(id)); + } }), getStates: ((pattern, ...args) => { - if (!pattern.startsWith(ret.namespace)) - pattern = ret.namespace + '.' + pattern; + if (!pattern.startsWith(ret.namespace)) { + pattern = `${ret.namespace}.${pattern}`; + } const callback = getCallback(...args); - if (callback) + if (callback) { callback(null, db.getStates(pattern)); + } }), getForeignStates: ((pattern, ...args) => { const callback = getCallback(...args); - if (callback) + if (callback) { callback(null, db.getStates(pattern)); + } }), delState: ((id, ...args) => { - if (!id.startsWith(ret.namespace)) - id = ret.namespace + '.' + id; + if (!id.startsWith(ret.namespace)) { + id = `${ret.namespace}.${id}`; + } db.deleteState(id); const callback = getCallback(...args); - if (callback) + if (callback) { callback(undefined); + } }), delForeignState: ((id, ...args) => { db.deleteState(id); const callback = getCallback(...args); - if (callback) + if (callback) { callback(undefined); + } }), getHistory: asyncEnabledStub, setBinaryState: asyncEnabledStub, diff --git a/build/tests/unit/mocks/mockAdapterCore.d.ts b/build/tests/unit/mocks/mockAdapterCore.d.ts index 10d27ccd..dcd56e59 100644 --- a/build/tests/unit/mocks/mockAdapterCore.d.ts +++ b/build/tests/unit/mocks/mockAdapterCore.d.ts @@ -1,9 +1,5 @@ -import { MockAdapter } from './mockAdapter'; +import { type MockAdapter } from './mockAdapter'; import type { MockDatabase } from './mockDatabase'; -interface MockAdapterConstructor { - new (nameOrOptions: string | ioBroker.AdapterOptions): MockAdapter; - (nameOrOptions: string | ioBroker.AdapterOptions): MockAdapter; -} export interface MockAdapterCoreOptions { onAdapterCreated?: (adapter: MockAdapter) => void; adapterDir?: string; @@ -11,9 +7,8 @@ export interface MockAdapterCoreOptions { export declare function mockAdapterCore(database: MockDatabase, options?: MockAdapterCoreOptions): { controllerDir: string; getConfig: () => Record; - Adapter: MockAdapterConstructor; - adapter: MockAdapterConstructor; + Adapter: (this: MockAdapter | void, nameOrOptions: string | ioBroker.AdapterOptions) => MockAdapter; + adapter: (this: MockAdapter | void, nameOrOptions: string | ioBroker.AdapterOptions) => MockAdapter; getAbsoluteDefaultDataDir: () => string; getAbsoluteInstanceDataDir: (adapterObject: MockAdapter) => string; }; -export {}; diff --git a/build/tests/unit/mocks/mockAdapterCore.js b/build/tests/unit/mocks/mockAdapterCore.js index b54d62b5..932ea373 100644 --- a/build/tests/unit/mocks/mockAdapterCore.js +++ b/build/tests/unit/mocks/mockAdapterCore.js @@ -37,7 +37,6 @@ exports.mockAdapterCore = mockAdapterCore; const os = __importStar(require("os")); const path = __importStar(require("path")); const mockAdapter_1 = require("./mockAdapter"); -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type function mockAdapterCore(database, options = {}) { /** * The root directory of JS-Controller @@ -65,12 +64,14 @@ function mockAdapterCore(database, options = {}) { } const AdapterConstructor = function (nameOrOptions) { // This needs to be a class with the correct `this` context or the ES6 tests won't work - if (!(this instanceof AdapterConstructor)) + if (!(this instanceof AdapterConstructor)) { return new AdapterConstructor(nameOrOptions); + } const createAdapterMockOptions = typeof nameOrOptions === 'string' ? { name: nameOrOptions } : nameOrOptions; mockAdapter_1.createAdapterMock.bind(this)(database, createAdapterMockOptions); - if (typeof options.onAdapterCreated === 'function') + if (typeof options.onAdapterCreated === 'function') { options.onAdapterCreated(this); + } return this; }; return { diff --git a/build/tests/unit/mocks/mockDatabase.d.ts b/build/tests/unit/mocks/mockDatabase.d.ts index 07ad459e..77fc93de 100644 --- a/build/tests/unit/mocks/mockDatabase.d.ts +++ b/build/tests/unit/mocks/mockDatabase.d.ts @@ -33,19 +33,20 @@ export declare class MockDatabase { /** * Returns a collection of predefined assertions to be used in unit tests * Those include assertions for: - * * State exists - * * State has a certain value, ack flag, object property - * * Object exists - * * Object has a certain common or native part + * - State exists + * - State has a certain value, ack flag, object property + * - Object exists + * - Object has a certain common or native part + * * @param db The mock database to operate on * @param adapter The mock adapter to operate on */ export declare function createAsserts(db: MockDatabase, adapter: MockAdapter): { - assertObjectExists(id: string | string[]): void; - assertStateExists(id: string | string[]): void; - assertStateHasValue(id: string | string[], value: any): void; - assertStateIsAcked(id: string | string[], ack?: boolean): void; - assertStateProperty(id: string | string[], property: string, value: any): void; - assertObjectCommon(id: string | string[], common: ioBroker.ObjectCommon): void; - assertObjectNative(id: string | string[], native: Record): void; + assertObjectExists: (id: string | string[]) => void; + assertStateExists: (id: string | string[]) => void; + assertStateHasValue: (id: string | string[], value: any) => void; + assertStateIsAcked: (id: string | string[], ack: boolean) => void; + assertStateProperty: (id: string | string[], property: string, value: any) => void; + assertObjectCommon: (id: string | string[], common: ioBroker.ObjectCommon) => void; + assertObjectNative: (id: string | string[], native: Record) => void; }; diff --git a/build/tests/unit/mocks/mockDatabase.js b/build/tests/unit/mocks/mockDatabase.js index 85481508..88d6c48d 100644 --- a/build/tests/unit/mocks/mockDatabase.js +++ b/build/tests/unit/mocks/mockDatabase.js @@ -2,7 +2,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.MockDatabase = void 0; exports.createAsserts = createAsserts; -// @ts-expect-error no types const objects_1 = require("alcalzone-shared/objects"); const str2regex_1 = require("../../../lib/str2regex"); const objectTemplate = Object.freeze({ @@ -33,10 +32,12 @@ class MockDatabase { this.clearStates(); } publishObject(obj) { - if (obj._id == null) + if (obj._id == null) { throw new Error('An object must have an ID'); - if (obj.type == null) + } + if (obj.type == null) { throw new Error('An object must have a type'); + } const completeObject = (0, objects_1.extend)({}, objectTemplate, obj); this.objects.set(obj._id, completeObject); } @@ -73,28 +74,30 @@ class MockDatabase { } } hasObject(namespaceOrId, id) { - id = namespaceOrId + (id ? '.' + id : ''); + id = namespaceOrId + (id ? `.${id}` : ''); return this.objects.has(id); } getObject(namespaceOrId, id) { // combines getObject and getForeignObject into one - id = namespaceOrId + (id ? '.' + id : ''); + id = namespaceOrId + (id ? `.${id}` : ''); return this.objects.get(id); } hasState(namespaceOrId, id) { - id = namespaceOrId + (id ? '.' + id : ''); + id = namespaceOrId + (id ? `.${id}` : ''); return this.states.has(id); } getState(namespaceOrId, id) { // combines getObject and getForeignObject into one - id = namespaceOrId + (id ? '.' + id : ''); + id = namespaceOrId + (id ? `.${id}` : ''); return this.states.get(id); } - getObjects(namespaceOrPattern, patternOrType, type) { + getObjects(namespaceOrPattern, + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents + patternOrType, type) { // combines getObjects and getForeignObjects into one let pattern; if (type != null) { - pattern = namespaceOrPattern + (patternOrType ? '.' + patternOrType : ''); + pattern = namespaceOrPattern + (patternOrType ? `.${patternOrType}` : ''); } else if (patternOrType != null) { if (['state', 'channel', 'device'].indexOf(patternOrType) > -1) { @@ -102,35 +105,35 @@ class MockDatabase { pattern = namespaceOrPattern; } else { - pattern = namespaceOrPattern + '.' + patternOrType; + pattern = `${namespaceOrPattern}.${patternOrType}`; } } else { pattern = namespaceOrPattern; } const idRegExp = (0, str2regex_1.str2regex)(pattern); - return (0, objects_1.composeObject)([...this.objects.entries()] + return Object.fromEntries([...this.objects.entries()] .filter(([id]) => idRegExp.test(id)) .filter(([, obj]) => type == null || obj.type === type)); } getStates(pattern) { // combines getStates and getForeignStates into one const idRegExp = (0, str2regex_1.str2regex)(pattern); - return (0, objects_1.composeObject)([...this.states.entries()].filter(([id]) => idRegExp.test(id))); + return Object.fromEntries([...this.states.entries()].filter(([id]) => idRegExp.test(id))); } } exports.MockDatabase = MockDatabase; /** * Returns a collection of predefined assertions to be used in unit tests * Those include assertions for: - * * State exists - * * State has a certain value, ack flag, object property - * * Object exists - * * Object has a certain common or native part + * - State exists + * - State has a certain value, ack flag, object property + * - Object exists + * - Object has a certain common or native part + * * @param db The mock database to operate on * @param adapter The mock adapter to operate on */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type function createAsserts(db, adapter) { function normalizeID(id) { if (Array.isArray(id)) { diff --git a/build/tests/unit/mocks/mockLogger.d.ts b/build/tests/unit/mocks/mockLogger.d.ts index 0a3928f5..8290ff33 100644 --- a/build/tests/unit/mocks/mockLogger.d.ts +++ b/build/tests/unit/mocks/mockLogger.d.ts @@ -1,4 +1,4 @@ -import { Mock } from './tools'; +import { type Mock } from './tools'; export type MockLogger = Mock & { resetMock(): void; resetMockHistory(): void; diff --git a/build/tests/unit/mocks/tools.js b/build/tests/unit/mocks/tools.js index dea384e4..02650f7a 100644 --- a/build/tests/unit/mocks/tools.js +++ b/build/tests/unit/mocks/tools.js @@ -3,23 +3,25 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.doResetHistory = doResetHistory; exports.doResetBehavior = doResetBehavior; exports.stubAndPromisifyImplementedMethods = stubAndPromisifyImplementedMethods; -// @ts-expect-error no types const async_1 = require("alcalzone-shared/async"); const sinon_1 = require("sinon"); function doResetHistory(parent) { for (const prop of Object.keys(parent)) { const val = parent[prop]; - if (val && typeof val.resetHistory === 'function') + if (val && typeof val.resetHistory === 'function') { val.resetHistory(); + } } } function doResetBehavior(parent, implementedMethods) { for (const prop of Object.keys(parent)) { - if (prop in implementedMethods || (prop.endsWith('Async') && prop.slice(0, -5) in implementedMethods)) + if (prop in implementedMethods || (prop.endsWith('Async') && prop.slice(0, -5) in implementedMethods)) { continue; + } const val = parent[prop]; - if (val && typeof val.resetBehavior === 'function') + if (val && typeof val.resetBehavior === 'function') { val.resetBehavior(); + } } } function dontOverwriteThis() { @@ -29,26 +31,27 @@ function stubAndPromisifyImplementedMethods(parent, implementedMethods, allowUse // The methods implemented above are no stubs, but we claimed they are // Therefore hook them up with a real stub for (const methodName of Object.keys(implementedMethods)) { - if (methodName.endsWith('Async')) + if (methodName.endsWith('Async')) { continue; + } const originalMethod = parent[methodName]; const callbackFake = (parent[methodName] = (0, sinon_1.stub)()); callbackFake.callsFake(originalMethod); // Prevent the user from changing the stub's behavior - if (allowUserOverrides.indexOf(methodName) === -1) { + if (!allowUserOverrides.includes(methodName)) { callbackFake.returns = dontOverwriteThis; callbackFake.callsFake = dontOverwriteThis; } // Construct the async fake if there's any const asyncType = implementedMethods[methodName]; - if (asyncType === 'none') + if (asyncType === 'none') { continue; + } const promisifyMethod = asyncType === 'no error' ? async_1.promisifyNoError : async_1.promisify; const asyncFake = (0, sinon_1.stub)().callsFake(promisifyMethod(originalMethod, parent)); parent[`${methodName}Async`] = asyncFake; // Prevent the user from changing the stub's behavior - if (allowUserOverrides.indexOf(methodName) === -1 || - allowUserOverrides.indexOf((methodName + 'Async')) === -1) { + if (!allowUserOverrides.includes(methodName) || !allowUserOverrides.includes(`${methodName}Async`)) { asyncFake.returns = dontOverwriteThis; asyncFake.callsFake = dontOverwriteThis; } diff --git a/src/tests/integration/lib/adapterSetup.ts b/src/tests/integration/lib/adapterSetup.ts index 3e29f46d..e6e07f3e 100644 --- a/src/tests/integration/lib/adapterSetup.ts +++ b/src/tests/integration/lib/adapterSetup.ts @@ -1,6 +1,4 @@ // Add debug logging for tests -// @ts-expect-error no types -import { entries } from 'alcalzone-shared/objects'; import debugModule from 'debug'; import { copy, pathExists, readJSON, remove, unlink, writeJSON } from 'fs-extra'; import * as path from 'path'; @@ -78,7 +76,7 @@ export class AdapterSetup { const packageJsonPath = path.join(this.testDir, 'package.json'); const packageJson = await readJSON(packageJsonPath); packageJson.dependencies[this.adapterFullName] = `file:./${tarballName}`; - for (const [dep, version] of entries(getAdapterDependencies(this.adapterDir))) { + for (const [dep, version] of Object.entries(getAdapterDependencies(this.adapterDir))) { // Don't overwrite the js-controller GitHub dependency with a probably lower one if (dep === 'js-controller') { continue; diff --git a/src/tests/unit/mocks/mockAdapter.ts b/src/tests/unit/mocks/mockAdapter.ts index d666a745..50a8c232 100644 --- a/src/tests/unit/mocks/mockAdapter.ts +++ b/src/tests/unit/mocks/mockAdapter.ts @@ -1,5 +1,4 @@ -// @ts-expect-error no types -import { extend, values } from 'alcalzone-shared/objects'; +import { extend } from 'alcalzone-shared/objects'; import { stub } from 'sinon'; import type { MockDatabase } from './mockDatabase'; import { createLoggerMock, type MockLogger } from './mockLogger'; @@ -196,7 +195,7 @@ export function createAdapterMock( } const callback = getCallback>(...args); if (typeof callback === 'function') { - let objects: ioBroker.Object[] = values(db.getObjects('*')); + let objects: ioBroker.Object[] = Object.values(db.getObjects('*')); objects = objects.filter(obj => obj.type === search); if (startkey) { objects = objects.filter(obj => obj._id >= startkey); @@ -228,7 +227,7 @@ export function createAdapterMock( const callback = getCallback>(...args); if (typeof callback === 'function') { - let objects: ioBroker.Object[] = values(db.getObjects('*')); + let objects: ioBroker.Object[] = Object.values(db.getObjects('*')); if (startkey) { objects = objects.filter(obj => obj._id >= startkey); } diff --git a/src/tests/unit/mocks/mockDatabase.ts b/src/tests/unit/mocks/mockDatabase.ts index 2fe38243..dde4a258 100644 --- a/src/tests/unit/mocks/mockDatabase.ts +++ b/src/tests/unit/mocks/mockDatabase.ts @@ -1,5 +1,4 @@ -// @ts-expect-error no types -import { composeObject, extend } from 'alcalzone-shared/objects'; +import { extend } from 'alcalzone-shared/objects'; import { str2regex } from '../../../lib/str2regex'; import type { MockAdapter } from './mockAdapter'; @@ -135,7 +134,7 @@ export class MockDatabase { const idRegExp = str2regex(pattern); - return composeObject( + return Object.fromEntries( [...this.objects.entries()] .filter(([id]) => idRegExp.test(id)) .filter(([, obj]) => type == null || obj.type === type), @@ -145,7 +144,7 @@ export class MockDatabase { public getStates(pattern: string): Record { // combines getStates and getForeignStates into one const idRegExp = str2regex(pattern); - return composeObject([...this.states.entries()].filter(([id]) => idRegExp.test(id))) as Record< + return Object.fromEntries([...this.states.entries()].filter(([id]) => idRegExp.test(id))) as Record< string, ioBroker.State >; diff --git a/tsconfig.build.json b/tsconfig.build.json index 3f84faf2..963ec044 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -5,7 +5,8 @@ "allowJs": false, "checkJs": false, "noEmit": false, - "declaration": true + "declaration": true, + "moduleResolution": "node16" }, "include": ["src/**/*.ts"], "exclude": ["src/**/*.test.ts"]