assertExists with if-else statement - javascript

I use this code:
var x = require('casper').selectXPath;
...
casper.waitForSelector(x("//a[contains(#id,'cell_13_1')]"), function() {
this.test.assertExists(x("//a[contains(#id,'cell_13_1')]"), 'Clickable');
this.click(x("//a[contains(#id,'cell_13_1')]"));
});
I am trying to use if-else with assertExists to click another element if the first is not there:
casper.waitForSelector(x("//a[contains(#id,'cell_13_1')]"), function() {
if(this.test.assertExists(x("//a[contains(#id,'cell_13_1')]")==="PASS"){
this.click(x("//a[contains(#id,'cell_11_1')]"));}
else{
this.click(x("//a[contains(#id,'cell_22_1')]"));
}
});
But that does not seem to work. How would one do it correctly?

That's exactly what casper.exists() is for. You can also explicitly pass or fail some things:
casper.waitForSelector(x("//a[contains(#id,'cell_13_1')]"), function() {
if(this.exists(x("//a[contains(#id,'cell_13_1')]")){
this.test.pass("Clickable");
this.click(x("//a[contains(#id,'cell_11_1')]"));
} else {
this.test.fail("Clickable");
//this.click(x("//a[contains(#id,'cell_22_1')]"));
}
});
This code is equivalent to your first snippet. Comment the fail() call and uncomment the last click in order to get your "intended" behavior.
Btw, it doesn't make sense to fail some assertion and still continue with the script. You have to think about what exactly you want to test and what part of your script is supposed to be navigation to the component under test.

Related

How to add 2 myFunctions?

I have added a myFunction code to my site. No problem, it works. Then I tried to add a second myFunction. The problem is, if I ad a second myFunction it doesn't work. It will only run succesfully when I use 1 myFunction. I tried everything to do, changed the name (myFunction2), people said me to do that but it doesn't work.
My question is now: How to add 2 myFunctions?
Script 1:
var myVar;
function myFunction(loader) {
myVar = setTimeout(showPage, 3000);
}
function showPage(loader) {
document.getElementById("loader").style.display = "none";
document.getElementById("myDiv").style.display = "block";
}
Script 2:
window.onscroll = function(scroller) {myFunction(scroller)};
function myFunction(scroller) {
if (document.body.scrollTop > 550 || document.documentElement.scrollTop > 550) {
document.getElementById("content").className = "slideUp";
}
}
You can't have more than one function with the same name. Try changing the second script to myScrollerFunction and when you call it, change it to myScrollerFunction as well.
No two functions can share the same name.
function UniqueFunctionName(parameters){
//put code here
}
UniqueFunctionName(startparameters); //not necessary
That is how to make a function.
function AnotherFunctionName(parameters){
//put code here
}
AnotherFunctionName(startparameters); //not necessary
That is how to make another function.
In your case this should be:
function myFunction1(){
//code in here
}
function myFunction2(){
//code in here
}
myFunction1(); // or not
myFunction2(); // or not
If it is like that the the specific code in the function needs to be debugged.
From what you have said i think that you just need to debug the code.
http://five.agency/javascript-debugging-for-dummies/
Learn to find and remove mistakes in your code and all will be easier.
If the code is flawless then the only other possibility is that it is not called.
To test for that insert this code like so:
function myFunction2(){
console.log('The function loads!');
//put code here
console.log('The function works!');
}
Save the code and open the webpage.
Open a debugger and search for the console.
If you see both messages then the code does not work the way it is expected.
If you see the first message and an error message then the code NEEDS debugging.
If you see neither messages then the code does not even load and you need to debug the input mechanisms/setInterval/setTimeout and/or parameters depending on the error message. If you have no input mechanisms/setInterval/setTimeout then add one or call the function directly from the code like shown in most of my code blocks above otherwise the code will never EVER load.
EDIT: parameter scroller is not defined. change 'function(scroller) {myFunction(scroller)};' to 'function(){myFunction2()};' and 'function myFunction(scroller) {' to 'function myFunction2(){'

Fire jQuery .on('change') always at least once

I often find myself using pieces of code like the following:
$('[name=myradio]').on('change', function(){
if($(this).val() == 0){
$('#div').toggle();
}
else if(....){
$('#div2').toggle();
}
//...and more code going on
});
However, often I want to execute these pieces of code always at least once (for example when validation fails)! Of course I could make a method of this code and then execute the method from within the change anonymous function, as wel as in a $(document).ready() method, but I wonder if there is an easy way to execute that code at least once but also keep that code only in the change method to keep everything nice and clean instead of making a method for something that would be used only twice.
you can just trigger change in $(document).ready(...)
$(document).ready(function() {
$('[name=myradio]').on('change', function() {
if ($(this).val() == 0) {
$('#div').toggle();
} else if (....) {
$('#div2').toggle();
}
//...and more code going on
}).trigger("change");
});
Use trigger() method. This way you can keep the code only in the change method and trigger it from anywhere.
$(selector).trigger("change");

Testing event handlers

I have an event handler that modifies some global variable based on the click action. How can I test it? For example:
function initEvent() {
function enable() {
var arr = Context.get('arr');
arr.push('aaa');
};
function disable() {
var arr = Context.get('arr');
arr.push('bbb');
};
$('#content').on('click', '#element', function () {
if (this.checked) {
enable();
} else {
disable();
}
});
};
This is the function I am calling after the HTML has been rendered. It calls enable() and disable() functions based on the user action. I want to test these functions and check if they behave correctly. How can I do that?
You want to test your code. You should never test code with console.log or alert. These are great to debug something on the fly, but they are not test tools. These promote manual testing, where you need to manually run the code and see that pass, that's horrible time waste.
You should use Jasmine in this case (you can use other testing frameworks, though Jasmine is super easy) to test your code. You can setup browser tests or headless tests, which is out of the scope of this question, there are tons of tutorials on the subject.
Now, in order to test this code, I assume that the Context has a static method get which returns an array which is on the Context IIFE scope. If the case is different feel free to fiddle around with the specs and make it serve your needs, or alternatively if you get stuck, update this question or ask another one on Stackoverflow.
I have setup Jasmine, with jasmine-fixture to test your code, the jQuery click event behavior. In this plunk you will find everything you need.
I am using the browser to test the code, so I need jasmine's html reporter.
The real tests are in script-spec.js, where I am using Jasmine's API, by describing a spec suite (with describe) and defining each spec with it method.
In beforeEach I prepare the code to run before each spec executes. Essentially here, I create a simple div with #content id and a child input element of type checkbox with #element id. I do this by using the
setFixtures('<div id="content"><input type="checkbox" id="element" /></div>');
Which is a method jasmine-fixture library provides.
Now I can test the code, wiring up the specs:
it("Should return an array with 'aaa' element when #element is checked", function() {
// Arrange
initEvent();
var checkbox = $("#content").find("#element");
// Act
checkbox.click();
// Assert
expect(Context.get('arr').length).toBe(1);
expect(Context.get('arr')).toEqual(['aaa']);
});
I run the initEvent method and get a reference of the checkbox element. In Act section I click the element manually, marking it as checked, which is the normal behavior. In Assert, I test the Context.get('arr') return value.
Again, link to plunk is here.
Hope this helps.
One simple test you can do to test enable, disable and the click handler is to create a function that checks the contents of arr in Context, and call it after each of the functions within the click handler that add something to arr.
The general way to test conditions in your code is with assertions which will throw an error if the condition you pass into them is false. You can use console.assert just for that:
$('#content').on('click', '#element', function() {
if (this.checked) {
enable();
// assert last element in `arr` is the enabled string 'aaa'
console.assert(
Context.get('arr')[Context.get('arr').length - 1] === 'aaa',
'enable() works'
);
} else {
disable();
// assert last element in `arr` is the disabled string 'bbb'
console.assert(
Context.get('arr')[Context.get('arr').length - 1] === 'bbb',
'disable() works'
);
}
});
If any of the tests run after you click your element, you know initEvent assigned the click handler and it works. Then, you just toggle the checked flag to test enable()/disable() as well.
If there are no errors in your browser console, the tests have passed. Otherwise, there will be an error in your console containing the message passed as the second argument to console.assert.
You could even make a helper function to simplify the testing a bit:
function assertLastElementInContextArr(elem, msg) {
var arr = Context.get('arr');
// assert last item in `arr` is equal to `elem`
console.assert(arr[arr.length - 1] === elem, msg);
}
$('#content').on('click', '#element', function() {
if (this.checked) {
enable();
// assert last element in `arr` is the enabled string 'aaa'
assertLastElementInContextArr('aaa', 'enable() works');
} else {
disable();
// assert last element in `arr` is the disabled string 'bbb'
assertLastElementInContextArr('bbb', 'disable() works');
}
});
EDIT based on your comment
But how do I mock the click event? I mean, I want to automatically test all those events, no I have to somehow trigger the click automatically. How do I do that?
If you want to programmatically invoke click events, you can use JS to trigger them in code. Since you're using jQuery, it already comes with a method trigger to do just that.
All you need to do is:
$('#content').trigger('click')
And it will activate your click handler and run the assertions tests from above.
In fact, jQuery even comes with aliased handlers for specific events so you can just do:
$('#content').click();
To automate the testing, you can create a function that will trigger the clicks and set the checked state as well, to test both cases.
function test(checked) {
var elem = $('#content');
elem.prop('checked', checked);
elem.click();
}
Important thing to be careful about is that these events will happen asynchronously so you must do something to manage a proper testing order if you're going to trigger multiple clicks. Otherwise you will set checked to true, trigger the click and then run the second test that will set checked to false before the click events even happen.
For demonstration purposes, here's one way to safely test multiple successive clicks by adding an event handler just for testing and removing it once you're done. One requirement for this to work is to attach the handler after all your other handlers have been attached, to make sure the test handler runs last. Additionally, you can run your assertions here as well to not pollute your code and keep the testing fully separated:
function test(checked, value, msg, done) {
var elem = $('#content');
elem.prop('checked', checked);
// attach a test event handler and trigger the click
elem.on('click', testClick);
elem.click();
// once the click is executed,
// remove the test handler,
// run the assertions and then
// call the callback to signal the test is done
function testClick() {
elem.off('click', runTest);
assertLastElementInContextArr(value, msg);
done();
}
}
// run your code before the tests
initEvent();
// test enable(), then once that's done, test disable()
test(true, 'aaa', 'enable() works', function() {
test(false, 'bbb', 'disable() works', function() {
console.log('All tests finished');
});
});
If you're going to be testing your entire app like this, you'd probably want to use a test framework like QUnit, Mocha, Jasmine which will handle all these async issues for you and give you a nice API to work with.
Just add console.log(<some variable>) or alert(<some variable>) at function calls. e.g.:
function initEvent() {
function enable() {
alert("enable called!");
var arr = Context.get('arr');
arr.push('aaa');
};
function disable() {
alert("disable called!");
var arr = Context.get('arr');
arr.push('bbb');
};
$('#content').on('click', '#element', function () {
alert("click occured!");
if (this.checked) {
enable();
} else {
disable();
}
});
};
Or use your browsers developer tools setting breakpoints at these spots.

How to create a condition in protractor for when an element exists or not

I'm using Protractor JS. And the site is written in Angular JS.
So I have a toggle switch. And I noticed the value in the toggle switch goes from true to false and
false to true when you switch it off or on.
I am trying create a condition when Protractor visits my page when it sees the toggle switch 'off' it will turn it 'on'. If the toggle switch is already 'on', it will first turn it 'off' then turn it 'on' again.
I came up with this code, but for some reason it is not working:
if( expect(element(By.id('toggle-switch')).element(By.css('[value="false"]')).isDisplayed()) ) {
element(By.id('toggle-switch')).click();
console.log('in the if')
}
else{
element(By.id('toggle-switch')).click();
browser.sleep(3000);
element(By.id('toggle-switch')).click();
console.log('in the else')
}
This code appears to work only for the if statement. For some reason it will never go to the else. Here is the error I'm receiving:
NoSuchElementError: No element found using locator: By.cssSelector("[value=\"false\"]")
So then I tried
.isPresent() instead of .isDisplayed()
I don't receive the above error anymore, but for some reason when using .isPresent() it always goes to the if statement and only runs that, and never the else statement. No errors displayed.
If there is a better way please let me know. This seems very limiting to not be able to create proper conditions in this framework.
Remember that isDisplayed() returns a promise, you can try with:
element(anyFinder).isDisplayed().then(function(result) {
if ( result ) {
//Whatever if it is true (displayed)
} else {
//Whatever if it is false (not displayed)
}
});
isDisplayed() did not work for me. The API may have been changed. isPresent() is my solution:
var logoutButton = element(by.css('[ng-click="log_out()"]'));
logoutButton.isPresent().then(function(result) {
if ( result ) {
logoutButton.click();
} else {
//do nothing
}
});
The problem is that isDisplayed(), as a lot of methods in WebDriverJS/Protractor, returns a promise which by definition is "truthy" which makes it difficult to debug problems like this.
Let's work through an example to get a better understanding.
Imagine, you have the following code, which may look okay at the first glance:
var elm = $("#myid");
if (elm.isDisplayed()) {
// do smth
} else {
// do smth else
}
Now, it has a serious problem. do smth else part will never be reached, since elm.isDisplayed() is not a boolean value - it is a promise. Even if the element is not displayed, you would still have // do smth part executed.
Instead, if you need to check the value of isDisplayed() to use inside a conditional expression, you have to resolve the promise with then() explicitly:
var elm = $("#myid");
elm.isDisplayed().then(function (isDisplayed) {
if (isDisplayed) {
// do smth
} else {
// do smth else
}
});
There is also a way to catch these kind of errors without even running the code - statically with ESLint and eslint-plugin-protractor plugin. There is a relevant rule that watches if certain Protractor methods are used inside if conditions directly.
Here is what it would output for the code above:
$ eslint test.js
test.js
2:1 warning Unexpected "isDisplayed()" inside if condition protractor/no-promise-in-if
Or try this solution implemented from the top of my head, Schedules a command to test if an element is present on the page. If any errors occur while evaluating the wait, they will be allowed to propagate.
function alwaysSwitchOn(element) {
browser.driver.isElementPresent(element).then(function(isPresent) {
if (isPresent) {
isPresent = true;
}
else {
browser.driver.wait(function () {
return browser.driver.isElementPresent(element);
}, 5000);
}
// to fail the test, then uncomment this line
//expect(isPresent).toBeTruthy();
}).then(function () {
if (element.getAttribute('value') === 'OFF') {
element.click();
}
else {
// turn it OFF
element.click();
// turn it back ON
element.click();
}
});
}
fn usage is to keep trying again and again for 5 seconds till it's true. if the element cannot be found within 5 sec then it'll result in an error code; No such an element is found.Note, If the condition is fulfilled before wait (5s) it'll quickly move to then(...).
If you're in 2021 or the following years
Forget about .then(). Do this instead:
it('test case', async () => {
if (await element(anyFinder).isDisplayed()) {
// Whatever if it is true (displayed)
} else {
// Whatever if it is false (not displayed)
}
});

Mocking $.fn.find with Jasmine seems to have magic behaviour

I’m writing a method that’s a simple wrapper around calls to $.fn.modal. The method takes a selector and that selector should be passed to jQuery, so jQuery can find an element matching that selector.
Here’s what my spec looks like:
describe('The modal presenter', function() {
it('operates on the provided selector', function() {
spyOn($.fn, 'find');
myModule.presentModal('#the-modal', {});
expect($.fn.find).toHaveBeenCalledWith('#the-modal');
});
});
This is all fine. The odd behaviour is happening in my production code.
var myModule = {
presentModal: function(selector, event) {
event.preventDefault();
$(selector, document).modal('show'); // This line works
$(document).find(selector).modal('show'); // This line doesn’t work?!
// This should be fairly obvious, but I’m not running both
// of the above lines at the same time. I comment one out
// and run the method with one line at a time.
}
}
As far as I’m aware, those two lines that I’ve added comments to should be equivalent. They should both call $.fn.find with selector. The second commented line should definitely call .find, as I’ve written that explicitly.
When I run my test, I get TypeError: $(...).find(...) is undefined. How could this be?

Categories

Resources