Set timeout for Cypress expect assertion - javascript

My issue is that I'd like to have a timeout on the expect assertion from Cypress's library but can't seem to figure out a method to do so.
I've tried greatly increasing the global timeout in cypress.conf; however, that didn't work.
if ($widget.find('report-widget')) {
SVGCount++;
expect(barGraphsChange[index]).to.not.equal(undefined)
console.log(barGraphsChange, index)
cy.getBarGraphTotal(SVGCount, barGraphsChange[index])
}
If not a timeout then a reasonable workaround would be nice as well, Thanks!
Also a note: barGraphsChange[index] is in the process of getting calculated and assigned from a called custom Cypress command earlier on during this phase.

You can customise your expectations with should(), and this will automatically retry your assertions for either the specified timeout, or the global defaultCommandTimeout if none provided.
In your case I could probably imagine wrapping your object, specify a timeout and then passing your logic to should:
cy.wrap(someFunction, {timeout: 25000}).should(someFunction => expect(someFunction()).to.eq(someValue))
Read more here: https://docs.cypress.io/api/commands/should.html#Function

Related

Mixpanel returning a response object instead of undefined even when opted out in AVA Unit Test

I'm trying to implement an AVA Unit Test for my mixpanel implementation. To do this, I'm comparing the result of mixpanel.track() where if it returns anything, the track was successful, otherwise, it should be undefined.
I thought maybe it was that it was using a different mixpanel instance so I tried creating a named instance and ensuring that but it was to no avail. I'm also trying the same process but with Amplitude and it seems to be working fine (when I am opted out, the response fails as expected)
I have done this in my components where if
const test = mixpanel.track('event_name', {}) is successful, !!test === true but if I do mixpanel.opt_out.tracking() prior to const test = mixpanel.track('event_name', {}), then !!test === undefined.
Expected behaviour (and the observed behaviour when I use it in my components):
trackResponse === undefined
Observed behaviour:
trackResponse === { event: 'asdf',
properties:
{ '$browser': 'Safari',
'$current_url': 'about:blank',
'$browser_version': null,
'$screen_height': 0,
'$screen_width': 0,
mp_lib: 'web',
'$lib_version': '2.30.1',
time: 1572898982.142,
distinct_id: '[some_id]',
'$device_id': '[some_id]',
'$initial_referrer': '$direct',
'$initial_referring_domain': '$direct',
token: '[token]' } }
where [some_id] and [token] are some distinct values I've deleted.
I don't understand why in the AVA test, I'm receiving a response when normally a failed track() results in an undefined response. Could someone shine some light on this?
Let me know if I need to provide any additional information. Thanks.
I figured it out in case anyone else runs into this issue.
I used a debugger to step into the mixpanel.track() calls and figured out that to see if the user had opted out, mixpanel checks for a property in the localStorage and compares it to see if it's === to '0'. If this fails, it assumes the user has not opted out and carries out the track call as normal.
I guess during the AVA test, it was unable to access this property and assumed the user had not opted out. To fix it, in my call to mixpanel.init(), I added opt_out_tracking_persistence_type: 'cookie' as an option so that my opt_out call was being saved somewhere that the property could be accessed during the test.

`waitForEnabled()` says "selector needs to be typeof `string`" even though string was passed

