Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow giving detox config file as param, with fallbacks #906

Closed
wants to merge 17 commits into from
Empty file modified detox-cli/cli.js
100644 → 100755
Empty file.
11 changes: 7 additions & 4 deletions detox/local-cli/detox-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

const _ = require('lodash');
const program = require('commander');
const configurationResolver = require('../src/utils/ConfigurationResolver').default;
const path = require('path');
const cp = require('child_process');
program.description(`[convenience method] run the command defined in 'configuration.build'`)
.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('--config-path [configPath]',
'Select a device config-file path, if not supplied, detox will default to the package.json, and if not found there, detox will fallback to .detoxrc')
noomorph marked this conversation as resolved.
Show resolved Hide resolved
.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')
.parse(process.argv);

const config = require(path.join(process.cwd(), 'package.json')).detox;
const config = configurationResolver.getDetoxConfiguration(program.configPath);

let buildScript;
if (program.configuration) {
Expand All @@ -26,4 +29,4 @@ if (buildScript) {
cp.execSync(buildScript, {stdio: 'inherit'});
} else {
throw new Error(`Could not find build script in detox.configurations["${program.configuration}"]`);
}
}
10 changes: 7 additions & 3 deletions detox/local-cli/detox-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const _ = require('lodash');
const environment = require('../src/utils/environment');
const buildDefaultArtifactsRootDirpath = require('../src/artifacts/utils/buildDefaultArtifactsRootDirpath');
const DetoxConfigError = require('../src/errors/DetoxConfigError');
const config = require(path.join(process.cwd(), 'package.json')).detox;
const configurationResolver = require('../src/utils/ConfigurationResolver').default;

program
.option('-o, --runner-config [config]',
Expand All @@ -21,6 +21,8 @@ program
'Disable colors in log output')
.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', getDefaultConfiguration())
.option('--config-path [configPath]',
'Select a device config-file path, if not supplied, detox will default to the package.json, and if not found there, detox will fallback to .detoxrc')
.option('-r, --reuse',
'Reuse existing installed app (do not delete and re-install) for a faster run.')
.option('-u, --cleanup',
Expand Down Expand Up @@ -49,6 +51,7 @@ program
'Override the device name specified in a configuration. Useful for running a single build configuration on multiple devices.')
.parse(process.argv);

const config = configurationResolver.getDetoxConfiguration(program.configPath);
program.artifactsLocation = buildDefaultArtifactsRootDirpath(program.configuration, program.artifactsLocation);

clearDeviceRegistryLockFile();
Expand Down Expand Up @@ -204,8 +207,9 @@ function clearDeviceRegistryLockFile() {
}

