How to make nightwatch.js test more DRY - nightwatch.js

I'm learning nightwatch.js and am finding a lot of repeated code. For example,
// works
objects.expect.element('#user').to.be.present
objects.expect.element('#user').value.to.match(/\S/)
objects.expect.element('#user').value.to.not.equal(username)
Where objects.expect.element('#user') is obviously repeated. In this case, if I don't repeat that for each line, then I end up failing.
For example, if instead I use
// fails
objects.expect.element('#user').to.be.present
.value.to.match(/\S/)
.value.to.not.equal(username)
results in a fail message starting with, Expected element <#who> to be present not equal: "Ned the Nighthawk" not match: "/\S/" - expected "not present" but got: present
Is there a way to make this code more DRY?

Unfortunately nightwatch's expect namespace doesn't offer chaining.
In order to chain your assertions you need to use classic assert/verify library.
http://nightwatchjs.org/api#assertions

Related

XPath returning IndexError: list index out of range with Specific Example

In order to test lxml's XPath, I have created a test file called output1. This test file is a small, isolated snippet of XML. Running XPath on output1 works as expected. However, when I try to run my code on the full test file that output1 is extracted from, the code throws an error that does not make sense.
This is the file:
<ADMIN-DATA>
<SOLAR>
<PLANET GID="SOLAR-SYSTEM">
<MOON GID="EDITABLE">
<EARTH GID="XPATH-EDITABLE">
<NUM GID="NAME">BongiornoPizza</NUM>
</EARTH>
</MOON>
</PLANET>
</SOLAR>
</ADMIN-DATA>
My script begins parsing PLANET's children (starting with MOON, who has GID="EDITABLE"). The issue is that I can't use XPath to get the direct and only child of MOON, which is EARTH. However, I can iterate using a for loop and manually get to EARTH. This is strange, considering I also tested it on an even smaller example using XPath and it worked.
The for loop for inner_node in node: works fine, and will get me EARTH.
Breaking code: (Using XPath to get EARTH)
if "function Comment" not in str(node.tag):
if node.attrib["GID"] == "EDITABLE": # passes
xpath_editable_node = node.xpath('./EARTH')[0] # BREAKS ON THIS LINE
name_node = xpath_editable_node.xpath('./NUM')[0]
print(name_node.text) # should contain BongiornoPizza
Working code: (Uses for loop to get EARTH)
if "function Comment" not in str(node.tag):
if node.attrib["GID"] == "EDITABLE": # passes
for inner_node in node:
print(inner_node)
if inner_node.attrib["GID"] == "XPATH-EDITABLE":
print("INSIDE")
name_node = inner_node.xpath('./NUM')[0]
print(name_node.text) # should contain BongiornoPizza
I can achieve what I want with for loops and manual iteration, but it would be nice to be able to consistently use the XPath library in a clean manner. I think I'm probably missing a simple thing and that's why it is not working.

Ruby Capybara Element not found error handling

internet.find(:xpath, '/html/body/div[1]/div[10]/div[2]/div[2]/div[1]/div[1]/div[1]/div[5]/div/div[2]/a').text
I am looping through a series of pages and sometimes this xpath will not be available. How do I continue to the next url instead of throwing an error and stopping the program? Thanks.
First, stop using xpaths like that - they're going to be ultra-fragile and horrendous to read. Without seeing the HTML I can't give you a better one - but at least part way along there has to be an element with an id you can target instead.
Next, you could catch the exception returned from find and ignore it or better yet you could check if page has the element first
if internet.has_xpath?(...)
internet.find(:xpath, ...).text
...
else
... whatever you want to do when it's not on the page
end
As an alternative to accepted answer you could consider #first method that accepts count argument as the number of expected matches or null to allow empty results as well
internet.first(:xpath, ..., count: nil)&.text
That returns element's text if one's found and nil otherwise. And there's no need to ignore rescued exception
See Capybara docs

