I need to check if a text ("Skadesaken min") is present on the next page I am navigating to using this code:
describe('Folg skade Test', function() {
it('Enter the app', function() {
cy.visit('http://localhost:3000/')
})
it('Select claim', () => {
cy.get('#app > section > article:nth-child(3) > a:nth-child(2)').click()
.next().should('contain', 'Skadesaken min>')
})
})
Using the selector when inspecting the element in cypress developer tool I get this:
#app > section.col-md-9 > article > h1.hidden-xs
However the error when replaying the script says that the element is
"cy.next() failed because this element is detached from the DOM."
any idea how to solve this?
Sounds like the original element got removed from the dom and a new one went in its place
You can get around this easily.
const selector = '#app > section > article:nth-child(3) > a:nth-child(2)';
cy.get(selector).click();
cy.get(selector).should('contain, 'Skadesaken min>')
Related
This is basically for a ReactSelect element (behaves like a Select2 element with multi-select enabled), on which I'm trying to select some values which are not already selected.
If an option is selected, then there'll be an element as given below in the DOM
<div class="select__multi-value__label">option1</div>
and hence that options won't be present in the list. So, any code to click() that "option" will fail.
Is there a way to check whether an element with some particular text is available in the DOM?
something like,
options = ['option1','option2','option3'];
options.forEach(option =>{
cy.get('[test-id="react-select"]').then(reactSelect =>{
if(reactSelect.find('[class="select__multi-value__label"]').contains(option).length == 0){
//code to select that option
cy.get('div.select__menu-list > div[role="option"]').contains(option).click();
}
})
})
This find().contains() part doesn't work.
How can I achieve this?
Thanks for any help.
Edit
Adding to the solution given below, can I get an exact match selector - say using a Regex?
let r = new RegExp("^" + option + "$");
...........
const selector = `div.select__multi-value__label:contains(${r})`;
This somehow doesn't work. Found a thread that recommends using filter(), but I don't know how to use it.
Is it possible?
You can do it by moving the contains() inside the selector,
options = ['option1','option2','option3'];
options.forEach(option =>{
cy.get('[test-id="react-select"]').then(reactSelect =>{
const selector = `[class="select__multi-value__label"]:contains(${option})`
if(reactSelect.find(selector).length == 0){
//code to select that option
cy.get('div.select__menu-list > div[role="option"]')
.contains(option)
.click();
}
})
})
The .find().contains() part in your code is using a different .contains() to to the Cypress .contains().
It's invoking the jQuery .contains() which has a different purpose
Description: Check to see if a DOM element is a descendant of another DOM element.
I suppose you could also select the options directly and iterate them
options = ['option1','option2','option3'];
cy.get('div.select__menu-list > div[role="option"]')
.each(option =>{
if (options.includes(option.text()) {
option.click();
}
})
Exact match
options = ['option1','option2','option3'];
options.forEach(option =>{
cy.get('[test-id="react-select"]').then(reactSelect =>{
const matchedOptions = reactSelect
.find('[class="select__multi-value__label"]')
.filter((index, el) => el.innerText === option) // exact
if(matchedOptions.length === 0){
//code to select that option
cy.get('div.select__menu-list > div[role="option"]')
.contains(option)
.click();
}
})
})
You should avoid conditionals in tests as it goes against best practice.
A solution in this case following good practices would be to mock the response that comes from the API for you to handle the request as you want, so you will know exactly when there will be specific text on the screen instead of being a random behavior and you won't have to do conditionals.
You can read more about mocks here: https://docs.cypress.io/api/commands/intercept#Stubbing-a-response
But I also advise you to read this Cypress documentation on testing with conditionals: https://docs.cypress.io/guides/core-concepts/conditional-testing#What-you-ll-learn
I'm having trouble recreating this test.
Problem:
It seems that Cypress is only visiting one link from this list whilst looping over each item.
Notes:
I added the length check to make sure that the array of nodes is the correct size
The code within each seems to work fine, since the test runner navigates to the first link
I was looking into the .next() methods, but that returns the next DOM nodes. Still not clear if that might be the issues here
seems like there's no iterator within the each() method
Test Case
GIVEN a personal website
WHEN when I navigate to the /blog page
THEN Cypress find the list of blog posts
AND checks the number of total posts
THEN Cypress loops over those list items
THEN Cypress collects the href
THEN Cypress visits that page
THEN Cypress checks that href includes 'posts'
AND wait 1s
Test Code
describe("Visual regression on /posts/{id}", () => {
sizes.forEach((size) => {
it(`Should match screenshot, when '${size}' resolution'`, () => {
cy.visit("/blog")
cy.get("ul > li > a")
.should("have.length", 9)
.each((element) => {
cy.wrap(element)
.invoke("attr", "href")
.then((href) => {
cy.visit(href);
});
cy.wrap(element)
.should("have.attr", "href")
.and("include", "posts"
cy.wait(1000);
});
});
});
});
Solution
cy.visit("/blog");
cy.get("ul > li > a").should("have.length", 9);
cy.get("ul > li > a").each(($element) => {
cy.wrap($element)
.invoke("attr", "href")
.then((href) => {
cy.wrap(href).should("exist")
cy.visit(href);
cy.wait(2000);
});
});
I seems like the block below is letting Cypress loose track of the current element.
.should("have.length", 9)
Once I split out the rules of the test, I'm no longer seeing any issues and Cypress is correctly navigating through all the pages I need.
I am trying to click an element inside frame. When i use cy.wrap i get Error
" Timed out retrying: Expected to find element: '[name="border_top"].contents() body #acceptInvitation', but never found it.
Code
it('Automate C# Corner Page', function() {
cy.get('[name="border_top"]').then(function ($iframe) {
const doc = $iframe.contents().find('body')
// doc.find('#acceptInvitation').click({force:true})
cy.wrap(doc.find('#acceptInvitation')).click({force:true})
})
After you add a helper function to your cypress/support/commands.js as I describe in my answer here (Cypress - run test in iframe), you can evaluate whether the button exists with:
cy.get('#iframe-id')
.iframe('body #yourButtonId')
.should('exist')
I think clicking the element in the iframe could be done like this.
cy.get('#iframe-id')
.iframe('body #yourButtonId')
.click({force:true})
install cypress-iframe()
import cypress-iframe in the file
cy.iframe().find('selector');
you can declare one method into base js file like shown below:
getIframeBody() {
return cy
.get('object')
.its('0.contentDocument').
its('body')
.then(cy.wrap)
};
Then use this method as:
getIframeBody().find("cssLocater).click(); OR getIframeBody().xpath("xpath).click();
This will work for sure.
When run in nightwatch.js the following code:
module.exports = {
'simple test todomvc with angular2' : function (client) {
client
.url('http://todomvc.com/examples/angular2/')
.waitForElementVisible('input.new-todo', 1000) // this works
.assert.visible('input.new-todo') // this passes
.setValue('input.new-todo', 'task A\r\n')
.setValue('input.new-todo', 'task B\r\n')
.pause(1000)
.assert.visible('ul.todo-list') // this passes
.assert.visible('ul.todo-list li:first-child') // this fails
.end();
}
};
results in the following output:
Running: simple test todomvc with angular2
? Element was visible after 724 milliseconds.
? Testing if element is visible.
? Testing if element is visible.
? Testing if element is visible. Element could not be located. - expected "true" but got: null
... ERROR: Unable to locate element: "ul.todo-list li:first-child" using: css selector
Why is it that the todo-list is visible, but the first child element of that list is not visible?
What is the best way to locate and inspect the elements within the todo-list?
(Note that the app under test is implemented with AngularJS, but without using the ng-app directive. The todo-list is generated with Angular's ng-for directive, which is imported via some kind of bootstrapped Angular - if I understand correctly.)
Here's the code for a working test:
module.exports = {
'simple test todomvc with angular2' : function (client) {
client
.url('http://todomvc.com/examples/angular2/')
.waitForElementVisible('input.new-todo', 1000)
.assert.visible('input.new-todo')
.setValue('input.new-todo', 'task A\r\n')
.setValue('input.new-todo', 'task B\r\n')
//.pause(2000)
.assert.visible('ul.todo-list')
//.assert.visible('ul.todo-list > li:nth-child(2) > div > label')
.assert.containsText('ul.todo-list > li:nth-child(2) > div > label', 'task A')
.assert.containsText('ul.todo-list > li:nth-child(3) > div > label', 'task B')
.end();
}
};
As you can see, I needed to learn a little more about how to specify the proper CSS selector.
I have some kind of problem with twitter-bootstrap and firefox.
I have a button and a dropdown menu with an input text. If I right click on the input ( for right-click + Paste for example), firefox closes the dropdown. And that quite boring.
Is there any solution for prevent that behaviour ?
Thanks !
As an immediate stop-gap workaround you can use something along the following lines to prevent the click event from propagating when the click event is a right-click
JS
$(document).on('click', function(e) {
e.button === 2 && e.stopImmediatePropagation()
})
This would need to be placed between jQuery loading and Bootstrap loading.
Plunk
However, this is a rather blunt approach and you'd probably be better off doing something more subtle if possible.
Update
One way to circumvent this issue in a more targeted manner is to disable the original event listener and replace it with one that checks for right-clicks first.
JS
// obtain a reference to the original handler
var _clearMenus = $._data(document, "events").click.filter(function (el) {
return el.namespace === 'data-api.dropdown' && el.selector === undefined
})[0].handler;
// disable the old listener
$(document)
.off('click.data-api.dropdown', _clearMenus)
.on('click.data-api.dropdown', function (e) {
// call the handler only when not right-click
e.button === 2 || _clearMenus()
})
Unlike the previous example, this code would need to run after Bootstrap has loaded.
Plunk
For Bootstrap 3 you have to update the namespace to bs.data-api.dropdown.
JS
// obtain a reference to the original handler
var _clearMenus = $._data(document, "events").click.filter(function (el) {
return el.namespace === 'bs.data-api.dropdown' && el.selector === undefined
})[0].handler;
// disable the old listener
$(document)
.off('click.data-api.dropdown', _clearMenus)
.on('click.data-api.dropdown', function (e) {
// call the handler only when not right-click
e.button === 2 || _clearMenus()
})
Plunk