I have a code that uses window.foo.abc as a condition to display something.
I want to test this functionality with cypress and I want to mock this value to be false and true.
How can I do that?
I've tried
before(function() {
Cypress.on('window:before:load', win => {
win.foo.abc = true;
});
and
cy.window().then(win => {
window.foo.abc = true;
});
with no success.
How can I mock this value?
thanks 🙏
This code is incorrect,
Cypress.on('window:before:load', win => {
window.foo.abc = true;
});
It should be
Cypress.on('window:before:load', win => {
win.foo.abc = true;
});
You don't have to use it in before(), but it should be at the top of the spec.
But I suspect it still won't work after correcting, most likely the app resets foo to a new object during loading, i.e during cy.visit()
You can use the 2nd block
cy.visit('...') // visit before changing
cy.window().then(win => {
win.foo.abc = true; // correct the syntax here as well
})
Related
I came accross capture screen with electron when rendering a web site when I needed a solution for enabling screenshare inside my electron app;
however the desktopCapturer is always undefined, on my side and the only way I can access;
sources is inside the main process;
I would like to know if there is a way to have sources define when I do something like this
let all_sources = undefined
ipcRenderer.on('SET_SOURCES', (ev, sources) => {
all_sources = sources
console.log("The sources are : ", all_sources)
})
const wait_function = function() {
return new Promise(resolve => {
setTimeout(function() {
resolve(all_sources);
}, 4000);
});
};
contextBridge.exposeInMainWorld("myCustomGetDisplayMedia", async () => {
await ipcRenderer.send('GET_SOURCES')
await wait_function(); // want to make sure all_sources is defined
const selectedSource = all_sources[0]; // take the Entire Screen just for testing purposes
return selectedSource;
});
this is inside the preload js script.
thanks
I have written a function like this:
const myFunction = () => {
return 'text';
};
exports.myFunction = myFunction;
if (require.main === module) {
console.log(myFunction());
}
and this is my test:
const { myFunction } = require('../myFunction');
describe('test', () => {
it('should return the text', () => {
expect(myFunction()).toMatch('text');
});
});
According to code coverage tools, every line in the code is covered except for this line line:
console.log(myFunction());
Based on comments, I think maybe the reality is that this line cannot be tested, so I'm updating my question:
How can I:
Test this line with Jest, understanding that it may not actually tick the "covered" box, but so I can literally test it. Because not every one of my files has such trivial code in that block. Sometimes I do want to test it for real.
Cause the coverage statistic to show the file as 100% covered? Not because I am pedantic, but I like using the coverage report to find things I need to add tests for, and having dozens of "false negatives" in my report makes that more difficult.
Based on a suggestion in the comments, I found that I can use a child_process exec call within the test to test the output from the command line like this:
const util = require('util');
const exec = util.promisify(require('child_process').exec);
const { myFunction } = require('../myFunction');
describe('test', () => {
it('should return the text', () => {
expect(myFunction()).toBe('text');
});
it('should return the text when called via command line too', async () => {
const { stdout } = await exec('node myFunction', {
encoding: 'utf8',
});
expect(stdout).toBe('text\n');
});
});
Further comments pointed out that without exporting that section of code, Jest can never see it, and hence, never test it, meaning it will never show as "covered". Therefore, once I am satisfied that it is "tested well enough" I can exclude it form my report by adding /* istanbul ignore next */ before the offending line like this:
const myFunction = () => {
return 'text';
};
exports.myFunction = myFunction;
if (require.main === module) {
/* istanbul ignore next */
console.log(myFunction());
}
As explained here, Node.js require wraps script contents with wrapper code specified in Module.wrapper and evaluates it with vm.runInThisContext. This can be implemented in a test. It can be something like:
let Module = require('module');
...
jest.resetModules();
jest.spyOn(console, 'log');
let myModPath = require.resolve('../myFunction');
let wrapper = Module.wrap(fs.readFileSync(myModPath));
let compiledWrapper = vm.runInThisContext(wrapper, {});
let mockModule = new Module(myModPath);
let mockExport = mockModule.exports;
let mockRequire = Module.createRequire(myModPath);
mockRequire.main = mockModule;
wrapper(mockExport, mockRequire, mockModule, path.basename(myModPath), path.dirname(myModPath));
expect(console.log).toBeCalledWith('test');
How to check if element is present or not, so that certain steps can be performed if element is present. Else certain different steps can be performed if element is not present.
I tried something like below but it didn't work:
Cypress.Commands.add('deleteSometheingFunction', () => {
cy.get('body').then($body => {
if ($body.find(selectors.ruleCard).length) {
let count = 0;
cy.get(selectors.ruleCard)
.each(() => count++)
.then(() => {
while (count-- > 0) {
cy.get('body')
// ...
// ...
}
});
}
});
});
I am looking for a simple solution, which can be incorporated with simple javascript
if else block or then() section of the promise
Something similar to Webdriver protocol's below implementions:
driver.findElements(By.yourLocator).size() > 0
check for presenece of element in wait
Kindly advise. Thanks
I'll just add that if you decide to do if condition by checking the .length property of cy.find command, you need to respect the asynchronous nature of cypress.
Example:
Following condition evaluates as false despite appDrawerOpener button exists
if (cy.find("button[data-cy=appDrawerOpener]").length > 0) //evaluates as false
But this one evaluates as true because $body variable is already resolved as you're in .then() part of the promise:
cy.get("body").then($body => {
if ($body.find("button[data-cy=appDrawerOpener]").length > 0) {
//evaluates as true
}
});
Read more in Cypress documentation on conditional testing
it has been questioned before: Conditional statement in cypress
Thus you can basically try this:
cy.get('header').then(($a) => {
if ($a.text().includes('Account')) {
cy.contains('Account')
.click({force:true})
} else if ($a.text().includes('Sign')) {
cy.contains('Sign In')
.click({force:true})
} else {
cy.get('.navUser-item--account .navUser-action').click({force:true})
}
})
cypress all steps are async ,, so that you should make common function in commands file or page object file,,..
export function checkIfEleExists(ele){
return new Promise((resolve,reject)=>{
/// here if ele exists or not
cy.get('body').find( ele ).its('length').then(res=>{
if(res > 0){
//// do task that you want to perform
cy.get(ele).select('100').wait(2000);
resolve();
}else{
reject();
}
});
})
}
// here check if select[aria-label="rows per page"] exists
cy.checkIfEleExists('select[aria-label="rows per page"]')
.then(e=>{
//// now do what if that element is in ,,..
})
.catch(e=>{
////// if not exists...
})
I found a solution, hope it helps!
You can use this:
cy.window().then((win) => {
const identifiedElement = win.document.querySelector(element)
cy.log('Object value = ' + identifiedElement)
});
You can add this to your commands.js file in Cypress
Cypress.Commands.add('isElementExist', (element) => {
cy.window().then((win) => {
const identifiedElement = win.document.querySelector(element)
cy.log('Object value = ' + identifiedElement)
});
})
Cypress official document has offered a solution addressing the exact issue.
How to check Element existence
// click the button causing the new
// elements to appear
cy.get('button').click()
cy.get('body')
.then(($body) => {
// synchronously query from body
// to find which element was created
if ($body.find('input').length) {
// input was found, do something else here
return 'input'
}
// else assume it was textarea
return 'textarea'
})
.then((selector) => {
// selector is a string that represents
// the selector we could use to find it
cy.get(selector).type(`found the element by selector ${selector}`)
})
For me the following command is working for testing a VS code extension inside Code server:
Cypress.Commands.add('elementExists', (selector) => {
return cy.window().then($window => $window.document.querySelector(selector));
});
And I'm using it like this in my E2E test for a Code Server extension:
cy.visit("http://localhost:8080");
cy.wait(10000); // just an example here, better use iframe loaded & Promise.all
cy.elementExists("a[title='Yes, I trust the authors']").then((confirmBtn) => {
if(confirmBtn) {
cy.wrap(confirmBtn).click();
}
});
Just ensure that you're calling this check once everything is loaded.
If you're using Tyepscript, add the following to your global type definitions:
declare global {
namespace Cypress {
interface Chainable<Subject> {
/**
* Check if element exits
*
* #example cy.elementExists("#your-id").then($el => 'do something with the element');
*/
elementExists(element: string): Chainable<Subject>
}
}
}
Aside
VS Code server relies heavily on Iframes which can be hard to test. The following blog post will give you an idea - Testing iframes with Cypress.
The above code is needed to dismiss the "trust modal" if it's shown. Once the feature disable-workspace-trust is released it could be disabled as CLI option.
This command throws no error if element does not exist. If it does, it returns the actual element.
cypress/support/commands.js
elementExists(selector) {
cy.get('body').then(($body) => {
if ($body.find(selector).length) {
return cy.get(selector)
} else {
// Throws no error when element not found
assert.isOk('OK', 'Element does not exist.')
}
})
},
Usage:
cy.elementExists('#someSelectorId').then(($element) => {
// your code if element exists
})
In case somebody is looking for a way to use cy.contains to find an element and interact with it based on the result. See this post for more details about conditional testing.
Use case for me was that user is prompted with options, but when there are too many options, an extra click on a 'show more' button needs to be done before the 'desired option' could be clicked.
Command:
Cypress.Commands.add('clickElementWhenFound', (
content: string,
) => {
cy.contains(content)
// prevent previous line assertion from triggering
.should((_) => {})
.then(($element) => {
if (!($element || []).length) {
/** Do something when element was not found */
} else {
cy.contains(content).click();
}
});
});
Usage:
// Click button with 'Submit' text if it exists
cy.clickElementWhenFound('Submit');
Using async/await gives a clean syntax:
const $el = await cy.find("selector")
if ($el.length > 0) {
...
More info here: https://medium.com/#NicholasBoll/cypress-io-using-async-and-await-4034e9bab207
I had the same issue like button can appear in the webpage or not. I fixed it using the below code.
export function clickIfExist(element) {
cy.get('body').then((body) => {
cy.wait(5000).then(() => {
if (body.find(element).length > 0) {
cy.log('Element found, proceeding with test')
cy.get(element).click()
} else {
cy.log('Element not found, skipping test')
}
})
})
}
I need Cypress to wait for any xhr requests to complete by default before performing any operations. Is there any way to make this as a default or any other alternatives because the application I am testing is slow and makes a lot of api calls?
Edit: By writing a single statement for every api request is getting messy and unnecessary work. Need a way to make this easier.
If what you want is to wait for a specific xhr you can do it making use of cy.route(). I use this in some scenarios and it is really useful. The general steps to use it are:
cy.server()
cy.route('GET','**/api/my-call/**').as('myXHR');
Do things in the UI such as clicking on a button that will trigger such api calls
cy.wait(#myXHR)
This way if such call isn't triggered your test will fail. You can find extensive documentation about this here
Found something that works for me here https://github.com/PinkyJie/cypress-auto-stub-example
Look for cy.waitUntilAllAPIFinished
I partialy solve the problem adding a waitAll command and ovewrite route command in support folder:
const routeCallArr = [];
Cypress.Commands.overwrite('route', (route, ...params) => {
const localRoute = route(...params);
if (localRoute.alias === undefined) return;
localRoute.onRequest = function() {
routeCallArr.push({alias: `#${localRoute.alias}`, starTime: Date.now()});
}
localRoute.onResponse = function() {
clearCall(`#${localRoute.alias}`);
}
})
const waitAll = (timeOut = 50000, options = {verbose: false, waitNested: false}) => {
const filterRouteCallArr = [];
const date = Date.now();
for (const routeCall of routeCallArr) {
if ((date - routeCall.starTime) > timeOut) continue;
filterRouteCallArr.push(routeCall.alias);
}
if (options.verbose ){
console.table(routeCallArr.map(routeCall => ({
deltaTime: date - routeCall.starTime,
alias: routeCall.alias,
starTime: routeCall.starTime,
})));
console.log(routeCallArr, filterRouteCallArr)
};
routeCallArr.length = [];
if (filterRouteCallArr.length > 0) {
const waiter = cy.wait(filterRouteCallArr, {timeout: timeOut});
options.waitNested && waiter.then(() => {
if (routeCallArr.length > 0) {
waitAll(timeOut, options);
}
});
}
}
Cypress.Commands.add('waitAll', waitAll)
And in the test instead of use cy.wait(['#call01',..., '#callN']); I use cy.waitAll();
The problem with this implementation came when have nested calls in a relative separate time interval from original calls. In that case you can use a recursive wait cy.waitAll(50000, {waitNested: true});
I'm new on protractor, and I'm trying to implement an e2e test.
I don't know if this is the right way to do this, but...
The page that I want to test is not a full angular page based, so... I'm having some trouble.
On my first spec I have:
describe('should open contact page', function() {
var ptor = protractor.getInstance();
beforeEach(function(){
var Login = require('./util/Login');
new Login(ptor);
});
I have created this Login class, but after login I want to open the contact page, but protractor immediately try to find element before the page is fully loaded.
I've tried to use:
browser.driver.wait(function() {
expect(browser.findElement(by.xpath("//a[#href='#/contacts']")).isDisplayed());
ptor.findElement(by.xpath("//a[#href='#/contacts']")).click();
});
But it doesn't work... it always try to find the element before the page loads.
I tried this one too:
browser.driver.wait(function() {
expect(ptor.isElementPresent(by.xpath("//a[#href='#/contacts']")));
ptor.findElement(by.xpath("//a[#href='#/contacts']")).click();
});
I'm able to do that using browser.sleep(); but I don't think that is a good option. Any idea? On my login class I have:
ptor.ignoreSynchronization = true;
How can I wait for this #href='#/contacts before protractor tries to click on it?
Protractor 1.7.0 has also introduced a new feature: Expected Conditions.
There are several predefined conditions to explicitly wait for. In case you want to wait for an element to become present:
var EC = protractor.ExpectedConditions;
var e = element(by.id('xyz'));
browser.wait(EC.presenceOf(e), 10000);
expect(e.isPresent()).toBeTruthy();
See also:
Expected conditions in protractor
I finally find out...
var waitLoading = by.css('#loading.loader-state-hidden');
browser.wait(function() {
return ptor.isElementPresent(waitLoading);
}, 8000);
expect(ptor.isElementPresent(waitLoading)).toBeTruthy();
var openContact = by.xpath("//a[#href='#/contacts']");
element(openContact).click();
With this protractor could wait for that element until it loading page disappears.
Thanks for those who tried to help XD.
I had the same problem you were having for the longest time while using protractor. In my e2e test I start in a non angular app, then get into an angular portion, then get back out to a non angular portion. Made things tricky. The key is to understand promises and how they work. Here's some examples of my real world code in a functioning e2e test. Hoping this gives you an idea of how to structure your tests. Probably some bad practice in this code, please feel free to improve upon this, but I know that it works, maybe not the best way.
To get to angular I use
var ptor;
var events = require('events');
var eventEmitter = new events.EventEmitter();
var secondClick = require('./second-click');
beforeEach(function () {
browser.driver.get('http://localhost:8080/');
},10000);
it("should start the test", function () {
describe("starting", function () {
it("should find the link and start the test", function(){
var elementToFind = by.linkText('Start'); //what element we are looking for
browser.driver.isElementPresent(elementToFind).then(function(isPresent){
expect(isPresent).toBe(true); //the test, kind of redundant but it helps pass or fail
browser.driver.findElement(elementToFind).then(function(start){
start.click().then(function(){ //once we've found the element and its on the page click it!! :)
ptor = protractor.getInstance(); //pass down protractor and the events to other files so we can emit events
secondClick(eventEmitter, ptor); //this is your callback to keep going on to other actions or test in another file
});
});
});
});
});
},60000);
While in angular this code works
describe("type in a message ", function(){
it("should find and type in a random message", function(){
var elementToFind = by.css('form textarea.limited');
browser.driver.isElementPresent(elementToFind).then(function(isPresent){
element(elementToFind).sendKeys(randomSentence).then(function(){
console.log("typed in random message");
continueOn();
});
});
});
},15000);
After exiting angular
browser.driver.wait(function(){
console.log("polling for a firstName to appear");
return browser.driver.isElementPresent(by.name('firstName')).then(function(el){
return el === true;
});
}).
then(function(){
somefunctionToExecute()
});
Hope that gives some guidance and helps you out!
browser.driver.wait(function() {
return browser.driver.isElementPresent(by.xpath("//a[#href='#/contacts']"));
});
This works for me too (without the timeout param)..
for more information, see http://angular.github.io/protractor/#/api?view=webdriver.WebDriver.prototype.wait
Thanks to answers above, this was my simplified and updated usage
function waitFor (selector) {
return browser.wait(function () {
return browser.isElementPresent(by.css(selector));
}, 50000);
}
Have you tried putting the ng-app in the <html> tag (assuming this part of code is under your control)? This solved a lot of initialization timing problems for me.
Best way to use wait conditions in protractor that helps to show proper error message to particular element if test case failed
const EC = ExpectedConditions;
const ele = element(by.xpath(your xpath));
return browser.wait(EC.visibilityOf(ele),9000,'element not found').then(() => {
ele.click();
});
I'm surprised that nobody has added this solution. Basically, if you are using modal dialogues you often get an element visible and available to click but not being clickable due to the modal dialogue being in front of it. This happens because protractor moves faster than angular and is ready to click the next element while angular is still closing the modal.
I suggest using
public async clickElementBug(elementLocator: Locator) {
const elem = await element(elementLocator);
await browser.wait(
async function() {
try {
await elem.click();
return true;
} catch (error) {
return false;
}
},
this.TIMEOUT_MILLIS,
'Clicking of element failed: ' + elem
);
}
browser.wait may sound too ordinary, but it's not!
browser.wait is the way to go. Just pass a function to it that would have a condition which to wait for. For example wait until there is no loading animation on the page
let $animation = $$('.loading');
await browser.wait(
async () => (await animation.count()) === 0, // function; if returns true it stops waiting; can wait for anything in the world if you get creative with it
5000, // timeout
`message on timeout` // comment on error
);
Make sure to use await
You can also use existing library called ExpectedConditions that has lots of predefined conditions to wait for
You can't imagine what you can do with it...
A few of my favorite ones:
wait until the number of browser's tab's is 2
// wait until the number of browser's tab's is 2
await browser.wait(
async () => {
let tabCount = await browser.getAllWindowHandles();
return tabCount.length === 2;
},
5000,
'the url didnt open in a new window'
);
wait until the loading animation is gone for at last 750ms
// wait until the loading animation is gone for at last 750ms
await browser.wait(
async () => (await this.$$loadAnimations.count()) === 0 && !(await browser.sleep(750)) && (await this.$$loadAnimations.count()) === 0,
5000,
`waiting timeout`
);
wait for ANY number of elements to be present
// wait for any number of elements to be present
async waitForElements($elem, timeout = 120000, start = +new Date()) {
let conditions = [];
for (let i = 0; i < $elem.length; i++) {
conditions.push(ExpectedConditions.presenceOf($elem[i]));
}
await browser.wait(
ExpectedConditions.and(...conditions),
remainingTimeout(timeout, start),
`wait for all elements`
);
}
// and use
await waitForElements([
$usernameField,
$passwordFiend,
$submitButton
])