Getting Text from Repeater in Protractor - jasmine

<li ng-repeat="menu in menulist" ng-class="isActive(menu.type, menu.complete)" ng-click="setTopNav(menu.type, menu.complete)">{{menu.label}}</li>
Is the code for our menu. I'm trying to detect an array of the label names with
this.menuNav = element.all(by.repeater('menu in menulist').column('label'));
I bring that page object in a test and try to use the assertion
expect(navHeader.menuNav[1].getText()).toEqual('Label 2');
And I get this error
TypeError: Cannot call method 'getText' of undefined
What am I doing wrong?

Protractor returns a promise, you need to change your code into
element.all(by.repeater('menu in menulist').column('label')).then(function(elems) {
expect(elems[1].getText()).toEqual('Label 2');
});

Related

How Cypress test HTML element is assigned after Ajax call

I have a search input and result display area that are being handled by Ajax call. When user enter a keyword, Ajax call the backend and return HTML string. The JS handler then appends the response HTML into result display area.
Here my steps:
Search input: Vancouver (auto input by browser location)
Result: Welcome to Vancouver
Type into search input: Calgary
Expected: Welcome to Calgary
// Test case
Cypress.Commands.add('assessHeadingInfo', (options) => {
cy.fixture('selectors/index-page').then((selectors) => {
cy.xpath(selectors.head_info).then((heading) => {
cy.searchForArea('Calgary'); // Steps to input keyword and click search
cy.get(heading)
.children('h1')
.should('be.visible');
cy.get(heading)
.children('h1')
.invoke('text')
.should('equals', 'Welcome to Calgary');
});
});
});
Test error:
AssertionError
Timed out retrying after 4000ms: expected 'Welcome to Vancouver' to equal 'Welcome to Calgary'
However, the visual screenshot showed that the 'Welcome to Calgary' text was displayed while the test could not see.
I did follow the guide from Cypress app
Timed out retrying after 4000ms: cy.should() failed because this element is detached from the DOM.
...
You typically need to re-query for the element or add 'guards' which delay Cypress from running new commands. Learn more
I added 'a guard', cy.wait()...but nothing works.
Would you please teach me how to handle this issue?
Thank you.
UPDATE SOLUTION:
Thanks for #jhelguero helped me to find out.
Solution 1:
I added this and it worked well.
cy.contains('h1', 'Welcome to Calgary')
.should('be.visible');
Because I have put all the tests in block of
cy.xpath(selectors.head_info).then((heading) => {
...
});
So, when I do cy.get(heading) it caught the element before Ajax done. It was 'Welcome to Vancouver' instead.
Solution 2:
Then, I separated test steps like this.
cy.searchForArea('Calgary');
cy.xpath(selectors.head_info)
.children('h1')
.should('have.text', 'Welcome to Calgary');
and it was successful.
Summary:
Do this:
cy.xpath({selector}).should(have.text,'ABC');
cy.xpath({selector}).find('h1').should('have.text, '123');
DON'T do this:
cy.xpath({selector}).then((ele) => {
cy.get(ele).should(have.text, 'ABC');
cy.get(ele).find('h1').should('have.text, '123');
});
It looks like your DOM rerenders after the ajax call and thus the element('Welcome to') you were previously reference has been detached.
To fix this you will need to re query using the same selector after the ajax call is completed. You will not need cy.xpath(selectors.head_info).then().
Cypress.Commands.add('assessHeadingInfo', (options) => {
cy.fixture('selectors/index-page').then((selectors) => {
cy.searchForArea('Calgary'); // Steps to input keyword and click search
// use .contains() to search a selector with regex text
cy.contains(selectors.head_info, /Welcome to Calgary/)
.should('be.visible');
});
});
If you visually confirmed the text "Welcome to Calgary", the problem might be that .should('equals', 'Welcome to Calgary') is not retrying enough of preceding steps.
Try shortening the query.
You can also increase the timeout if you notice a lag before the text appears.
cy.get(`${heading} > h1`, {timeout:10_000})
.should('have.text', 'Welcome to Calgary')

Cypress E2E testing: How to test the text message on toast container (or) popup on a web page?

screenshot of html code for the error toast popupI am trying to get the element of popup (or) toast-container and asserting the text, but I am getting an error that element never found. Please someone help?
describe('Wholesoft Login Page', function(){
it('Check Login popup', function(){
cy.visit('https://https://platform.wholesoftmarket.com/login')
cy.get('#email').type('kj#gmail.com')
cy.get('#password').type('hello')
cy.get('button.btn.active').click()
cy.get('div').within(($div)=>{
cy.get('div.overlay-container').should('have.text','no record found')
})
})
})
You need to set a bigger timeout for the element, waiting to be present in the DOM:
cy.get('div[aria-label="Error"]', {timeout: 10000}).should('have.text','no record found')
// maybe you can use the class selector on that div (div.toast-title.ng-star-inserted)
// default timeout is 6000
// increase it until it is caught by Cypress
You need to wait till the toast or pop-up element is visible to access it for further steps.
Please make sure element selector is right.
following cmd
cy.get('div.overlay-container').should('be.visible').should('have.text','no record found')

Compare two base64 image strings in Cypress

I would like to compare the two following base64 strings to ensure that a photo is uploaded as expected. How would I do that? This my code below, I currently get the error message:
You attempted to make a chai-jQuery assertion on an object that is neither a DOM object or a jQuery object.
The chai-jQuery assertion you used was:
> text
This is my code:
it('should allow the user to select a navbar component and replace a logo', () => {
cy.getIframeBody('builder-showcase').find('[data-cy="navbar-component"]').eq(0).click();
cy.getIframeBody('builder-showcase').find('[data-cy="navbar-logo-image"]').eq(0).click();
cy.get('[data-cy="builder-sidebar-menu-select-image-navbar"]').eq(0).click();
const navbarLogoImage = 'navbar-logo-image.png';
cy.fixture(navbarLogoImage).then(originalLogoImage => {
cy.get('[data-cy="image-file-input"]').attachFile(navbarLogoImage);
cy.get('[data-cy="image-alt-text"]')
.clear()
.type('Twitter logo image');
cy.get('.source-image').invoke('attr', 'src')
.then(uploadedLogoImage => {
expect(uploadedLogoImage).to.have.text(originalLogoImage);
})
});
});
The chai chainer to.have.text verifies that an element (DOM or JQuery) contains the expected text. Your uploadedLogoImage is a string. You should then use a chainer that will work with a string like:
expect(uploadedLogoImage).to.eq(originalLogoImage);
or
expect(uploadedLogoImage).to.contain(originalLogoImage);
or with should(), you can query the element directly:
cy.get('.source-image').should('have.attr', 'src', originalLogoImage);
Hint: instead of using eq(0), you may also use first() which gives a better readability.

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.

Sinon JQuery selectors mock

I am trying to test a function using mocha/sinonjs. The function I want to test is responsible for showing or hiding some element in my DOM.
This is my function
var updateUI = function() {
$('#login').show();
$('#logout').hide();
};
I tried to mock using sinon but I'm not sure if it is possible or the correct thing to do in this case.
This is what I have tried but I keep getting an error "TypeError: undefined is not a function" during the expect call
var mockLogin = sinon.mock($);
mockLogin.withArgs('#login').expects("show").once();
I simple want to test my 2 jquery calls have been called. I tried to use spies but continue to get exceptions
Looking further into the sinon doc I found that the following worked for me.
var jQueryShow = sinon.stub($.fn, 'show');
var jQueryHide = sinon.stub($.fn, 'hide');
jQueryShow.callCount.should.be.equal(1);
jQueryShow.thisValues[0].selector.should.be.equal("#login");
jQueryHide.callCount.should.be.equal(1);
jQueryHide.thisValues[0].selector.should.be.equal("#logout");
I'm not sure if there an easier way but it checks for the selectors I need

Resources