topological-nodered-wdio 1.1.0 → 1.1.2

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "topological-nodered-wdio",
3
3
  "description": "Open source WebdriverIO nodes for Node-RED",
4
- "version": "1.1.0",
4
+ "version": "1.1.2",
5
5
  "author": "topological",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -22,8 +22,10 @@
22
22
  color: '#a6bbcf',
23
23
  defaults: {
24
24
  name: { value: '' },
25
+ locateType: { value: false },
25
26
  locateUsing: { value: 'xpath' },
26
27
  locateValue: { value: '' },
28
+ locateValues: { value: [] },
27
29
  action: { value: 'selectByAttr' },
28
30
  text: { value: '' },
29
31
  attribute: { value: '' },
@@ -37,30 +39,37 @@
37
39
  return this.name || 'dropdown action'
38
40
  },
39
41
  oneditprepare: function () {
42
+ var that = this
40
43
  setDropdownAction()
41
- }
44
+ showSelectors()
45
+ multiSelectorsUI(that)
46
+ },
47
+ oneditsave: function () {
48
+ var node = this
49
+ var locateType = $('#node-input-locateType').prop('checked')
50
+ node.locateType = locateType
51
+ if (locateType) {
52
+ var locateValues = $('#node-input-locateValues-container').editableList('items')
53
+ node.locateValues = []
54
+ locateValues.each(function () {
55
+ var using = $(this).find('.node-input-locateValues-using').val()
56
+ var value = $(this).find('.node-input-locateValues-value').val()
57
+ node.locateValues.push({ using, value })
58
+ })
59
+ } else {
60
+ node.locateValues = []
61
+ node.locateValue = $('#node-input-locateValue').val()
62
+ node.locateUsing = $('#node-input-locateUsing').val()
63
+ }
64
+ }
42
65
  })
43
66
  </script>
44
67
 
45
68
  <script type="text/x-red" data-template-name="dropdown-action">
46
69
  <div class="form-row">
47
- <label for="node-input-locateUsing"><i class="fa fa-tasks"></i> Locate Method</label>
48
- <select type="text" id="node-input-locateUsing" style="width:70%;">
49
- <option value="id">id</option>
50
- <option value="name">name</option>
51
- <option value="className">Class Name</option>
52
- <option value="css selector">CSS selector</option>
53
- <option value="link text">Link text</option>
54
- <option value="partial link text">Partial link text</option>
55
- <option value="tag name">Tag name</option>
56
- <option value="xpath" selected>XPath</option>
57
- </select>
58
- </div>
59
- <div class="form-row">
60
- <label for="node-input-locateValue"><i class="fa fa-tasks"></i> Selector</label>
61
- <input id="node-input-locateValue" type="text">
70
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
71
+ <input id="node-input-name" type="text">
62
72
  </div>
63
-
64
73
  <div class="form-row">
65
74
  <label for="node-input-action"><i class="fa fa-tasks"></i> Action</label>
66
75
  <select type="text" id="node-input-action" style="width:70%;" onchange="setDropdownAction()">
@@ -87,8 +96,29 @@
87
96
  <input id="node-input-index" type="text">
88
97
  </div>
89
98
  <div class="form-row">
90
- <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
91
- <input id="node-input-name" type="text">
99
+ <label for="node-input-locateType"><i class="fa fa-tasks"></i> Multiple Selector?</label>
100
+ <input id="node-input-locateType" type="checkbox" style="width:30px;" onchange="showSelectors()">
101
+ </div>
102
+ <div class="form-row">
103
+ <label for="node-input-locateUsing"><i class="fa fa-tasks"></i> Locate Method</label>
104
+ <select type="text" id="node-input-locateUsing" style="width:70%;">
105
+ <option value="id">id</option>
106
+ <option value="name">name</option>
107
+ <option value="className">Class Name</option>
108
+ <option value="css selector">CSS selector</option>
109
+ <option value="link text">Link text</option>
110
+ <option value="partial link text">Partial link text</option>
111
+ <option value="tag name">Tag name</option>
112
+ <option value="xpath" selected>XPath</option>
113
+ </select>
114
+ </div>
115
+ <div class="form-row">
116
+ <label for="node-input-locateValue"><i class="fa fa-tasks"></i> Selector</label>
117
+ <input id="node-input-locateValue" type="text">
118
+ </div>
119
+ <div class="form-row node-input-locateValues-container-row" style="position:relative">
120
+ <h4 style="margin-bottom: 3px;">Selectors :</h4>
121
+ <ol id="node-input-locateValues-container"></ol>
92
122
  </div>
93
123
  </script>
94
124
 
@@ -1,6 +1,6 @@
1
1
  const common = require('./wdio-common')
2
2
 
