Test only if element still present - cypress

I have a notification message which pops up upon saving a file and wish to close it only if it is still present when I need to press the next button which may be covered by this notification.
So far this is how I have tried to conditionally close the notification
Cypress.Commands.add('notificationClose', () => {
if (cy.get('.notification').should('be.visible')) {
console.log('CLOSE ME');
cy.get('.notification-dismiss').click({mulitple: true});
}else{
console.log('ALREADY GONE');
}
});
I have also tried a jQuery solution I found here too.
Cypress.Commands.add('notificationClose', () => {
if (Cypress.$('.notifications-wrapper') == true) {
cy.get('.notification-dismiss').click({mulitple: true});
}
});
I only use this immediately before needing to click the subsequent button, so the notification may close on its own.
The element in question is only temporarily attached to the DOM after saving the file.
How do I ask if this element is attached to the DOM?

I'm not completely sure that I understand your issue. But this is what I assume you mean:
You have a popup that isn't closed sometimes.
If the popup isn't closed you want to close it.
If the popup is closed you want to continue the test.
If that is correct we only need to fix step 1 and 2 since those are special. Continuing the test is just like you normally do.
To get the behaviour you need to get() the element and assert if it is present. You can do that with this code:
cy.get('body').then($body => {
if ($body.find('.notification').length === 1) {
cy.get('.notification-dismiss').click()
}
})
// continu the test

Related

When to close the child window when parent window is being closed in Angular

I've seen many articles but don't seem to find an answer
when I try to close the parent window childWindow doesn't get closed.
So I have these two events so far
#HostListener('window:beforeunload', ['$event'])
beforeUnload(e:any){
if(this.childWindow && !this.childWindow.closed){
e.preventDefault();
e.returnValue = false;
}
}
#HostListener('window:unload', ['$event'])
onUnload(e:any){
if(this.childWindow && !this.childWindow.closed){
this.childWindow.close();
}
}
This code blocks are working fine
but when I close the last parent window, the connected child window doesn't get closed.
note scenario: When I debug the unload event line by line, the child window is getting closed but not when I click continue
probable reason: the parent window's by the time time unload is triggered, it doesn't care about the child window
Am I doing something wrong or is there any other event that I could use?
One way to handle this would be to give the child window some time to close before the parent window is closed. You can use the setTimeout function to delay the closing of the parent window for a short period of time, such as 100ms. This would give the childWindow.close() method enough time to execute and close the child window before the parent window is closed.
#HostListener('window:beforeunload', ['$event'])
beforeUnload(e: any) {
if (this.childWindow && !this.childWindow.closed) {
e.preventDefault();
e.returnValue = false;
setTimeout(() => {
this.childWindow.close();
}, 100);
}
}
As for capturing the user action on the browser's default dialog, it's not possible to capture whether the user has clicked 'Leave' or 'Cancel' as the browser's default dialog does not provide any way to capture the user's choice.
You could try to use e.returnValue to provide your own message on the browser's default dialog and then you could set a flag based on the user's choice, but it will be different across the browsers.
#HostListener('window:beforeunload', ['$event'])
beforeUnload(e: any) {
if (this.childWindow && !this.childWindow.closed) {
e.returnValue = 'Are you sure you want to close the window?';
}
}
You can also try to use localStorage or sessionStorage to store a flag indicating if the user has confirmed to close the window, but this will only work if the user does not clear the browser's storage.
If it is closing the child window, even when user has clicked cancel on dialog
One way to address this would be to use the localStorage or sessionStorage to store a flag indicating whether the user has confirmed that they want to close the window.
You can set this flag to true when the user confirms that they want to close the window, and then check the flag's value in the beforeunload event handler. If the flag is true, you can proceed with closing the child window, otherwise, you can cancel the close operation.
#HostListener('window:beforeunload', ['$event'])
beforeUnload(e: any) {
if (this.childWindow && !this.childWindow.closed) {
if (localStorage.getItem('closeConfirmed') === 'true') {
this.childWindow.close();
} else {
e.preventDefault();
e.returnValue = 'Are you sure you want to close the window?';
}
}
}
// Set the flag when the user confirms they want to close the window
confirmClose() {
localStorage.setItem('closeConfirmed', 'true');
window.close();
}
And you can add a confirm button on your dialog that calls the confirmClose method.
closing windows programmatically may be blocked by some browser so to be on a safer side provide a way for the user to close the window manually.

Cypress Click if item exist

I need a way to easily trigger a click() event only if an element exist on page.
the fact that the element (confirm modal dialog) itself exist is not an issue for my test, but this can stop next steps. so I want to click OK only if the dialog is shown.
I tried something like this, but it didn't work:
cy.get('body').find("button[data-cy=sometag]").then(items => {
if(items.length) {
cy.get('button[data-cy=sometag]').click();
}
});
If you want to test that the modal exists but don't want to fail the test if it doesn't, use a jQuery selector.
const found = Cypress.$('button[data-cy=sometag]').length
Modals are likely to animate on opening, so you you will need to repeat the check a few times, which you can do in a function
function clearModal(selector, timeout = 1000, attempts = 0)
if (attempts > (timeout / 100)) {
return; // modal never opened
}
if (!Cypress.$(selector).length) { // not there yet, try again
cy.wait(100)
clearModal(selector, timeout, ++attempts)
else {
Cypress.$(selector).click() // modal is up, now close it
}
}
clearModal('button[data-cy=sometag]')
If you use the find like this cy.get('body').find("button[data-cy=sometag]") this will fail always whenever the element is not present. Instead you can use the find command inside the If condition and check its length.
cy.get('body').then($body => {
if ($body.find("button[data-cy=sometag]").length > 0) {
cy.get('button[data-cy=sometag]').click();
} else {
//Do something
}
})
Instead of body, you can also use the parent element of the element in question, which you know for sure is visible and present on the page every time.

