I have a test case of my site with cypress. however the site, which is been built in vue.js has lets say 2 journeys. in one of them there is an button which I want to click. So, what I want to do is the following:
if ('button exists') {
cy.get(#"button-class").click() //basically click the button
}
If the button isn't there, which can be true for the second journey then carry on, as no button will be there.
Whatever I have tried so far failed. All I want is a simple if statement in cypress.
This became a lot easier with the release of cypress-if package
cy.get(#"button-class")
.if()
.click()
Update for Cypress v12
Recent internal changes in Cypress have made this package stop working.
Please see this issue Can we please overwrite query commands #25078 and register your "vote" if you like the package and wish it were fixed.
You can get the parent element of the button, which should be displayed everytime and query for your button inside its then() callback
cy.get('css of always displayed parent').then($parent => {
if ($parent.find('#button-class').length) {
cy.wrap($parent).find('#button-class').click();
}
});
Related
I have an app made with Quasar. I make e2e tests with python selenium using firefox. I log in and then would like to logout. To perform this I need as usually click on profile icon to show the menu and select Logout.
The issue is that whatever I tried the menu doesn't appear. The code looks as follows.
#pytest.mark.nondestructive
def test_must_login_and_logout_properly(logged_in_selenium, wait):
driver = logged_in_selenium
avatar = wait.until(EC.presence_of_element_located((By.XPATH, '//header//img[#alt="Profile image"]/ancestor::button/parent::span')))
# "natively" via selenium
ActionChains(driver).pause(1).move_to_element(avatar).click().perform()
# via javascript
driver.execute_script('document.evaluate(\'//header//img[#alt="Profile image"]/ancestor::button/parent::span\', document).iterateNext().dispatchEvent(new Event("click"))')
The above code doesn't work UNTIL I really click this element with the mouse. By putting time.sleep(10) at first line in test function and click. After that any method via avatar.click() or execute_script(...) works fine.
I also have an option to use PyAutoGUI to find the avatar on the screen and perform that click, but I don't like this variant.
-- UPDATE
I found out why it behaves like this, but still don't know how to work this around. Initially (after page load) click event handler is not attached to //header//img[#alt="Profile image"]/ancestor::button/parent::span element. It only appears after manual click in this area.
Any ideas?
You didn't share a link to this page, so I can only guess.
I would try to:
Wait for element visibility, not just existance.
Try to remove click from ActionChains action.
Add a short delay between moving to the element and click on it..
Still not sure, but possibly this will work
avatar = wait.until(EC.visibility_of_element_located((By.XPATH, '//header//img[#alt="Profile image"]/ancestor::button/parent::span')))
ActionChains(driver).pause(1).move_to_element(avatar).perform()
time.sleep(0.2)
avatar.click()
UPD
Try this:
wait.until(EC.visibility_of_element_located((By.XPATH, "//div[#class='q-avatar']"))).click()
wait.until(EC.visibility_of_element_located((By.XPATH, "//div[text()='Logout']"))).click()
Eventually it was necessary to click the element twice. Due to maybe Quasar implementation detail.
#pytest.mark.nondestructive
def test_must_login_and_logout_properly(logged_in_selenium, wait):
driver = logged_in_selenium
avatar = wait.until(EC.visibility_of_element_located((By.XPATH, '//header//img[#alt="Profile image"]/ancestor::button/parent::span')))
ActionChains(driver).pause(1).move_to_element(avatar).click().pause(0.5).click().perform()
wait.until(EC.visibility_of_element_located((By.XPATH, "//div[text()='Logout']"))).click()
wait.until(EC.visibility_of_element_located((By.XPATH, '//div[text()="You have logged out"]')))
We are using VueDraggable (and Vue) in our front-end and we are testing our front-end with Dusk.
I am currently trying to use $browser->drag('selector', 'selector') from dusk to drag objects from one list to the other, but I don't see anything happening during the test (although it might be the action is not visible) nor is the right result shown, the object does not end up in the indicated list.
I was wondering if anybody made a working example already of using $browser->drag() combined with Vue.draggable? I am asking since I don't know if I am trying the impossible or not.
There is an open issue for this on Dusk's Github. I had to open a new issue that can be found here since the original was closed for comment. The link contains a more thorough explanation, but the short answer and solution are highlighted here:
Problem: Laravel's Dusk does not trigger Vue.draggable events. To simulate a drag-and-drop Dusk does a "mouse down", "move mouse to location", and "mouse up" sequence. In theory this is correct but does not trigger Vue' s events.
Solution: Dusk's method does trigger mouse down and mouse up events, so we can simply use those events to trigger the ones desired.
$("a[draggable='true']").on("mousedown", function(event) {
$(this).trigger("dragstart");
});
$("div[droppable='true']").on("mouseup", function(event) {
$(this).trigger("drop");
});
This JSFiddle is an example of how it would work (though you need to implement it on a Laravel project to truly test, of course!).
There's a single button element on the page and the following click stream:
let submitClick$ = sources.DOM.select(buttonSel)
.events("click")
.mapTo(true)
.debug(console.log)
Once I click on the button, true is logged, which is correct.
However, when I map the stream, the code inside runs twice:
let submitDeal$ = submitClick$.map(() => {
console.log("Clicked")
// ...
})
No other event handlers should be attached to the button, and the element itself sits inside a div:
button(".btn--add", "Submit")
The usual event.preventDefault() and event.stopPropagation() doesn't make a difference, and inspecting the event does show that it is fired from the same element button.btn--add.
Not really sure what's going on, any ideas are appreciated! Thanks!
Versions:
"#cycle/dom": "^12.2.5"
"#cycle/http": "^11.0.1"
"#cycle/xstream-run": "^3.1.0"
"xstream": "^6.4.0"
Update 1: I triple checked and no JS files are loaded twice. I'm using Webpack that bundles a single app.js file that's loaded on the page (Elixir/Phoenix app). Also when inspecting the button in the Event Listeners tab in Chrome's Developer Tools, it seems that only 1 event handled is attached.
Update 2: Gist with the code
Too little information is given to resolve this problem. However some things come to mind:
You shouldn't use .debug(console.log) but .debug(x => console.log(x)) instead. In fact .debug() is enough, it will use console.log internally.
Then, is the button inside a <form>? That may be affecting the events. In general this question needs more details.
Turns out this was due to a bug in xstream, which was fixed in xstream#7.0.0.
The webpage that I'm testing is using knockout. On other pages on our site that are not currently using knockout I'm not having the same problem. The scenario I have is where the page opens, I enter in various required fields and click the save button. At some point between when it enters a value in the last text field and when it clicks the save button the fields that previously had values become cleared out, and thus the script can't continue. Here is an example of the code that I'm running:
driver.findElement(By.id("sku")).clear();
driver.findElement(By.id("sku")).sendKeys(itemNo);
driver.findElement(By.id("desktopThankYouPage")).clear();
driver.findElement(By.id("desktopThankYouPage")).sendKeys(downloadUrl);
driver.findElement(By.id("mobileThankYouPage")).clear();
driver.findElement(By.id("mobileThankYouPage")).sendKeys(mobileDownloadUrl);
driver.findElement(By.id("initialPrice")).clear();
driver.findElement(By.id("initialPrice")).sendKeys(initialPrice);
driver.findElement(By.id("submitSiteChanges")).click();
Like I said, between the time it enters text in the last field and the time it clicks save the fields that previously had text in them get cleared out, and thus my test fails. The problem is it doesn't always happen. Sometimes the test runs fine, other times it doesn't.
I've tried putting Thread.sleep(x); all over the place to see if pausing at certain points would fix the problem. I also have tried using javascript to wait in the background for any ajax that might be running. Also have the implicit wait of driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS). None of it seemingly has made any difference.
I'm running version 2.13 of selenium server and all my tests run on Firefox 8.
Any help on this would be greatly appreciated!
Firefox has a bug which prevents some events from being executed while the browser window is out of focus. This could be an issue when you're running your automation tests - which might be typing even if the window is out of focus.
The point is that knockout model updates are triggered (by default) with the change event. If it's not being executed, it's underlying model won't be up-to-date.
To fix this issue I triggered the change event "manually", injecting javascript into my tests.:
//suppose "element" is an input field
element.sendKeys("value");
JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
jsExecutor.executeScript("$(arguments[0]).change();", element);
As you might have noticed, I'm using jQuery to trigger the change event. If you're not using jQuery on your app, you can check here how to trigger it using vanilla javascript.
Hope that helps somebody.
I had the exact same problem. I would guess also that your code works fine in Chrome but not firefox, and that it always works when you do it manually?
Anyway, the problem is likely to be that Selenium doesn't really behave the same way as a real user, and doesnt trigger the same events. When you "submit" the form, it sometimes won't have executed the "change" event on a text area, meaning that it won't have changed.
I had the same problem testing Backbone.modelbinding, which uses the "change" event to update the model from the form. Knockout also uses the "change" event, but fortunately it can also use the "keyup" event. See valueUpdate in the docs:
<input data-bind="value: someValue, valueUpdate: 'keyup'" />
Anyway, that reproducibly solved it for me, and didnt need any sleeps once I had that done. The downside is that you'd be running the event more than is necessary in production, in order to make tests work. Another downside is that you if you want to run some code when a value changes, you'll now get one event per keypress instead of one per field change, which sucks sometimes.
There is another solution, which is to make Selenium fire the change event yourself, for example: Selenium IE change event not fired. It's also suboptimal, but what can you do.
You could also try putting the focus on a button before you click it. Don't know if that will work, I haven't tried it.
I was facing the same issue, while using JavaScriptExecutor for sending keys to text fields.
Using below code in IE (same code is working with chrome):
(JavascriptExecutor) driver.executeScript("arguments[0].value = '" + value + "';", element);
After updating the code to simple "sendKeys()" method, it resolved my issue:
element.sendKeys("some text");
I'm trying to build a web app using Dojo. I have a form that posts data via Dojo's xhrPost capabilities to a server side program that saves changes made on the form whenever the user hits the "save" button. What I would like to do is disable the save button after a successful save until the next time something is changed in any of the form's fields to avoid repeated attempts to save an unchanged document.
I tried having Dojo's event watching functionality watch for changes, but have not been successful. The event intended to trigger reenabling the save button never does anything. Here is what I tried:
eventWatching.push(dojo.connect(dijit.byId('editForm'), 'onChange', function() { dijit.byId('saveButton').set('disabled', false); }));
Using onKeyPress instead of onChange seemed promising, but that did not (obviously) reenable the button when the form was edited using the mouse alone.
Prior to 1.6, I don't think dijit.form.Form connects its children's onChange to its own, which is likely why your onChange idea didn't work.
In Dojo 1.6, what you're asking for is easily possible by taking advantage of the fact that widgets now inherit dojo.Stateful functionality:
form.watch('value', function(property, oldvalue, newvalue) {
/* ... */
});
In 1.5 or lower this might take some work; can't think of an easy way off the top of my head but maybe someone else has an idea or one will hit me later.
You can find the code responsible for hooking up the onChange and value-watching functionality in 1.6 here: https://github.com/dojo/dijit/blob/master/form/_FormMixin.js#L396-429
If the newvalue is emptyString the form is valid.
frm1.watch('state',function(property, oldvalue, newvalue) {
console.log(newvalue)
})