Jasmine 2 custom matcher for hasClass in Protactor - jasmine

I upgrade my Jasmine 1.3 to 2.0 so I added a custom matcher to check css is present.Below is the code to check the matcher
hasClass = function(actual,expected){
return actual.getAttribute('class').then(function (classes) {
return classes.split(' ').indexOf(expected) !== -1;
});
}
But when I upgrade to Jasmine 2 then promise throws error by protactor as it expect return but below is async process
hasClass = function(){
return compare: function(actual,expected){
return actual.getAttribute('class').then(function (classes) {
return {pass: classes.split(' ').indexOf(expected) !== -1};
});
}
}
How can I test class is present in element I don't want to use jasmine-jquery??

The pass should be a promise, not resolved in one. Try to place this in your beforeEach:
this.addMatchers({
hasClass: function() {
return {
compare: function(actual, expected) {
return {
pass: actual.getAttribute('class').then(function(classes) {
return classes.split(' ').indexOf(expected) !== -1;
})
};
}
};
}
});

Related

issue when cypress executes code asynchronously

i have this method that returns an object. if i run the code below i find that the second console.log() hits first and the object returns undefined
private routeAndPassengerDataObject: undefined | RouteAndPassenger;
public getDataFromRouteAndPassengerObject(fileName?: string): RouteAndPassenger {
if (this.routeAndPassengerDataObject) {
return this.routeAndPassengerDataObject;
}
if (typeof fileName !== 'string') {
throw new Error(`${fileName} has to be a string`);
}
cy.fixture(fileName).then((data: unknown) => {
if (!isValidRouteAndPassengerObject(data)) {
throw new Error(`${data} is not valid`);
}
console.log(`this is first`);
this.routeAndPassengerDataObject = data;
});
console.log(`this is second`);
return this.routeAndPassengerDataObject!;
}
}
const routeAndPassengerData = getDataFromRouteAndPassengerObject()
console.log(routeAndPassengerData);
result-
this is second
this is first
undefined
would like to know how to handle this please.
returing the object as cypress.chainable like below has worked for me-
public getDataFromRouteAndPassengerObject(fileName?: string): Cypress.Chainable<RouteAndPassenger> {
if (this.routeAndPassengerDataObject) {
return cy.wrap(this.routeAndPassengerDataObject);
}
if (typeof fileName !== 'string') {
throw new Error(`${fileName} has to be a string`);
}
return cy.fixture(fileName).then((data: unknown) => {
if (!isValidRouteAndPassengerObject(data)) {
throw new Error(`${data} is not valid`);
}
console.log(`this is first`);
this.routeAndPassengerDataObject = data;
return cy.wrap(this._routeAndPassengerDataObject);
});
}
and then calling the object like so-
const routeAndPassengerData = getDataFromRouteAndPassengerObject().then((routeAndPassenger)=>{
console.log(routeAndPassenger)
})

Not all code paths return a value in Typescript Promise

I have a method that returns a Typescript promise. I am tightening up my code by enabling TSLint and it says that "Not all code paths return a value" for this method.
Please critique my code - I cannot work out what path it is referring to:
public getExtendedProfile(): Promise<any> {
//only do this if they are authenticated
if (this.authenticated()) {
if (typeof this.userProfile.user_id !== 'undefined') {
if (typeof this.user == 'undefined') {
this.profileService.getExtendedUserProfile(this.userProfile.user_id)
.then(data => {
return Promise.resolve(data);
})
.catch((error: any) => {
return Promise.reject(error)
});
}
else {
return Promise.resolve(this.user);
}
}
else {
return Promise.reject("No user stored");
}
}
else {
return Promise.reject("Not Authenticated");
}
}
// una alternativa podria ser
:Promise<any>
// cambiar a
:Promise<any|void>
docs typescript funciones: https://www.typescriptlang.org/docs/handbook/functions.html

jasmine unit test for modalInstance result

i am new to jasmine tests i could not able to find solution for this after trying in all ways i am posting it here can any body suggest to write test case for this code which is in angular js
modalInstance.result.then(function (modalOutput) {
if (modalOutput) {
if ('okToUndoEdits' === modalOutput[1]) {
$scope.chargebackDetail.cbDetails = angular.copy($scope.chargebackDetailBackup.cbDetails);
} else if ('okToSaveUnmatched' === modalOutput[1]) {
$('#noMatchWarning').show();
$scope.isMatchedDoc = false;
}
}
}, function () {
});