I was originally using the mocha command line tool to run my tests and they were working fine. I switched to using the wdio command to run my tests. My tests now throw an error with this line of code:
browser.waitForEnabled('#div_id');
With this error:
Promise was rejected with the following reason: Error: selector needs to be typeof `string`
running chrome
Error: Promise was rejected with the following reason: Error: selector needs to be typeof `string`
at elements() - isEnabled.js:18:17
at isEnabled() - waitForEnabled.js:37:22
This was working fine until I started using wdio (specifically I run wdio --spec path/to/file.js). I've run the typeof function on the selector in question and verified that it is, in fact, a string.
The div in question looks like this:
<div class="highlight" id="div_id">
A fair bit of content goes here.
</div>
Why am I seeing this error? How do I fix it?
waitForEnabled() documentation => http://webdriver.io/api/utility/waitForEnabled.html
wdio documentation => http://webdriver.io/guide/testrunner/gettingstarted.html
Update:
I've tried adding a timeout to the waitForEnabled() function. Since I've done so, it sometimes fails, and sometimes does not. More often it fails though.
I'm not marking this as the answer because I have no idea why it works. But passing in all of the optional params to waitForEnabled() makes it work just fine.
As in:
waitForEnabled('#div_id'); Fails. Wheras:
waitForEnabled('#div_id', 99999, false); works without errors.
The fact that you're passing milliseconds & the false option should not make the test pass.
I'd suggest using some of the other waitFor commands, like waitForVisible() or waitForExist(), because I'm assuming this is what you really want from the code example you gave above. The waitForEnabled() command waits for a field that had a disabled html attribute, to not have it anymore. You can read more on that here: https://www.w3schools.com/tags/att_disabled.asp.

How can I make this test pass in Jasmine?

I have a Jasmine unit test and in it I have this 'expect'...
expect(mockService.create).toHaveBeenCalledWith(new ToDoItem('a#b.com', 'get milk'));
In my controller I have the following...
todoService.create($scope.newToDo,
function() {
}, function() {
});
But I always get an error because of the final two functions that I pass the service for success and failure. How can I stop this from happening? How do I add them to the expect clause?
Thanks
You may be able to use jasmine.any(Function), or jasmine.objectContaining.
Fair warning, I've never done this myself. However, from the documentation, at least one of them should provide the behaviour you want.

Why does exception within frame get no notification in qUnit?

