Skip to content

Commit c2c46f7

Browse files
author
kanhaiya
committed
wdio-lambdatest-service for webdriverio
1 parent 243f325 commit c2c46f7

File tree

14 files changed

+815
-1
lines changed

14 files changed

+815
-1
lines changed

.eslintrc.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
module.exports = {
2+
extends: [
3+
'eslint:recommended',
4+
'plugin:import/errors',
5+
'plugin:import/warnings'
6+
],
7+
env: {
8+
node: true,
9+
es6: true
10+
},
11+
parser: 'babel-eslint',
12+
parserOptions: {
13+
ecmaVersion: 2016,
14+
sourceType: 'module'
15+
},
16+
rules: {
17+
semi: ['error', 'never'],
18+
quotes: ['error', 'single'],
19+
indent: [2, 4],
20+
21+
'import/no-unresolved': [2, { commonjs: true, amd: true }],
22+
'import/named': 2,
23+
'import/namespace': 2,
24+
'import/default': 2,
25+
'import/export': 2,
26+
27+
'no-multiple-empty-lines': [2, {'max': 1, 'maxEOF': 1}],
28+
'array-bracket-spacing': ['error', 'never'],
29+
'brace-style': ['error', '1tbs', { allowSingleLine: true }],
30+
camelcase: ['error', { properties: 'never' }],
31+
'comma-spacing': ['error', { before: false, after: true }],
32+
'no-lonely-if': 'error',
33+
'no-else-return': 'error',
34+
'no-tabs': 'error',
35+
'no-trailing-spaces': ['error', {
36+
skipBlankLines: false,
37+
ignoreComments: false
38+
}],
39+
quotes: ['error', 'single', { avoidEscape: true }],
40+
'unicode-bom': ['error', 'never'],
41+
'object-curly-spacing': ['error', 'always'],
42+
'require-atomic-updates': 0
43+
}
44+
}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules/
2+
*.log
3+
build/
4+
coverage/
5+
.eslintcache

.npmignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
src
2+
coverage
3+
node_modules
4+
tests
5+
*.log
6+
.eslintcache

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
message = "wdio-lambdatest-service: bump version %s"