3
- module.exports = function(RED) {
3
+ module.exports = function (RED) {
4
4
  function dropdownAction(config) {
5
5
  RED.nodes.createNode(this, config)
6
6
  const node = this
@@ -8,16 +8,48 @@ module.exports = function(RED) {
8
8
 
9
9
  node.on('input', async (msg) => {
10
10
  try {
11
- let locateUsing = config.locateUsing || msg.locateUsing
12
- let locateValue = config.locateValue || msg.locateValue
11
+ common.clearStatus(node);
12
+ let multiple = config.locateType || msg.locateType;
13
+ let locateValues = config.locateValues || msg.locateValues;
14
+ let locateUsing = config.locateUsing || msg.locateUsing;
15
+ let locateValue = config.locateValue || msg.locateValue;
16
+ let element = null;
17
+ node.log = "";
13
18
 
14
19
  let browser = await common.getBrowser(node.context())
15
- let locator = await common.getLocator(
16
- browser,
17
- locateUsing,
18
- locateValue
19
- )
20
- let element = await browser.$(locator)
20
+ if(!multiple){
21
+ let locator = await common.getLocator(
22
+ locateUsing,
23
+ locateValue
24
+ )
25
+ element = await browser.$(locator)
26
+ if (!element) {
27
+ throw new Error(`Element not found using ${locateUsing}: ${locateValue}`);
28
+ }
29
+ }
30
+ else{
31
+ for(let i = 0; i <locateValues.length; i++){
32
+ const { using, value } = locateValues[i];
33
+ let locator = await common.getLocator(
34
+ locateUsing,
35
+ locateValue
36
+ )
37
+ element = await browser.$(locator)
38
+ if (element) {
39
+ node.log += `Attempt ${i + 1}: Element found using ${using}: ${value}\n`;
40
+ locateUsing = using
41
+ locateValue = value
42
+ break;
43
+ }
44
+ else {
45
+ node.log += `Attempt ${i + 1}: Element not found using ${using}: ${value}\n`;
46
+ node.warn(`Element not found using ${using}: ${value}`);
47
+ }
48
+ }
49
+ if (!element) {
50
+ throw new Error(`Element not found using all selector values`);
51
+ }
52
+ }
21
53
 
22
54
  let text = config.text || msg.text
23
55
  let attribute = config.attribute || msg.attribute
@@ -25,16 +57,16 @@ module.exports = function(RED) {
25
57
  let value = config.value || msg.value
26
58
 
27
59
  if (config.action === 'selectByAttr') {
28
- node.log = `Select the dropdown value using Attribute: ${attribute} with Value: ${value}.`
60
+ node.log += `Select the dropdown value using Attribute: ${attribute} with Value: ${value}.`
29
61
  await element.selectByAttribute(attribute, value)
30
62
  } else if (config.action === 'selectByIndex') {
31
- node.log = `Select the dropdown value using Index: ${index}.`
63
+ node.log += `Select the dropdown value using Index: ${index}.`
32
64
  await element.selectByIndex(parseInt(index))
33
65
  } else if (config.action === 'selectByText') {
34
- node.log = `Select the dropdown value using Visible text: ${text}.`
66
+ node.log += `Select the dropdown value using Visible text: ${text}.`
35
67
  await element.selectByVisibleText(text)
36
68
  } else if (config.action === 'getValue') {
37
- node.log = 'Get selected drop down value.'
69
+ node.log += 'Get selected drop down value.'
38
70
  msg.payload = await element.getValue()
39
71
  }
40
72
  await common.log(node)
@@ -144,7 +144,7 @@
144
144
  <input id="node-input-locateType" type="checkbox" style="width:30px;" onchange="showSelectors()">
145
145
  </div>
146
146
  <div class="form-row">
147
- <label for="node-input-locateUsing"><i class="fa fa-tasks"></i> Locate Method</label>
147
+ <label for="node-input-locateUsing"><i class="fa fa-tasks"></i> Locate Using</label>
148
148
  <select type="text" id="node-input-locateUsing" style="width:70%;">
149
149
  <option value="id">id</option>
150
150
  <option value="name">name</option>
@@ -160,7 +160,7 @@
160
160
  <input id="node-input-locateValue" type="text">
161
161
  </div>
162
162
  <div class="form-row node-input-locateValues-container-row" style="position:relative">
163
- <h4 style="margin-bottom: -3px;">Selectors :</h4>
163
+ <h4 style="margin-bottom: 3px;">Selectors :</h4>
164
164
  <ol id="node-input-locateValues-container"></ol>
165
165
  </div>
166
166
  </script>
@@ -1,107 +1,134 @@
1
- const common = require('./wdio-common')
1
+ const common = require("./wdio-common");
2
2
 
3
- module.exports = function(RED) {
3
+ module.exports = function (RED) {
4
4
  function elementAction(config) {
5
- RED.nodes.createNode(this, config)
6
- const node = this
7
- const context = node.context()
8
- common.clearStatus(node)
5
+ RED.nodes.createNode(this, config);
6
+ const node = this;
7
+ const context = node.context();
9
8
 
10
9
  var getTypeInputValue = async (msg, type, value) => {
11
- var r = ''
10
+ var r = "";
12
11
  switch (type) {
13
- case 'msg':
14
- r = RED.util.getMessageProperty(msg, value)
15
- break
16
- case 'flow':
17
- r = context.flow.get(value)
18
- break
19
- case 'global':
20
- r = context.global.get(value)
21
- break
22
- case 'str':
12
+ case "msg":
13
+ r = RED.util.getMessageProperty(msg, value);
14
+ break;
15
+ case "flow":
16
+ r = context.flow.get(value);
17
+ break;
18
+ case "global":
19
+ r = context.global.get(value);
20
+ break;
21
+ case "str":
23
22
  try {
24
- r = unescape(JSON.parse('"' + value + '"'))
23
+ r = unescape(JSON.parse('"' + value + '"'));
25
24
  } catch (e) {
26
- r = value
25
+ r = value;
27
26
  }
28
- break
29
- case 'num':
30
- r = parseFloat(value)
31
- break
32
- case 'json':
33
- if (value !== '') {
34
- r = JSON.parse(value)
27
+ break;
28
+ case "num":
29
+ r = parseFloat(value);
30
+ break;
31
+ case "json":
32
+ if (value !== "") {
33
+ r = JSON.parse(value);
35
34
  } else {
36
- r = undefined
35
+ r = undefined;
37
36
  }
38
37
  }
39
- return r
40
- }
38
+ return r;
39
+ };
41
40
 
42
- node.on('input', async (msg) => {
43
- try {
44
- let multiple = config.locateType || msg.locateType
45
- let locateValues = config.locateValues || msg.locateValues
46
- let locateUsing = multiple ? locateValues[0].using : config.locateUsing || msg.locateUsing
47
- let locateValue = multiple ? locateValues[0].value : config.locateValue || msg.locateValue
48
-
49
- let browser = await common.getBrowser(context)
50
- let capabilities = browser.capabilities
51
- let elementId = await common.getElementId(
52
- browser,
53
- locateUsing,
54
- locateValue
55
- )
41
+ node.on("input", async (msg) => {
42
+ try {
43
+ common.clearStatus(node);
44
+ let multiple = config.locateType || msg.locateType;
45
+ let locateValues = config.locateValues || msg.locateValues;
46
+ let locateUsing = config.locateUsing || msg.locateUsing;
47
+ let locateValue = config.locateValue || msg.locateValue;
56
48
 
57
- let attribute = config.attribute || msg.attribute
49
+ let browser = await common.getBrowser(context);
50
+ let capabilities = browser.capabilities;
51
+ let elementId = null;
52
+ node.log = "";
58
53
 
59
- if (config.action === 'click') {
60
- node.log = `Click on the webelement identified using ${locateUsing}: "${locateValue}".`
61
- await browser.elementClick(elementId)
62
- } else if (config.action === 'clear') {
63
- node.log = `Clear the Value of the webelement identified using ${locateUsing}: "${locateValue}".`
64
- await browser.elementClear(elementId)
65
- } else if (config.action === 'sendKeys') {
54
+ if (!multiple) {
55
+ elementId = await common.getElementId(
56
+ browser,
57
+ locateUsing,
58
+ locateValue
59
+ );
60
+ if (!elementId) {
61
+ throw new Error(`Element not found using ${locateUsing}: ${locateValue}`);
62
+ }
63
+ } else {
64
+ for (let i = 0; i < locateValues.length; i++) {
65
+ const { using, value } = locateValues[i];
66
+ elementId = await common.getElementId(browser, using, value);
67
+ if (elementId) {
68
+ node.log += `Attempt ${i + 1}: Element found using ${using}: ${value}\n`;
69
+ locateUsing = using
70
+ locateValue = value
71
+ break;
72
+ }
73
+ else {
74
+ node.log += `Attempt ${i + 1}: Element not found using ${using}: ${value}\n`;
75
+ node.warn(`Element not found using ${using}: ${value}`);
76
+ }
77
+ }
78
+ if (!elementId) {
79
+ throw new Error(`Element not found using all selector values`);
80
+ }
81
+ }
82
+
83
+ let attribute = config.attribute || msg.attribute;
84
+
85
+ if (config.action === "click") {
86
+ node.log += `Click on the webelement identified using ${locateUsing}: "${locateValue}".`;
87
+ await browser.elementClick(elementId);
88
+ } else if (config.action === "clear") {
89
+ node.log += `Clear the Value of the webelement identified using ${locateUsing}: "${locateValue}".`;
90
+ await browser.elementClear(elementId);
91
+ } else if (config.action === "sendKeys") {
66
92
  let value = await getTypeInputValue(
67
93
  msg,
68
94
  config.object,
69
95
  config.sendKeys
70
- )
71
- node.log = `Enter the Value: "${value}" to the webelement identified using ${locateUsing}: "${locateValue}".`
96
+ );
97
+ node.log += `Enter the Value: "${value}" to the webelement identified using ${locateUsing}: "${locateValue}".`;
72
98
  await browser.elementSendKeys(
73
99
  elementId,
74
100
  capabilities.version ? Array.from(value) : value
75
- )
76
- } else if (config.action === 'getValue') {
77
- node.log = `Get the Value of webelement identified using ${locateUsing}: "${locateValue}".`
78
- msg.payload = await browser.getElementAttribute(elementId, 'value')
79
- } else if (config.action === 'getText') {
80
- node.log = `Get the Text of webelement identified using ${locateUsing}: "${locateValue}".`
81
- msg.payload = await browser.getElementText(elementId)
82
- } else if (config.action === 'getAttribute') {
83
- node.log = `Get the Attribute: "${attribute}" of webelement identified using ${locateUsing}: "${locateValue}".`
84
- msg.payload = await browser.getElementAttribute(elementId, attribute)
85
- } else if (config.action === 'takeScreenShot') {
86
- node.log = 'Take the screenshot of the webelement.'
87
- msg.payload = await browser.takeElementScreenshot(elementId)
88
- } else if (config.action === 'hover') {
101
+ );
102
+ } else if (config.action === "getValue") {
103
+ node.log += `Get the Value of webelement identified using ${locateUsing}: "${locateValue}".`;
104
+ msg.payload = await browser.getElementAttribute(elementId, "value");
105
+ } else if (config.action === "getText") {
106
+ node.log += `Get the Text of webelement identified using ${locateUsing}: "${locateValue}".`;
107
+ msg.payload = await browser.getElementText(elementId);
108
+ } else if (config.action === "getAttribute") {
109
+ node.log += `Get the Attribute: "${attribute}" of webelement identified using ${locateUsing}: "${locateValue}".`;
110
+ msg.payload = await browser.getElementAttribute(elementId, attribute);
111
+ } else if (config.action === "takeScreenShot") {
112
+ node.log += "Take the screenshot of the webelement.";
113
+ msg.payload = await browser.takeElementScreenshot(elementId);
114
+ } else if (config.action === "hover") {
89
115
  let element = await common.getElement(
90
116
  browser,
91
117
  locateUsing,
92
118
  locateValue
93
- )
94
- node.log = `Hover on the webelement identified using ${locateUsing}: "${locateValue}".`
95
- msg.payload = await element.moveTo()
119
+ );
120
+ node.log += `Hover on the webelement identified using ${locateUsing}: "${locateValue}".`;
121
+ msg.payload = await element.moveTo();
96
122
  }
97
- await common.log(node)
98
- common.successStatus(node)
99
- node.send(msg)
123
+ await common.log(node);
124
+ common.successStatus(node);
125
+ node.send(msg);
100
126
  } catch (e) {
101
- await common.log(node)
102
- common.handleError(e, node, msg)
127
+ node.log = `Error: ${e.message}`;
128
+ await common.log(node);
129
+ common.handleError(e, node, msg);
103
130
  }
104
- })
131
+ });
105
132
  }
106
- RED.nodes.registerType('element-action', elementAction)
107
- }
133
+ RED.nodes.registerType("element-action", elementAction);
134
+ };
@@ -4,8 +4,10 @@
4
4
  color: '#a6bbcf',
5
5
  defaults: {
6
6
  name: { value: '' },
7
+ locateType: { value: false },
7
8
  locateUsing: { value: 'xpath' },
8
9
  locateValue: { value: '' },
10
+ locateValues: { value: [] },
9
11
  check: { value: 'selected' }
10
12
  },
11
13
  inputs: 1,
@@ -13,11 +15,54 @@
13
15
  icon: 'white-globe.png',
14
16
  label: function() {
15
17
  return this.name || 'element check'
16
- }
18
+ },
19
+ oneditprepare: function () {
20
+ var that = this
21
+ showSelectors()
22
+ multiSelectorsUI(that)
23
+ },
24
+ oneditsave: function () {
25
+ var node = this
26
+ var locateType = $('#node-input-locateType').prop('checked')
27
+ node.locateType = locateType
28
+ if (locateType) {
29
+ var locateValues = $('#node-input-locateValues-container').editableList('items')
30
+ node.locateValues = []
31
+ locateValues.each(function () {
32
+ var using = $(this).find('.node-input-locateValues-using').val()
33
+ var value = $(this).find('.node-input-locateValues-value').val()
34
+ node.locateValues.push({ using, value })
35
+ })
36
+ } else {
37
+ node.locateValues = []
38
+ node.locateValue = $('#node-input-locateValue').val()
39
+ node.locateUsing = $('#node-input-locateUsing').val()
40
+ }
41
+ }
17
42
  })
18
43
  </script>
19
44
 
20
45
  <script type="text/x-red" data-template-name="element-check">
46
+ <div class="form-row">
47
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
48
+ <input id="node-input-name" type="text">
49
+ </div>
50
+ <div class="form-row">
51
+ <label for="node-input-check"><i class="fa fa-tasks"></i> Check</label>
52
+ <select type="text" id="node-input-check" style="width:70%;">
53
+ <option value="clickable">Is Clickable</option>
54
+ <option value="displayed">Is Displayed</option>
55
+ <option value="displayedInView">Is Displayed in Viewport</option>
56
+ <option value="enabled">Is Enabled</option>
57
+ <option value="existing">Is Existing</option>
58
+ <option value="focused">Is Focused</option>
59
+ <option value="selected">Is Selected</option>
60
+ </select>
61
+ </div>
62
+ <div class="form-row">
63
+ <label for="node-input-locateType"><i class="fa fa-tasks"></i> Multiple Selector?</label>
64
+ <input id="node-input-locateType" type="checkbox" style="width:30px;" onchange="showSelectors()">
65
+ </div>
21
66
  <div class="form-row">
22
67
  <label for="node-input-locateUsing"><i class="fa fa-tasks"></i> Locate Method</label>
23
68
  <select type="text" id="node-input-locateUsing" style="width:70%;">
@@ -34,22 +79,9 @@
34
79
  <label for="node-input-locateValue"><i class="fa fa-tasks"></i> Selector</label>
35
80
  <input id="node-input-locateValue" type="text">
36
81
  </div>
37
-
38
- <div class="form-row">
39
- <label for="node-input-check"><i class="fa fa-tasks"></i> Check</label>
40
- <select type="text" id="node-input-check" style="width:70%;">
41
- <option value="clickable">Is Clickable</option>
42
- <option value="displayed">Is Displayed</option>
43
- <option value="displayedInView">Is Displayed in Viewport</option>
44
- <option value="enabled">Is Enabled</option>
45
- <option value="existing">Is Existing</option>
46
- <option value="focused">Is Focused</option>
47
- <option value="selected">Is Selected</option>
48
- </select>
49
- </div>
50
- <div class="form-row">
51
- <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
52
- <input id="node-input-name" type="text">
82
+ <div class="form-row node-input-locateValues-container-row" style="position:relative">
83
+ <h4 style="margin-bottom: 3px;">Selectors :</h4>
84
+ <ol id="node-input-locateValues-container"></ol>
53
85
  </div>
54
86
  </script>
55
87
 
@@ -4,40 +4,68 @@ module.exports = function (RED) {
4
4
  function elementCheck(config) {
5
5
  RED.nodes.createNode(this, config)
6
6
  const node = this
7
- common.clearStatus(node)
8
7
 
9
8
  node.on('input', async (msg) => {
10
9
  try {
11
- let locateUsing = config.locateUsing || msg.locateUsing
12
- let locateValue = config.locateValue || msg.locateValue
10
+ common.clearStatus(node)
11
+ let multiple = config.locateType || msg.locateType;
12
+ let locateValues = config.locateValues || msg.locateValues;
13
+ let locateUsing = config.locateUsing || msg.locateUsing;
14
+ let locateValue = config.locateValue || msg.locateValue;
13
15
 
14
- let browser = await common.getBrowser(node.context())
15
- let locator = await common.getLocator(
16
- browser,
17
- locateUsing,
18
- locateValue
19
- )
16
+ let browser = await common.getBrowser(context);
17
+ let elementId = null;
18
+ node.log = "";
19
+
20
+ if (!multiple) {
21
+ elementId = await common.getElementId(
22
+ browser,
23
+ locateUsing,
24
+ locateValue
25
+ );
26
+ if (!elementId) {
27
+ throw new Error(`Element not found using ${locateUsing}: ${locateValue}`);
28
+ }
29
+ } else {
30
+ for (let i = 0; i < locateValues.length; i++) {
31
+ const { using, value } = locateValues[i];
32
+ elementId = await common.getElementId(browser, using, value);
33
+ if (elementId) {
34
+ node.log += `Attempt ${i + 1}: Element found using ${using}: ${value}\n`;
35
+ locateUsing = using
36
+ locateValue = value
37
+ break;
38
+ }
39
+ else {
40
+ node.log += `Attempt ${i + 1}: Element not found using ${using}: ${value}\n`;
41
+ node.warn(`Element not found using ${using}: ${value}`);
42
+ }
43
+ }
44
+ if (!elementId) {
45
+ throw new Error(`Element not found using all selector values`);
46
+ }
47
+ }
20
48
 
21
49
  if (config.check === 'clickable') {
22
- node.log = `Check the webelement is clickable, identified using ${locateUsing}: "${locateValue}".`
50
+ node.log += `Check the webelement is clickable, identified using ${locateUsing}: "${locateValue}".`
23
51
  msg.payload = await browser.$(locator).isClickable()
24
52
  } else if (config.check === 'displayed') {
25
- node.log = `Check the webelement is displayed, identified using ${locateUsing}: "${locateValue}".`
26
- msg.payload = await browser.$(locator).isDisplayed()
53
+ node.log += `Check the webelement is displayed, identified using ${locateUsing}: "${locateValue}".`
54
+ msg.payload = await browser.$(locator).isDisplayed()
27
55
  } else if (config.check === 'displayedInView') {
28
- node.log = `Check the webelement is displayed in view port, identified using ${locateUsing}: "${locateValue}".`
56
+ node.log += `Check the webelement is displayed in view port, identified using ${locateUsing}: "${locateValue}".`
29
57
  msg.payload = await browser.$(locator).isDisplayedInViewport()
30
58
  } else if (config.check === 'enabled') {
31
- node.log = `Check the webelement is enabled, identified using ${locateUsing}: "${locateValue}".`
32
- msg.payload = await browser.$(locator).isEnabled()
59
+ node.log += `Check the webelement is enabled, identified using ${locateUsing}: "${locateValue}".`
60
+ msg.payload = await browser.$(locator).isEnabled()
33
61
  } else if (config.check === 'existing') {
34
- node.log = `Check the webelement is existing, identified using ${locateUsing}: "${locateValue}".`
62
+ node.log += `Check the webelement is existing, identified using ${locateUsing}: "${locateValue}".`
35
63
  msg.payload = await browser.$(locator).isExisting()
36
64
  } else if (config.check === 'focused') {
37
- node.log = `Check the webelement is focused, identified using ${locateUsing}: "${locateValue}".`
38
- msg.payload = await browser.$(locator).isFocused()
65
+ node.log += `Check the webelement is focused, identified using ${locateUsing}: "${locateValue}".`
66
+ msg.payload = await browser.$(locator).isFocused()
39
67
  } else if (config.check === 'selected') {
40
- node.log = `Check the webelement is selected, identified using ${locateUsing}: "${locateValue}".`
68
+ node.log += `Check the webelement is selected, identified using ${locateUsing}: "${locateValue}".`
41
69
  msg.payload = await browser.$(locator).isSelected()
42
70
  }
43
71
  await common.log(node)
@@ -52,9 +80,9 @@ module.exports = function (RED) {
52
80
  // node.send(msg)
53
81
  // }
54
82
  // else{
55
- await common.log(node)
56
- common.handleError(e, node, msg)
57
- }
83
+ await common.log(node)
84
+ common.handleError(e, node, msg)
85
+ }
58
86
  //}
59
87
  })
60
88
  }
@@ -4,8 +4,10 @@
4
4
  color: '#a6bbcf',
5
5
  defaults: {
6
6
  name: { value: '' },
7
+ locateType: { value: false },
7
8
  locateUsing: { value: 'xpath' },
8
9
  locateValue: { value: '' },
10
+ locateValues: { value: [] },
9
11
  action: { value: 'sync' },
10
12
  script: { value: '' }
11
13
  },
@@ -14,11 +16,53 @@
14
16
  icon: 'white-globe.png',
15
17
  label: function() {
16
18
  return this.name || 'execute script'
17
- }
19
+ },
20
+ oneditprepare: function () {
21
+ var that = this
22
+ showSelectors()
23
+ multiSelectorsUI(that)
24
+ },
25
+ oneditsave: function () {
26
+ var node = this
27
+ var locateType = $('#node-input-locateType').prop('checked')
28
+ node.locateType = locateType
29
+ if (locateType) {
30
+ var locateValues = $('#node-input-locateValues-container').editableList('items')
31
+ node.locateValues = []
32
+ locateValues.each(function () {
33
+ var using = $(this).find('.node-input-locateValues-using').val()
34
+ var value = $(this).find('.node-input-locateValues-value').val()
35
+ node.locateValues.push({ using, value })
36
+ })
37
+ } else {
38
+ node.locateValues = []
39
+ node.locateValue = $('#node-input-locateValue').val()
40
+ node.locateUsing = $('#node-input-locateUsing').val()
41
+ }
42
+ }
18
43
  })
19
44
  </script>
20
45
 
21
46
  <script type="text/x-red" data-template-name="execute-script">
47
+ <div class="form-row">
48
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
49
+ <input id="node-input-name" type="text">
50
+ </div>
51
+ <div class="form-row">
52
+ <label for="node-input-action"><i class="fa fa-tasks"></i> Action</label>
53
+ <select type="text" id="node-input-action" style="width:70%;">
54
+ <option value="sync">Sync</option>
55
+ <option value="aSync">Async</option>
56
+ </select>
57
+ </div>
58
+ <div class="form-row">
59
+ <label for="node-input-script"><i class="fa fa-tasks"></i> Script</label>
60
+ <input id="node-input-script" type="text" placeholder="javascript in string">
61
+ </div>
62
+ <div class="form-row">
63
+ <label for="node-input-locateType"><i class="fa fa-tasks"></i> Multiple Selector?</label>
64
+ <input id="node-input-locateType" type="checkbox" style="width:30px;" onchange="showSelectors()">
65
+ </div>
22
66
  <div class="form-row">
23
67
  <label for="node-input-locateUsing"><i class="fa fa-tasks"></i> Locate Method</label>
24
68
  <select type="text" id="node-input-locateUsing" style="width:70%;">
@@ -35,21 +79,10 @@
35
79
  <label for="node-input-locateValue"><i class="fa fa-tasks"></i> Selector</label>
36
80
  <input id="node-input-locateValue" type="text">
37
81
  </div>
38
- <div class="form-row">
39
- <label for="node-input-action"><i class="fa fa-tasks"></i> Action</label>
40
- <select type="text" id="node-input-action" style="width:70%;">
41
- <option value="sync">Sync</option>
42
- <option value="aSync">Async</option>
43
- </select>
44
- </div>
45
- <div class="form-row">
46
- <label for="node-input-script"><i class="fa fa-tasks"></i> Script</label>
47
- <input id="node-input-script" type="text" placeholder="javascript in string">
48
- </div>
49
- <div class="form-row">
50
- <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
51
- <input id="node-input-name" type="text">
52
- </div>
82
+ <div class="form-row node-input-locateValues-container-row" style="position:relative">
83
+ <h4 style="margin-bottom: 3px;">Selectors :</h4>
84
+ <ol id="node-input-locateValues-container"></ol>
85
+ </div>
53
86
  </script>
54
87
 
55
88
  <script type="text/x-red" data-help-name="execute-script">
@@ -1,30 +1,58 @@
1
1
  const common = require('./wdio-common')
2
2
 
3
- module.exports = function(RED) {
3
+ module.exports = function (RED) {
4
4
  function executeScript(config) {
5
5
  RED.nodes.createNode(this, config)
6
6
  const node = this
7
- common.clearStatus(node)
8
7
 
9
8
  node.on('input', async (msg) => {
10
9
  try {
11
- let locateUsing = config.locateUsing || msg.locateUsing
12
- let locateValue = config.locateValue || msg.locateValue
10
+ common.clearStatus(node);
11
+ let multiple = config.locateType || msg.locateType;
12
+ let locateValues = config.locateValues || msg.locateValues;
13
+ let locateUsing = config.locateUsing || msg.locateUsing;
14
+ let locateValue = config.locateValue || msg.locateValue;
13
15
 
14
- let browser = await common.getBrowser(node.context())
15
- let element = await common.getElement(
16
- browser,
17
- locateUsing,
18
- locateValue
19
- )
16
+ let browser = await common.getBrowser(context);
17
+ let elementId = null;
18
+ node.log = "";
19
+
20
+ if (!multiple) {
21
+ elementId = await common.getElementId(
22
+ browser,
23
+ locateUsing,
24
+ locateValue
25
+ );
26
+ if (!elementId) {
27
+ throw new Error(`Element not found using ${locateUsing}: ${locateValue}`);
28
+ }
29
+ } else {
30
+ for (let i = 0; i < locateValues.length; i++) {
31
+ const { using, value } = locateValues[i];
32
+ elementId = await common.getElementId(browser, using, value);
33
+ if (elementId) {
34
+ node.log += `Attempt ${i + 1}: Element found using ${using}: ${value}\n`;
35
+ locateUsing = using
36
+ locateValue = value
37
+ break;
38
+ }
39
+ else {
40
+ node.log += `Attempt ${i + 1}: Element not found using ${using}: ${value}\n`;
41
+ node.warn(`Element not found using ${using}: ${value}`);
42
+ }
43
+ }
44
+ if (!elementId) {
45
+ throw new Error(`Element not found using all selector values`);
46
+ }
47
+ }
20
48
 
21
49
  let script = config.script || msg.script
22
50
 
23
51
  if (config.action === 'sync') {
24
- node.log = `Execute synchronous Javascript: "${script}"${element?` By passing the webelement identified using ${locateUsing}: "${locateValue}"`:''}.`
52
+ node.log += `Execute synchronous Javascript: "${script}"${element ? ` By passing the webelement identified using ${locateUsing}: "${locateValue}"` : ''}.`
25
53
  await browser.executeScript(script, Array.from(element))
26
54
  } else if (config.action === 'aSync') {
27
- node.log = `Execute the asynchronous Javascript: "${script}"${element?` By passing the webelement identified using ${locateUsing}: "${locateValue}"`:''}.`
55
+ node.log += `Execute the asynchronous Javascript: "${script}"${element ? ` By passing the webelement identified using ${locateUsing}: "${locateValue}"` : ''}.`
28
56
  await browser.executeAsyncScript(script, Array.from(element))
29
57
  }
30
58
  await common.log(node)
@@ -26,16 +26,20 @@
26
26
  inputs: 1,
27
27
  outputs: 1,
28
28
  icon: 'white-globe.png',
29
- label: function() {
29
+ label: function () {
30
30
  return this.name || 'explicit wait'
31
31
  },
32
- oneditprepare: function() {
32
+ oneditprepare: function () {
33
33
  setExplicitAction()
34
34
  }
35
35
  })
36
36
  </script>
37
37
 
38
38
  <script type="text/x-red" data-template-name="explicit-wait">
39
+ <div class="form-row">
40
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
41
+ <input id="node-input-name" type="text">
42
+ </div>
39
43
  <div class="form-row">
40
44
  <label for="node-input-locateUsing"><i class="fa fa-tasks"></i> Locate Method</label>
41
45
  <select type="text" id="node-input-locateUsing" style="width:70%;">
@@ -76,11 +80,7 @@
76
80
  <div class="form-row" id="actionError" >
77
81
  <label for="node-input-error"><i class="fa fa-tasks"></i> Error Message</label>
78
82
  <input id="node-input-error" type="text" placeholder="error message">
79
- </div>
80
- <div class="form-row">
81
- <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
82
- <input id="node-input-name" type="text">
83
- </div>
83
+ </div>
84
84
  </script>
85
85
 
86
86
  <script type="text/x-red" data-help-name="explicit-wait">
@@ -121,7 +121,4 @@
121
121
  <p><b>Time to Wait</b> Set time to wait in milliseconds. <br></p>
122
122
  <p><b>Reverse</b> If set to true, webdriver will wait for the <i>opposite</i> of the action selected. <br></p>
123
123
  <p><b>Error Message</b> If an error exists this error message overrides the default error message. <br></p>
124
-
125
-
126
-
127
124
  </script>
@@ -13,7 +13,6 @@ module.exports = function(RED) {
13
13
 
14
14
  let browser = await common.getBrowser(node.context())
15
15
  let locator = await common.getLocator(
16
- browser,
17
16
  locateUsing,
18
17
  locateValue
19
18
  )
@@ -45,4 +44,4 @@ module.exports = function(RED) {
45
44
  })
46
45
  }
47
46
  RED.nodes.registerType('explicit-wait', explicitWait)
48
- }
47
+ }
@@ -1,13 +1,13 @@
1
- const wdio = require('webdriverio')
2
- let newSessionNode
1
+ const wdio = require("webdriverio");
2
+ let newSessionNode;
3
3
 
4
4
  module.exports.getBrowser = (context) => {
5
- let browser = context.flow.get('wdio_browser')
5
+ let browser = context.flow.get("wdio_browser");
6
6
  if (!browser || !browser.sessionId)
7
- throw new Error('No session defined - call newSession first')
7
+ throw new Error("No session defined - call newSession first");
8
8
 
9
- return browser
10
- }
9
+ return browser;
10
+ };
11
11
 
12
12
  /*
13
13
  config = {
@@ -25,139 +25,131 @@ config = {
25
25
  }
26
26
  */
27
27
  module.exports.newSession = async (config, node, context) => {
28
- let browser
28
+ let browser;
29
29
  try {
30
- browser = await wdio.remote(config)
31
- context.flow.set('wdio_browser', browser)
32
- newSessionNode = node
30
+ browser = await wdio.remote(config);
31
+ context.flow.set("wdio_browser", browser);
32
+ newSessionNode = node;
33
33
  } catch (e) {
34
- throw e
34
+ throw e;
35
35
  }
36
- return browser
37
- }
36
+ return browser;
37
+ };
38
38
 
39
39
  module.exports.deleteSession = async (context) => {
40
- let b
41
- let browser = context.flow.get('wdio_browser')
40
+ let b;
41
+ let browser = context.flow.get("wdio_browser");
42
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)
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
48
  } catch (e) {}
49
- return b
50
- }
49
+ return b;
50
+ };
51
51
 