function getDefaultConfiguration() {
if (_.size(config.configurations) === 1) {
return _.keys(config.configurations)[0];
const file = configurationResolver.getDetoxConfiguration();
if (file && _.size(file.configurations) === 1) {
return _.keys(file.configurations)[0];
}
}

Expand Down
16 changes: 13 additions & 3 deletions detox/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ const exportWrapper = require('./exportWrapper');
const argparse = require('./utils/argparse');
const log = require('./utils/logger').child({ __filename });
const configuration = require('./configuration');
const configurationResolver = require('./utils/ConfigurationResolver').default;

let detox;

function getDeviceConfig(configurations) {
const configurationName = argparse.getArgValue('configuration');
const deviceOverride = argparse.getArgValue('device-name');


const deviceConfig = (!configurationName && _.size(configurations) === 1)
? _.values(configurations)[0]
Expand All @@ -37,9 +39,17 @@ function getDeviceConfig(configurations) {
return deviceConfig;
}

async function initializeDetox(config, params) {
if (!config) {
throw new Error(`No configuration was passed to detox, make sure you pass a config when calling 'detox.init(config)'`);
function getDetoxConfig() {
const configPath = argparse.getArgValue('config-path');

return configurationResolver.getDetoxConfiguration(configPath);
}

async function initializeDetox(configParam, params) {
const config = getDetoxConfig();
if (configParam && !_.isEqual(config, configParam)) {
throw new Error(`Configuration does not match the one set in the detox CLI command,
when providing a config other than the default, call detox.init(null, settings)`);
}

if (!(config.configurations && _.size(config.configurations) >= 1)) {
Expand Down
39 changes: 39 additions & 0 deletions detox/src/utils/ConfigurationResolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const path = require('path');
const fs = require('fs');
const packageJson = 'package.json';
const detoxRc = '.detoxrc';

class ConfigurationResolver {
constructor(cwd = process.cwd()) {
this.cwd = cwd;
}

getDetoxConfiguration(configPath) {
if (configPath) {
return this.loadConfiguration(configPath);
}

const { detox } = this.loadConfiguration(packageJson);
if (detox) {
return detox;
}
return this.loadDetoxrcConfiguration()
}

loadDetoxrcConfiguration() {
var data = fs.readFileSync(this.resolvePath(detoxRc)).toString();
Copy link
Collaborator

@noomorph noomorph Oct 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a specific consideration of why you want to limit .detoxrc to JSON format only?
Personally, I'd love to be able to evaluate it as JS, I'm confident that some people would be happy too and sure they'll find their way how to leverage that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what do you suggest ? this can (and should) be separated and added in a later PR IMO.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this == js evaluated config (.detoxrc.js, detox.config.js, etc)

Copy link
Collaborator

@noomorph noomorph Oct 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rotemmiz , I suggest using plain require() instead of JSON.parse(fs.readFileSync...

return JSON.parse(data);
}

resolvePath(suffix) {
return path.resolve(this.cwd, suffix);
}

loadConfiguration(configPath) {
return require(this.resolvePath(configPath));
}
}

ConfigurationResolver.default = new ConfigurationResolver();

module.exports = ConfigurationResolver;
40 changes: 40 additions & 0 deletions detox/src/utils/ConfigurationResolver.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
describe('getConfigurationFile', () => {
let ConfigurationResolver = require('./ConfigurationResolver');


it(`gets default config from package.json when no configuration path is provided`, async () => {
const fixture = `${__dirname}/__mocks__/configuration-resolver/detoxrc-and-package-json-and-config`
const configResolver = new ConfigurationResolver(fixture);

const config = configResolver.getDetoxConfiguration();
expect(config).toBeDefined();
expect(config['test']).toEqual("package.json");
});

it(`gets config from provided config-path`, () => {
const fixture = `${__dirname}/__mocks__/configuration-resolver/detoxrc-and-package-json-and-config`
const configResolver = new ConfigurationResolver(fixture);

const config = configResolver.getDetoxConfiguration('./detox-config.json');
expect(config).toBeDefined();
expect(config['test']).toEqual("some-config.json");
});

it(`fails to get config from provided config-path when path does not exist`, () => {
const fixture = `${__dirname}/__mocks__/configuration-resolver/detoxrc-and-package-json-and-config`
const configResolver = new ConfigurationResolver(fixture);

const config = configResolver.getDetoxConfiguration('./some-other-config.json');
expect(config).toBeNull();
});


it(`gets config from .detoxrc when no detox in package.json is found and no config is provided`, () => {
const fixture = `${__dirname}/__mocks__/configuration-resolver/detoxrc`
const configResolver = new ConfigurationResolver(fixture);

const config = configResolver.getDetoxConfiguration();
expect(config).toBeDefined();
expect(config['test']).toEqual(".detoxrc");
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"test": ".detoxrc"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"test": "some-config.json"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"detox": {
"test": "package.json"
}
}
Empty file.
2 changes: 2 additions & 0 deletions docs/APIRef.DetoxCLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Initiating your test suite
| -l, --loglevel [value] | Log level: fatal, error, warn, info, verbose, trace |
| --no-color | Disable colors in log output |
| -c, -configuration \<device config\> | Select a device configuration from your defined configurations,if not supplied, and there's only one configuration, detox will default to it |
| --config-path \<configPath\> | Select a device config-file path, if not supplied, detox will default to the package.json, and if not found there, detox will fallback to .detoxrc |
| -r, --reuse | Reuse existing installed app (do not delete and re-tall) for a faster run. |
| -u, --cleanup | Shutdown simulator when test is over, useful for CI ipts, to make sure detox exists cleanly with no residue |
| -d, --debug-synchronization \<value\> | When an action/expectation takes a significant amount time use this option to print device synchronization status. The status will be printed if the ion takes more than [value]ms to complete |
Expand All @@ -71,6 +72,7 @@ Run a command defined in 'configuration.build'
| --- | --- |
| -h, --help | output usage information |
| -c, --configuration \<device config\> | Select a device configuration from your defined configurations,if not supplied, and there's only one configuration, detox will default to it |
| --config-path \<configPath\> | Select a device config-file path, if not supplied, detox will default to the package.json, and if not found there, detox will fallback to .detoxrc |


### run-server
Expand Down
23 changes: 21 additions & 2 deletions docs/Introduction.GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,13 @@ Read the [Jest integration guide](Guide.Jest.md) for more details and gotchas.
npm install mocha --save-dev
```

#### 3. Add Detox config to package.json
#### 3. Add Detox config
By default, detox will search for the configuration the `package.json`.
You can also write your config in a `.detoxrc` file if you prefer to separate between the configuration and the app's `package.json`.
Another option is to create a config file and supply it when calling `detox init`.
Either way the config should look like this:

The basic configuration for Detox should be in your `package.json` file under the `detox` property:
##### in package.json

```json
"detox": {
Expand All @@ -121,6 +125,21 @@ The basic configuration for Detox should be in your `package.json` file under th
}
```

##### in .detoxrc or any config file

```json
{
"configurations": {
"ios.sim.debug": {
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/example.app",
"build": "xcodebuild -project ios/example.xcodeproj -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
"type": "ios.simulator",
"name": "iPhone 7"
}
}
}
```

In the above configuration example, change `example` to your actual project name. Under the key `"binaryPath"`, `example.app` should be `<your_project_name>.app`. Under the key `"build"`, `example.xcodeproj` should be `<your_project_name>.xcodeproj` and `-scheme example` should be `-scheme <your_project_name>`.

For iOS apps in a workspace (eg: CocoaPods) use `-workspace ios/example.xcworkspace` instead of `-project`.
Expand Down