README.md

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,65 @@
1-
# wdio-lambdatest-service
21
WebdriverIO LambdaTest Service
2+
========================
3+
4+
> A WebdriverIO service that manages tunnel and job metadata for LambdaTest users.
5+
6+
## Installation
7+
8+
9+
The easiest way is to keep `wdio-lambdatest-service` as a devDependency in your `package.json`.
10+
11+
```json
12+
{
13+
"devDependencies": {
14+
"wdio-lambdatest-service": "^1.0.0"
15+
}
16+
}
17+
```
18+
19+
You can simple do it by:
20+
21+
```bash
22+
npm i wdio-lambdatest-service --save-dev
23+
```
24+
25+
Instructions on how to install `WebdriverIO` can be found [here.](https://webdriver.io/docs/gettingstarted.html)
26+
27+
28+
## Configuration
29+
30+
WebdriverIO has LambdaTest support out of the box. You should simply set `user` and `key` in your `wdio.conf.js` file. This service plugin provides supports for [LambdaTest Tunnel](https://www.lambdatest.com/support/docs/troubleshooting-lambda-tunnel/). Set `tunnel: true` also to activate this feature.
31+
32+
```js
33+
// wdio.conf.js
34+
export.config = {
35+
// ...
36+
user: process.env.LT_USERNAME,
37+
key: process.env.LT_ACCESS_KEY,
38+
services: [
39+
['lambdatest', {
40+
tunnel: true
41+
}]
42+
],
43+
// ...
44+
};
45+
```
46+
47+
## Options
48+
49+
In order to authorize to the LambdaTest service your config needs to contain a [`user`](https://webdriver.io/docs/options.html#user) and [`key`](https://webdriver.io/docs/options.html#key) option.
50+
51+
### tunnel
52+
Set this to true to enable routing connections from LambdaTest cloud through your computer. You will also need to set `tunnel` to true in browser capabilities.
53+
54+
Type: `Boolean`<br>
55+
Default: `false`
56+
57+
### lambdatestOpts
58+
Specified optional will be passed down to LambdaTest Tunnel. See [this list](https://www.lambdatest.com/support/docs/lambda-tunnel-modifiers/) for details.
59+
60+
Type: `Object`<br>
61+
Default: `{}`
62+
63+
----
64+
65+
For more information on WebdriverIO see the [homepage](https://webdriver.io).

babel.config.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module.exports = {
2+
presets: [
3+
['@babel/preset-env', {
4+
targets: {
5+
node: 8
6+
}
7+
}]
8+
],
9+
plugins: [
10+
'@babel/plugin-proposal-function-bind',
11+
'@babel/plugin-proposal-class-properties',
12+
'@babel/plugin-proposal-optional-catch-binding'
13+
],
14+
env: {
15+
development: {
16+
sourceMaps: 'inline',
17+
plugins: ['source-map-support']
18+
}
19+
},
20+
comments: false
21+
}

package.json

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
{
2+
"name": "wdio-lambdatest-service",
3+
"version": "1.0.0",
4+
"description": "A WebdriverIO service that manages tunnel and job metadata for LambdaTest.",
5+
"author": "LambdaTest <[email protected]>",
6+
"contributors": [
7+
"LambdaTest <[email protected]> (https://www.npmjs.com/~lambdatestdev)"
8+
],
9+
"homepage": "https://github.com/LambdaTest/wdio-lambdatest-service",
10+
"repository": {
11+
"type": "git",
12+
"url": "git://github.com/LambdaTest/wdio-lambdatest-service.git"
13+
},
14+
"license": "MIT",
15+
"main": "./build/index",
16+
"engines": {
17+
"node": ">=10.0.0"
18+
},
19+
"scripts": {
20+
"build": "run-s clean compile",
21+
"clean": "rimraf ./build",
22+
"compile": "babel src/ -d build/ --config-file ./babel.config.js",
23+
"test": "run-s test:*",
24+
"test:eslint": "eslint --cache src tests",
25+
"test:coverage": "jest --coverage"
26+
},
27+
"keywords": [
28+
"webdriver",
29+
"wdio",
30+
"wdio-service",
31+
"lambdatest"
32+
],
33+
"bugs": {
34+
"url": "https://github.com/LambdaTest/wdio-lambdatest-service/issues"
35+
},
36+
"dependencies": {
37+
"@wdio/logger": "6.0.0-alpha.0",
38+
"@lambdatest/node-tunnel": "*",
39+
"@lambdatest/node-rest-client": "*"
40+
},
41+
"peerDependencies": {
42+
"@wdio/cli": "^5.0.0"
43+
},
44+
"publishConfig": {
45+
"access": "public"
46+
},
47+
"devDependencies": {
48+
"@babel/cli": "^7.6.2",
49+
"@babel/core": "^7.6.2",
50+
"@babel/node": "^7.6.2",
51+
"@babel/plugin-proposal-class-properties": "^7.5.5",
52+
"@babel/plugin-proposal-function-bind": "^7.2.0",
53+
"@babel/plugin-proposal-optional-catch-binding": "^7.2.0",
54+
"@babel/plugin-syntax-export-default-from": "^7.2.0",
55+
"@babel/preset-env": "^7.6.2",
56+
"@babel/register": "^7.6.2",
57+
"@octokit/rest": "^16.35.2",
58+
"@types/jest": "^24.0.18",
59+
"aws-sdk": "^2.539.0",
60+
"babel-core": "^6.26.3",
61+
"babel-eslint": "^10.0.3",
62+
"babel-jest": "^24.9.0",
63+
"babel-plugin-source-map-support": "^2.1.1",
64+
"cheerio": "^1.0.0-rc.3",
65+
"codecov": "^3.6.1",
66+
"copyfiles": "^2.1.1",
67+
"cucumber": "^5.1.0",
68+
"del": "^5.1.0",
69+
"dox": "^0.9.0",
70+
"eslint": "^6.5.1",
71+
"eslint-plugin-import": "^2.18.2",
72+
"husky": "^3.0.7",
73+
"inquirer": "^7.0.0",
74+
"jest": "^24.9.0",
75+
"lerna": "^3.16.4",
76+
"lerna-changelog": "^1.0.0",
77+
"markdox": "^0.1.10",
78+
"mime-types": "^2.1.24",
79+
"np": "^5.1.0",
80+
"npm-run-all": "^4.1.5",
81+
"recursive-readdir": "^2.2.2",
82+
"rimraf": "^3.0.0",
83+
"shelljs": "^0.8.3",
84+
"source-map-support": "^0.5.13",
85+
"tempy": "^0.3.0",
86+
"typescript": "^3.6.3"
87+
},
88+
"husky": {
89+
"hooks": {
90+
"pre-commit": "git diff-index --name-only --diff-filter=d HEAD | grep -E \"(.*)\\.js$\" | xargs node_modules/eslint/bin/eslint.js -c .eslintrc.js",
91+
"pre-push": "npm run test:eslint"
92+
}
93+
},
94+
"jest": {
95+
"testMatch": [
96+
"<rootDir>/tests/*.test.js"
97+
],
98+
"coverageDirectory": "./coverage/",
99+
"collectCoverage": true,
100+
"coverageThreshold": {
101+
"global": {
102+
"branches": 80,
103+
"functions": 95,
104+
"lines": 95,
105+
"statements": 95
106+
}
107+
},
108+
"testEnvironment": "node",
109+
"coveragePathIgnorePatterns": [
110+
"node_modules/"
111+
]
112+
},
113+
"greenkeeper": {
114+
"lockfiles": {
115+
"outOfRangeUpdatesOnly": true
116+
}
117+
}
118+
}

src/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const TUNNEL_STOP_FAILED = 'LambdaTest Tunnel failed to stop within 60 seconds!'
2+
export const TUNNEL_START_FAILED = 'LambdaTest Tunnel failed to start within 60 seconds!'
3+
export const TUNNEL_STOP_TIMEOUT = 60000

src/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import LambdaTestLauncher from './launcher'
2+
import LambdaTestService from './service'
3+
4+
export default LambdaTestService
5+
export const launcher = LambdaTestLauncher

src/launcher.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { performance, PerformanceObserver } from 'perf_hooks'
2+
import logger from '@wdio/logger'
3+
import LambdaTestTunnelLauncher from '@lambdatest/node-tunnel'
4+
import { TUNNEL_START_FAILED, TUNNEL_STOP_FAILED, TUNNEL_STOP_TIMEOUT } from './constants'
5+
const log = logger('@wdio/lambdatest-service')
6+
export default class LambdaTestLauncher {
7+
constructor(options) {
8+
this.options = options
9+
}
10+
11+
// modify config and launch tunnel
12+
onPrepare(config, capabilities) {
13+
if (!this.options.tunnel) {
14+
return
15+
}
16+
17+
const tunnelArguments = {
18+
user: config.user,
19+
key: config.key,
20+
...this.options.lambdatestOpts
21+
}
22+
23+
this.lambdatestTunnelProcess = new LambdaTestTunnelLauncher()
24+
25+
if (Array.isArray(capabilities)) {
26+
capabilities.forEach(capability => {
27+
capability.tunnel = true
28+
})
29+
} else if (typeof capabilities === 'object') {
30+
capabilities.tunnel = true
31+
}
32+
// measure LT boot time
33+
const obs = new PerformanceObserver(list => {
34+
const entry = list.getEntries()[0]
35+
log.info(
36+
`LambdaTest Tunnel successfully started after ${entry.duration}ms`
37+
)
38+
})
39+
obs.observe({ entryTypes: ['measure'], buffered: false })
40+
41+
let timer
42+
performance.mark('ltTunnelStart')
43+
return Promise.race([
44+
new Promise((resolve, reject) => {
45+
this.lambdatestTunnelProcess.start(tunnelArguments, err => {
46+
if (err) return reject(err)
47+
/* istanbul ignore next */
48+
this.lambdatestTunnelProcess.getTunnelName(tunnelName => {
49+
if (Array.isArray(capabilities)) {
50+
capabilities.forEach(capability => {
51+
capability.tunnelName = tunnelName
52+
})
53+
} else if (typeof capabilities === 'object') {
54+
capabilities.tunnelName = tunnelName
55+
}
56+
resolve()
57+
})
58+
})
59+
}),
60+
new Promise((resolve, reject) => {
61+
/* istanbul ignore next */
62+
timer = setTimeout(() => { reject( new Error(TUNNEL_START_FAILED)) }, TUNNEL_STOP_TIMEOUT)
63+
})
64+
]).then(
65+
/* istanbul ignore next */
66+
(result) => {
67+
clearTimeout(timer)
68+
performance.mark('ltTunnelEnd')
69+
performance.measure('bootTime', 'ltTunnelStart', 'ltTunnelEnd')
70+
return Promise.resolve(result)
71+
},
72+
(err) => {
73+
clearTimeout(timer)
74+
return Promise.reject(err)
75+
}
76+
)
77+
}
78+
79+
onComplete() {
80+
if (
81+
!this.lambdatestTunnelProcess ||
82+
typeof this.lambdatestTunnelProcess.isRunning !== 'function' ||
83+
!this.lambdatestTunnelProcess.isRunning()
84+
) {
85+
return
86+
}
87+
88+
let timer
89+
return Promise.race([
90+
new Promise((resolve, reject) => {
91+
this.lambdatestTunnelProcess.stop(err => {
92+
if (err) return reject(err)
93+
resolve()
94+
})
95+
}),
96+
new Promise((resolve, reject) => {
97+
/* istanbul ignore next */
98+
timer = setTimeout(() => reject( new Error(TUNNEL_STOP_FAILED)), TUNNEL_STOP_TIMEOUT)
99+
})
100+
]).then(
101+
() => {
102+
clearTimeout(timer)
103+
return Promise.resolve()
104+
},
105+
/* istanbul ignore next */
106+
(err) => {
107+
clearTimeout(timer)
108+
return Promise.reject(err)
109+
}
110+
)
111+
}
112+
}

0 commit comments

Comments
 (0)