How to test an Alert with Cypress - cypress

I'm testing a data form using Cypress, but am stuck on a step that displays an alert on the page.
This is the test, but it's not working.
describe('Alert is displayed with warning text', () => {
it('Assert that the alert is displayed after entering data', () => {
cy.visit('/')
cy.get('input').type('some data').blur()
cy.on ('window:alert', (text) => {
cy.wrap(text).should('eq', 'alert text')
})
})
})
How do I test this alert that pops up on the page?

The code cy.on('window:alert') is an event listener.
Instead of adding a callback as you would do for other commands, you can add a stub and check the stub function has been called plus the text it is called with.
Also because it's a listener, you must set it up before the event that triggers the event.
describe('Alert is displayed with warning text', () => {
it('Assert that the alert is displayed after entering data', () => {
cy.visit('/')
const stub = cy.stub()
cy.on ('window:alert', stub)
cy.get('input').type('some data').blur()
// wait for the event to be handled
cy.then(() => {
expect(stub.getCall(0)).to.be.calledWith('alert text')
})
})
})

You can modify your code to use expect() instead of should().
The problem is Cypress does not like commands inside event listeners.
You must use a done() callback to avoid a false positive when the alert is not fired.
describe('Alert is displayed with warning text', () => {
it('Assert that the alert is displayed after entering data', (done) => {
cy.visit('/')
cy.on ('window:alert', (text) => {
expect(text).to.eq('alert text')
done() // waiting for event, fails on timeout
)
cy.get('input').type('some data').blur()
})
})

Related

Cypress test: Writing multiple elements to database failed

Trying to write multiple elements into database failed, only the last one is written:
// my_test.cy.js
import CreateProductPage from "../pages/CreateProductPage";
describe('product detail page', () => {
beforeEach(() => {
cy.login('admin', 'shop')
})
it('should print typed product', () => {
cy.createProduct(null, 'Component Product 0', '0')
CreateProductPage.elements.productDetailTabs().should('exist') // <--- to detect that the entity is written
cy.createProduct('combinable', 'Combined Test Product', '0')
CreateProductPage.elements.productDetailTabs().should('exist') // <--- to detect that the entity is written
})
})
// commands.js
Cypress.Commands.add('createProduct', (type, name, grossPrice) => {
cy.visit('/#/sw/product/create')
CreateProductPage.elements.productDetailTabs().should('not.exist').then(() => {
if(type === 'combinable') {
CreateProductPage.elements.radioBtnCombinableProduct().click()
}
CreateProductPage.elements.inputProductName().clear().type(name)
CreateProductPage.elements.inputPriceFieldGross().type(grossPrice)
SwPageHeader.elements.btnProductSave().click()
})
})
Questions:
This failed because of asynchronous nature of cypress?
If so, how to interrupt? Chaining with then(), the behavior is the same
With this is code (adding wait()) it works, but i'm looking for the right way
// my_test.cy.js
describe('product detail page', () => {
beforeEach(() => {
cy.login('admin', 'shop')
})
it('should print typed product', () => {
cy.createProduct(null, 'Component Product 0', '0')
CreateProductPage.elements.productDetailTabs().should('exist')
cy.wait(300)
cy.createProduct('combinable', 'Combined Test Product', '0')
CreateProductPage.elements.productDetailTabs().should('exist')
})
})
EDIT 1
// pages/CreateProductPage.js
class CreateProductPage {
elements = {
productDetailTabs: () => cy.get('div.sw-product-detail-page__tabs'),
radioBtnCombinableProduct: () => cy.get('.sw-product-detail-base__info input#type_combinable_product-0'),
radioBtnUnCombinableProduct: () => cy.get('.sw-product-detail-base__info input#type_combinable_product-1'),
inputProductName: () => cy.get('input#sw-field--product-name'),
inputPriceFieldGross: () => cy.get('div.sw-list-price-field__price input#sw-price-field-gross'),
}
}
module.exports = new CreateProductPage();
If the problem is one of waiting, you will need to figure out something that indicates to the user that the save was successful and test that.
For example, if there was a toast message on screen:
...
SwPageHeader.elements.btnProductSave().click()
cy.contains('span', 'Product was saved').should('be.visible')
Blockquote
This failed because of asynchronous nature of cypress?
Code behaves synchronously if you have only cy commands inside the code block.
As suggested in https://stackoverflow.com/a/74721728/9088832 you should wait for some element to appear or for an API request to be completed that is responsible for the product creation.

Angular jasmine how to spy an async spy method

I am new to writing test with async/await in angular.
I have the following code. The service method is an async method. The test fails saying component.options.length is 0.
Can anyone please help me how to fix the error so the options has got the value i set in spy?
Thanks
spec.ts
spySideNavService = jasmine.createSpyObj('SideNavService', [], {
setOrgUserDetails: () => {},
loadMenus: () =>
[
{
id: 'my-menu',
label: 'My Menu',
icon: 'far fa-envelope fa-2x',
url: 'url'
}
] as NavOption[]
});
describe('ngOnInit', () => {
it('should add navigation options', () => {
expect(component.options.length).toBeGreaterThan(0);
});
});
component:
ngOnInit(): void {
this.options = await this.sideNavService.loadMenus();
}
SideNavService:
async loadMenus(): Promise<NavOption[]> {
//logic
}
Tried answer given below but still not working:
describe('ngOnInit', () => {
it('should add navigation options', fakeAsync(() => {
// !! call tick(); to tell the test to resolve all promises
// before coming to my expect line
tick();
expect(component.options.length).toBeGreaterThan(0);
}));
});
You need to use fakeAsync/tick to control promises.
// !! add fakeAsync
it('should add navigation options', fakeAsync(() => {
// !! call tick(); to tell the test to resolve all promises
// before coming to my expect line
// !! call ngOnInit
component.ngOnInit();
console.log(component.options);
tick();
console.log(component.options);
expect(component.options.length).toBeGreaterThan(0);
}));
Before, the test would go to the await line and go back to the test for the expect because the await is saying to do this later. Now with the tick, we are saying if they are any promises created, resolve them before moving forward.
Also, I think you're missing a Promise.resolve on loadMenus.
loadMenus: () => Promise.resolve(
[
{
id: 'my-menu',
label: 'My Menu',
icon: 'far fa-envelope fa-2x',
url: 'url'
}
] as NavOption[])
I am thinking the Promise.resolve is required so it can be awaited.
edit
I don't think the done callback will help you.
You can try using await fixture.whenStable() to wait for the promise(s). Try this:
describe('ngOnInit', () => {
it('should add navigation options', async () => {
component.ngOnInit();
await fixture.whenStable();
expect(component.options.length).toBeGreaterThan(0);
});
});

Cypress.Cookies.preserveOnce('session_id') isn't working when called in afterEach hook

I'm working with cypress tests and I want to avoid having to log in before each test. so, I wanted to preserve the cookies in each test file.
The log statement in the afterEach hook is triggered, however cookies are cleared in the second testcase.
describe('Users Page Scenarios', () => {
before(() => {
myApp.pages.Login.navigate();
myApp.pages.Login.login(
credentials.globalAdmin.email,
credentials.globalAdmin.password
);
});
beforeEach('navigate to users page before each test', () => {
myApp.sharedComponents.Header.navigateToUsers();
});
afterEach(() => {
Cypress.Cookies.preserveOnce('session_id');
cy.log('test');
});
describe('Users List', () => {
it('Should redirect the user to users page after clicking on users in the navigation header', () => {
cy.url().should('eq', `${Cypress.config().baseUrl}/user`);
});
})
describe('New User Creation', () => {
it('Should open new user modal after clicking on invite administrator', () => {
myApp.pages.Users.UsersList.inviteAdministrator();
cy.url().should('eq', `${Cypress.config().baseUrl}/user/new`);
});
it('Should create a new user successfully', () => {
myApp.pages.Users.UsersList.inviteAdministrator();
myApp.pages.Users.UsersInfo.createNewUser(user.generateUser());
})
});
The docs indicate that Cypress.Cookies.preserveOnce('session_id') is used in beforeEach().
Looks like after is too late.
describe('Dashboard', () => {
before(() => {
// log in only once before any of the tests run.
// your app will likely set some sort of session cookie.
// you'll need to know the name of the cookie(s), which you can find
// in your Resources -> Cookies panel in the Chrome Dev Tools.
cy.login()
})
beforeEach(() => {
// before each test, we can automatically preserve the
// 'session_id' and 'remember_token' cookies. this means they
// will not be cleared before the NEXT test starts.
//
// the name of your cookies will likely be different
// this is an example
Cypress.Cookies.preserveOnce('session_id', 'remember_token')
})
If you have localStorage or sessionStorage to preserve, or you have not identified all cookies correctly, try with cy.session()
beforeEach(() => { // must be beforeEach()
cy.session('mySession', () => { // preserves localStorage, sessionStorage, cookies
myApp.pages.Login.navigate();
myApp.pages.Login.login(...); // only called once (despite beforeEach())
})
})

How to wait for the api call to finish and then check if element present using cypress?

i am new to cypress and i am trying to check if the element exists on a page once the api call is finished.
i do a http post to url 'things/thing1' and once this api finishes i want to check if span element is present on page.
i have tried something like below.
const setUp = () => {
cy.apiPatchSomethings(something1)
.then(() => {
cy.reload();
});
}
describe('Suite name', () => {
before(() => {
setUp();
});
it('test case', () => {
cy.contains('span');
}
});
the above code doesnt work. even before span element is seen on page it checks for span element.
if i use cy.wait(10000) like below it works
it('test case', () => {
cy.wait(10000);
cy.contains('span');
});
but i dont want to use cy.wait. is there some other way to solve this. could someone help me with this. thanks.
Cypress command cy.contains() when called with a single argument is looking for content,
Syntax
cy.contains(content)
cy.contains(content, options)
cy.contains(selector, content)
cy.contains(selector, content, options)
but I'm guessing you are looking for a span element, so use
cy.get('span')
or
cy.contains('span', 'my-content-in-span')
Assuming that's not the problem, just some arbitrary sample code...
Your can modify the setup function to return a promise, in order to wait for the reload.
const setUp = () => {
return cy.apiPatchSomethings(something1) // need a return here
.then(() => {
return new Cypress.Promise(resolve => { // inner return also
cy.reload()
resolve(true) // resolve will signal reload is finished
})
});
}
Because setup() is invoked inside before() Cypress will wait for the promise to resolve before proceeding.
Please don't add extra waits or timeouts, which is too often suggested. This will only lead to flaky tests.
Note if you don't mind ditching the setup() function, it becomes a lot simpler
describe('Suite name', () => {
before(() => {
cy.apiPatchSomethings(something1)
.then(() => cy.reload() ); // all commands here will be completed
// before the tests start
});
it('test case', () => {
cy.contains('span', 'my-content-in-span');
}
});
1.You can wait for span to be visible. The default timeout that cypress provides is 4 seconds.
cy.contains('span').should('be.visible')
2.If you want to give a custom timeout(eg. 10 sec) specific to this command, you can use:
cy.contains('span', { timeout: 10000 }).should('be.visible')
3.If you want to increase the timeout globally you mention this in your cypress.json file:
"defaultCommandTimeout": 10000
and, then just use:
cy.contains('span').should('be.visible')
Now, all your commands will have a default timeout for 10 seconds.

How can I close the browser after a mocha test?

I am new to WebdriverIO and Mocha and I wrote 2 tests in order to check our web app.
I want to close the browser and sign-in again, after I run the first test.
When I used browser.close(), I got an error the browser.close() is not a function and basically the second test runs right after the first test with the browser being open.
Is there a way to close to browser after a test in Mocha?
describe('Verify that a signed-in user can get to the page', () => {
it('Title assertion ', () => {
const viUrl = 'https://buyermanage.com/bmgt/lock?useMock=pre.json';
signInPage.signIn('rich221', 'password', viUrl);
assert.equal(preApp.getTitle(), 'Pre-App', 'Title Mismatch');
});
});
describe("Verify that a not signed-in user can't get to the page and is redirected to login page", () => {
it('Title assertion ', () => {
const viUrl = 'https://buyermanage.com/bmgt/lock?useMock=pre.json';
assert.equal(preApp.getTitle(), 'Pre-App', 'Title Mismatch');
});
});
Try using browser.reloadSession():
after(() => {
// Start a new session for every 'locale' (wipe browser cache):
browser.reloadSession();
});
In your particular example, you'd need to use it in the afterEach() hook, and wrap the describes, respectively the it statements (depending on your test-suite requirements) inside a parent describe block:
describe('The world is your oister', () => {
describe('Verify that a signed-in user can get to the page', () => {
it('Title assertion ', () => {
// bla bla bla
});
});
describe("Verify that a not signed-in user can't get to the page and is redirected to login page", () => {
it('Title assertion ', () => {
// bla bla bla
});
});
afterEach(() => {
browser.reloadSession();
});
});

Resources