How can I override jasmine's buildExpectationResult in order to modify message() function?

I am using protractor for my e2e tests and jasmine2 as framework. I am using a plugin for html reporter with screenshots ( html-report for protractor ).
In these reports there will be shown a list of all failed/passed expects. When the expect fails I get a descriptive message of the expectation. However when the expect passes I only see the word: Passed. The reason behind that is that jasmine overrides the message when the expect passes.
That is done in the following file:
node_modules/protractor/node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine.js
getJasmineRequireObj().buildExpectationResult = function () {
function buildExpectationResult(options) {
var messageFormatter = options.messageFormatter || function () {
},
stackFormatter = options.stackFormatter || function () {
};
var result = {
matcherName: options.matcherName,
message: message(),
stack: stack(),
passed: options.passed
};
if (!result.passed) {
result.expected = options.expected;
result.actual = options.actual;
}
return result;
function message() {
if (options.passed) {
// Here is the message overriden
return 'Passed.';
} else if (options.message) {
return options.message;
} else if (options.error) {
return messageFormatter(options.error);
}
return '';
}
function stack() {
if (options.passed) {
return '';
}
var error = options.error;
if (!error) {
try {
throw new Error(message());
} catch (e) {
error = e;
}
}
return stackFormatter(error);
}
}
return buildExpectationResult;
};
What I wanted is to override this function in my protractor protractor.conf.js file. And replace it with one with the desired behaviour.
I've tried to do so unsuccessfully doing the following:
onPrepare: function () {
jasmine.buildExpectationResult = function () {
function buildExpectationResult(options) {
var messageFormatter = options.messageFormatter || function () {
},
stackFormatter = options.stackFormatter || function () {
};
return {
matcherName: options.matcherName,
expected: options.expected,
actual: options.actual,
message: message(),
stack: stack(),
passed: options.passed
};
function message() {
if (options.message) {
return options.message;
} else if (options.error) {
return messageFormatter(options.error);
}
return "";
}
function stack() {
if (options.passed) {
return "";
}
var error = options.error;
if (!error) {
try {
throw new Error(message());
} catch (e) {
error = e;
}
}
return stackFormatter(error);
}
}
return buildExpectationResult;
};
}
Then my questions is: What is the right way to override a jasmine method?
Since we use gulp task to run protractor tests, we override the lib (like jasmine lib) as one of the gulp task with custom copy. We do that as part of installation or every test execution.
I didn't find any good way to override it unless we create another npm module.
I had the same issue, I'm not sure if my solution
onPrepare: function () {
// ...
jasmine.Spec.prototype.addExpectationResult = function(passed, data, isError) {
var buildExpectationResult = function(options) {
var messageFormatter = options.messageFormatter || function() {},
stackFormatter = options.stackFormatter || function() {};
var result = {
matcherName: options.matcherName,
message: message(),
stack: stack(),
passed: options.passed
};
if(!result.passed) {
result.expected = options.expected;
result.actual = options.actual;
}
return result;
function message() {
if (options.passed) {
return options.message ? options.message : 'Passed';
} else if (options.message) {
return options.message;
} else if (options.error) {
return messageFormatter(options.error);
}
return '';
}
function stack() {
if (options.passed) {
return '';
}
var error = options.error;
if (!error) {
try {
throw new Error(message());
} catch (e) {
error = e;
}
}
return stackFormatter(error);
}
}
var exceptionFormatter = jasmine.ExceptionFormatter;
var expectationResultFactory = function(attrs) {
attrs.messageFormatter = exceptionFormatter.message;
attrs.stackFormatter = exceptionFormatter.stack;
return buildExpectationResult(attrs);
}
var expectationResult = expectationResultFactory(data);
if (passed) {
this.result.passedExpectations.push(expectationResult);
} else {
this.result.failedExpectations.push(expectationResult);
if (this.throwOnExpectationFailure && !isError) {
throw new j$.errors.ExpectationFailed();
}
}
};
// ...
}

Jasmine toEqual for complex objects (mixed with functions)

