I'm using testcafe to run some tests in an ecommerce page, but a random pop up is breaking the test. When it appears on the window, the Testcafe is unable to click on the next selector and move forward with the test, and then fail.
Currently, I'm using .js files to hold the selectors, like:
import { Selector } from 'testcafe';
export default class Checkout {
constructor () {
//address
this.addressName = Selector('input#CC-checkoutCepAddressBook-sfirstname');
this.addressLastname = Selector('input#CC-checkoutCepAddressBook-slastname');
//Rest of selectors...
}
Then, I import them to another .js and declare the tests like functions:
import { ClientFunction } from 'testcafe';
import { Selector } from 'testcafe';
import Fixture from '../../../DesktopModel/Chrome/fixture.js';
import Home from '../../../DesktopModel/Chrome/home.js';
import Cart from '../../../DesktopModel/Chrome/cart.js';
...
const fixtureUrlBase = new Fixture();
const home = new Home();
const pdp = new Pdp();
const cart = new Cart();
...
export async function checkoutLoggedBoleto(t) {
await t
.click(pdp.addToCartBtn)
.click(home.finishOrderBtn)
.click(cart.finishOrderBtn)
//Rest of the test actions...}
Finally, I'm executing another.js where I declare the tests using test command:
test
.before(async t => {
await login(t);
})
('Desktop - User Login + Checkout with Invoice', async t => {
// Function Login => Search => PDP => Checkout with Invoice
await checkoutLoggedBoleto(t);
});
Since it is a random event (it happens in different moments, like sometimes in the product page and sometimes in the checkout page), is possible to use some conditional test just bypass this popup, like if the pop up 'x' appears on the screen, click on 'close popup' and continue with test, else continue with the test.
I search in testcafe Test API and have not found such a function.
I'm using testcafe 0.17.0.
TestCafe doesn't provide an API for that. To handle you case, you can check whether the popup appears before each action.
Optionally, to make your code cleaner, you can wrap TestCafe API actions in the following way:
import { t, Selector } from 'testcafe';
const closePopupBtn = Selector('.close-popup');
async function checkPopup () {
if(await closePopupBtn.exists)
await t.click(closePopupBtn);
}
const tc = {
click: async selector => {
await checkPopup();
await t.click(selector);
}
}
test('my test', async () => {
await tc.click('.btn1');
await tc.click('.btn2');
});
Related
This is my Spec:
import {DotComPage} from "../../../pages/web/dotComPage";
import {browser, element, by, $, $$, ProtractorExpectedConditions, ElementFinder, protractor, } from "protractor";
import {HkZhMarket} from "./HkZhMarket.e2e-spec";
import {AuZhMarket} from "./AuZhMarket.e2e-spec";
export function AuEnMarket() {
describe("Australia English", () => {
const dotComPage = new DotComPage();
const EC = protractor.ExpectedConditions;
browser.waitForAngularEnabled(false)
it('Clicks on the market Australia and language English', async () => {
dotComPage.get();
browser.wait(protractor.ExpectedConditions.visibilityOf(dotComPage.firstTimeVisitorButton))
dotComPage.firstTimeVisitorButton.click();
browser.waitForAngular();
browser.wait(EC.presenceOf($('.optanon-alert-box-wrapper[style="bottom: 0px;"]')), 5000);
browser.wait(EC.elementToBeClickable(dotComPage.acceptCookiesButton), 5000);
dotComPage.acceptCookiesButton.click();
dotComPage.usFlagButton.click();
browser.wait(protractor.ExpectedConditions.visibilityOf(dotComPage.auMarketButton), 5000)
dotComPage.auMarketButton.click();
dotComPage.auFlagButtonEnglish.click();
browser.wait(EC.urlContains('enu-AU'));
});
});
}
This is the test runner that I created which pulls the function that I wrapped around my test:
import {AuEnMarket} from "..//FlagMarketLanguage/AuEnMarket.e2e-spec";
import {AuZhMarket} from "../FlagMarketLanguage/AuZhMarket.e2e-spec";
describe ('FlagMarketLanguageTests', async ()=> {
AuEnMarket()
AuZhMarket()
})
When I run the FlagMarketlanguageTests the first test always passes but the browser does not close. The 2nd tests (and I have more that I didn't post) always fails. Errors that I get are:
Failed: This driver instance does not have a valid session ID (did you call WebDriver.quit()?) and may no longer be used.
and
A Jasmine spec timed out. Resetting the WebDriver Control Flow.
or
Failed: invalid session id
Just write your spec in the export file. Don't have to export in a method format.
import {DotComPage} from "../../../pages/web/dotComPage";
import {browser, element, by, $, $$, ProtractorExpectedConditions, ElementFinder, protractor, } from "protractor";
import {HkZhMarket} from "./HkZhMarket.e2e-spec";
import {AuZhMarket} from "./AuZhMarket.e2e-spec";
describe("Australia English", () => {
const dotComPage = new DotComPage();
const EC = protractor.ExpectedConditions;
browser.waitForAngularEnabled(false)
it('Clicks on the market Australia and language English', async () => {
dotComPage.get();
browser.wait(protractor.ExpectedConditions.visibilityOf(dotComPage.firstTimeVisitorButton))
dotComPage.firstTimeVisitorButton.click();
browser.waitForAngular();
browser.wait(EC.presenceOf($('.optanon-alert-box-wrapper[style="bottom: 0px;"]')), 5000);
browser.wait(EC.elementToBeClickable(dotComPage.acceptCookiesButton), 5000);
dotComPage.acceptCookiesButton.click();
dotComPage.usFlagButton.click();
browser.wait(protractor.ExpectedConditions.visibilityOf(dotComPage.auMarketButton), 5000)
dotComPage.auMarketButton.click();
dotComPage.auFlagButtonEnglish.click();
browser.wait(EC.urlContains('enu-AU'));
});
});
In the main specs just require the file and delete it from cache before loading it for second time.
describe ('FlagMarketLanguageTests', async ()=> {
require('../FlagMarketLanguage/AuZhMarket.e2e-spec');
let resolved = require.resolve('../FlagMarketLanguage/AuZhMarket.e2e-spec');
delete require.cache[resolved];
require('../FlagMarketLanguage/AuZhMarket.e2e-spec');
})
Looking to set custom referrer for my tests with Test Cafe but cannot find right solution for that. On Firefox you can easily change referrer with some plugins but how to do it within Test Cafe ?
You can use the Request Hooks mechanism for this purpose. I created an example to demonstrate this approach:
import { RequestHook } from 'testcafe';
fixture `fixture`
.page `http://example.com`;
export class MyRequestHook extends RequestHook {
constructor (requestFilterRules, responseEventConfigureOpts) {
super(requestFilterRules, responseEventConfigureOpts);
}
async onRequest (event) {
event.requestOptions.headers['Referer'] = 'http://my-modified-referer.com';
}
async onResponse (event) {
}
}
const hook = new MyRequestHook();
test.requestHooks(hook)('referer', async t => {
await t.navigateTo('https://www.whatismyreferer.com/');
await t.debug();
});
Agenda: I wanted to run login method before all tests and Logout method after all tests, so that if the before hook fails, the test execution won't happen.
I added login logic in fixture.before hook as shown in the code below. But it's giving the following error, can some help me to fix it.
Test file
import { Selector } from "testcafe";
import LoginPage from '../page-objects/login.po';
const loginPage = new LoginPage();
fixture`Getting Started`
.page`https://example.com/`
.before(async t => {
await loginPage.login();
});
test("My First Test", async t => {
const str = await Selector('.home-container h1').textContent;
console.log(str);
});
Pageobjects class
import { Selector, t } from 'testcafe';
import CommonFunctions from '../commons/common-fns'
export default class LoginPage{
constructor () {
this.emailTxtBox = Selector('input[type="email"]');
this.nextBttn = Selector('button[type="submit"]');
this.microsoftNextBttn = Selector('input[type="submit"]');
this.passwordTxtBox = Selector('input[type="password"]');
this.signinBttn = Selector('input[type="submit"]');
this.noBttn = Selector('#idBtn_Back');
}
async login() {
await t
.typeText(this.emailTxtBox, '')
.click(this.nextBttn)
.typeText(this.emailTxtBox, '')
.click(this.microsoftNextBttn)
.typeText(this.passwordTxtBox, '')
.click(this.signinBttn)
.click(this.noBttn);
}
}
You have to use beforeEach fixture hook instead of before
https://devexpress.github.io/testcafe/documentation/test-api/test-code-structure.html#fixture-hooks
I have the following scenario:
Load page
Expect spinner is hidden
Type username Click search
Expect spinner display
After a few seconds delay, expect spinner to hide
Assert the right user details are displayed
Here is the working demo
I have mocked the network request in my test spec, but I am unable to understand how to assert spinner is visible after I click the search button
Here is my test spec:
import {Selector, RequestMock} from "testcafe";
import mockUser from "../mocks/mockUser.json";
var apiMocks = RequestMock()
.onRequestTo(/\/api\/users/)
.respond(mockUser, 200, {
'access-control-allow-credentials': "*",
'access-control-allow-origin': "*"
})
fixture `When a user is searched`
.page(`http://localhost:3000/`)
.requestHooks(apiMocks);
test("Should fetch user details", async t => {
const spinnerEl = Selector("[data-test-id='spinner']");
await t.expect(spinnerEl.exists).notOk();
await t
.typeText("[data-test-id='txt-search']", "foo")
.click("[data-test-id='btn-search']");
// This line does not work
// await t.expect(spinnerEl.exists).ok();
await t.expect(Selector("[data-test-id='username']").innerText).eql("Foo Bar");
await t.expect(Selector("[data-test-id='userid']").innerText).eql("foo");
})
I am new to TestCafe, could someone help me with this.
Thanks!
It is difficult to check whether the described spinner element is shown due to the following:
It is displayed only during a short period of time. This does not allow TestCafe to check it in time. Using mocks makes the spinner appear only for milliseconds.
TestCafe waits for all requests and does not perform any actions until XHR requests are completed. This means that assertions will not start until your request is finished.
However, it's still possible to work around the issue.
You can use MutationObserver and TestCafe ClientFunctions mechanism.
You can create your element observer using the ClientFunction. The observer will watch for the app element changes. If the spinner element appears the observer will be notified and set the window.spinnerWasShown variable to true.
After the button is clicked, you can check that the windows.spinnerWasShown variable is set to true.
Here is the full example:
import { Selector, RequestMock, ClientFunction } from "testcafe";
import mockUser from "../mocks/mockUser.json";
var apiMocks = RequestMock()
.onRequestTo(/\/api.github.com\/users/)
.respond(mockUser, 200, {
'access-control-allow-credentials': "*",
'access-control-allow-origin': "*"
});
fixture`When a user is searched`
.page(`http://localhost:3000/`)
.requestHooks(apiMocks);
const spinnerWasShown = ClientFunction(() => window.spinnerWasShown);
const observeSpinner = ClientFunction(() => {
var appEl = document.querySelector('.app');
const config = { attributes: true, childList: true };
const callback = function(mutationsList) {
for(let mutation of mutationsList) {
if (mutation.type === 'childList') {
for (var i =0; i < mutation.addedNodes.length; i++ )
window.spinnerWasShown = window.spinnerWasShown || mutation.addedNodes[i].className.indexOf('spinner') > -1;
}
}
};
const observer = new MutationObserver(callback);
observer.observe(appEl, config);
});
test("Should fetch user details", async t => {
const spinnerEl = Selector("[data-test-id='spinner']");
await t.expect(spinnerEl.exists).notOk();
await t.typeText("[data-test-id='txt-search']", "foo");
await observeSpinner();
await t.click("[data-test-id='btn-search']");
await t.expect(spinnerWasShown()).eql(true);
await t.expect(spinnerEl.exists).notOk();
await t.expect(Selector("[data-test-id='username']").innerText).eql("Foo Bar");
await t.expect(Selector("[data-test-id='userid']").innerText).eql("foo");
});
I am new to JS and TestCafe.
Using PageObjects in TestCafe my goal is to launch a login page and authenticate before running a test.
The .open call works fine from fixtures. Also from with .before.
fixture `Check for new emails`
.page `https://www.mail.com/myemails`
and
fixture `Check for new emails`
.page `https://www.mail.com/myemails`
.beforeEach(async t => {
console.log("before");
.page `https://www.mail.com/login`;
myloginScreen.performLogin();
})
test ('', async t => {
await t
console.log("In the test step");
});)
PageObject look like this:
import { Selector, t } from 'testcafe';
export default class LoginPage {
constructor () {
this.loginInput = Selector('input').withAttribute('id','email');
this.passwordInput = Selector('input').withAttribute('id','password');
this.signInButton = Selector('button').withAttribute('class','big-button');
this.userMenu = Selector('a').withAttribute('data-which-id', 'gn-user-menu-toggle-button');
}
async performLogin() {
console.log("function entered")
.typeText(this.loginInput, 'user#mail.com')
.typeText(this.passwordInput, 'password')
.click(this.signInButton);
console.log("Form submitted");
}
}
But I want to move the Login URL load to the PageObject like this:
async performLogin() {
console.log("function entered")
.navigateTo ("https://www.mail.com/login")
await t
.typeText(this.loginInput, 'user#mail.com')
.typeText(this.passwordInput, 'password')
.click(this.signInButton);
console.log("Form submitted");
}
The code calls the function fine but quits the .before and jumps to the test step.
I am not sure what I am doing wrong here, will appreciate any help.
The performLogin is an asynchronous method. So, you need to call it with the await keyword:
fixture `Check for new emails`
.page `https://www.mail.com/myemails`
.beforeEach(async t => {
console.log("before");
await myloginScreen.performLogin();
});