protractor property of argument "is not a function" - javascript

I'm trying to write protractor tests where in some tests XMLHttpRequest needs to be mocked out. I'm trying to do this by passing an imported xhr-moc to the browser using executeScript. The instance is passed to the browser but methods become uncallable as they become string instances.
E.g.:
it('demo', function() {
var x = mock;
browser.executeScript(function (mock) {
mock.setup()
}, mock)
});
Output:
...
Failed: unknown error: mock.setup is not a function
...
When further investigating:
it('demo', function() {
console.log('from spec: ', typeof mock.setup)
browser.executeScript(function (mock) {
return typeof mock.setup
}, mock)
.then(function(output) {
console.log('from browser:', output)
});
});
Output:
...
Started
from spec: function
from browser: string
.
...
Is there a a sane way to pass this object to the browser without having the methods converted to strings?
Or can I serialize the object before sending and deserialize the object in the script to work around this?

Try using the below code.
it('demo', function() {
var x = mock;
browser.executeScript(function (arguments[0]) {
arguments[0].setup()
}, mock)
});
The parameters that you are passing to executeScript method should be accessed using arguments array.and you can also pass any number of arguments and access it using the index.

Related

How can I set a JS object's property to both call a function and also be a nested hash?

Bit of a weird one. I'm fascinated with the context object in Azure, although I have not been able to find the full source code for it.
When using it within Azure function apps, you can do both context.log and context.log.error. I'm trying to replicate this in a local mock like so:
let context = {
log(message){
console.log(message)
},
log: {
info: console.log,
error: console.error
}
}
But clearly the second log property overrides the function so I can't do both context.log and context.log.error.
I also tried
let context = {
'log': console.log,
'log.error': console.error
}
But here, context.log.error is undefined, however context['log.error'] works.
Eventually, I found one that does work:
let context = {
'log': console.log,
}
context.log['error'] = console.error
context.log['info'] = console.log
Which does do what i want:
context.log('message') // prints 'message'
context.log.error('message') // prints error 'message'
But I would like to understand why that last bit worked, and additionally, how I can declare all of it in one go instead of relying on context.log[error] = value
You want to create a function object, and then assign properties to that. You can do that in the object literal with Object.assign:
{
log: Object.assign(function log() { /*...*/ }, {
error() { /*...*/ },
}),
}

Symbol vs. String when retrieving property from target object, using ES6 Proxy object

Using the following code:
const assrt = function () {
try {
return chaiAssert.apply(null, arguments);
}
catch (e) {
return handleError(e);
}
};
v.assert = new Proxy(assrt, {
get: function (target, prop) {
if(typeof prop === 'symbol'){
// I don't know what to do with symbols, so return
return Reflect.get(...arguments);
}
// but here! we still get properties that don't exist
if(!chaiAssert[prop]){
return handleError(
new Error(`The assertion library used does not have '${prop}' property or method.`)
);
}
return function () {
try {
return chaiAssert[prop].apply(null, arguments);
}
catch (e) {
return handleError(e);
}
}
}
});
the error I get with this code is:
TypeError: Cannot convert a Symbol value to a string
and this occurs on the line:
new Error(`The assertion library used does not have '${prop}' property or method.`));
I have used Proxies before, and I have never seen Symbols being passed to the get method of the Proxy. Does anyone know how to circumvent this problem?
Why are Symbols being passed to the Proxy get function and how do I properly handle that?
Why are Symbols being passed to the Proxy get function?
We don't know, you didn't show any code that actually uses the proxy. But many symbols are accessed by builtin methods, e.g. when you iterate the proxy it uses the Symbol.iterator method.
and how do I properly handle that?
You cannot concatenate a symbol with a string, you need to be explicit about doing this. You can use either prop.toString() or just switch based on typeof prop.

Passing metadata between functions

I created an API with Node.js, and I don't want the API to change nor do I want to add extra parameters to a function. However, the internal code in the library needs to now send some metadata between an internal API method and an external facing method.
Is there a way to pass (meta) data between functions somehow in JS that does not involve parameters/arguments?
TL;DR, it would be really useful to pass metadata between functions for the purposes of JS APIs, that should not change signatures.
(One trick is if the function is created everytime it is called, you can assign data onto the function object itself, but that is not true in this case (function is not being created everytime it is called).)
The trick I am currently using - and it is not a good one - there is an options {} object being used in the API. I am passing a hidden property in that objects object "__preParsed". The user will use that objects object as they normally would, behind the scenes I use it for some bookkeeping stuff that they don't need to know about.
Ok here is the code:
//public API
beforeEach.cb = function (desc, opts, fn) {
const _args = pragmatik.parse(arguments, rules.hookSignature);
_args[ 1 ].cb = true;
return beforeEach.apply(ctx, _args);
};
beforeEach = function (desc, opts, aBeforeEach) {
handleSetupComplete(zuite);
const _args = pragmatik.parse(arguments, rules.hookSignature);
const obj = { //have to do this until destructuring works
desc: _args[ 0 ],
opts: _args[ 1 ],
fn: _args[ 2 ]
};
handleBadOptionsForEachHook(obj.opts, zuite);
return 'there is more code but I omitted it';
};
as you can see the first method calls the second, or the second can be called directly, both are public APIs.
We need to parse the arguments in both calls, but as an optimization, we shouldn't have to parse them a second time if the second method was called by the first instead of directly.
The solution I will use for the moment is:
beforeEach.cb = function (desc, opts, fn) {
const _args = pragmatik.parse(arguments, rules.hookSignature);
_args[ 1 ].cb = true;
_args[ 1 ].__preParsed = true;
return beforeEach.apply(ctx, _args);
};
the opts options object is public, but the user won't know about the __preParsed property. The internal API will.
The problem with this is that the user can call the public API directly without an options object, and since the signature is very much varargs, then I really don't know until I have parsed it with my parse engine, which arg if any is the objects object!
You could abuse the this object to carry non-argument metadata in as follows by invoking your function using Function.prototype.call:
function inner (arg1, arg2) {
console.log('inner called with', arg1, arg2)
console.log('inner metadata', this._meta_count)
}
inner.call({_meta_count: 17}, 'ARG ONE', 'ARG TWO')
inner.call({_meta_count: 18}, 'ARG ONE B', 'ARG TWO B')
You could just add a new undocumented parameter to the end. JavaScript won't care and previous calls will still work, why is that a problem for you?
If you are checking parameter count and throwing errors, you could expect the hidden parameter to be an object with a magic property, if it's not, throw the error.
function go(a, b, c, _internal) {
if (_internal && ! _internal.hasOwnProperty('_magic')) {
throw new Error('invalid internal parameter passed');
}
}
You can get a little more paranoid and store the magic property as a Symbol, then the caller couldn't pass it by accident, they would have to be acting nefariously.
function go(a, b, c, _internal) {
if (_internal && !_internal.hasOwnProperty(go.internalParamProp)) {
throw new Error('invalid internal parameter passed');
}
console.log("Internal param", _internal && _internal[go.internalParamProp])
}
// Symbol for the magic property name to avoid accidental passing of internal param
go.internalParamProp = Symbol('');
// Passing the internal param
// Uses JS syntax that is not yet supported in some browsers
// If it's a concern, use or var obj={}; obj[go.internalParamProp] = 45
go(1, 2, 3, {
[go.internalParamProp]: 45
})
// Regular call
go(1, 2, 3)
// Invalid call
go(1, 2, 3, 4)

