diff --git a/.travis.yml b/.travis.yml index ac5343c0ef..3e29dd2de6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,6 +56,16 @@ matrix: - ./scripts/install.android.sh script: - ./scripts/ci.android.sh + # Example Projects + - language: objective-c + os: osx + osx_image: xcode9 + env: + - REACT_NATIVE_VERSION=0.49.3 + install: + - ./scripts/install.ios.sh + script: + - ./scripts/demo-projects.ios.sh branches: only: diff --git a/detox/local-cli/detox-test.js b/detox/local-cli/detox-test.js index d18b39657d..8eeb1b2e2a 100644 --- a/detox/local-cli/detox-test.js +++ b/detox/local-cli/detox-test.js @@ -4,13 +4,12 @@ const program = require('commander'); const path = require('path'); const cp = require('child_process'); program - .option('-r, --runner [runner]', 'Test runner (supports mocha and jest)') - .option('-o, --runner-config [config]', 'Test runner config file', 'mocha.opts') + .option('-o, --runner-config [config]', `Test runner config file, defaults to e2e/mocha.opts for mocha and e2e/config.json' for jest`) .option('-l, --loglevel [value]', 'info, debug, verbose, silly, wss') .option('-c, --configuration [device configuration]', 'Select a device configuration from your defined configurations,' + 'if not supplied, and there\'s only one configuration, detox will default to it') - .option('-r, --reuse', 'Reuse existing installed app (do not delete and re-install) for a faster run.', false) - .option('-u, --cleanup', 'Shutdown simulator when test is over, useful for CI scripts, to make sure detox exists cleanly with no residue', false) + .option('-r, --reuse', 'Reuse existing installed app (do not delete and re-install) for a faster run.') + .option('-u, --cleanup', 'Shutdown simulator when test is over, useful for CI scripts, to make sure detox exists cleanly with no residue') .option('-d, --debug-synchronization [value]', 'When an action/expectation takes a significant amount of time use this option to print device synchronization status. ' + 'The status will be printed if the action takes more than [value]ms to complete') @@ -19,13 +18,10 @@ program .parse(process.argv); const config = require(path.join(process.cwd(), 'package.json')).detox; -const testFolder = config.specs || 'e2e'; - -let runner = config.runner || 'mocha'; -if (program.runner) { - runner = program.runner; -} +const testFolder = config.specs || 'e2e'; +const runner = config['test-runner'] || 'mocha'; +const runnerConfig = program.runnerConfig || config['runner-config'] || getDefaultRunnerConfig(); if (typeof program.debugSynchronization === "boolean") { program.debugSynchronization = 3000; @@ -48,18 +44,19 @@ function runMocha() { const cleanup = program.cleanup ? `--cleanup` : ''; const reuse = program.reuse ? `--reuse` : ''; const artifactsLocation = program.artifactsLocation ? `--artifacts-location ${program.artifactsLocation}` : ''; + const configFile = runnerConfig ? `--opts ${runnerConfig}` : ''; const debugSynchronization = program.debugSynchronization ? `--debug-synchronization ${program.debugSynchronization}` : ''; - const command = `node_modules/.bin/mocha ${testFolder} --opts ${testFolder}/${program.runnerConfig} ${configuration} ${loglevel} ${cleanup} ${reuse} ${debugSynchronization} ${artifactsLocation}`; + const command = `node_modules/.bin/mocha ${testFolder} ${configFile} ${configuration} ${loglevel} ${cleanup} ${reuse} ${debugSynchronization} ${artifactsLocation}`; console.log(command); cp.execSync(command, {stdio: 'inherit'}); } function runJest() { - const command = `node_modules/.bin/jest ${testFolder} --runInBand`; + const configFile = runnerConfig ? `--config=${runnerConfig}` : ''; + const command = `node_modules/.bin/jest ${testFolder} ${configFile} --runInBand`; console.log(command); - cp.execSync(command, { stdio: 'inherit', env: Object.assign({}, process.env, { @@ -72,3 +69,17 @@ function runJest() { }) }); } + + +function getDefaultRunnerConfig() { + let defaultConfig; + switch (runner) { + case 'mocha': + defaultConfig = 'e2e/mocha.opts'; + break; + case 'jest': + defaultConfig = 'e2e/config.json' + } + console.log(`Missing 'runner-config' value in detox config in package.json, using '${defaultConfig}' as default for ${runner}`); + return defaultConfig; +} \ No newline at end of file diff --git a/detox/src/ios/earlgreyapi/GREYMatchers.js b/detox/src/ios/earlgreyapi/GREYMatchers.js index 2b8ab723e5..5786930b81 100644 --- a/detox/src/ios/earlgreyapi/GREYMatchers.js +++ b/detox/src/ios/earlgreyapi/GREYMatchers.js @@ -274,8 +274,8 @@ to a minimum value. } /*Matcher for UI element that is sufficiently visible to the user. EarlGrey considers elements -with visible area percentage greater than @c kElementSufficientlyVisiblePercentage (0.75) -to be sufficiently visible. +that are more than @c kElementSufficientlyVisiblePercentage (75 %) visible areawise to be +sufficiently visible. @return A matcher intialized with a visibility percentage that confirms an element is sufficiently visible. @@ -290,7 +290,7 @@ sufficiently visible. }; } - /*Matcher for UI element that are not visible to the user i.e. has a zero visible area. + /*Matcher for UI element that is not visible to the user at all i.e. it has a zero visible area. @return A matcher for verifying if an element is not visible. */static matcherForNotVisible() { @@ -304,13 +304,11 @@ sufficiently visible. }; } - /*Matcher for UI element that matches EarlGrey's criteria for user interaction currently it must + /*Matcher for UI element that matches EarlGrey's criteria for user interaction. Currently it must satisfy at least the following criteria: - +1) At least a few pixels of the element are visible to the user. +2) The element's accessibility activation point OR the center of the element's visible area +is completely visible. @return A matcher that checks if a UI element is interactable. */static matcherForInteractable() { diff --git a/detox/src/utils/argparse.js b/detox/src/utils/argparse.js index a2f1408d51..c40ddbc9ea 100644 --- a/detox/src/utils/argparse.js +++ b/detox/src/utils/argparse.js @@ -8,6 +8,9 @@ function getArgValue(key) { } else { const camelCasedKey = key.replace(/(\-\w)/g, (m) => m[1].toUpperCase()); value = process.env[camelCasedKey]; + if (value === 'undefined') { + value = undefined; + } } return value; diff --git a/detox/src/utils/argparse.test.js b/detox/src/utils/argparse.test.js index b230418279..cce7ffeb94 100644 --- a/detox/src/utils/argparse.test.js +++ b/detox/src/utils/argparse.test.js @@ -6,6 +6,7 @@ describe('argparse', () => { beforeEach(() => { process.env.fooBar = 'a value'; + process.env.testUndefinedProp = 'undefined'; argparse = require('./argparse'); }); @@ -16,6 +17,10 @@ describe('argparse', () => { it(`existing key should return a result`, () => { expect(argparse.getArgValue('foo-bar')).toBe('a value'); }); + + it('should return undefiend if process.env contain something with a string of undefiend' ,() => { + expect(argparse.getArgValue('testUndefinedProp')).toBe(undefined); + }); }); describe('using arguments', () => { diff --git a/docs/APIRef.Configuration.md b/docs/APIRef.Configuration.md index b3ccb63b23..51100b56c4 100644 --- a/docs/APIRef.Configuration.md +++ b/docs/APIRef.Configuration.md @@ -43,6 +43,7 @@ Detox can either initialize a server using a generated configuration, or can be ``` Session can also be set per configuration: + ```json "detox": { ... @@ -58,19 +59,30 @@ Session can also be set per configuration: } ``` -### Test Root Folder - +### Test Runner Configuration -##### Optional: setting a custom test root folder -Applies when using `detox-cli` by running `detox test` command, default is `e2e`. - +##### Optional: setting a test runner (Mocha as default, Jest is supported) +##### Mocha ```json "detox": { ... - "specs": "path/to/tests" + "test-runner": "mocha" + "runner-config": "path/to/mocha.opts" + "specs": "path/to/tests/root" } ``` +`mocha.opts` refers to `--opts` in https://mochajs.org/#mochaopts +##### Jest +```json + "detox": { + ... + "test-runner": "jest" + "runner-config": "path/to/config.json" + } +``` +`config.json` refers to `--config` in https://facebook.github.io/jest/docs/en/configuration.html +>NOTE: jest tests will run in band, as Detox does not currently supports parallelization. ## detox-cli ### Build Configuration diff --git a/docs/APIRef.DetoxCLI.md b/docs/APIRef.DetoxCLI.md index 5023576cff..9a7bec74af 100644 --- a/docs/APIRef.DetoxCLI.md +++ b/docs/APIRef.DetoxCLI.md @@ -43,8 +43,7 @@ Initiating your test suite | Option| Description | | --- | --- | | -h, --help | output usage information | -| -r, --runner [runner] | Test runner (supports mocha and jest) | -| -o, --runner-config \ | Test runner config file | +| -o, --runner-config \ | Test runner config file, defaults to 'e2e/mocha.opts' for mocha and 'e2e/config.json' for jest | | -l, --loglevel [value] | info, debug, verbose, silly, wss | | -c, -configuration \ | Select a device configuration from your defined figurations,if not supplied, and there's only one configuration, detox will default to it | | -r, --reuse | Reuse existing installed app (do not delete and re-tall) for a faster run. | diff --git a/docs/Guide.Jest.md b/docs/Guide.Jest.md index 18fbc35c98..e0ada524ae 100644 --- a/docs/Guide.Jest.md +++ b/docs/Guide.Jest.md @@ -32,25 +32,29 @@ beforeAll(async () => { afterAll(async () => { await detox.cleanup(); }); +``` -beforeEach(async () => { - await device.reloadReactNative(); -}); +### 4. Configure Detox to run with Jest + +Add a Jest config file `e2e/config.json`: + +```json +{ + "setupTestFrameworkScriptFile" : "./init.js" +} ``` -### 4. Run jest -Add this part to your `package.json`: +In `package.json`: + ```json -"jest": { - "setupTestFrameworkScriptFile": "./e2e/init.js" -}, "scripts": { "test:e2e": "detox test -c ios.sim.debug", "test:e2e:build": "detox build" }, "detox": { - "runner": "jest", + "test-runner": "jest", + "runner-config": "e2e/config.json" ... } ``` @@ -65,4 +69,4 @@ There are some things you should notice: ## How to run unit test and E2E tests in the same project - If you have a setup file for the unit tests pass `./jest/setup` implementation into your unit setup. -- Call your E2E tests like mentioned above +- Call your E2E tests using `detox-cli`: `detox test` diff --git a/examples/demo-react-native-jest/e2e/config.json b/examples/demo-react-native-jest/e2e/config.json new file mode 100644 index 0000000000..13f1a82b7a --- /dev/null +++ b/examples/demo-react-native-jest/e2e/config.json @@ -0,0 +1,3 @@ +{ + "setupTestFrameworkScriptFile" : "./init.js" +} \ No newline at end of file diff --git a/examples/demo-react-native-jest/e2e/init.js b/examples/demo-react-native-jest/e2e/init.js index 429a53580d..32bdb2e81a 100644 --- a/examples/demo-react-native-jest/e2e/init.js +++ b/examples/demo-react-native-jest/e2e/init.js @@ -11,7 +11,3 @@ beforeAll(async () => { afterAll(async () => { await detox.cleanup(); }); - -beforeEach(async () => { - await device.reloadReactNative(); -}); diff --git a/examples/demo-react-native-jest/package.json b/examples/demo-react-native-jest/package.json index 7a4d2a43be..476bb6ff60 100644 --- a/examples/demo-react-native-jest/package.json +++ b/examples/demo-react-native-jest/package.json @@ -1,12 +1,11 @@ { - "name": "DemoReactNativeJest", + "name": "demo-react-native-jest", "version": "0.0.1", "private": true, "scripts": { "start": "node node_modules/react-native/local-cli/cli.js start", - "test": "jest", "test-ios": "detox build --configuration ios.sim.debug && detox test --configuration ios.sim.debug", - "test-android": "detox build --configuration android.emu.debug && detox test --configuration android.emu.debug" + "test-android": "detox build --configuration android.emu.debug && detox test --configuration android.emu.debug -l verbose" }, "dependencies": { "react": "16.0.0-beta.5", @@ -19,11 +18,9 @@ "jest": "21.2.1", "react-test-renderer": "16.0.0-beta.5" }, - "jest": { - "setupTestFrameworkScriptFile": "./e2e/init.js" - }, "detox": { - "runner": "jest", + "test-runner": "jest", + "specs": "e2e", "configurations": { "ios.sim.release": { diff --git a/examples/demo-react-native-jest/rn-cli.config.js b/examples/demo-react-native-jest/rn-cli.config.js new file mode 100644 index 0000000000..804c94f44f --- /dev/null +++ b/examples/demo-react-native-jest/rn-cli.config.js @@ -0,0 +1,7 @@ +const metroBundler = require('metro-bundler'); + +module.exports = { + getBlacklistRE: function() { + return metroBundler.createBlacklist([/test\/.*/]); + } +}; \ No newline at end of file diff --git a/examples/demo-react-native/package.json b/examples/demo-react-native/package.json index 1ddc1fcf19..c81f414706 100644 --- a/examples/demo-react-native/package.json +++ b/examples/demo-react-native/package.json @@ -10,11 +10,13 @@ "react-native": "0.44.0" }, "devDependencies": { - "mocha": "^3.2.0", + "mocha": "^4.0.1", "detox": "^5.0.0" }, "detox": { + "test-runner": "mocha", "specs": "e2e", + "runner-config": "e2e/mocha.opts", "configurations": { "ios.sim.release": { "binaryPath": "ios/build/Build/Products/Release-iphonesimulator/example.app", diff --git a/lerna.json b/lerna.json index bfecd76541..e691baad37 100644 --- a/lerna.json +++ b/lerna.json @@ -7,12 +7,14 @@ "examples/demo-native-android", "examples/demo-native-ios", "examples/demo-react-native", + "examples/demo-react-native-jest", "detox/test", "generation" ], "commands": { "publish": { "ignore": [ + "demo-react-native-jest", "detox-demo-native-android", "detox-demo-native-ios", "detox-demo-react-native", diff --git a/scripts/demo-projects.ios.sh b/scripts/demo-projects.ios.sh new file mode 100755 index 0000000000..7226cf0a7d --- /dev/null +++ b/scripts/demo-projects.ios.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +$(dirname "$0")/bootstrap.sh + +cd examples/demo-react-native +detox build -c ios.sim.release +detox test -c ios.sim.release + +cd examples/demo-react-native-jest +detox build -c ios.sim.release +detox test -c ios.sim.release \ No newline at end of file