diff --git a/README.md b/README.md index 45371de..189bc6e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ALEX CLI -A command line interface for running tests and learning experiments with [ALEX](https://github.com/LearnLib/alex) **(v1.6.0)**. +A command line interface for running tests and learning experiments with [ALEX](https://github.com/LearnLib/alex) **(v1.7.0)**. ## Requirements @@ -27,9 +27,9 @@ node alex-cli.js -h ## Usage -1. Export the symbols from ALEX ([see here](http://learnlib.github.io/alex/book/1.4.0/contents/user-manual/symbol-modeling/#export--import)). +1. Export the symbols from ALEX ([see here](http://learnlib.github.io/alex/book/1.7.0/contents/user-manual/symbol-modeling/#export--import)). * When asked, select the option **Export symbols only** -2. Export the tests from ALEX ([see here](http://learnlib.github.io/alex/book/1.4.0/contents/user-manual/testing.html)). +2. Export the tests from ALEX ([see here](http://learnlib.github.io/alex/book/1.7.0/contents/user-manual/testing.html)). Execute `node alex-cli.js -h` to see a complete list of parameters and their descriptions. For examples see the section below. @@ -46,9 +46,8 @@ For examples see the section below. "implicitlyWait": 0, "pageLoadTimeout": 10, "scriptTimeout": 10, - "name": "firefox", - "headless": true, - "xvfbPort": null + "name": "chrome", + "headless": true } } ``` @@ -60,31 +59,32 @@ For examples see the section below. |implicitlyWait|Selenium implicit timeout value| |pageLoadTimeout|Selenium page load timeout value| |scriptTimeout|Selenium script timeout value| -|name|The name of the browser, 'firefox', 'chrome', 'htmlUnit'| +|name|The name of the browser, 'firefox', 'chrome', 'htmlUnit', 'ie', 'safari', 'edge'| |headless|If the browser is run headless. Only for Firefox and Chrome| -|xvfbPort|The port of the virtual display. Only for Firefox and Chrome| ### CLI #### Testing ```bash -node alex-cli.js --uri "http://alex.some-server.de" \ - --target "https://www.google.com" \ +node alex-cli.js --uri "http://localhost:8080" \ + --targets "https://www.google.com,https://www.google.com" \ -a "test" \ -u "admin@alex.example:admin" \ -s "./symbols.json" \ -t "./tests.json" \ -c "./config.testing.json" + --clean-up ``` #### Learning ```bash node alex-cli.js --uri "http://alex.some-server.de" \ - --target "https://www.google.com" \ + --target "https://www.google.com,https://www.google.com" \ -a "learn" \ -u "admin@alex.example:admin" \ -s "./symbols.json" \ -c "./config.learning.json" + --clean-up ``` diff --git a/alex-cli.js b/alex-cli.js index d4f502c..866cfde 100644 --- a/alex-cli.js +++ b/alex-cli.js @@ -37,9 +37,10 @@ function credentials(value) { } program - .version('1.1.0') + .version('1.2.0') .option('--uri [uri]', 'The URI where ALEX is running without trailing \'/\'') - .option('--target [target]', 'The base URL of the target application') + .option('--targets [targets]', 'The base URL and mirrors of the target application as comma separated list') + .option('--clean-up', 'If the project is deleted after a test or learning process') .option('-a, --action [action]', 'What do you want to do with ALEX? [test|learn]') .option('-u, --user [credentials]', 'Credentials with the pattern "email:password"', credentials) .option('-s, --symbols [file]', 'Add the json file that contains all necessary symbols') @@ -152,7 +153,7 @@ function login(user) { */ function createProject() { const createProjectName = () => { - let text = 'cli-'; + let text = 'alex-cli-'; const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < 24; i++) { @@ -162,6 +163,9 @@ function createProject() { return text; }; + const urls = program.targets.split(',').map(u => ({url: u, default: false})); + urls[0].default = true; + return request({ method: 'POST', uri: `${_uri}/projects`, @@ -171,9 +175,7 @@ function createProject() { }, body: JSON.stringify({ name: createProjectName(), - urls: [ - {url: program.target, default: true} - ] + urls: urls }) }); } @@ -223,7 +225,7 @@ function createTests() { function prepareTestCase(tc) { const mapSymbolIds = (steps) => { steps.forEach(step => { - step.pSymbol.symbol = _symbols.find(s => s.name === step.pSymbol.symbolFromName).id; + step.pSymbol.symbol = {id: _symbols.find(s => s.name === step.pSymbol.symbol.name).id}; }); }; @@ -366,8 +368,22 @@ function startTesting() { * @return {Promise<*>} */ function startLearning() { + + // symbolId -> parameterName -> parameter + // needed to set the ids of the parameters by name + const inputParamMap = {}; + _symbols.forEach(sym => { + inputParamMap[sym.id] = inputParamMap[sym.id] == null ? {} : inputParamMap[sym.id]; + sym.inputs.forEach(input => { + inputParamMap[sym.id][input.name] = input; + }); + }); + const mapSymbolIds = (pSymbol) => { - pSymbol.symbol = _symbols.find(s => s.name === pSymbol.symbolFromName).id; + pSymbol.symbol = {id: _symbols.find(s => s.name === pSymbol.symbol.name).id}; + pSymbol.parameterValues.forEach(pv => { + pv.parameter.id = inputParamMap[pSymbol.symbol.id][pv.parameter.name].id; + }) }; _config.symbols.forEach(mapSymbolIds); @@ -387,7 +403,7 @@ function startLearning() { 'Authorization': `Bearer ${_jwt}` }, body: JSON.stringify(_config) - }).then(() => { + }).then(res => { const poll = () => { getLearnerStatus() .then(res1 => { @@ -397,9 +413,7 @@ function startLearning() { .then(res2 => { const data2 = JSON.parse(res2); if (!data2.error) { - console.log('\n'); - console.log(data2.hypothesis); - console.log('\n'); + console.log('\n', data2.hypothesis, '\n'); resolve('The learning process finished.'); } else { reject(data2.errorMessage); @@ -412,7 +426,6 @@ function startLearning() { }) .catch(reject); }; - poll(); }).catch(reject); }); @@ -438,7 +451,7 @@ try { } // validate target URL - if (!program.target) { + if (!program.targets) { throw 'You haven\'t specified the URL of the target application.'; } @@ -521,18 +534,19 @@ try { * @param {Function} fn The callback that processes the message. */ function terminate(message, fn) { - if (_action === 'test') { + if (program.cleanUp) { deleteProject() - .then(() => { - console.log(chalk.white.dim(`Project has been deleted.`)); - fn(message); - }) - .catch(() => fn(message)); + .then(() => { + console.log(chalk.white.dim(`Project has been deleted.`)); + fn(message); + }) + .catch(() => fn(message)); } else { fn(message); } } + // execute the tests / learning process login(_user).then((data) => { _jwt = JSON.parse(data).token; @@ -540,7 +554,7 @@ login(_user).then((data) => { return createProject().then((data) => { _project = JSON.parse(data); - console.log(chalk.white.dim(`Project has been created.`)); + console.log(chalk.white.dim(`Project ${_project.name} has been created.`)); return createSymbols().then((data) => { _symbols = JSON.parse(data); diff --git a/examples/google/config.learning.json b/examples/google/config.learning.json index 5587681..70eb22f 100644 --- a/examples/google/config.learning.json +++ b/examples/google/config.learning.json @@ -1,28 +1,51 @@ { - "symbols": [{ - "symbolFromName": "search", - "parameterValues": [{ - "parameter": { - "type": "input", - "name": "term", - "parameterType": "STRING", - "private": false + "symbols": [ + { + "symbol": { + "name": "search" }, - "value": "potato" - }] - }], + "parameterValues": [ + { + "parameter": { + "type": "input", + "name": "term", + "parameterType": "STRING", + "private": false + }, + "value": "test" + } + ] + } + ], + "urls": [], "maxAmountOfStepsToLearn": -1, - "eqOracle": {"type": "random_word", "minLength": 5, "maxLength": 5, "maxNoOfTests": 5, "seed": 42}, - "algorithm": {"name": "TTT"}, - "resetSymbol": {"symbolFromName": "reset", "parameterValues": []}, + "eqOracle": { + "batchSize": 1, + "minLength": 5, + "maxLength": 5, + "maxNoOfTests": 5, + "seed": 42, + "type": "random_word" + }, + "algorithm": { + "name": "TTT" + }, + "resetSymbol": { + "symbol": { + "name": "reset" + }, + "parameterValues": [] + }, + "comment": null, "driverConfig": { - "width": 1920, - "height": 1080, + "name": "chrome", + "headless": true, + "height": 1661, "implicitlyWait": 0, "pageLoadTimeout": 10, "scriptTimeout": 10, - "name": "firefox", - "headless": true + "width": 2953 }, - "useMQCache": true -} + "useMQCache": true, + "postSymbol": null +} \ No newline at end of file diff --git a/examples/google/config.testing.json b/examples/google/config.testing.json index 3c5b045..381efc4 100644 --- a/examples/google/config.testing.json +++ b/examples/google/config.testing.json @@ -5,8 +5,7 @@ "implicitlyWait": 0, "pageLoadTimeout": 10, "scriptTimeout": 10, - "name": "firefox", - "headless": true, - "xvfbPort": null + "name": "chrome", + "headless": true } } \ No newline at end of file diff --git a/examples/google/symbols.json b/examples/google/symbols.json index 53686b8..34e3dd8 100644 --- a/examples/google/symbols.json +++ b/examples/google/symbols.json @@ -1,5 +1,5 @@ { - "version": "1.6.0", + "version": "1.7.0", "type": "symbols", "symbols": [ { diff --git a/examples/google/tests.json b/examples/google/tests.json index f314a97..5d5893c 100644 --- a/examples/google/tests.json +++ b/examples/google/tests.json @@ -1,5 +1,5 @@ { - "version": "1.6.0", + "version": "1.7.0", "type": "tests", "tests": [ { @@ -12,7 +12,9 @@ "expectedResult": "", "pSymbol": { "parameterValues": [], - "symbolFromName": "reset" + "symbol": { + "name": "reset" + } } }, { @@ -30,7 +32,9 @@ "value": "potato" } ], - "symbolFromName": "search" + "symbol": { + "name": "search" + } } } ], diff --git a/package-lock.json b/package-lock.json index 4881615..0a46461 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "alex-cli", - "version": "1.0.1", + "version": "1.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -66,9 +66,9 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -102,9 +102,9 @@ } }, "commander": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz", - "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==" + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" }, "core-util-is": { "version": "1.0.2", @@ -264,9 +264,9 @@ } }, "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, "mime-db": { "version": "1.36.0", @@ -345,21 +345,21 @@ } }, "request-promise-core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", - "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", + "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", "requires": { - "lodash": "^4.13.1" + "lodash": "^4.17.11" } }, "request-promise-native": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", - "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", + "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", "requires": { - "request-promise-core": "1.1.1", - "stealthy-require": "^1.1.0", - "tough-cookie": ">=2.3.3" + "request-promise-core": "1.1.2", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" } }, "safe-buffer": { @@ -402,11 +402,19 @@ } }, "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "requires": { - "punycode": "^1.4.1" + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + } } }, "tunnel-agent": { diff --git a/package.json b/package.json index 4fd259a..5c7fe74 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "alex-cli", - "version": "1.1.0", + "version": "1.2.0", "description": "A command line interface for executing tests and learning experiments with ALEX", "main": "alex-cli.js", "scripts": { @@ -12,9 +12,9 @@ "author": "Alexander Bainczyk", "license": "Apache-2.0", "dependencies": { - "chalk": "^2.4.1", - "commander": "^2.18.0", + "chalk": "^2.4.2", + "commander": "^2.19.0", "request": "^2.88.0", - "request-promise-native": "^1.0.5" + "request-promise-native": "^1.0.7" } }