Skip to content
This repository was archived by the owner on Jan 19, 2021. It is now read-only.

Commit 74dc43f

Browse files
authored
Merge pull request #17 from TestArmada/more-sauce-args
allows to read more appium capabilities from config file
2 parents 94d74eb + 3da05fd commit 74dc43f

File tree

10 files changed

+103
-13
lines changed

10 files changed

+103
-13
lines changed

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ Please follow the following steps:
5151
--sauce_list_browsers List the available browsers configured (Guacamole integrated).
5252
--sauce_create_tunnels undefined
5353
--sauce_tunnel_id=testtunnel123123 Use an existing secure tunnel (exclusive with --sauce_create_tunnels)
54+
--sauce_app=sauce-storage:your_app.apSpecify the app name in sauce temporary storage
55+
--sauce_app_capabilities_config=sauceSpecify a configuration file containing customized appium desiredCapabilities for saucelabs VM
5456
--shared_sauce_parent_account=testsauSpecify parent account name if existing shared secure tunnel is in use (exclusive with --sauce_create_tunnels)
5557
```
5658

@@ -92,13 +94,35 @@ tunnel config json example
9294

9395
For all supported flags please refer to [here](https://github.com/bermi/sauce-connect-launcher#advanced-usage).
9496

97+
## Customize appium desiredCapabilities (for app and mobile web test only)
98+
99+
`testarmada-magellan-saucelabs-executor` supports customized appium desiredCapabilities in `.json` file via `--sauce_app_capabilities_config`. Any content in the `.json` file will be merged into desiredCapabilities directly.
100+
101+
```javascript
102+
customized appium desiredCapabilities file
103+
104+
{
105+
"appiumVersion": "1.6.5",
106+
"automationName": "XCUITest",
107+
"sendKeyStrategy": "setValue"
108+
}
109+
```
110+
95111
### Loading rules for env variables and customized flags
96112

97113
Some parameters can be passed in from both env variables and customized flags (such as `SAUCE_USERNAME` from env variables and `username` from flags). It is very important to understand which one will take effect if set up both.
98114

99115
1. env variable always has top priority.
100116
2. customized flags only work when no corresponding env variable set
101117

118+
### Loading rules for command line args, profile and configuration file content
119+
120+
Some arguments can be passed in to excutor from both command line args, profile and configuration files, for example the temporary app location on saucelabs. The rule for deciding the final value of such argument is ordered as following
121+
122+
1. configuration file content
123+
2. profile
124+
3. command line args (except `--sauce_app`, command line value of `--sauce_app` has the top priority)
125+
102126
## Example
103127
To run test in latest chrome on Windows10 without a tunnel
104128
```console

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "testarmada-magellan-saucelabs-executor",
3-
"version": "4.10.0",
3+
"version": "4.11.0",
44
"description": "test executor for magellan test to run over saucelabs cloud",
55
"main": "index.js",
66
"scripts": {

src/configuration.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ const _loadConfig = (filename) => {
1010
try {
1111
/*eslint-disable global-require*/
1212
const config = require(filepath);
13-
logger.log(`Loaded tunnel config file ${filename}`);
14-
logger.debug(`Tunnel config from ${filename}:`);
13+
logger.log(`Loaded config file ${filename}`);
14+
logger.debug(`Loading config from ${filename}:`);
1515
logger.debug(`${JSON.stringify(config)}`);
1616
return _.cloneDeep(config);
1717
} catch (err) {
18-
logger.err(`Cannot load tunnel config file from ${filename}`);
18+
logger.err(`Cannot load config file from ${filename}`);
1919
logger.err(err);
2020
throw new Error(err);
2121
}
@@ -61,6 +61,16 @@ export default {
6161
settings.config.tunnel.tunnelIdentifier = runArgv.sauce_tunnel_id;
6262
}
6363

64+
// optional:
65+
if (runArgv.sauce_app) {
66+
settings.config.app = runArgv.sauce_app;
67+
}
68+
69+
// optional:
70+
if (runArgv.sauce_app_capabilities_config) {
71+
settings.config.appCapabilitiesConfig = _loadConfig(runArgv.sauce_app_capabilities_config);
72+
}
73+
6474
// optional: *Outbound* HTTP Sauce-specific proxy configuration. Note
6575
// that this is for Selenium outbound control traffic only, not the
6676
// return path, and not to be confused with sauceconnect.

src/help.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@ export default {
2727
"example": "testtunnel123123",
2828
"description": "Use an existing secure tunnel (exclusive with --sauce_create_tunnels)"
2929
},
30+
"sauce_app": {
31+
"visible": true,
32+
"type": "string",
33+
"example": "sauce-storage:your_app.apk",
34+
"description": "Specify the app name in sauce temporary storage"
35+
},
36+
"sauce_app_capabilities_config": {
37+
"visible": true,
38+
"type": "string",
39+
"example": "sauceAppCapabilitiesConfig.json",
40+
"description": "Specify a configuration file containing customized appium "
41+
+ "desiredCapabilities for saucelabs VM"
42+
},
3043
"shared_sauce_parent_account": {
3144
"visible": true,
3245
"type": "string",

src/locks.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default class Locks {
1313
this.api = this.apiMock || new LocksAPI(this.options);
1414

1515
logger.log(`Using locks server at ${this.options.locksServerLocation
16-
} for VM traffic control.`);
16+
} for VM traffic control.`);
1717

1818
return new Promise((resolve, reject) => {
1919
this.api.connect((err) => {
@@ -85,10 +85,16 @@ export default class Locks {
8585
}
8686

8787
release(token) {
88+
if (!this.api) {
89+
return;
90+
}
8891
this.api.release(token);
8992
}
9093

9194
teardown() {
95+
if (!this.api) {
96+
return;
97+
}
9298
this.api.close();
9399
}
94100
}

src/locks_socket_api.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export default class LocksAPI {
8484
// Reject unexpected or garbled messages
8585
const nextClaim = this.claims.shift();
8686
if (nextClaim) {
87-
return nextClaim(new Error(`Received an unexpected message `
87+
return nextClaim(new Error("Received an unexpected message "
8888
+ `from locks server: ${message}`));
8989
}
9090

@@ -111,7 +111,7 @@ export default class LocksAPI {
111111

112112
claim(callback) {
113113
this.claims.push(callback);
114-
this.socket.send(JSON.stringify({type: "claim"}));
114+
this.socket.send(JSON.stringify({ type: "claim" }));
115115
}
116116

117117
release(token) {

src/profile.js

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import SauceBrowsers from "guacamole";
33
import listSauceCliBrowsers from "guacamole/src/cli_list";
44
import { argv } from "yargs";
55
import logger from "./logger";
6+
import settings from "./settings";
67

78
const FIREFOX_MARIONETTE = 48;
89

@@ -16,6 +17,26 @@ const _patchFirefox = (capabilities) => {
1617
return capabilities;
1718
};
1819

20+
const _patchAppium = (capabilities) => {
21+
let tempCap = _.cloneDeep(capabilities);
22+
// for customized app capabilities
23+
if (settings.config.appCapabilitiesConfig) {
24+
tempCap = _.merge(capabilities,
25+
settings.config.appCapabilitiesConfig);
26+
}
27+
28+
// if app location is passed via command line arg
29+
if (settings.config.app) {
30+
tempCap.app = settings.config.app;
31+
}
32+
33+
if (tempCap.app) {
34+
delete tempCap.browserName;
35+
}
36+
37+
return tempCap;
38+
};
39+
1940
export default {
2041
getNightwatchConfig: (profile, sauceSettings) => {
2142
const capabilities = _.assign({}, profile.desiredCapabilities);
@@ -71,6 +92,8 @@ export default {
7192
id: runArgv.sauce_browser
7293
};
7394

95+
p.desiredCapabilities = _patchAppium(p.desiredCapabilities);
96+
7497
logger.debug(`detected profile: ${JSON.stringify(p)}`);
7598

7699
resolve(p);
@@ -90,6 +113,8 @@ export default {
90113
id: b
91114
};
92115

116+
p.desiredCapabilities = _patchAppium(p.desiredCapabilities);
117+
93118
returnBrowsers.push(p);
94119
});
95120

@@ -138,11 +163,10 @@ export default {
138163
// for appium test
139164
if (profile.appium) {
140165
p.desiredCapabilities = _.merge(p.desiredCapabilities, profile.appium);
141-
142-
if (p.desiredCapabilities.app) {
143-
delete p.desiredCapabilities.browserName;
144-
}
145166
}
167+
168+
p.desiredCapabilities = _patchAppium(p.desiredCapabilities);
169+
146170
resolve(p);
147171
} catch (e) {
148172
reject(`Executor sauce cannot resolve profile ${

src/settings.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ const config = {
2121

2222
sauceOutboundProxy: null,
2323

24+
app: null,
25+
appCapabilitiesConfig: null,
26+
2427
locksServerLocation: null,
2528
locksOutageTimeout: 1000 * 60 * 5,
2629
locksPollingInterval: 5000,

test/config/appCapabilities.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"appiumVersion": "1.6.5",
3+
"automationName": "xcuitest",
4+
"sendKeyStrategy": "setValue",
5+
"waitForAppScript": "true",
6+
"platform": "OSX"
7+
}

test/src/locks.test.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,12 @@ describe("Locks", () => {
135135
describe("release lock", () => {
136136
it("no lock server configured", () => {
137137
locks = new Locks({}, mockLocksAPI);
138-
expect(() => {
138+
139+
try{
139140
locks.release("FAKE_TOKEN");
140-
}).to.throw(Error);
141+
}catch(e){
142+
assert(false, "shouldn't reach here");
143+
}
141144
});
142145

143146
it("lock server called", (done) => {

0 commit comments

Comments
 (0)