diff --git a/README.md b/README.md index 02608ee..971246a 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ The `constants.yaml` file is *per-flavor*. In the file, you can define strings ( buildDir: 'build' sourceDir: 'src' preBuild: 'scripts/main.js' +runUnitTestsScript: 'scripts/runTests.js' mainFlavor: 'flavorName' flavors: { flavorName: { @@ -182,29 +183,42 @@ NOTE: you will need to define a `packageReference` and `packageKey` in `ukor.pro ## Testing -Ukor uses `UnitTestFramework.brs` as part of its unit test runner and test reporting feature. This is especially useful if you plan on having Continuous Integration as part of your workflow. +With ukor you have possibility to use whatever unit test framework you want ! + +Checkout ours [unit testing scripts](http://github.com/willowtreeapps/ukor/tree/master/unit-testing-scripts) ### Setup -First, copy the modified `UnitTestFramework.brs` in [lib/brs/](../master/lib/brs/UnitTestFramework.brs) to your `src/test/source/` folder, so it loads at startup for when testing. +#### 1. Define you custom tests running js script: + +Example: `/scripts/runTests.js` +``` +const child_process = require('child_process') + +const args = process.argv.slice(2) +const ip = args[0] // the roku device ip given as an argument by ukor + +// main function call +main() + +function main() { + child_process.execSync(`npx rooibosC -c ./.rooibosrc.json`) + child_process.execSync(`curl -d '' "http://${ip}:8060/launch/dev?RunTests=true"`) +} +``` -> Note that the original `UnitTestFramework.brs` can be found [here](https://github.com/rokudev/unit-testing-framework) +#### 2. Add the path of your script to ukor.properties.yaml -Next, dd the following snippet in your startup function, after `screen.show()` but before the event loop +``` +... +buildDir: build +sourceDir: src +preBuild: scripts/preBuild.js -```brightscript -if params.RunTests = "true" - runner = TestRunner() - if params.host <> invalid - runner.logger.SetServer(params.host, params.port) - else - runner.logger.SetServerURL(params.url) - end if - ' other setup if needed - runner.run() -end if +runUnitTestsScript: scripts/runTests.js ``` + You should now be able to execute your test suite using the `test` command. ``` @@ -213,11 +227,14 @@ ukor test [flavor] [roku] ### What's happening? -Basically, we modified the rokudev `UnitTestFramework.brs` file to make a `JSON` of test results, and then `POST` that to the specified server. `ukor test [flavor]` builds and deploys the specified flavor with the `test` src folder, and then restarts the channel with parameters to run tests and point the results to the client machine. `ukor` will log the results, and also output results in `xml` and `junit` format to `.out/tests/ukorTests.[xml|junit]`. +After running the test command, ukor will deploy the channel on given device. +Once the channel is deployed, ukor starts to run the `runTests.js` script with the device `ip` as argument. -notes: -- Ukor now copies `UnitTestFramework.brs` with `ukor init`! -- `UnitTestFramework.brs` is now up to date with the rokudev repo! +You can get `ip` inside of `runTests.js` by: +``` +const args = process.argv.slice(2) +const ip = args[0] // the roku device ip given as an argument by ukor +``` # Contributing to Ukor diff --git a/bin/ukor-init.js b/bin/ukor-init.js index 3105901..97000a6 100644 --- a/bin/ukor-init.js +++ b/bin/ukor-init.js @@ -6,7 +6,7 @@ const yaml = require('js-yaml') try { fs.copySync(path.resolve(__dirname, '../lib/sample'), './', { - overwrite: false, + overwrite: false, errorOnExist: false }) log.info('created src directory') @@ -14,31 +14,5 @@ try { log.error('failed to create src directory') log.error(e) } - -const propsFile = './ukor.properties.yaml' -if (!fs.existsSync(propsFile)) { - const project = fs.createWriteStream(propsFile) - const defaults = { - name: path.basename(process.cwd()), - version: '0.0.1', - buildDir: 'build', - sourceDir: 'src', - defaults: { - flavor: 'main' - }, - flavors: { - main: { - src: ['main'] - } - } - } - project.write(yaml.safeDump(defaults), () => { - project.close() - log.info(`created ${propsFile}`) - }) -} else { - log.info(`${propsFile} already exists`) -} log.info('Ukor works greate with Wist, run "wist -i" to generate a default .wistrc.json') - diff --git a/lib/commands/make.js b/lib/commands/make.js index 64599a6..445026a 100644 --- a/lib/commands/make.js +++ b/lib/commands/make.js @@ -1,13 +1,13 @@ -const archiver = require('archiver') const fs = require('fs') const path = require('path') -const log = require('../utils/log') -const properties = require('../utils/properties') const mkdirp = require('mkdirp') const fse = require('fs-extra') const rimraf = require('rimraf') +const log = require('../utils/log') +const archiver = require('archiver') +const utils = require('../utils/utils') const inserter = require('../utils/inserter') -const childProcess = require('child_process') +const properties = require('../utils/properties') const temp = '.ukor' function moveToTempDir(flavor) { @@ -31,18 +31,8 @@ function moveToTempDir(flavor) { } } -function execPreBuildScript() { - if (properties.preBuild != null && properties.preBuild != '') { - log.info('exec ' + properties.preBuild) - let result = childProcess.execFileSync('node', [properties.preBuild]) - if (log.level == 'verbose') { - log.verbose([properties.preBuild, 'exec:', result].join(' ')) - } - } -} - function bundleFlavor(options, callback) { - execPreBuildScript() + if (properties.preBuild) utils.execScript(properties.preBuild) const flavor = options.flavor let flavorSrc = properties.flavors[flavor].src diff --git a/lib/commands/test.js b/lib/commands/test.js index 9a4a41e..f0a071f 100644 --- a/lib/commands/test.js +++ b/lib/commands/test.js @@ -1,169 +1,20 @@ const request = require('request') const log = require('../utils/log') -const path = require('path') const installer = require('./install') -const http = require('http') -const fs = require('fs') -const getIP = require('ip') -const XMLWriter = require('xml-writer') -const mkdirp = require('mkdirp') +const utils = require('../utils/utils') const properties = require('../utils/properties') -function runLogServer(ip, port, timeout, callback) { - let logServer = http.createServer((req, res) => { - let body = [] - req.on('data', data => { - body.push(data) - }) - req.on('end', () => { - try { - body = Buffer.concat(body).toString() - let stats = JSON.parse(body) - writeJunit(stats) - if (stats) { - log.info('------------') - log.info('Test Results') - log.info( - 'Pass: %d | Fail: %d | Crash: %d', - stats.correct, - stats.fail, - stats.crash - ) - log.info('===================================') - stats.suites.forEach(suite => { - log.info('%s: %d tests', suite.name, suite.tests.length) - suite.tests.forEach(test => { - log.info('\t%s: %s', test.name, test.result) - if (test.result != 'Success') { - log.info( - '\t\tError %d: %s', - test.error.code, - test.error.message - ) - } - }) - log.info('---------------------------------') - }) - log.info( - 'Pass: %d | Fail: %d | Crash: %d', - stats.correct, - stats.fail, - stats.crash - ) - if (stats.fail > 0) { - process.exit(-1) - } - } - callback ? callback(stats) : null - res.statusCode = '200' - } catch (e) { - log.error(e.message) - res.statusCode = '403' - } - res.end('ok', () => { - logServer.close() - }) - }) - }) - logServer.listen(parseInt(port), getIP.address(), () => { - log.info('listening for tests on %s:%s', getIP.address(), port) - }) - return logServer +function runTestScript(script, execArgs) { + log.info(`Running ${script} script with args ${execArgs}...`) + utils.execScript(script, execArgs) } -function writeJunit(stats) { - log.info('building junit xml') - const xml = new XMLWriter() - xml.startDocument() - xml - .startElement('testsuites') - .writeAttribute('name', 'rokuTests') - .writeAttribute('tests', '' + stats.total) - .writeAttribute('failures', '' + stats.fail) - stats.suites.forEach(suite => { - xml - .startElement('testsuite') - .writeAttribute('name', suite.name) - .writeAttribute('tests', '' + suite.total) - .writeAttribute('failures', suite.fail) - .writeAttribute('skipped', suite.skipped) - suite.tests.forEach(test => { - xml - .startElement('testcase') - .writeAttribute('name', test.name) - .writeAttribute('time', '' + test.time * 0.001) - if (test.result.toLowerCase() === 'fail') { - xml - .startElement('failure') - .writeAttribute('type', '' + test.error.code) - .text(test.error.message) - .endElement() - } else if (test.result.toLowerCase() === 'skipped') { - xml - .startElement('skipped') - .endElement() - } - xml.endElement() - }) - xml.endElement() +function test(options, callback) { + installer.installTest(options, (ip, success) => { + if (success) properties.runUnitTestsScript ? runTestScript(properties.runUnitTestsScript, [ip]) : log.error(`runUnitTestScript missing from properties`) }) - xml.endElement() - xml.endDocument() - const testOut = `./${properties.sourceDir}/tests` - mkdirp(testOut) - log.info(`writing ${testOut}/ukorTests.xml and .junit`) - fs.writeFileSync(path.join(testOut, 'ukorTests.junit'), xml.toString()) - fs.writeFileSync(path.join(testOut, 'ukorTests.xml'), xml.toString()) - log.info('successfully wrote junit xml') -} - -function runTests(ip, auth, port, callback) { - setTimeout(() => { - let url = - 'http://' + - ip + - ':8060/launch/dev?RunTests=true&host=' + - getIP.address() + - '&port=' + - port - log.debug('starting tests with: %s', url) - request.post( - url, - { - auth: auth - }, - (err, response, body) => { - if (response.statusCode != 200) { - log.error('Error starting tests: %d', response.statusCode) - process.exit(-1) - } else { - log.info('Tests started') - } - callback ? callback(response.statusCode == 200) : null - } - ) - }, 10*1000) //run tests 10 seconds after pushing } module.exports = { - test: (options, callback) => { - installer.installTest(options, (ip, success) => { - log.info('finished install, running tests') - if (!success) return - let port = options.port || '8086' - let server = runLogServer(ip, port, 5 * 60 * 1000, results => { - if (results) { - process.exit(0) - } else { - process.exit(-1) - } - }) - runTests(ip, options.auth, options.port, success => { - if (!success) { - server.close() - process.exit(-1) - } - }) - }) - } + test } diff --git a/lib/sample/src/main/components/AppScene.brs b/lib/sample/src/main/components/AppScene.brs index b07b4f3..fdbd0f7 100644 --- a/lib/sample/src/main/components/AppScene.brs +++ b/lib/sample/src/main/components/AppScene.brs @@ -1,3 +1,3 @@ sub init() - -end sub \ No newline at end of file + ' your code here +end sub diff --git a/lib/sample/src/main/components/AppScene.xml b/lib/sample/src/main/components/AppScene.xml index cd21910..9c4b581 100644 --- a/lib/sample/src/main/components/AppScene.xml +++ b/lib/sample/src/main/components/AppScene.xml @@ -1,9 +1,3 @@ - - - - - -