topological-nodered-wdio 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +15 -0
- package/examples/basic.json +332 -0
- package/examples/basic.png +0 -0
- package/package.json +43 -0
- package/src/alert-action.html +79 -0
- package/src/alert-action.js +51 -0
- package/src/browser-action.html +114 -0
- package/src/browser-action.js +69 -0
- package/src/delete-session.html +26 -0
- package/src/delete-session.js +28 -0
- package/src/dropdown-action.html +121 -0
- package/src/dropdown-action.js +48 -0
- package/src/element-action.html +133 -0
- package/src/element-action.js +104 -0
- package/src/element-check.html +87 -0
- package/src/element-check.js +46 -0
- package/src/execute-script.html +92 -0
- package/src/execute-script.js +39 -0
- package/src/explicit-wait.html +123 -0
- package/src/explicit-wait.js +53 -0
- package/src/frame-action.html +67 -0
- package/src/frame-action.js +37 -0
- package/src/implicit-wait-config.html +85 -0
- package/src/implicit-wait-config.js +40 -0
- package/src/new-session.html +96 -0
- package/src/new-session.js +102 -0
- package/src/wdio-common.js +151 -0
- package/src/window-action.html +91 -0
- package/src/window-action.js +42 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const common = require('./wdio-common')
|
|
2
|
+
|
|
3
|
+
module.exports = function(RED) {
|
|
4
|
+
function newSession(config) {
|
|
5
|
+
RED.nodes.createNode(this, config)
|
|
6
|
+
const node = this
|
|
7
|
+
|
|
8
|
+
common.clearStatus(node)
|
|
9
|
+
|
|
10
|
+
node.on('input', async (msg) => {
|
|
11
|
+
try {
|
|
12
|
+
const webdriverConfig = Object.assign(
|
|
13
|
+
{ logLevel: config.logLevel },
|
|
14
|
+
parseUri(config.webdriverUri || msg.webdriverUri, node),
|
|
15
|
+
getCapabilities(
|
|
16
|
+
config.webdriverProvider,
|
|
17
|
+
config.webdriverBrowser || msg.webdriverBrowser,
|
|
18
|
+
config.browserlessToken
|
|
19
|
+
)
|
|
20
|
+
)
|
|
21
|
+
node.log = `Open new browser.`
|
|
22
|
+
let b = await common.newSession(webdriverConfig, node, node.context())
|
|
23
|
+
await common.log(node)
|
|
24
|
+
common.connectedStatus(node)
|
|
25
|
+
msg.payload = b.sessionId
|
|
26
|
+
node.send(msg)
|
|
27
|
+
} catch (e) {
|
|
28
|
+
common.handleError(e, node, msg)
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
node.on('close', async (done) => {
|
|
33
|
+
try {
|
|
34
|
+
if (config.killSession) {
|
|
35
|
+
let b = await common.deleteSession(node.context())
|
|
36
|
+
let sessionId = ''
|
|
37
|
+
if (b && b.sessionId) sessionId = b.sessionId
|
|
38
|
+
common.disconnectedStatus(node)
|
|
39
|
+
node.log('Disconnected webdriver session ' + sessionId)
|
|
40
|
+
}
|
|
41
|
+
} catch (e) {
|
|
42
|
+
common.handleError(e, node, msg)
|
|
43
|
+
}
|
|
44
|
+
done()
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
RED.nodes.registerType('new-session', newSession)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const parseUri = (uri, node) => {
|
|
51
|
+
let uriComponents
|
|
52
|
+
try {
|
|
53
|
+
if (uri[uri.length - 1] !== '/') uri += '/'
|
|
54
|
+
let parsed = uri.match(/(\w+):\/\/(.+):(\d+)(\/.*)/)
|
|
55
|
+
uriComponents = {
|
|
56
|
+
protocol: parsed[1],
|
|
57
|
+
hostname: parsed[2],
|
|
58
|
+
port: parseInt(parsed[3]),
|
|
59
|
+
path: parsed[4]
|
|
60
|
+
}
|
|
61
|
+
} catch (e) {
|
|
62
|
+
common.handleError(
|
|
63
|
+
new Error(
|
|
64
|
+
'Invalid URI, expected format "<protocol>://<host>:<port>/<path>'
|
|
65
|
+
),
|
|
66
|
+
node
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return uriComponents
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const getCapabilities = (vendor, browser) => {
|
|
74
|
+
let capabilities
|
|
75
|
+
|
|
76
|
+
if (vendor === 'browserless.io') {
|
|
77
|
+
capabilities = {
|
|
78
|
+
browserName: browser,
|
|
79
|
+
'goog:chromeOptions': {
|
|
80
|
+
args: ['--headless', '--no-sandbox']
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} else if (vendor === 'local' && browser === 'chromium') {
|
|
84
|
+
capabilities = {
|
|
85
|
+
browserName: 'chrome',
|
|
86
|
+
'goog:chromeOptions': {
|
|
87
|
+
args: ['--headless', '--no-sandbox'],
|
|
88
|
+
w3c: false
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} else if (vendor === 'local') {
|
|
92
|
+
capabilities = {
|
|
93
|
+
browserName: browser,
|
|
94
|
+
//platformName: 'Linux',
|
|
95
|
+
'goog:chromeOptions': {
|
|
96
|
+
w3c: false
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return { capabilities }
|
|
102
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
const wdio = require('webdriverio')
|
|
2
|
+
let newSessionNode
|
|
3
|
+
|
|
4
|
+
module.exports.getBrowser = (context) => {
|
|
5
|
+
let browser = context.flow.get('wdio_browser')
|
|
6
|
+
if (!browser || !browser.sessionId)
|
|
7
|
+
throw new Error('No session defined - call newSession first')
|
|
8
|
+
|
|
9
|
+
return browser
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
config = {
|
|
14
|
+
logLevel: 'error',
|
|
15
|
+
protocol: 'https',
|
|
16
|
+
hostname: '<key>@chrome.browserless.io',
|
|
17
|
+
port: 443,
|
|
18
|
+
path: '/webdriver',
|
|
19
|
+
capabilities: {
|
|
20
|
+
browserName: 'chrome',
|
|
21
|
+
chromeOptions: {
|
|
22
|
+
args: ['--headless', '--no-sandbox']
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
*/
|
|
27
|
+
module.exports.newSession = async (config, node, context) => {
|
|
28
|
+
let browser
|
|
29
|
+
try {
|
|
30
|
+
browser = await wdio.remote(config)
|
|
31
|
+
context.flow.set('wdio_browser', browser)
|
|
32
|
+
newSessionNode = node
|
|
33
|
+
} catch (e) {
|
|
34
|
+
throw e
|
|
35
|
+
}
|
|
36
|
+
return browser
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports.deleteSession = async (context) => {
|
|
40
|
+
let b
|
|
41
|
+
let browser = context.flow.get('wdio_browser')
|
|
42
|
+
try {
|
|
43
|
+
b = { sessionId: browser.sessionId }
|
|
44
|
+
await browser.closeWindow()
|
|
45
|
+
await browser.deleteSession()
|
|
46
|
+
context.flow.set('wdio_browser', null)
|
|
47
|
+
if (newSessionNode) module.exports.disconnected(newSessionNode)
|
|
48
|
+
} catch (e) {}
|
|
49
|
+
return b
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports.getElementId = async (browser, using, value) => {
|
|
53
|
+
let elementId
|
|
54
|
+
try {
|
|
55
|
+
const element = await browser.findElement(using, value)
|
|
56
|
+
if (element && Object.keys(element)) {
|
|
57
|
+
elementId = element[Object.keys(element)[0]]
|
|
58
|
+
} else {
|
|
59
|
+
let e
|
|
60
|
+
if (element && element.message) {
|
|
61
|
+
e = element.message
|
|
62
|
+
} else {
|
|
63
|
+
e = 'Element not found'
|
|
64
|
+
}
|
|
65
|
+
throw new Error(e)
|
|
66
|
+
}
|
|
67
|
+
} catch (e) {
|
|
68
|
+
throw e
|
|
69
|
+
}
|
|
70
|
+
return elementId
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports.getElement = async (browser, using, value) => {
|
|
74
|
+
let selector = ''
|
|
75
|
+
let element
|
|
76
|
+
switch (using) {
|
|
77
|
+
case 'id':
|
|
78
|
+
selector = '#' + value
|
|
79
|
+
break
|
|
80
|
+
case 'name':
|
|
81
|
+
selector = value
|
|
82
|
+
break
|
|
83
|
+
case 'className':
|
|
84
|
+
selector = '.' + value
|
|
85
|
+
break
|
|
86
|
+
case 'selector':
|
|
87
|
+
selector = value
|
|
88
|
+
break
|
|
89
|
+
default:
|
|
90
|
+
selector = value
|
|
91
|
+
break
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
element = await browser.$(selector)
|
|
96
|
+
} catch (e) {
|
|
97
|
+
throw e
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return element
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports.handleError = (e, node, msg) => {
|
|
104
|
+
console.log(e)
|
|
105
|
+
module.exports.errorStatus(node)
|
|
106
|
+
node.error(e, msg)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports.clearStatus = (node) => {
|
|
110
|
+
node.status({})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports.connectedStatus = (node) => {
|
|
114
|
+
node.status({
|
|
115
|
+
fill: 'green',
|
|
116
|
+
shape: 'dot',
|
|
117
|
+
text: 'connected'
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports.disconnectedStatus = (node) => {
|
|
122
|
+
node.status({
|
|
123
|
+
fill: 'green',
|
|
124
|
+
shape: 'ring',
|
|
125
|
+
text: 'disconnected'
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports.successStatus = (node) => {
|
|
130
|
+
node.status({
|
|
131
|
+
fill: 'green',
|
|
132
|
+
shape: 'ring',
|
|
133
|
+
text: 'done'
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports.errorStatus = (node) => {
|
|
138
|
+
node.status({
|
|
139
|
+
fill: 'red',
|
|
140
|
+
shape: 'ring',
|
|
141
|
+
text: 'error'
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports.log = async (node) => {
|
|
146
|
+
let context = node.context()
|
|
147
|
+
let stepCount = await (context.global.get('stepCount') || 0) + 1
|
|
148
|
+
let document = await context.global.get('document') || ''
|
|
149
|
+
await context.global.set('document', `${document}\n${stepCount}. Node: ${node.name} - \t${node.log}`)
|
|
150
|
+
await context.global.set('stepCount', stepCount)
|
|
151
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
function setWindowAction() {
|
|
3
|
+
let action = $('#node-input-action').val()
|
|
4
|
+
$('#actionValue').hide()
|
|
5
|
+
$('#actionIndex').hide()
|
|
6
|
+
if (action === 'byName' || action === 'open') {
|
|
7
|
+
$('#actionValue').show()
|
|
8
|
+
}
|
|
9
|
+
if (action == 'byIndex') {
|
|
10
|
+
$('#actionIndex').show()
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
RED.nodes.registerType('window-action', {
|
|
15
|
+
category: 'Webdriver IO',
|
|
16
|
+
color: '#a6bbcf',
|
|
17
|
+
defaults: {
|
|
18
|
+
name: { value: '' },
|
|
19
|
+
action: { value: 'byName' },
|
|
20
|
+
value: { value: '' },
|
|
21
|
+
index: { value: '' }
|
|
22
|
+
},
|
|
23
|
+
inputs: 1,
|
|
24
|
+
outputs: 1,
|
|
25
|
+
icon: 'white-globe.png',
|
|
26
|
+
label: function() {
|
|
27
|
+
return this.name || 'window action'
|
|
28
|
+
},
|
|
29
|
+
oneditprepare: function() {
|
|
30
|
+
setWindowAction()
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<script type="text/x-red" data-template-name="window-action">
|
|
36
|
+
<div class="form-row">
|
|
37
|
+
<label for="node-input-action"><i class="fa fa-tasks"></i> Action</label>
|
|
38
|
+
<select type="text" id="node-input-action" style="width:70%;" onchange="setWindowAction()">
|
|
39
|
+
<option value="byName">Switch to Window by Name</option>
|
|
40
|
+
<option value="byIndex">Switch to Window by Index</option>
|
|
41
|
+
<option value="getHandle">Get Window handle</option>
|
|
42
|
+
<option value="close">Close Window</option>
|
|
43
|
+
<option value="open">Open Window</option>
|
|
44
|
+
</select>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="form-row" id="actionValue">
|
|
47
|
+
<label for="node-input-value"><i class="fa fa-tasks"></i> Window Name</label>
|
|
48
|
+
<input id="node-input-value" type="text" placeholder="URL or Title of window">
|
|
49
|
+
</div>
|
|
50
|
+
<div class="form-row" id="actionIndex">
|
|
51
|
+
<label for="node-input-index"><i class="fa fa-tasks"></i> Index</label>
|
|
52
|
+
<input id="node-input-index" type="text" placeholder="starts 0">
|
|
53
|
+
</div>
|
|
54
|
+
<div class="form-row">
|
|
55
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
56
|
+
<input id="node-input-name" type="text">
|
|
57
|
+
</div>
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<script type="text/x-red" data-help-name="window-action">
|
|
61
|
+
<h3>Selected action of the node performs on browser windows.</h3>
|
|
62
|
+
|
|
63
|
+
<h3>Inputs</h3>
|
|
64
|
+
<dl class="message-properties">
|
|
65
|
+
<dt><code>msg.value</code>: <span class="property-type">string</span></dt>
|
|
66
|
+
<dd>URL or Title the window to target</dd>
|
|
67
|
+
<dt><code>msg.index</code><span class="property-type">string</span></dt>
|
|
68
|
+
<dd>zero-based index to target the desired window<br></dd>
|
|
69
|
+
</dl>
|
|
70
|
+
<h3>Details</h3>
|
|
71
|
+
<dl class="message-properties">
|
|
72
|
+
<dt>Action: <span class="property-type">Select</span></dt>
|
|
73
|
+
<dd>
|
|
74
|
+
<ul>
|
|
75
|
+
<li><b>Switch to Window by Name</b></li>
|
|
76
|
+
<ul>
|
|
77
|
+
<li>Use URL or Title to switch to the browser tab/window.</li>
|
|
78
|
+
</ul>
|
|
79
|
+
<li><b>Switch to Window by Index</b></li>
|
|
80
|
+
<ul>
|
|
81
|
+
<li>Use a zero-based index to target the browser tab/window</li>
|
|
82
|
+
</ul>
|
|
83
|
+
<li><b>Get window Handle</b></li>
|
|
84
|
+
<li><b>Close Window</b></li>
|
|
85
|
+
<li><b>Open Window</b></li>
|
|
86
|
+
</ul>
|
|
87
|
+
</dd>
|
|
88
|
+
</dl>
|
|
89
|
+
|
|
90
|
+
</script>
|
|
91
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const common = require('./wdio-common')
|
|
2
|
+
|
|
3
|
+
module.exports = function(RED) {
|
|
4
|
+
function windowAction(config) {
|
|
5
|
+
RED.nodes.createNode(this, config)
|
|
6
|
+
const node = this
|
|
7
|
+
common.clearStatus(node)
|
|
8
|
+
|
|
9
|
+
node.on('input', async (msg) => {
|
|
10
|
+
try {
|
|
11
|
+
let browser = await common.getBrowser(node.context())
|
|
12
|
+
|
|
13
|
+
let value = config.value || msg.value
|
|
14
|
+
let index = config.index || msg.index
|
|
15
|
+
|
|
16
|
+
if (config.action === 'byName') {
|
|
17
|
+
node.log = `Switch to the browser window with window title: "${value}".`
|
|
18
|
+
await browser.switchWindow(value)
|
|
19
|
+
} else if (config.action === 'byIndex') {
|
|
20
|
+
node.log = `Switch to the browser window with window index: "${index}".`
|
|
21
|
+
let handles = await browser.getWindowHandles()
|
|
22
|
+
await browser.switchWindow(handles[index])
|
|
23
|
+
} else if (config.action === 'getHandle') {
|
|
24
|
+
node.log = `Get all the browser windows.`
|
|
25
|
+
msg.payload = await browser.getWindowHandles()
|
|
26
|
+
} else if (config.action === 'close') {
|
|
27
|
+
node.log = `Close the active browser window.`
|
|
28
|
+
await browser.closeWindow()
|
|
29
|
+
} else if (config.action === 'open') {
|
|
30
|
+
node.log = `Open a new window with url: "${value}".`
|
|
31
|
+
await browser.createWindow(value)
|
|
32
|
+
}
|
|
33
|
+
await common.log(node)
|
|
34
|
+
common.successStatus(node)
|
|
35
|
+
node.send(msg)
|
|
36
|
+
} catch (e) {
|
|
37
|
+
common.handleError(e, node, msg)
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
RED.nodes.registerType('window-action', windowAction)
|
|
42
|
+
}
|