52
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
53
+ const element = await browser.findElement(using, value);
54
+ return element.ELEMENT ?? "";
55
+ };
56
+
57
+ module.exports.getLocator = async (using, value) => {
58
+ let locator = "";
59
+ switch (using) {
60
+ case "id":
61
+ locator = `#${value}`;
62
+ break;
63
+ case "name":
64
+ locator = `[name='${value}']`;
65
+ break;
66
+ case "className":
67
+ locator = `.${value}`;
68
+ break;
69
+ case "tagName":
70
+ locator = value;
71
+ break;
72
+ case "cssSelector":
73
+ locator = value;
74
+ break;
75
+ case "text":
76
+ locator = `=${value}`;
77
+ break;
78
+ case "partialText":
79
+ locator = `*=${value}`;
80
+ break;
81
+ case "xPath":
82
+ locator = value;
83
+ break;
84
+ default:
85
+ locator = value;
69
86
  }
70
- return elementId
71
- }
72
87
 
73
- module.exports.getLocator = async (browser, using, value) => {
74
- let locator = ''
75
- switch (using) {
76
- case 'id':
77
- locator = `#${value}`
78
- break
79
- case 'name':
80
- locator = `[name='${value}']`
81
- break
82
- case 'className':
83
- locator = `.${value}`
84
- break
85
- case 'tagName':
86
- locator = value
87
- break
88
- case 'cssSelector':
89
- locator = value
90
- break
91
- case 'text':
92
- locator = `=${value}`
93
- break
94
- case 'partialText':
95
- locator = `*=${value}`
96
- break
97
- case 'xPath':
98
- locator = value
99
- break
100
- default:
101
- locator = value
102
- }
103
-
104
- return locator
105
- }
88
+ return locator;
89
+ };
106
90
 
