diff --git a/README.md b/README.md index 3b36aec..926688b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Pings specified IP addresses in a defined interval and monitors the results. **This adapter uses Sentry libraries to automatically report exceptions and code errors to the developers.** For more details and for information how to disable the error reporting see [Sentry-Plugin Documentation](https://github.com/ioBroker/plugin-sentry#plugin-sentry)! Sentry reporting is used starting with js-controller 3.0. ## Ping from javascript adapter -You can ping any IP address from the javascript adapter with command: +You can ping any IP address from the JavaScript adapter with command: ``` sendTo('ping.0', 'ping', '192.168.1.1', (res) => { console.log('Result: ' + JSON.stringify(res)); // Result: {"result": {"host": "192.168.1.1", "alive": true, "ms": 250}} diff --git a/package.json b/package.json index 033c452..f1b6e8a 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@alcalzone/release-script-plugin-license": "^3.7.0", "@alcalzone/release-script-plugin-manual-review": "^3.7.0", "@iobroker/adapter-dev": "^1.3.0", - "@iobroker/testing": "^4.1.3", + "@iobroker/legacy-testing": "^1.0.12", "axios": "^1.7.4", "mocha": "^10.7.3", "chai": "^4.5.0" diff --git a/src-admin/src/PingBrowseComponent.jsx b/src-admin/src/PingBrowseComponent.jsx index 06bdc7d..a3e6666 100644 --- a/src-admin/src/PingBrowseComponent.jsx +++ b/src-admin/src/PingBrowseComponent.jsx @@ -138,7 +138,7 @@ class PingBrowseComponent extends ConfigGeneric { const selectable = this.state.ips.filter(ip => !exists.find(item => item.ip === ip)); const allSelected = selectable.length === this.state.selected.length; - return
+ return

{I18n.t('custom_ping_title')}

diff --git a/test/guiHelper.js b/test/guiHelper.js new file mode 100644 index 0000000..07f5899 --- /dev/null +++ b/test/guiHelper.js @@ -0,0 +1,66 @@ +const puppeteer = require('puppeteer'); +const { blue, cyan, red, yellow } = require('colorette'); +const fs = require('node:fs'); + +let rootDir = `${__dirname}/../`; +let gBrowser; +let gPage; + +async function startBrowser(headless) { + const browser = await puppeteer.launch({ + headless: headless === undefined ? false : headless, + args: ['--no-sandbox', '--disable-setuid-sandbox'], + }); + const pages = await browser.pages(); + const timeout = 5000; + pages[0].setDefaultTimeout(timeout); + + await pages[0].setViewport( { + width: 1920, + height: 1080, + deviceScaleFactor: 1, + }); + + gBrowser = browser; + gPage = pages[0]; + + // LOGGING + gPage + .on('console', message => { + const type = message.type().substr(0, 3).toUpperCase(); + const colors = { + LOG: text => text, + ERR: red, + WAR: yellow, + INF: cyan, + }; + + const color = colors[type] || blue; + console.log(color(`[BROWSER] ${type} ${message.text()}`)); + }) + .on('pageerror', ({ message }) => console.log(red(`[BROWSER] ${message}`))); + + await gPage.goto(`http://127.0.0.1:18081`, { waitUntil: 'domcontentloaded' }); + + // Create directory + !fs.existsSync(`${rootDir}tmp/screenshots`) && fs.mkdirSync(`${rootDir}tmp/screenshots`); + await gPage.screenshot({path: `${rootDir}tmp/screenshots/00_starting.png`}); + + return { browser, page: pages[0] }; +} + +async function stopBrowser(browser) { + browser = browser || gBrowser; + await browser.close(); +} + +async function screenshot(page, fileName) { + page = page || gPage; + await page.screenshot({ path: `${rootDir}tmp/screenshots/${fileName}.png` }); +} + +module.exports = { + startBrowser, + stopBrowser, + screenshot, +} diff --git a/test/lib/setup.js b/test/lib/setup.js deleted file mode 100644 index f8aa782..0000000 --- a/test/lib/setup.js +++ /dev/null @@ -1,968 +0,0 @@ -/* jshint -W097 */// jshint strict:false -/*jslint node: true */ -// check if tmp directory exists -const fs = require('fs'); -const path = require('path'); -const child_process = require('child_process'); -const rootDir = path.normalize(__dirname + '/../../'); -const pkg = require(rootDir + 'package.json'); -const debug = typeof v8debug === 'object'; -pkg.main = pkg.main || 'main.js'; - -let JSONLDB; - -let adapterName = path.normalize(rootDir).replace(/\\/g, '/').split('/'); -adapterName = adapterName[adapterName.length - 2]; -let adapterStarted = false; - -function getAppName() { - const parts = __dirname.replace(/\\/g, '/').split('/'); - return parts[parts.length - 3].split('.')[0]; -} - -function loadJSONLDB() { - if (!JSONLDB) { - const dbPath = require.resolve('@alcalzone/jsonl-db', { - paths: [rootDir + 'tmp/node_modules', rootDir, rootDir + 'tmp/node_modules/' + appName + '.js-controller'] - }); - console.log('JSONLDB path: ' + dbPath); - try { - const { JsonlDB } = require(dbPath); - JSONLDB = JsonlDB; - } catch (err) { - console.log('Jsonl require error: ' + err); - } - } -} - -const appName = getAppName().toLowerCase(); - -let objects; -let states; - -let pid = null; - -let systemConfig = null; - -function copyFileSync(source, target) { - - let targetFile = target; - - //if target is a directory a new file with the same name will be created - if (fs.existsSync(target)) { - if ( fs.lstatSync( target ).isDirectory() ) { - targetFile = path.join(target, path.basename(source)); - } - } - - try { - fs.writeFileSync(targetFile, fs.readFileSync(source)); - } - catch (err) { - console.log('file copy error: ' +source +' -> ' + targetFile + ' (error ignored)'); - } -} - -function copyFolderRecursiveSync(source, target, ignore) { - let files = []; - - let base = path.basename(source); - if (base === adapterName) { - base = pkg.name; - } - //check if folder needs to be created or integrated - const targetFolder = path.join(target, base); - if (!fs.existsSync(targetFolder)) { - fs.mkdirSync(targetFolder); - } - - //copy - if (fs.lstatSync(source).isDirectory()) { - files = fs.readdirSync(source); - files.forEach(function (file) { - if (ignore && ignore.indexOf(file) !== -1) { - return; - } - - const curSource = path.join(source, file); - const curTarget = path.join(targetFolder, file); - if (fs.lstatSync(curSource).isDirectory()) { - // ignore grunt files - if (file.indexOf('grunt') !== -1) return; - if (file === 'chai') return; - if (file === 'mocha') return; - copyFolderRecursiveSync(curSource, targetFolder, ignore); - } else { - copyFileSync(curSource, curTarget); - } - }); - } -} - -if (!fs.existsSync(rootDir + 'tmp')) { - fs.mkdirSync(rootDir + 'tmp'); -} - -async function storeOriginalFiles() { - console.log('Store original files...'); - const dataDir = rootDir + 'tmp/' + appName + '-data/'; - - if (fs.existsSync(dataDir + 'objects.json')) { - const f = fs.readFileSync(dataDir + 'objects.json'); - const objects = JSON.parse(f.toString()); - if (objects['system.adapter.admin.0'] && objects['system.adapter.admin.0'].common) { - objects['system.adapter.admin.0'].common.enabled = false; - } - if (objects['system.adapter.admin.1'] && objects['system.adapter.admin.1'].common) { - objects['system.adapter.admin.1'].common.enabled = false; - } - - fs.writeFileSync(dataDir + 'objects.json.original', JSON.stringify(objects)); - console.log('Store original objects.json'); - } - - if (fs.existsSync(dataDir + 'states.json')) { - try { - const f = fs.readFileSync(dataDir + 'states.json'); - fs.writeFileSync(dataDir + 'states.json.original', f); - console.log('Store original states.json'); - } catch (err) { - console.log('no states.json found - ignore'); - } - } - - if (fs.existsSync(dataDir + 'objects.jsonl')) { - loadJSONLDB(); - const db = new JSONLDB(dataDir + 'objects.jsonl'); - await db.open(); - - const admin0 = db.get('system.adapter.admin.0'); - if (admin0) { - if (admin0.common) { - admin0.common.enabled = false; - db.set('system.adapter.admin.0', admin0); - } - } - - const admin1 = db.get('system.adapter.admin.1'); - if (admin1) { - if (admin1.common) { - admin1.common.enabled = false; - db.set('system.adapter.admin.1', admin1); - } - } - await db.close(); - - const f = fs.readFileSync(dataDir + 'objects.jsonl'); - fs.writeFileSync(dataDir + 'objects.jsonl.original', f); - console.log('Store original objects.jsonl'); - } - - if (fs.existsSync(dataDir + 'states.jsonl')) { - const f = fs.readFileSync(dataDir + 'states.jsonl'); - fs.writeFileSync(dataDir + 'states.jsonl.original', f); - console.log('Store original states.jsonl'); - } -} - -function restoreOriginalFiles() { - console.log('restoreOriginalFiles...'); - const dataDir = rootDir + 'tmp/' + appName + '-data/'; - - if (fs.existsSync(dataDir + 'objects.json.original')) { - const f = fs.readFileSync(dataDir + 'objects.json.original'); - fs.writeFileSync(dataDir + 'objects.json', f); - } - if (fs.existsSync(dataDir + 'objects.json.original')) { - const f = fs.readFileSync(dataDir + 'states.json.original'); - fs.writeFileSync(dataDir + 'states.json', f); - } - - if (fs.existsSync(dataDir + 'objects.jsonl.original')) { - const f = fs.readFileSync(dataDir + 'objects.jsonl.original'); - fs.writeFileSync(dataDir + 'objects.jsonl', f); - } - if (fs.existsSync(dataDir + 'objects.jsonl.original')) { - const f = fs.readFileSync(dataDir + 'states.jsonl.original'); - fs.writeFileSync(dataDir + 'states.jsonl', f); - } -} - -async function checkIsAdapterInstalled(cb, counter, customName) { - customName = customName || pkg.name.split('.').pop(); - counter = counter || 0; - const dataDir = rootDir + 'tmp/' + appName + '-data/'; - console.log('checkIsAdapterInstalled...'); - - try { - if (fs.existsSync(dataDir + 'objects.json')) { - const f = fs.readFileSync(dataDir + 'objects.json'); - const objects = JSON.parse(f.toString()); - if (objects['system.adapter.' + customName + '.0']) { - console.log('checkIsAdapterInstalled: ready!'); - setTimeout(function () { - if (cb) cb(); - }, 100); - return; - } else { - console.warn('checkIsAdapterInstalled: still not ready'); - } - } else if (fs.existsSync(dataDir + 'objects.jsonl')) { - loadJSONLDB(); - const db = new JSONLDB(dataDir + 'objects.jsonl'); - try { - await db.open(); - } catch (err) { - if (err.message.includes('Failed to lock DB file')) { - console.log('checkIsAdapterInstalled: DB still opened ...'); - } - throw err; - } - - const obj = db.get('system.adapter.' + customName + '.0'); - await db.close(); - - if (obj) { - console.log('checkIsAdapterInstalled: ready!'); - setTimeout(function () { - if (cb) cb(); - }, 100); - return; - } else { - console.warn('checkIsAdapterInstalled: still not ready'); - } - } else { - console.error('checkIsAdapterInstalled: No objects file found in datadir ' + dataDir); - } - - } catch (err) { - console.log('checkIsAdapterInstalled: catch ' + err); - } - - if (counter > 20) { - console.error('checkIsAdapterInstalled: Cannot install!'); - if (cb) cb('Cannot install'); - } else { - console.log('checkIsAdapterInstalled: wait...'); - setTimeout(function() { - checkIsAdapterInstalled(cb, counter + 1); - }, 1000); - } -} - -async function checkIsControllerInstalled(cb, counter) { - counter = counter || 0; - const dataDir = rootDir + 'tmp/' + appName + '-data/'; - - console.log('checkIsControllerInstalled...'); - try { - if (fs.existsSync(dataDir + 'objects.json')) { - const f = fs.readFileSync(dataDir + 'objects.json'); - const objects = JSON.parse(f.toString()); - if (objects['system.certificates']) { - console.log('checkIsControllerInstalled: installed!'); - setTimeout(function () { - if (cb) cb(); - }, 100); - return; - } - } else if (fs.existsSync(dataDir + 'objects.jsonl')) { - loadJSONLDB(); - const db = new JSONLDB(dataDir + 'objects.jsonl'); - try { - await db.open(); - } catch (err) { - if (err.message.includes('Failed to lock DB file')) { - console.log('checkIsControllerInstalled: DB still opened ...'); - } - throw err; - } - - const obj = db.get('system.certificates'); - await db.close(); - - if (obj) { - console.log('checkIsControllerInstalled: installed!'); - setTimeout(function () { - if (cb) cb(); - }, 100); - return; - } - - } else { - console.error('checkIsControllerInstalled: No objects file found in datadir ' + dataDir); - } - } catch (err) { - - } - - if (counter > 20) { - console.log('checkIsControllerInstalled: Cannot install!'); - if (cb) cb('Cannot install'); - } else { - console.log('checkIsControllerInstalled: wait...'); - setTimeout(function() { - checkIsControllerInstalled(cb, counter + 1); - }, 1000); - } -} - -function installAdapter(customName, cb) { - if (typeof customName === 'function') { - cb = customName; - customName = null; - } - customName = customName || pkg.name.split('.').pop(); - console.log('Install adapter...'); - const startFile = 'node_modules/' + appName + '.js-controller/' + appName + '.js'; - // make first install - if (debug) { - child_process.execSync('node ' + startFile + ' add ' + customName + ' --enabled false', { - cwd: rootDir + 'tmp', - stdio: [0, 1, 2] - }); - checkIsAdapterInstalled(function (error) { - if (error) console.error(error); - console.log('Adapter installed.'); - if (cb) cb(); - }); - } else { - // add controller - const _pid = child_process.fork(startFile, ['add', customName, '--enabled', 'false'], { - cwd: rootDir + 'tmp', - stdio: [0, 1, 2, 'ipc'] - }); - - waitForEnd(_pid, function () { - checkIsAdapterInstalled(function (error) { - if (error) console.error(error); - console.log('Adapter installed.'); - if (cb) cb(); - }); - }); - } -} - -function waitForEnd(_pid, cb) { - if (!_pid) { - cb(-1, -1); - return; - } - _pid.on('exit', function (code, signal) { - if (_pid) { - _pid = null; - cb(code, signal); - } - }); - _pid.on('close', function (code, signal) { - if (_pid) { - _pid = null; - cb(code, signal); - } - }); -} - -function installJsController(cb) { - console.log('installJsController...'); - if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller') || - !fs.existsSync(rootDir + 'tmp/' + appName + '-data')) { - // try to detect appName.js-controller in node_modules/appName.js-controller - // travis CI installs js-controller into node_modules - if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller')) { - console.log('installJsController: no js-controller => copy it from "' + rootDir + 'node_modules/' + appName + '.js-controller"'); - // copy all - // stop controller - console.log('Stop controller if running...'); - let _pid; - if (debug) { - // start controller - _pid = child_process.exec('node ' + appName + '.js stop', { - cwd: rootDir + 'node_modules/' + appName + '.js-controller', - stdio: [0, 1, 2] - }); - } else { - _pid = child_process.fork(appName + '.js', ['stop'], { - cwd: rootDir + 'node_modules/' + appName + '.js-controller', - stdio: [0, 1, 2, 'ipc'] - }); - } - - waitForEnd(_pid, function () { - // copy all files into - if (!fs.existsSync(rootDir + 'tmp')) fs.mkdirSync(rootDir + 'tmp'); - if (!fs.existsSync(rootDir + 'tmp/node_modules')) fs.mkdirSync(rootDir + 'tmp/node_modules'); - - if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')){ - console.log('Copy js-controller...'); - copyFolderRecursiveSync(rootDir + 'node_modules/' + appName + '.js-controller', rootDir + 'tmp/node_modules/'); - } - - console.log('Setup js-controller...'); - let __pid; - if (debug) { - // start controller - _pid = child_process.exec('node ' + appName + '.js setup first --console', { - cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller', - stdio: [0, 1, 2] - }); - } else { - __pid = child_process.fork(appName + '.js', ['setup', 'first', '--console'], { - cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller', - stdio: [0, 1, 2, 'ipc'] - }); - } - waitForEnd(__pid, function () { - checkIsControllerInstalled(function () { - // change ports for object and state DBs - const config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json'); - config.objects.port = 19001; - config.states.port = 19000; - - // TEST WISE! - //config.objects.type = 'jsonl'; - //config.states.type = 'jsonl'; - fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2)); - console.log('Setup finished.'); - - copyAdapterToController(); - - installAdapter(async function () { - await storeOriginalFiles(); - if (cb) cb(true); - }); - }); - }); - }); - } else { - // check if port 9000 is free, else admin adapter will be added to running instance - const client = new require('net').Socket(); - client.on('error', () => {}); - client.connect(9000, '127.0.0.1', function() { - console.error('Cannot initiate fisrt run of test, because one instance of application is running on this PC. Stop it and repeat.'); - process.exit(0); - }); - - setTimeout(function () { - client.destroy(); - if (!fs.existsSync(rootDir + 'tmp/node_modules/' + appName + '.js-controller')) { - console.log('installJsController: no js-controller => install dev build from npm'); - - child_process.execSync('npm install ' + appName + '.js-controller@dev --prefix ./ --production', { - cwd: rootDir + 'tmp/', - stdio: [0, 1, 2] - }); - } else { - console.log('Setup js-controller...'); - let __pid; - if (debug) { - // start controller - child_process.exec('node ' + appName + '.js setup first', { - cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller', - stdio: [0, 1, 2] - }); - } else { - child_process.fork(appName + '.js', ['setup', 'first'], { - cwd: rootDir + 'tmp/node_modules/' + appName + '.js-controller', - stdio: [0, 1, 2, 'ipc'] - }); - } - } - - // let npm install admin and run setup - checkIsControllerInstalled(function () { - let _pid; - - if (fs.existsSync(rootDir + 'node_modules/' + appName + '.js-controller/' + appName + '.js')) { - _pid = child_process.fork(appName + '.js', ['stop'], { - cwd: rootDir + 'node_modules/' + appName + '.js-controller', - stdio: [0, 1, 2, 'ipc'] - }); - } - - waitForEnd(_pid, function () { - // change ports for object and state DBs - const config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json'); - config.objects.port = 19001; - config.states.port = 19000; - - // TEST WISE! - //config.objects.type = 'jsonl'; - //config.states.type = 'jsonl'; - fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/' + appName + '.json', JSON.stringify(config, null, 2)); - - copyAdapterToController(); - - installAdapter(async function () { - await storeOriginalFiles(); - if (cb) cb(true); - }); - }); - }); - }, 1000); - } - } else { - setTimeout(function () { - console.log('installJsController: js-controller installed'); - if (cb) cb(false); - }, 0); - } -} - -function copyAdapterToController() { - console.log('Copy adapter...'); - // Copy adapter to tmp/node_modules/appName.adapter - copyFolderRecursiveSync(rootDir, rootDir + 'tmp/node_modules/', ['.idea', 'test', 'tmp', '.git', appName + '.js-controller']); - console.log('Adapter copied.'); -} - -function clearControllerLog() { - const dirPath = rootDir + 'tmp/log'; - let files; - try { - if (fs.existsSync(dirPath)) { - console.log('Clear controller log...'); - files = fs.readdirSync(dirPath); - } else { - console.log('Create controller log directory...'); - files = []; - fs.mkdirSync(dirPath); - } - } catch(e) { - console.error('Cannot read "' + dirPath + '"'); - return; - } - if (files.length > 0) { - try { - for (let i = 0; i < files.length; i++) { - const filePath = dirPath + '/' + files[i]; - fs.unlinkSync(filePath); - } - console.log('Controller log cleared'); - } catch (err) { - console.error('cannot clear log: ' + err); - } - } -} - -function clearDB() { - const dirPath = rootDir + 'tmp/iobroker-data/sqlite'; - let files; - try { - if (fs.existsSync(dirPath)) { - console.log('Clear sqlite DB...'); - files = fs.readdirSync(dirPath); - } else { - console.log('Create controller log directory...'); - files = []; - fs.mkdirSync(dirPath); - } - } catch(e) { - console.error('Cannot read "' + dirPath + '"'); - return; - } - if (files.length > 0) { - try { - for (let i = 0; i < files.length; i++) { - const filePath = dirPath + '/' + files[i]; - fs.unlinkSync(filePath); - } - console.log('Clear sqlite DB'); - } catch (err) { - console.error('cannot clear DB: ' + err); - } - } -} - -function setupController(cb) { - installJsController(async function (isInited) { - try { - clearControllerLog(); - clearDB(); - - if (!isInited) { - restoreOriginalFiles(); - copyAdapterToController(); - } - // read system.config object - const dataDir = rootDir + 'tmp/' + appName + '-data/'; - - if (fs.existsSync(dataDir + 'objects.json')) { - let objs; - try { - objs = fs.readFileSync(dataDir + 'objects.json'); - objs = JSON.parse(objs); - } catch (e) { - console.log('ERROR reading/parsing system configuration. Ignore'); - objs = {'system.config': {}}; - } - if (!objs || !objs['system.config']) { - objs = {'system.config': {}}; - } - - systemConfig = objs['system.config']; - if (cb) cb(objs['system.config']); - } else if (fs.existsSync(dataDir + 'objects.jsonl')) { - loadJSONLDB(); - const db = new JSONLDB(dataDir + 'objects.jsonl'); - await db.open(); - - let config = db.get('system.config'); - systemConfig = config || {}; - - await db.close(); - - if (cb) cb(systemConfig); - } else { - console.error('read SystemConfig: No objects file found in datadir ' + dataDir); - } - } catch (err) { - console.error('setupController: ' + err); - } - }); -} - -async function getSecret() { - var dataDir = rootDir + 'tmp/' + appName + '-data/'; - - if (systemConfig) { - return systemConfig.native.secret; - } - if (fs.existsSync(dataDir + 'objects.json')) { - let objs; - try { - objs = fs.readFileSync(dataDir + 'objects.json'); - objs = JSON.parse(objs); - } - catch (e) { - console.warn("Could not load secret. Reason: " + e); - return null; - } - if (!objs || !objs['system.config']) { - objs = {'system.config': {}}; - } - - return objs['system.config'].native.secre; - } else if (fs.existsSync(dataDir + 'objects.jsonl')) { - loadJSONLDB(); - const db = new JSONLDB(dataDir + 'objects.jsonl'); - await db.open(); - - let config = db.get('system.config'); - config = config || {}; - - await db.close(); - - return config.native.secret; - } else { - console.error('read secret: No objects file found in datadir ' + dataDir); - } - -} - -function encrypt (key, value) { - var result = ''; - for (var i = 0; i < value.length; ++i) { - result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i)); - } - return result; -} - -function startAdapter(objects, states, callback) { - if (adapterStarted) { - console.log('Adapter already started ...'); - if (callback) callback(objects, states); - return; - } - adapterStarted = true; - console.log('startAdapter...'); - if (fs.existsSync(rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main)) { - try { - if (debug) { - // start controller - pid = child_process.exec('node node_modules/' + pkg.name + '/' + pkg.main + ' --console silly', { - cwd: rootDir + 'tmp', - stdio: [0, 1, 2] - }); - } else { - // start controller - pid = child_process.fork('node_modules/' + pkg.name + '/' + pkg.main, ['--console', 'silly'], { - cwd: rootDir + 'tmp', - stdio: [0, 1, 2, 'ipc'] - }); - } - } catch (error) { - console.error(JSON.stringify(error)); - } - } else { - console.error('Cannot find: ' + rootDir + 'tmp/node_modules/' + pkg.name + '/' + pkg.main); - } - if (callback) callback(objects, states); -} - -function startController(isStartAdapter, onObjectChange, onStateChange, callback) { - if (typeof isStartAdapter === 'function') { - callback = onStateChange; - onStateChange = onObjectChange; - onObjectChange = isStartAdapter; - isStartAdapter = true; - } - - if (onStateChange === undefined) { - callback = onObjectChange; - onObjectChange = undefined; - } - - if (pid) { - console.error('Controller is already started!'); - } else { - console.log('startController...'); - try { - const config = require(rootDir + 'tmp/' + appName + '-data/' + appName + '.json'); - - adapterStarted = false; - let isObjectConnected; - let isStatesConnected; - - // rootDir + 'tmp/node_modules - const objPath = require.resolve(`@iobroker/db-objects-${config.objects.type}`, { - paths: [ rootDir + 'tmp/node_modules', rootDir, rootDir + 'tmp/node_modules/' + appName + '.js-controller'] - }); - console.log('Objects Path: ' + objPath); - const Objects = require(objPath).Server; - objects = new Objects({ - connection: { - 'type': config.objects.type, - 'host': '127.0.0.1', - 'port': 19001, - 'user': '', - 'pass': '', - 'noFileCache': false, - 'connectTimeout': 2000 - }, - logger: { - silly: function (msg) { - console.log(msg); - }, - debug: function (msg) { - console.log(msg); - }, - info: function (msg) { - console.log(msg); - }, - warn: function (msg) { - console.warn(msg); - }, - error: function (msg) { - console.error(msg); - } - }, - connected: function () { - isObjectConnected = true; - if (isStatesConnected) { - console.log('startController: started!'); - if (isStartAdapter) { - startAdapter(objects, states, callback); - } else { - if (callback) { - callback(objects, states); - callback = null; - } - } - } - }, - change: onObjectChange - }); - - // Just open in memory DB itself - const statePath = require.resolve(`@iobroker/db-states-${config.states.type}`, { - paths: [ rootDir + 'tmp/node_modules', rootDir, rootDir + 'tmp/node_modules/' + appName + '.js-controller'] - }); - console.log('States Path: ' + statePath); - const States = require(statePath).Server; - states = new States({ - connection: { - type: config.states.type, - host: '127.0.0.1', - port: 19000, - options: { - auth_pass: null, - retry_max_delay: 15000 - } - }, - logger: { - silly: function (msg) { - console.log(msg); - }, - debug: function (msg) { - console.log(msg); - }, - info: function (msg) { - console.log(msg); - }, - warn: function (msg) { - console.log(msg); - }, - error: function (msg) { - console.log(msg); - } - }, - connected: function () { - isStatesConnected = true; - if (isObjectConnected) { - console.log('startController: started!!'); - if (isStartAdapter) { - startAdapter(objects, states, callback); - } else { - if (callback) { - callback(objects, states); - callback = null; - } - } - } - }, - change: onStateChange - }); - } catch (err) { - console.log(err); - } - } -} - -function stopAdapter(cb) { - if (!pid) { - console.error('Controller is not running!'); - if (cb) { - setTimeout(function () { - cb(false); - }, 0); - } - } else { - adapterStarted = false; - pid.on('exit', function (code, signal) { - if (pid) { - console.log('child process terminated due to receipt of signal ' + signal); - if (cb) cb(); - pid = null; - } - }); - - pid.on('close', function (code, signal) { - if (pid) { - if (cb) cb(); - pid = null; - } - }); - - pid.kill('SIGTERM'); - } -} - -function _stopController() { - if (objects) { - objects.destroy(); - objects = null; - } - if (states) { - states.destroy(); - states = null; - } -} - -function stopController(cb) { - let timeout; - if (objects) { - console.log('Set system.adapter.' + pkg.name + '.0'); - objects.setObject('system.adapter.' + pkg.name + '.0', { - common:{ - enabled: false - } - }); - } - - stopAdapter(function () { - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - - _stopController(); - - if (cb) { - cb(true); - cb = null; - } - }); - - timeout = setTimeout(function () { - timeout = null; - console.log('child process NOT terminated'); - - _stopController(); - - if (cb) { - cb(false); - cb = null; - } - pid = null; - }, 5000); -} - -// Setup the adapter -async function setAdapterConfig(common, native, instance) { - const id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0); - if (fs.existsSync(rootDir + 'tmp/' + appName + '-data/objects.json')) { - const objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString()); - if (common) objects[id].common = common; - if (native) objects[id].native = native; - fs.writeFileSync(rootDir + 'tmp/' + appName + '-data/objects.json', JSON.stringify(objects)); - } else if (fs.existsSync(rootDir + 'tmp/' + appName + '-data/objects.jsonl')) { - loadJSONLDB(); - const db = new JSONLDB(rootDir + 'tmp/' + appName + '-data/objects.jsonl'); - await db.open(); - - let obj = db.get(id); - if (common) obj.common = common; - if (native) obj.native = native; - db.set(id, obj); - - await db.close(); - } else { - console.error('setAdapterConfig: No objects file found in datadir ' + rootDir + 'tmp/' + appName + '-data/'); - } -} - -// Read config of the adapter -async function getAdapterConfig(instance) { - const id = 'system.adapter.' + adapterName.split('.').pop() + '.' + (instance || 0); - if (fs.existsSync(rootDir + 'tmp/' + appName + '-data/objects.json')) { - const objects = JSON.parse(fs.readFileSync(rootDir + 'tmp/' + appName + '-data/objects.json').toString()); - return objects[id]; - } else if (fs.existsSync(rootDir + 'tmp/' + appName + '-data/objects.jsonl')) { - loadJSONLDB(); - const db = new JSONLDB(rootDir + 'tmp/' + appName + '-data/objects.jsonl'); - await db.open(); - - let obj = db.get(id); - - await db.close(); - return obj; - } else { - console.error('getAdapterConfig: No objects file found in datadir ' + rootDir + 'tmp/' + appName + '-data/'); - } -} - -if (typeof module !== undefined && module.parent) { - module.exports.getAdapterConfig = getAdapterConfig; - module.exports.setAdapterConfig = setAdapterConfig; - module.exports.startController = startController; - module.exports.stopController = stopController; - module.exports.setupController = setupController; - module.exports.stopAdapter = stopAdapter; - module.exports.startAdapter = startAdapter; - module.exports.installAdapter = installAdapter; - module.exports.appName = appName; - module.exports.adapterName = adapterName; - module.exports.adapterStarted = adapterStarted; - module.exports.getSecret = getSecret; - module.exports.encrypt = encrypt; -} diff --git a/test/testAdapter.gui.js b/test/testAdapter.gui.js new file mode 100644 index 0000000..fceb442 --- /dev/null +++ b/test/testAdapter.gui.js @@ -0,0 +1,34 @@ +const engineHelper = require('./engineHelper'); +const guiHelper = require('./guiHelper'); + +let gPage; + +async function screenshot(page, fileName) { + page = page || gPage; + await page.screenshot({path: `${__dirname}/../tmp/screenshots/${fileName}.png`}); +} + +describe('admin-gui', () => { + before(async function () { + this.timeout(240_000); + + // install js-controller, web and vis-2-beta + await engineHelper.startIoBroker(); + const { page } = await guiHelper.startBrowser(process.env.CI === 'true'); + gPage = page; + }); + + it('Start admin', async function () { + this.timeout(120_000); + await gPage.waitForSelector('a[href="/#easy"]', { timeout: 120_000 }); + await screenshot(gPage, '00_started'); + }); + + after(async function () { + this.timeout(5_000); + await guiHelper.stopBrowser(); + console.log('BROWSER stopped'); + await engineHelper.stopIoBroker(); + console.log('ioBroker stopped'); + }); +}); diff --git a/test/testFunctions.js b/test/testFunctions.js index ed83975..8d87459 100644 --- a/test/testFunctions.js +++ b/test/testFunctions.js @@ -1,11 +1,14 @@ const expect = require('chai').expect; -const setup = require('./lib/setup'); +const fs = require('node:fs'); +const setup = require('@iobroker/legacy-testing'); +const guiHelper = require('./guiHelper'); let objects = null; let states = null; let onStateChanged = null; const onObjectChanged = null; -const hostname = require('os').hostname(); +const hostname = require('node:os').hostname(); +let gPage; function checkConnectionOfAdapter(cb, counter) { counter = counter || 0; @@ -24,31 +27,69 @@ function checkConnectionOfAdapter(cb, counter) { }); } -function checkValueOfState(id, value, cb, counter) { - counter = counter || 0; - if (counter > 20) { - cb && cb('Cannot check value Of State ' + id); - return; +function deleteFoldersRecursive(path) { + if (path.endsWith('/')) { + path = path.substring(0, path.length - 1); + } + if (fs.existsSync(path)) { + const files = fs.readdirSync(path); + for (const file of files) { + const curPath = `${path}/${file}`; + const stat = fs.statSync(curPath); + if (stat.isDirectory()) { + deleteFoldersRecursive(curPath); + fs.rmdirSync(curPath); + } else { + fs.unlinkSync(curPath); + } + } } +} - states.getState(id, (err, state) => { - if (err) console.error(err); - if (value === null && !state) { - cb && cb(); - } else - if (state && (value === undefined || state.val === value)) { +function checkIsAdminStarted(states, cb, counter) { + counter = counter === undefined ? 20 : counter; + if (counter === 0) { + return cb && cb(`Cannot check value of State system.adapter.admin.0.alive`); + } + + states.getState('system.adapter.admin.0.alive', (err, state) => { + console.log(`[${counter}]Check if admin is started "system.adapter.admin.0.alive" = ${JSON.stringify(state)}`); + err && console.error(err); + if (state?.val) { cb && cb(); } else { - setTimeout(() => checkValueOfState(id, value, cb, counter + 1), 500); + setTimeout(() => checkIsAdminStarted(states, cb, counter - 1), 500); } }); } +function checkIsAdminStartedAsync(states, counter) { + return new Promise(resolve => checkIsAdminStarted(states, resolve, counter)); +} + +async function screenshot(page, fileName) { + page = page || gPage; + await page.screenshot({ path: `${__dirname}/../tmp/screenshots/${fileName}.png` }); +} + describe('Test PING', function () { before('Test PING: Start js-controller', function (_done) { - this.timeout(600000); // because of first install from npm + this.timeout(600000); // because of the first installation from npm + + setup.setupController(['ping', 'admin'], async systemConfig => { + // disable statistics and set license accepted + systemConfig.common.licenseConfirmed = true; + systemConfig.common.diag = 'none'; + await setup.setObject('system.config', systemConfig); + + // lets the admin adapter start on port 18081 + const adminConfig = await setup.getAdapterConfig(0, 'admin'); + if (adminConfig?.common) { + adminConfig.native.port = 18081; + adminConfig.common.enabled = true; + await setup.setAdapterConfig(adminConfig.common, adminConfig.native, 0, 'admin'); + } - setup.setupController(async () => { const config = await setup.getAdapterConfig(); // enable adapter config.common.enabled = true; @@ -78,10 +119,14 @@ describe('Test PING', function () { true, (id, obj) => onObjectChanged && onObjectChanged(id, obj), (id, state) => onStateChanged && onStateChanged(id, state), - (_objects, _states) => { + async (_objects, _states) => { objects = _objects; states = _states; states.subscribe('*'); + setup.startCustomAdapter('admin', 0); + await checkIsAdminStartedAsync(states); + const { page } = await guiHelper.startBrowser(process.env.CI === 'true'); + gPage = page; _done(); }); }); @@ -94,13 +139,13 @@ describe('Test PING', function () { it('Test PING: check creation of state', done => { setTimeout(() => { // if object exists - objects.getObject('ping.0.' + hostname + '.192_168_168_168', (err, obj) => { + objects.getObject(`ping.0.${hostname}.192_168_168_168`, (err, obj) => { expect(err).to.be.not.ok; expect(obj).to.be.ok; - objects.getObject('ping.0.' + hostname + '.google_com', (err, obj) => { + objects.getObject(`ping.0.${hostname}.google_com`, (err, obj) => { expect(err).to.be.not.ok; expect(obj).to.be.ok; - objects.getObject('ping.0.' + hostname + '.127_0_0_1', (err, obj) => { + objects.getObject(`ping.0.${hostname}.127_0_0_1`, (err, obj) => { expect(err).to.be.not.ok; expect(obj).to.be.ok; setTimeout(done, 5000); @@ -111,7 +156,7 @@ describe('Test PING', function () { }).timeout(20000); it('Test PING: if localhost alive', done => { - const sID = 'ping.0.' + hostname + '.127_0_0_1'; + const sID = `ping.0.${hostname}.127_0_0_1`; states.getState(sID, (err, state) => { expect(err).to.be.not.ok; @@ -125,7 +170,7 @@ describe('Test PING', function () { } }; } else { - console.log(sID + ': ' + JSON.stringify(state)); + console.log(`${sID}: ${JSON.stringify(state)}`); expect(state.val).to.be.true; done(); } @@ -133,7 +178,7 @@ describe('Test PING', function () { }).timeout(8000); it('Test PING: if google alive', done => { - const sID = 'ping.0.' + hostname + '.google_com'; + const sID = `ping.0.${hostname}.google_com`; if (!((process.env.APPVEYOR && process.env.APPVEYOR === 'True') || (process.env.TRAVIS && process.env.TRAVIS === 'true'))) { done(); @@ -144,7 +189,7 @@ describe('Test PING', function () { expect(err).to.be.not.ok; if (!state || !state.ack) { onStateChanged = function (id, state) { - console.log(id + ': ' + JSON.stringify(state)); + console.log(`${id}: ${JSON.stringify(state)}`); if (id === sID) { onStateChanged = null; expect(state.val).to.be.true; @@ -152,7 +197,7 @@ describe('Test PING', function () { } }; } else { - console.log(sID + ': ' + JSON.stringify(state)); + console.log(`${sID}: ${JSON.stringify(state)}`); expect(state.val).to.be.true; done(); } @@ -160,7 +205,7 @@ describe('Test PING', function () { }).timeout(1000); it('Test PING: if not_exist not alive', done => { - const sID = 'ping.0.' + hostname + '.192_168_168_168'; + const sID = `ping.0.${hostname}.192_168_168_168`; if (!((process.env.APPVEYOR && process.env.APPVEYOR === 'True') || (process.env.TRAVIS && process.env.TRAVIS === 'true'))) { done(); @@ -171,7 +216,7 @@ describe('Test PING', function () { expect(err).to.be.not.ok; if (!state || !state.ack) { onStateChanged = function (id, state) { - console.log(id + ': ' + JSON.stringify(state)); + console.log(`${id}: ${JSON.stringify(state)}`); if (id === sID) { onStateChanged = null; expect(state.val).to.be.false; @@ -179,19 +224,36 @@ describe('Test PING', function () { } }; } else { - console.log(sID + ': ' + JSON.stringify(state)); + console.log(`${sID}: ${JSON.stringify(state)}`); expect(state.val).to.be.false; done(); } }); }).timeout(3000); + it('Start admin', async function () { + deleteFoldersRecursive(`${__dirname}/../tmp/screenshots`); + this.timeout(120_000); + await gPage.waitForSelector('a[href="/#easy"]', { timeout: 120_000 }); + await screenshot(gPage, '00_started'); + }); + + it('Test GUI', async () => { + await gPage.goto(`http://127.0.0.1:18081/#tab-instances/config/system.adapter.ping.0`, { waitUntil: 'domcontentloaded' }); + await gPage.waitForSelector('.ping_custom', { timeout: 20_000 }); + await screenshot(gPage, '01_instance'); + }).timeout(10000) + after('Test PING: Stop js-controller', function (done) { this.timeout(6000); + setup.stopCustomAdapter('admin', 0) + .then(async () => { + await guiHelper.stopBrowser(); - setup.stopController(normalTerminated => { - console.log('Adapter normal terminated: ' + normalTerminated); - done(); - }); + setup.stopController(normalTerminated => { + console.log(`Adapter normal terminated: ${normalTerminated}`); + done(); + }); + }); }); });