Cypress is looking for a property in the wrong object - testing

Here are the two tests:
it("It should now show the Send Reset Instructions link", () => {
fsrloginpage.SendResetInstructions("Send me reset").should('exist');
});
it("Ihe Send Reset Instructions link should be disabled if the Email is empty", () => {
fsrloginpage.UsernameEmailField().clear();
fsrloginpage.SendResetInstructions("Send me reset").should('have.attr','disabled','true');
});
Here is the .SendResetInstructions object definition:
SendResetInstructions(linklabel){
return cy.get('button[class^="mat-focus-indicator mat-button mat-raised-button
mat-button-base mat-primary"]').contains(linklabel);
}
Here are my results:
It should now show the Send Reset Instructions linkpassed
TEST BODY
1
getbutton[class^="mat-focus-indicator mat-button mat-raised-button mat-button-base mat-primary"]
2
containsSend me reset
3
assertexpected <span.mat-button-wrapper> to exist in the DOM
Ihe Send Reset Instructions link should be disabled if the Email is emptyfailed
TEST BODY
1
getinput[id="mat-input-2"]
2
clear
3
getbutton[class^="mat-focus-indicator mat-button mat-raised-button mat-button-base mat-primary"]
4
containsSend me reset
5
assertexpected <span.mat-button-wrapper> to have attribute disabled
AssertionError
Timed out retrying after 4000ms: expected '<span.mat-button-wrapper>' to have attribute 'disabled'
mcare/integration/FSRLoginBVT.spec.js:68:57
66 | it("Ihe Send Reset Instructions link should be disabled if the Email is empty", () => {
67 | fsrloginpage.UsernameEmailField().clear();
> 68 | fsrloginpage.SendResetInstructions("Send me reset").should('have.attr','disabled','true');
| ^
69 | });
70 |
71 | it("Ihe Send Reset Instructions link should be enabled if the Email is filled", () => {
So, it finds the object in the first test, but there is an assert (#1). On the second test, it seems to be ignoring the button, and trying to use the <span.mat-button-wrapper> object it mentioned in the assert (#2). I think it is doing this because the identifier for the button is within a span inside the button. Here is the code I am testing:
<button mat-button="" mat-raised-button="" color="primary" class="mat-focus-indicator mat-button mat-raised-button mat-button-base mat-primary mat-button-disabled" disabled="true">
<span class="mat-button-wrapper"> Send me reset password instructions </span>
<span matripple="" class="mat-ripple mat-button-ripple"></span>
<span class="mat-button-focus-overlay"></span>
</button>
Any thoughts on how to get around this? The best solution would be to get the developers to put IDs in their code, but that is not likely to happen in a timely manner.

You don't really want those classes in your selector, they are for Material Design styling and are likely to be so common they don't differentiate the button.
Just id the button by it's label contents
SendResetInstructions(linklabel) {
return cy.contains('button', linklabel);
}
I'd say the POM method naming also implies fixed text, for example you would never call this way, it would be confusing
fsrloginpage.SendResetInstructions("Log me in")
so you might as well encapsulate the text
SendResetInstructions() {
return cy.contains('button', 'Send me reset');
}
Lastly, use the be.disabled assertion not the have.attribute assertion because disabled attribute is (sometimes) present or absent not true or false. be.disabled covers both scenarios.
fsrloginpage.SendResetInstructions().should('be.disabled');

The issue comes from the fact that your .contains() yields the span element it finds, instead of the button element that wraps the three spans. The yielded span does not have disabled=true, so your assertion fails.
In order to return the parent element, there are a few strategies you could employ, but the easiest one would be to pass in the desired element type that .contains yields.
SendResetInstructions(linklabel){
return cy.get('button[class^="mat-focus-indicator mat-button mat-raised-button
mat-button-base mat-primary"]').contains('button', linklabel);
}

Related

Timed out retrying: cy.type() failed because this element is not visible. has CSS property: position: fixed and it's being covered by another element:

Unable to find the textbox element in a new pop-up window.
Actual Result:
Expected Result:
Able to type value in the text box.
Adding the cypress snippet below,
it("Add business test",function(){
cy.xpath("//a[contains(.,'1099/W-2')]").click({force:true});
cy.wait(5000);
cy.get(':nth-child(2) > .btn-hover-shrink > .v-btn__content').click({force: true});
cy.contains('Start Now').click({force:true});
//Add business pop-up open
cy.contains('Business Name').click({force: true}).type("Test LLC");
})
You can add {force: true} with type() to disable error checking -
cy.get('[id*="input-"]').type("Test LLC", {force: true});
The error message indicates that you are trying to type() into the label. That's because cy.contains('sometext') selects the element "owning" the text, which is the label, but you can also select a parent by using the pattern cy.contains(<parentSelector>, 'sometext')
Take a look at the page DOM, if you have a common parent of the <label> and the <textarea> (or <input>), like this
<div>
<label>Business Name</label>
<input />
</div>
you can target that parent in the .contains()
cy.contains('div', 'Business Name')
.find('input') // drill down to the element receiving the text
.should('be.visible') // since there's a toolbar in the mix, wait for visibility
.type('Test LLC')
An alternative might be to use .closest()
cy.contains('Business Name') // gives you the label
.closest('input') // nearby element receiving the text
.should('be.visible') // wait for visibility
.type('Test LLC')
Here's one more way, making use of the label's "for" attribute
cy.contains('Business Name') // gives you the label
.invoke('attr', 'for') // which id is it for?
.then(id => {
cy.get('#' + id) // get the actionable element
.should('be.visible') // wait for visibility
.type('Test LLC')
})
Taking a look at the Vuetify form component here which has a similar HTML to yours
<div class="v-text-field__slot">
<label for="input-6" class="v-label theme--light" style="left: 0px; right: auto; position: absolute;">Last name</label>
<input required="required" id="input-6" type="text">
</div>
the same test code you have succeeds on the sample code
cy.contains('Last name')
.click({force: true})
.type("Test LLC"); // text appears in the input
but if I simulate the covering toolbar, it fails with the same error you have.
Adding .type("Test LLC", {force: true}) also fails with a different error
cy.contains('Last name')
.click({force: true})
.type("Test LLC", {force: true});
cy.type() failed because it requires a valid typeable element.
Using the parent contains to find the "typeable element" and applying force: true option works
cy.contains('div', 'Business Name')
.find('input')
.should('be.visible')
.type("Test LLC", {force: true})
This assumes the toolbar remains static and does not animate away, in which case it would work without the force: true option.

Get text from notification login message

I have a Angular application which I would like to test using Selenium Web Driver with Java.
After successful or unsuccessful user login notification message is displayed into the web page:
<div class="overlay-container">
<div id="toast-container" class="toast-top-right toast-container">
<div toast-component="" class="toast-error ngx-toastr ng-trigger ng-trigger-flyInOut" style="opacity: 1;">
<!----><button aria-label="Close" class="toast-close-button ng-tns-c11-11 ng-star-inserted" style=""><span class="ng-tns-c11-11" aria-hidden="true">×</span></button><!----><!----><!---->
<div aria-live="polite" role="alertdialog" class="toast-message ng-star-inserted" aria-label="Logon failure: unknown user name or bad password." style=""> Logon failure: unknown user name or bad password. </div>
<!---->
</div>
</div>
</div>
I would like to intercept the text of the message. I tried this:
// //*[#id="toast-container"]
// //*[#id="toast-container"]/div/div
// Click Login button to submit login form
WebDriverWait failedLoginWebDriverWait = new WebDriverWait(driver, 7000);
WebElement failedLoginWebElement = failedLoginWebDriverWait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[#id='toast-container']")));
boolean displayed = failedLoginWebElement.isDisplayed();
WebElement element = failedLoginWebElement.findElement(By.xpath("//*[#id='toast-container']/div/div"));
String text = element.getText();
System.out.println("displayed label: " + displayed);
System.out.println("text: " + text);
I get false and NPE for the second findElement.
Do you know how I can solve this issue?
isDisplayed() is returning false. This might be because you're looking (waiting) for the presence of the element in the DOM. This doesn't mean it isDisplayed(). It's a bit complicated, there is a good answer here but in summation, it's size needs to be non-zero and some atrributes must be set for it to be considered Displayed. Likely the toast is IN the DOM, but not yet fully rendered to be considered displayed.
The second issue with the NPE might be the fact you're taking the toast element and doing a findElement on it which means the xpath is going to be executed relative to the toast element. Which means the xpath is actually executing as:
//*[#id='toast-container']/.//*[#id='toast-container']/div/div
which is not what you truly want. You likely want to just do:
WebElement element = failedLoginWebElement.findElement(By.xpath("/div/div"));

Protractor Failed: element not interactable in vue2-editor

I am using 'protractor' for testing my web application.
I add 'vue2-editor' in my project, I want to write something in the editor and save data.
this is the html that generate by Vue :
<div id="quill-container" class="ql-container ql-snow">
<div class="ql-editor ql-blank" data-gramm="false" contenteditable="true" data-placeholder="Add Note...">
<p>
<br>
</p>
</div>
<div class="ql-clipboard" contenteditable="true" tabindex="-1"></div>
<div class="ql-mention-list-container" style="display: none; position: absolute;">
<ul class="ql-mention-list"></ul>
</div>
</div>
this is my test code :
describe('When you save a note', function () {
beforeAll(function () {
browser.get('http://example');
element.all(by.css(" [id='quill-container'] > div.ql-editor.ql-blank")).click();
element.all(by.css(" [id='quill-container'] > div.ql-editor.ql-blank")).sendKeys('Some Notes add here');
element(by.css("[id='add-note-btn']")).click();
browser.sleep(1000);
});
it('You must see your note at the current page', function () {
expect(element(by.xpath('//p[text()="Some Notes add here"]')).isPresent()).toBe(true);
});
});
When I execute the code, Protractor gives this message :
Failed: element not interactable
What should i do ?
Basically, there are four reasons why an element is not interactable.
1) Timing - the time it takes for elements to load. For this, you need to check how to use implicit an explicit wait
2)Check if the element is in a frame
3) Incorrect locator
4) Wrong implementation of responsiveness. This still stems from no 3). Some websites have only one code turned on for mobile and web versions. So, the element will have more than one instances when you check the xxxxx.size. You will have to search through the list for the one whose display != none.

Is there a way to delay the ngx-bootstrap accordion opening?

<accordion [closeOthers]=true>
<accordion-group *ngFor="let activity of activities" [heading]="activity.Name" (click)="openPanel(activity)" (isOpenChange)="openStatusChange($event)">
<ul *ngFor="let chemical of chemicals">
<li>{{chemical.BrandName}}</li>
</ul>
<div *ngIf="!chemicals?.length > 0">No chemicals associated with this activity type.</div>
</accordion-group>
</accordion>
When the accordian header is clicked, it opens and runs and fires 'openPanel()' which is an http.get, which then populates the panel. If the array returns empty, the *ngIf will display the "no associated stuff" message.
The problem is there is a very slight lag between the time the accordion opens and the array is filled, so the chemicals array is always empty when the accordion opens. This makes it so the "no associated stuff" message appears for about half a second, then the list populates. So I am wondering if there is a way to either delay the opening until the array is populated, or suggestions welcome.
You could use a boolean flag or function for the ngIf. Set the flag when the http promise returns as successful, that way the ngIf only triggers after the http call promise is resolved.
something like:
http.get('url').subscribe(response => {
show = true;
});

Jaws screen reader IE 11 select tag issue

I have some issues with IE 11 and Jaws. Please take a look at the example below.
Simplified code example:
<input type="text" />
<span role="alert" aria-live="assertive" id="err"></span>
<select id="colours">
<option value="White">White invalid</option>
<option value="Green">Green invalid</option>
<option value="Red">Red</option>
<option value="Blue">Blue</option>
</select>
var i = 0;
$('#colours').on('keyup', function (e) {
if ($(this).prop('selectedIndex') < 2) {
$('#err').html('an error has occurred ' + i++);
}
else {
$('#err').html('');
}
});
jsfiddle: https://jsfiddle.net/obwapffq/2/
Basically, I am validating the selected element in a dropdown list (in the example 'white' and 'green' values are invalid). I am only using the keyboard's up and down buttons to change the selected value. If an invalid option is selected, I update the content of a span element with the appropriate error message. The span element has role="alert" and aria-live="assertive". I have 2 issues:
If the selected option is invalid, the error message is read out, but the selected option is not read out and so the user does not know what option is invalid i.e. what option caused that error.
Sometimes even the valid options are not read out. This mostly happens with the first valid option in the list i.e. in the example 'Red'
This is working 100% correct with NVDA.
Any ideas?
Please add aria-label="yourOptionText" attribute to your option tag.
for e.g. aria-label="White Invalid".
Hope this will help.