Ionic 2: publish multiple events on Modal dismiss sometimes doesn't work

I've got a modal window that when you click 'Add', it does its thing, dismisses, and then when the promise is resolved, publishes some events that tell relevant components to update:
this._viewControl.dismiss().then(() =>
this._events.publish('update_myJobsPage', null);
this._events.publish('update_assessmentsPage', null);
this._events.publish('update_buildingPage', null);
});
Problem is, sometimes it works and they update their views, sometimes not. The modal always dismisses and the events fire though.
Am I doing something fundamentally wrong here?
Thanks.
Problem is, sometimes it works and they update their views, sometimes
not.
As you can read in this answer, Application state change is caused by three things:
1) Events - User events like click, change, input, submit, …
2) XMLHttpRequests - E.g. when fetching data from a remote service Timers -
3) setTimeout(),setInterval(), because JavaScript
It turns out that these are the only cases when Angular is actually interested in updating the view.
So if you want to update other things outside Angular way, you will have to let Angular know that something has changed and needs to we aware of updating things. You can do this by first importing ngZone like this:
import { ..., NgZone } from '#angular/core';
Declaring it in your constructor like this:
constructor(..., private ngZone: NgZone ) { //... }
And then surrounding your code inside a zone
this._viewControl.dismiss().then(() =>
this.ngZone.run(() => {
// Execute here what you want and Angular will update the view for you.
// ...
this._events.publish('update_myJobsPage', null);
this._events.publish('update_assessmentsPage', null);
this._events.publish('update_buildingPage', null);
});
});
Have you tried onDismiss ?
this._viewControl.onDismiss(() => {
this._events.publish('update_myJobsPage', null);
this._events.publish('update_assessmentsPage', null);
this._events.publish('update_buildingPage', null);
});
So, it turns out, if I empty out my collection when I'm refreshing, so..
e.g
updatePage() {
this.myCollection = [];
this.someService.getItems().then(items => {
this.myCollection = items;
});
}
then the page always shows the update. So I'm going to put this one down to a timing/change detection bug in Angular 2 and move on!

how does Jasmine 'expect' waits for a protractor promise to resolve

I'm working on e2e testing.
I have a confirm pop which doesnt exist on the page till I click a button.
Once the confirm popup is create I get the text from an element there.
After that I click on OK button which causes the confirm popup to be delete from the DOM and also add a new element to the DOM with the value i got the text earlier.
the problem is, because getText() returns a promise, by the time I do the comparison the first element is not present on the screen and the test fails.
if I do expect while the confirm popup on the screen I can see the text of the confirm popup element.
how does Jasmine expect() resolve the promise?
thanks in advance
Something like this?
element(by.id('dangerous-activity')).click().then(function () {
element(by.id('confirmation-text')).getText().then(function (textToConfirm) {
element(by.id('confirm-button')).click().then(function () {
element(by.id('new-element')).getText().then(function (newText)) {
expect(newText).toBe(textToConfirm);
});
});
});
});
Here all promises are explicitly resolved, so Jasmine does not need to resolve any promise anymore.
You can let expect resolve the new-element promise, replacing the last two lines by:
....
expect(element(by.id('new-element')).getText()).toBe(textToConfirm);
....
But you cannot get the textToConfirm in the same expectation, since it is gone by then as you indicated.
This should be the simplest way to do what you want:
$('#open-popup').click();
var textToConfirm = $('#popup-text').getText();
$('#confirm-button').click();
var newText = $('#new-element').getText();
expect(newText).toBe(textToConfirm);
Note this will not work:
$('#open-popup').click();
var textToConfirmElement = $('#popup-text');
$('#confirm-button').click();
var newText = $('#new-element').getText();
expect(newText).toBe(textToConfirmElement.getText());
because here you get the text after the popup is already closed.

jQuery click event behaves differently with live function in Firefox

Using the event click with live function leads to strange behavior when using Firefox*.
With live in Firefox, click is triggered when right-clicking also! The same does not happen in Internet Explorer 7 neither in Google Chrome.
Example:
Without live, go to demo and try right clicking
the paragraphs. A dialog menu should
appear.
With live, go to demo and try right
clicking "Click me!". Now both dialog
menu and "Another paragraph" appear.
*tested with firefox 3.5.3
As far as I know, that is a known issue (bug?). You can easily work around it by testing which button was clicked as follows:
$('a.foo').live("click", function(e) {
if (e.button == 0) { // 0 = left, 1 = middle, 2 = right
//left button was clicked
} else {
//other button was clicked (do nothing?)
//return false or e.preventDefault()
}
});
you might prefer using a switch depending on your specific requirements, but generally you would probably just want to do nothing (or or simply return) if any button other than the left button is clicked, as above:
$('a.foo').live("click", function(e) {
switch(e.button) {
case 0 : alert('Left button was clicked');break;
default: return false;
}
});
I think it's a known "bug", you could potentially query the event object after attaching the click handler ( which gets attached to the document ) and see if its a right click, otherwise manually attach the click handler after you manipulate the DOM.
After looking it up, e.button is the property you want to query:
.live('click', function(e){
if ( e.button == 2 ) return false; // exit if right clicking
// normal action
});
See my answer here: if you don't mind changing the jQuery source a bit, adding a single line in the liveHandler() works around the problem entirely.

Resources