Repository: nightwatchjs/nightwatch
Pull request #3710 (2023-04-25 09:05:44)

Fix nesting strategies in findByLabelText.


Patch:
diff --git a/lib/api/assertions/hasAttribute.js b/lib/api/assertions/hasAttribute.js
index 802c9e34..54b3ac13 100644
--- a/lib/api/assertions/hasAttribute.js
+++ b/lib/api/assertions/hasAttribute.js
@@ -19,7 +19,7 @@
  * @api assertions
  */
 
-const {setElementSelectorProps, containsMultiple} = require('../../utils');
+const {setElementSelectorProps, isString} = require('../../utils');
 
 exports.assertion = function(definition, expectedAttribute, msg) {
   this.options = {
@@ -32,35 +32,31 @@ exports.assertion = function(definition, expectedAttribute, msg) {
  
 
   this.formatMessage = function() {
+    if (!isString(expectedAttribute)) {
+      throw new Error('Expected attribute must be a string');
+    }
+
     let message = msg || `Testing if element %s ${this.negate ? 'doesn\'t have attribute %s' : 'has attribute %s'}`;
  
     return {
       message,
-      args: [this.elementSelector, `'${Array.isArray(expectedAttribute) ? expectedAttribute.join(' ') : expectedAttribute}'`]
+      args: [this.elementSelector, `'${expectedAttribute}'`]
     };
   };
 
   this.evaluate = function() {
-    if (!this.attributeList) {
-      return false;
-    }
- 
-    return containsMultiple(this.attributeList, expectedAttribute, ' ');
-  };
+    const {result} = this;
 
-  this.value = function(result) {
     if (!result || !result.value) {
-      return '';
+      return false;
     }
-
-    this.attributeList = result.value.map(attribute =>  attribute.name);
-
-    return this.attributeList;
+ 
+    return true;
   };
 
   this.command = function(callback) {
     this.api.getAttribute(setElementSelectorProps(definition, {
       suppressNotFoundErrors: true
-    }), 'attributes', callback);
+    }), expectedAttribute, callback);
   };
 };