107
91
  module.exports.handleError = (e, node, msg) => {
108
- console.log(e)
109
- module.exports.errorStatus(node)
110
- node.error(e, msg)
111
- }
92
+ console.log(e);
93
+ module.exports.errorStatus(node);
94
+ node.error(e, msg);
95
+ };
112
96
 
113
97
  module.exports.clearStatus = (node) => {
114
- node.status({})
115
- }
98
+ node.status({});
99
+ };
116
100
 
117
101
  module.exports.connectedStatus = (node) => {
118
102
  node.status({
119
- fill: 'green',
120
- shape: 'dot',
121
- text: 'connected'
122
- })
123
- }
103
+ fill: "green",
104
+ shape: "dot",
105
+ text: "connected",
106
+ });
107
+ };
124
108
 
125
109
  module.exports.disconnectedStatus = (node) => {
126
110
  node.status({
127
- fill: 'green',
128
- shape: 'ring',
129
- text: 'disconnected'
130
- })
131
- }
111
+ fill: "green",
112
+ shape: "ring",
113
+ text: "disconnected",
114
+ });
115
+ };
132
116
 
133
117
  module.exports.successStatus = (node) => {
134
118
  node.status({
135
- fill: 'green',
136
- shape: 'ring',
137
- text: 'done'
138
- })
139
- }
119
+ fill: "green",
120
+ shape: "ring",
121
+ text: "done",
122
+ });
123
+ };
140
124
 