I noticed that qUnit doesn't give any notice when an exception happens in a later part of the test. For example, running this in a test():
stop();
function myfun(ed) {
console.log('resumed');
start(); //Resume qunit
ok(1,'entered qunit again');
ok(ed.getContent()== 'expected content') // < causes exception, no getContent() yet.
}
R.tinymce.onAddEditor.add(myfun)
in an inner iframe on the page will cause an exception (TypeError: ed.getContent is not a function),
but nothing in Qunit status area tells this. I see 0 failures.
(R being the inner iframe, using technique here: http://www.mattevanoff.com/2011/01/unit-testing-jquery-w-qunit/) Would I be correct in assuming this isn't the best way to go for testing sequences of UI interaction that cause certain results? Is it always better to use something like selenium, even for some mostly-javascript oriented frontend web-app tests?
As a side note, the Firefox console shows the console.log below the exception here, even though it happened first... why?
If you look into qUnit source code, there are two mechanisms handling exceptions. One is controlled by config.notrycatch setting and will wrap test setup, execution and teardown in try..catch blocks. This approach won't help much with exceptions thrown by asynchronous tests however, qUnit isn't the caller there. This is why there is an additional window.onerror handler controlled by Test.ignoreGlobalErrors setting. Both settings are false by default so that both kinds of exceptions are caught. In fact, the following code (essentially same as yours but without TinyMCE-specific parts) produces the expected results for me:
test("foo", function()
{
stop();
function myfun(ed)
{
start();
ok(1, 'entered qunit again');
throw "bar";
}
setTimeout(myfun, 1000);
});
I first see a passed tests with the message "entered qunit again" and then a failed one with the message: "uncaught exception: bar." As to why this doesn't work for you, I can see the following options:
Your qUnit copy is more than two years old, before qUnit issue 134 was fixed and a global exception handler added.
Your code is changing Test.ignoreGlobalErrors setting (unlikely).
There is an existing window.onerror handler that returns true and thus tells qUnit that the error has been handled. I checked whether TinyMCE adds one by default but it doesn't look like it does.
TinyMCE catches errors in event handlers when calling them. This is the logical thing to do when dealing with multiple callbacks, the usual approach is something like this:
for (var i = 0; i < callbacks.length; i++)
{
try
{
callbacks[i]();
}
catch (e)
{
console.error(e);
}
}
By redirecting all exceptions to console.error this makes sure that exceptions are still reported while all callbacks will be called even if one of them throws an exception. However, since the exception is handled jQuery can no longer catch it. Again, I checked whether TinyMCE implements this pattern - it doesn't look like it.
Update: Turns out there is a fifth option that I didn't think of: the exception is fired inside a frame and qUnit didn't set up its global error handler there (already because tracking frame creation is non-trivial, a new frame can be created any time). This should be easily fixed by adding the following code to the frame:
window.onerror = function()
{
if (parent.onerror)
{
// Forward the call to the parent frame
return parent.onerror.apply(parent, arguments);
}
else
return false;
}
Concerning your side-note: the console object doesn't guarantee you any specific order in which messages appear. In fact, the code console.log("foo");throw "bar"; also shows the exception first, followed by the log message. This indicates that log messages are queued and handled delayed, probably for performance reasons. But you would need to look into the implementation of the console object in Firefox to be certain - this is an implementation detail.

When developing a library, should we throw errors/exceptions?

I'm developing a pub/sub Mediator library to be used by others and I don't know how to handle errors.
Here's an example piece of code:
/**
*
* #param String channel Channel to subscribe
* #param String|function callback Function name or callback
* #param String context Context to bind function to
* #param Boolean once True to subscribe once
**/
this.subscribe = function (channel, callback, context, once) {
if (!_.isObject(context)) {
context = window;
}
if (!_.isFunction(subscription) && !_.isFunction(context[subscription])) {
throw new Error('Function passed as callback to channel subscription named ' + channel + ' seems invalid!');
}
// Everything ok, add to channels object
channels[channel].push({fn: subscription, context: context || this, once: once});
}
This method is part of the library. It's API expects a channel name and a valid callback (either the function name, a lambda or a callback).
If the channel argument is not a string, the library readily stop. If an invalid context is passed, window is assumed. Mistakes in both these parameters are easily debugaable.
However, if an invalid callback is passed, it propagates the error until an event is fired (a channel publishing is made). In a pub/sub system this might become a nightmare to debug if, for instance, the event is rarely fired.
So my question is...
In this specific scenario, taking in account that I'm developing a javascript library to others, should I:
throw Errors to prevent further error propagation?
return false/undefined/-1 and not register the subscription?
proceed as normal at let it die somewhere, leaving the debugging to the third party developer
NOTE:
There's a similar question but my situation is a bit different and the answers provided didn't put my spirit at ease.
I guess this answer is more of my opinion and what I follow. You have to ask yourself if you should return throw an error and let the consumer, of your library, handle the error. If the error is something that cannot be recovered from, there is no need for you to throw an error (in JS). Your library should be able to fallback gracefully to alternatives but if it cannot do that for a certain function, it should return undefined. Most JS developers do check for undefined and it's a pretty common practice to do so while using library functions. Returning false is usually done in event handlers if an event cannot be handled.
Option #3 should be avoided. At least, return false;. You even might register the invalid callback, expecting that no events are published as well (since something went wrong). This "silent fail if not everything went wrong" might not really be applicable to your pub/sub example, yet there might be use cases for it.
Option #2 sounds good. It somehow makes the callback parameter explicitly optional, which could be a feature of your library - if the user is not sure himself whether he has a reason to subscribe, he can omit that test if you are doing it anyway. You might combine this with a console.warn() message in debugging versions.
Option #1 should be chosen if something really unexpected happened. It allows you to fail with a very descriptive custom Error message. This could be the case if the callback is a truthy object, but not callable, or the channel argument is a boolean or something - depending on how closed your interface design is / how much overloading you provide (in your case you could accept strings to be evaled as callbacks, or arrays of channel strings for example).
Errors and Exceptions are supposed exactly to stop the execution and notify the caller about it.
"throw Errors to prevent further error propagation?" is exactly write choice if your code is not supposed to be executed in that case.
Still, if you expect something to be passed wrong, you can return some special value and describe it in the documentation. Default values are widely used by developers, that's not intuitive,but mostly works tho:
/*
* ...
* Returns a Socket if found, otherwise undefined
*/
function FindSocketOrUndefined(): Socket|undefined {
...
return undefined
}

Categories

Resources