diff --git a/lib/api/web-element/commands/findByLabelText.js b/lib/api/web-element/commands/findByLabelText.js
index 60f82299..44a5d55c 100644
--- a/lib/api/web-element/commands/findByLabelText.js
+++ b/lib/api/web-element/commands/findByLabelText.js
@@ -45,12 +45,11 @@ module.exports.command = function (text, {exact = true, timeout, retryInterval,
       return null;
     }
 
-    const elements = await labelWebElement.findElement(By.css('input'));
-    if (!elements) {
+    try {
+      return await labelWebElement.findElement(By.css('input'));
+    } catch (err) {
       return null;
     }
-
-    return elements[0];
   };
 
   const findByDeepNesting = async (text, {exact}) => {
@@ -69,13 +68,7 @@ module.exports.command = function (text, {exact = true, timeout, retryInterval,
     }
 
     try {
-      const element = await labelElement.findElement(By.css('input'));
-
-      if (element.length === 0) {
-        return null;
-      }
-
-      return element[0];
+      return await labelElement.findElement(By.css('input'));
     } catch (err) {
       return null;
     }


Test patch:
diff --git a/test/src/api/assertions/testHasAttribute.js b/test/src/api/assertions/testHasAttribute.js
index 390aa7ed..8037314b 100644
--- a/test/src/api/assertions/testHasAttribute.js
+++ b/test/src/api/assertions/testHasAttribute.js
@@ -13,7 +13,7 @@ describe('assert.hasAttribute', function () {
     return assertionTest({
       args: ['.test_element', 'data-test', 'Test message'],
       commandResult: {
-        value: [{name: 'data-track'}, {name: 'data-test'}]
+        value: 'data-track'
       },
       assertMessage: true,
       assertion({instance, failure}) {
@@ -28,7 +28,7 @@ describe('assert.hasAttribute', function () {
     return assertionTest({
       args: ['.test_element', 'data-test'],
       commandResult: {
-        value: [{name: 'data-track'}]
+        value: 'data-track'
       },
       negate: true,
       assertion({instance, queueOpts, message}) {
@@ -36,8 +36,8 @@ describe('assert.hasAttribute', function () {
         assert.strictEqual(queueOpts.negate, true);
 
         assert.strictEqual(instance.hasFailure(), false);
-        assert.deepStrictEqual(instance.getValue(), ['data-track']);
-        assert.deepStrictEqual(instance.getActual(), ['data-track']);
+        assert.deepStrictEqual(instance.getValue(), 'data-track');
+        assert.deepStrictEqual(instance.getActual(), 'data-track');
         assert.strictEqual(instance.message, 'Testing if element <.test_element> doesn\'t have attribute \'data-test\'');
         assert.ok(message.startsWith('Testing if element <.test_element> doesn\'t have attribute \'data-test\''), message);
       }
@@ -48,15 +48,15 @@ describe('assert.hasAttribute', function () {
     return assertionTest({
       args: ['.test_element', 'data-test'],
       commandResult: {
-        value: [{name: 'data-test'}]
+        value: 'data-test'
       },
       negate: true,
       assertError: true,
       assertion({instance, queueOpts, err}) {
         assert.strictEqual(queueOpts.negate, true);
         assert.strictEqual(instance.hasFailure(), false);
-        assert.deepStrictEqual(instance.getValue(), ['data-test']);
-        assert.deepStrictEqual(instance.getActual(), ['data-test']);
+        assert.deepStrictEqual(instance.getValue(), 'data-test');
+        assert.deepStrictEqual(instance.getActual(), 'data-test');
         assert.strictEqual(err.message, `Testing if element <.test_element> doesn't have attribute 'data-test' in 5ms - expected "has not data-test" but got: "data-test" (${instance.elapsedTime}ms)`);
       }
     });
@@ -66,13 +66,13 @@ describe('assert.hasAttribute', function () {
     return assertionTest({
       args: [{selector: '.test_element'}, 'data-test'],
       commandResult: {
-        value: [{name: 'data-test'}]
+        value: 'data-test'
       },
       assertResult: true,
       assertion({instance, failure, message, err}) {
         assert.strictEqual(err, undefined);
         assert.deepStrictEqual(instance.options, {elementSelector: true});
-        assert.deepStrictEqual(instance.getActual(), ['data-test']);
+        assert.deepStrictEqual(instance.getActual(), 'data-test');
         assert.strictEqual(instance.hasFailure(), false);
         assert.ok(message.startsWith('Testing if element <.test_element> has attribute \'data-test\''), message);
         assert.strictEqual(failure, false);
@@ -84,33 +84,13 @@ describe('assert.hasAttribute', function () {
     return assertionTest({
       args: ['.test_element', 'data-test', 'Test message'],
       commandResult: {
-        value: [{name: 'not_expected'}]
+        value: null
       },
       assertError: true,
       assertResult: true,
       assertion({instance, failure}) {
-        assert.deepStrictEqual(instance.getActual(), ['not_expected']);
-        assert.strictEqual(failure, 'Expected "has data-test" but got: "not_expected"');
-      }
-    });
-  });
-
-  it('hasAttribute assertion - element not found', function() {
-    return assertionTest({
-      args: ['.test_element', 'data-test', 'Test attribute %s from element "%s" == %s'],
-      commandResult: {
-        status: -1,
-        value: []
-      },
-      assertError: true,
-      assertFailure: true,
-      assertResult: true,
-      assertion({instance, failure, err}) {
-        assert.strictEqual(instance.getActual(), 'element could not be located');
-        assert.strictEqual(instance.expected(), 'has data-test');
-        assert.strictEqual(instance.getValue(), null);
-        assert.strictEqual(failure, 'Expected "has data-test" but got: "element could not be located"');
-        assert.strictEqual(err.message, `Test attribute <.test_element> from element "'data-test'" == %s in 5ms - expected "has data-test" but got: "element could not be located" (${instance.elapsedTime}ms)`);
+        assert.deepStrictEqual(instance.getActual(), null);
+        assert.strictEqual(failure, 'Expected "has data-test" but got: "null"');
       }
     });
   });
@@ -132,4 +112,17 @@ describe('assert.hasAttribute', function () {
       }
     });
   });
+
+  it('hasAttribute assertion failed for wrong parameters', function () {
+    return assertionTest({
+      args: ['.test_element', ['data-test', 'dummy'], 'Test message'],
+      commandResult: {
+        value: 'data-track'
+      },
+      assertMessage: true
+    }).catch((err) => {
+      assert.ok(err instanceof Error);
+      assert.strictEqual(err.message, 'Error while running "assert.hasAttribute" command: Expected attribute must be a string');
+    });
+  });
 });
diff --git a/test/src/api/commands/web-element/testFindByLabelText.js b/test/src/api/commands/web-element/testFindByLabelText.js
index 8751a133..e9ec85cd 100644
--- a/test/src/api/commands/web-element/testFindByLabelText.js
+++ b/test/src/api/commands/web-element/testFindByLabelText.js
@@ -184,7 +184,7 @@ describe('.findByLabelText() commands', function () {
         },
         method: 'POST',
         response: JSON.stringify({
-          value: [{'element-6066-11e4-a52e-4f735466cecf': '2'}]
+          value: {'element-6066-11e4-a52e-4f735466cecf': '2'}
         })
       }, true);
 
@@ -234,7 +234,7 @@ describe('.findByLabelText() commands', function () {
         },
         method: 'POST',
         response: JSON.stringify({
-          value: [{'element-6066-11e4-a52e-4f735466cecf': '2'}]
+          value: {'element-6066-11e4-a52e-4f735466cecf': '2'}
         })
       }, true);