141
125
  module.exports.errorStatus = (node) => {
142
126
  node.status({
143
- fill: 'red',
144
- shape: 'ring',
145
- text: 'error'
146
- })
147
- }
127
+ fill: "red",
128
+ shape: "ring",
129
+ text: "error",
130
+ });
131
+ };
148
132
 
149
133
  module.exports.log = async (node) => {
150
- let context = node.context()
151
- let stepCount = await (context.global.get('stepCount') || 0) + 1
152
- let document = await context.global.get('document') || ''
153
- await context.global.set('document', `${document}${stepCount}. Node: ${node.name} - ${node.log}\n`)
154
- await context.global.set('stepCount', stepCount)
155
- }
134
+ let context = node.context();
135
+ let stepCount = (await (context.global.get("stepCount") || 0)) + 1;
136
+ let document = (await context.global.get("document")) || "";
137
+ await context.global.set(
138
+ "document",
139
+ `${document}${stepCount}. Node (${node.id}): ${node.name} - ${node.log}\n`
140
+ );
141
+ await context.global.set("stepCount", stepCount);
142
+ };
156
143
 
157
144
  module.exports.document = async (node) => {
158
- let context = node.context()
159
- let document = await context.global.get('document') || ''
160
- document = node.line? `${document}\n${node.name}${node.refUrl? `\nRef: ${node.refUrl}`:''}\n\n` :
161
- `${document}********************\n${node.name}${node.refUrl? `\nRef: ${node.refUrl}`:''}\n********************\n`
162
- await context.global.set('document', document.replaceAll('\\n','\n'))
163
- }
145
+ let context = node.context();
146
+ let document = (await context.global.get("document")) || "";
147
+ document = node.line
148
+ ? `${document}\n${node.name}${
149
+ node.refUrl ? `\nRef: ${node.refUrl}` : ""
150
+ }\n\n`
151
+ : `${document}********************\n${node.name}${
152
+ node.refUrl ? `\nRef: ${node.refUrl}` : ""
153
+ }\n********************\n`;
154
+ await context.global.set("document", document.replaceAll("\\n", "\n"));
155
+ };