Currently, I have a function that sometimes return an object with some functions inside. When using expect(...).toEqual({...}) it doesn't seem to match those complex objects. Objects having functions or the File class (from input type file), it just can't. How to overcome this?
Try the Underscore _.isEqual() function:
expect(_.isEqual(obj1, obj2)).toEqual(true);
If that works, you could create a custom matcher:
this.addMatchers({
toDeepEqual: function(expected) {
return _.isEqual(this.actual, expected);
};
});
You can then write specs like the following:
expect(some_obj).toDeepEqual(expected_obj);
As Vlad Magdalin pointed out in the comments, making the object to a JSON string, it can be as deep as it is, and functions and File/FileList class. Of course, instead of toString() on the function, it could just be called 'Function'
function replacer(k, v) {
if (typeof v === 'function') {
v = v.toString();
} else if (window['File'] && v instanceof File) {
v = '[File]';
} else if (window['FileList'] && v instanceof FileList) {
v = '[FileList]';
}
return v;
}
beforeEach(function(){
this.addMatchers({
toBeJsonEqual: function(expected){
var one = JSON.stringify(this.actual, replacer).replace(/(\\t|\\n)/g,''),
two = JSON.stringify(expected, replacer).replace(/(\\t|\\n)/g,'');
return one === two;
}
});
});
expect(obj).toBeJsonEqual(obj2);
If anyone is using node.js like myself, the following method is what I use in my Jasmine tests when I am only concerned with comparing the simple properties while ignoring all functions. This method requires json-stable-stringify which is used to sort the object properties prior to serializing.
Usage:
var stringify = require('json-stable-stringify');
var obj1 = {
func: function() {
},
str1: 'str1 value',
str2: 'str2 value',
nest1: {
nest2: {
val1:'value 1',
val2:'value 2',
someOtherFunc: function() {
}
}
}
};
var obj2 = {
str2: 'str2 value',
str1: 'str1 value',
func: function() {
},
nest1: {
nest2: {
otherFunc: function() {
},
val2:'value 2',
val1:'value 1'
}
}
};
it('should compare object properties', function () {
expect(stringify(obj1)).toEqual(stringify(obj2));
});
Extending #Vlad Magdalin's answer, this worked in Jasmine 2:
http://jasmine.github.io/2.0/custom_matcher.html
beforeEach(function() {
jasmine.addMatchers({
toDeepEqual: function(util, customEqualityTesters) {
return {
compare: function(actual, expected) {
var result = {};
result.pass = _.isEqual(actual, expected);
return result;
}
}
}
});
});
If you're using Karma, put that in the startup callback:
callback: function() {
// Add custom Jasmine matchers.
beforeEach(function() {
jasmine.addMatchers({
toDeepEqual: function(util, customEqualityTesters) {
return {
compare: function(actual, expected) {
var result = {};
result.pass = _.isEqual(actual, expected);
return result;
}
}
}
});
});
window.__karma__.start();
});
here's how I did it using the Jasmine 2 syntax.
I created a customMatchers module in ../support/customMatchers.js (I like making modules).
"use strict";
/**
* Custom Jasmine matchers to make unit testing easier.
*/
module.exports = {
// compare two functions.
toBeTheSameFunctionAs: function(util, customEqualityTesters) {
let preProcess = function(func) {
return JSON.stringify(func.toString()).replace(/(\\t|\\n)/g,'');
};
return {
compare: function(actual, expected) {
return {
pass: (preProcess(actual) === preProcess(expected)),
message: 'The functions were not the same'
};
}
};
}
}
Which is then used in my test as follows:
"use strict";
let someExternalFunction = require('../../lib/someExternalFunction');
let thingBeingTested = require('../../lib/thingBeingTested');
let customMatchers = require('../support/customMatchers');
describe('myTests', function() {
beforeEach(function() {
jasmine.addMatchers(customMatchers);
let app = {
use: function() {}
};
spyOn(app, 'use');
thingBeingTested(app);
});
it('calls app.use with the correct function', function() {
expect(app.use.calls.count()).toBe(1);
expect(app.use.calls.argsFor(0)).toBeTheSameFunctionAs(someExternalFunction);
});
});
If you want to compare two objects but ignore their functions, you can use the methods _.isEqualWith together with _.isFunction from lodash as follows.
function ignoreFunctions(objValue, otherValue) {
if (_.isFunction(objValue) && _.isFunction(otherValue)) {
return true;
}
}
it('check object equality but ignore their functions', () => {
...
expect(_.isEqualWith(actualObject, expectedObject, ignoreFunctions)).toBeTrue();
});

Resources