How can I migrate my custom matchers from Jasmine 1 to Jasmine 2

Version 2 of the JavaScript testing framework jasmine unfortunately introduced several breaking changes. One of these changes is the way custom matchers are handled, as is outlined here:
http://jasmine.github.io/2.0/upgrading.html
The addMatchers function is no longer on the spec (this) it is now on the global jasmine object.
/* was:
this.addMatchers({
*/
jasmine.addMatchers({
A matcher is set up a bit different now. The factory receives a util object which contains things like jasmines equality functions, and any registered customEqualityTesters. The factory is expected to return an object with a compare function which will be called with the actual and expected directly, instead of the actual value being on this
/* was:
toBeCustom: function(expected) {
var passed = this.actual == expected;
*/
toBeCustom: function(util, customEqualityTesters) {
return {
compare: function(actual, expected) {
var passed = actual == expected
The comparison should now return an object with pass and message attributes.
I am looking for an easy way to migrate our existing matchers, so that we can easily switch to the new jasmine version.
To ease the transition to the new jasmine version the following special migration object will help.
Instead of adding the matcher on the this object, you add them on the jasmineMigrate object. But this is really all you need to to. The jasmineMigrate object will take care of the rest.
/* was:
this.addMatchers({
*/
jasmineMigrate .addMatchers({
The implementation of the migration object:
var jasmineMigrate = {};
jasmineMigrate.addMatchers = function (matchers) {
Object.keys(matchers).forEach(function (matcherName) {
var matcher = matchers[matcherName],
migratedMatcher = {};
migratedMatcher[matcherName] = function (util, customEqualityTesters) {
return {
compare: function (actual) {
var matcherArguments,
thisForMigratedMatcher,
matcherResult,
passed;
//In Jasmine 2 the first parameter of the compare function
//is the actual value.
//Whereas with Jasmine 1 the actual value was a property of the matchers this
//Therefore modify the given arguments array and remove actual
matcherArguments = [].slice.call(arguments)
matcherArguments.splice(0, 1);
//Add actual to the this object we'll be passing to the matcher
thisForMigratedMatcher = {
actual: actual
};
//Now call the original matcher aufgerufen, with the modified
//arguments and thisForMigratedMatcher which will be applied to
//the matcher
passed = matcher.apply(thisForMigratedMatcher, matcherArguments);
matcherResult = {
pass: passed,
message: thisForMigratedMatcher.message
};
return matcherResult;
}
}
};
jasmine.addMatchers(migratedMatcher);
});
}
The add-matchers library lets you write matchers which are compatible with Jasmine v1, Jasmine v2, and Jest.

Karma - test function with no return and which sets no scope variable

I've been reading up on karma mainly and jasmine a little and have begun to implement testing on my app.
I have the following function :
$scope.popup1 = function (isinData) {
var popup1 = window.open("views/Box_Ladder.html", "_blank",
"height = 400, width = 700");
shareDataService.setIsinClickValue(isinData);
}
How on earth do I test this using karma? The expected result is a popup window opening and the relevant data being passed to my service. How do I expect this?
You spy on window.open and expect it to be called with the right arguments.
Even if the function doesn't return something, it should at the minimum cause some side-effect. You need to test the side-effects.
To do this, create and inject a mock object + object method. An example would be as follows:
var window = {
open: function(url, target, specs) {
var spec, specKey;
this.href = url;
this.target = target;
// Parse through the spec string to grab the parameters you passed through
var specArray = specs.split(',');
for (specKey in specArray) {
spec = specArray[specKey].split('=');
this[String.trim(spec[0])] = String.trim(spec[1]);
}
}
};
Now you can expect(window.href).toEqual(url), expect(window.target).toEqual(target), expect(window.height).toEqual(400), etc.
Additionally, you need to see if sharedDataService.setIsinClickValue was invoked. If you cannot access this service within your test, you're going to have to create another mock object + method.

Categories

Resources