Proper way to assert text in Selenium Webdriver/RSpec

So im writing a framework in pure selenium-webdriver and am curious what the proper way to assert text exists (Such as in an alert message for instance on an invalid login for example). Specifically with RSpec.
I can think of two ways that comes to mind. Doing something like so:
text_to_check = driver.find_element(locator).text and then doing something like expect(text_to_check).to be("Bad Login text") The locator in this case would probably be xpath or css locator I guess? Although I feel like xpath would probably make more sense (Im not super familiar with xpath tbh though)
Use the driver.page_source() and then check against that....but that seems brittle if that text exists somewhere else on the page. Also it seems unnecessary to do that and pull in the whole page source to check what is essentially one element.
String expected = “abc.com;
String actualURL= “abc.com”;
Assert.assertEquals(expected, actualURL);
message – Message to be displayed in case of an Assertion Error.
condition – Condition against which the assertion needs to be applied.
Assert.assertTrue(“Assert True test message”, true);

Logging and asserting the number of previously-unknown DOM elements

I'ts my first tme using Cypress and I almost finalized my first test. But to do so I need to assert against a unknown number. Let me explain:
When the test starts, a random number of elements is generated and I shouldn't have control on such a number (is a requirement). So, I'm trying to get such number in this way:
var previousElems = cy.get('.list-group-item').its('length');
I'm not really sure if I'm getting the right data, since I can not log it (the "cypress console" shows me "[Object]" when I print it). But let's say such line returns (5) to exemplify.
During the test, I simulate a user creating extra elements (2) and removing an (1) element. Let's say the user just creates one single extra element.
So, at the end os the test, I need to check if the number of eements with the same class are equals to (5+2-1) = (6) elements. I'm doing it in this way:
cy.get('.list-group-item').its('length').should('eq', (previousTasks + 1));
But I get the following message:
CypressError: Timed out retrying: expected 10 to equal '[object Object]1'
So, how can I log and assert this?
Thanks in advance,
PD: I also tryed:
var previousTasks = (Cypress.$("ul").children)? Cypress.$("ul").children.length : 0;
But it always returns a fixed number (2), even if I put a wait before to make sure all the items are fully loaded.
I also tryed the same with childNodes but it always return 0.
Your problem stems from the fact that Cypress test code is run all at once before the test starts. Commands are queued to be run later, and so storing variables as in your example will not work. This is why you keep getting objects instead of numbers; the object you're getting is called a chainer, and is used to allow you to chain commands off other commands, like so: cy.get('#someSelector').should('...');
Cypress has a way to get around this though; if you need to operate on some data directly, you can provide a lambda function using .then() that will be run in order with the rest of your commands. Here's a basic example that should work in your scenario:
cy.get('.list-group-item').its('length').then(previousCount => {
// Add two elements and remove one...
cy.get('.list-group-item').its('.length').should('eq', previousCount + 1);
});
If you haven't already, I strongly suggest reading the fantastic introduction to Cypress in the docs. This page on variables and aliases should also be useful in this case.

How do I match a full link in capybara?

In Capybara+Rspec, I can check that a link is missing:
response.body.should_not have_link("link_content")
This is fine, but unfortunately the test fails for when "link_content" partially matches a link, such as "this_is_a_long_link_content". How can I change the test to make it pass in this case?
(That is, the matcher should not partially match, it should only fully match).
You can also use the following workaround:
response.body.should_not have_xpath("//a[normalize-space(text())='link_content']")
This is whitespace-agnostic and therefore a little more flexible than the raw HTML approach.
From the docs:
If all else fails, you can also use the page.html method to test against the raw HTML:
This works for me:
page.html.should match('>\s*Log in\s*</a>')
page.html.should_not match('>\s*link_content\s*</a>')
Note that the argument to match can a regular expression. That means that you can make the solution whitespace-agnostic by simply adding \s*.

Resources