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

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();
}
}
};
// ...
}

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)
})

How to call the function inside other function both defined in same export default?

My code are:-
function showData(data) {
return {
type: 'SHOWDATA',
data,
};
}
export default {
fetchData() {
return function (dispatch) {
getDataApi.getData().then((response)=>dispatch(showData(response)).catch()
};},
updateData{
return function (dispatch) {
getDataApi.getData().then((response)=>if(response.isSucess)
{dispatch(fetchData())}).catch()
};}
}
After update call of the action I want to refresh the list thats why I
called dispatch(fetchData()); but it is showing that fetchData not
defined.How can I call the method defined in same export default function.
Can this help you? Not really exported as default but its named.
export const Actions = {
getAll,
add,
update,
view,
search
}
function getAll(){
return dispatch => {
dispatch(request());
Service.getAll()
.then(
response => {
// todo...
},
error => {
// catch error
}
);
}
function request() { return { type: Constants.LIST_REQUEST } }
function success(data) { return { type: Constants.LIST_SUCCESS, data } }
function failure(error) { return { type: Constants.LIST_FAILURE, error } }
}
function add(data){
return dispatch => {
dispatch(request());
Service.add(data)
.then(
response => {
if(response.status === 'fail'){
// do something
}else{
dispatch(success(response));
dispatch(getAll());
}
},
error => {
// do something
}
);
}
function request() { return { type: Constants.ADD_REQUEST } }
function success(data) { return { type: Constants.ADD_SUCCESS, data } }
function failure(error) { return { type: Constants.ADD_FAILURE, error } }
}

Services in redux

I'm new in react/redux, I've try to call a service cap from my sagas cap, but when I try to call the axios.get() method I get always a promime with PromiseStatus = "pending" and PromiseValue= undefined, I've try to get the promise but I don't know how can I do it.
My Sagas.js
try
{
let response = yield
StudentsService.getStudentByEmail(action.payload.studentUser)
yield put({type: studentActionsTypes.STUDENT_DATA_SUCCESS,
studentResponse: response});
}
catch (error)
{
console.log('El servicio me devolvió un error: ' + error.message);
yield put({type: studentActionsTypes.STUDENT_DATA_ERROR});
}
My service
export class StudentsService
{
static getStudentByEmail(email)
{
var userT = '';
const state = {
studentF: {
studentId: '',
studentFullName: ''
}
};
let userPromise = UsersService.getUsers();
userPromise.then((response) =>
{
if(response.data.users.length >= 0)
{
response.data.users.map(u =>
{
if(u.email == email)
{
userT = u;
console.log('Nombre en este objeto: ' + userT.name)
this.state = {
studentF: {
studentId: userT.id,
studentFullName: userT.name + ' ' + userT.lastName
}
}
}
});
}
});
return state.studentF;
}
}
I'll be grateful of your help.
I was able to solve my problem with the the answer in this other question

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();
});

Backbone.js Model validate method

How can I validate only certain attributes on a model? Currently I check if the attribute exists in the object passed into validate:
validate: function(attrs) {
// Number
if (attrs.minimum) {
if (isNaN(attrs.minimum)) {
return -1;
}
}
if (attrs.maximum) {
if (isNaN(attrs.maximum)) {
return -1;
}
}
}
but if I want to validate string value then:
if (attrs.mystring) {
// Do validation
}
would fail and the validation never takes place.
Backbone now supports the has property. So you can do something like that:
var Person = Backbone.Model.extend({
defaults: {
"name": "Kevin",
"age" : 26,
"job" : "web"
},
validate: function(attrs, options) {
for(k in attrs) {
if(!this.has(k)) {
return k + ' attribute is not exist';
}
}
}
});
var person = new Person;
person.on("invalid", function(model, error) {
console.log(error);
});
Im a little confused by your wording, but I think you want to check if its not an empty string first? and also work out the possibility that it is undefined..if so then this is what you'll want to do..
validate: function(attrs) {
// Number
if (attrs.minimum) {
if (isNaN(attrs.minimum)) {
return -1;
}
}
if (attrs.maximum) {
if (isNaN(attrs.maximum)) {
return -1;
}
}
if (typeof(attrs.mystring) != "undefined"){
if (!attrs.mystring){
return -1;
}
}
}
if you want to only validate one of your attributes, you should write your validate function to accommodate the options accordingly
validate: function(attrs, option) {
if (!option){
// Number
if (attrs.minimum) {
if (isNaN(attrs.minimum)) {
return -1;
}
}
if (attrs.maximum) {
if (isNaN(attrs.maximum)) {
return -1;
}
}
if (!attrs.mystring){
return -1;
}
}else{
switch(option){
case("string"):
if (!attrs.mystring){
return -1;
}
break;
case("number"):
// Number
if (attrs.minimum) {
if (isNaN(attrs.minimum)) {
return -1;
}
}
if (attrs.maximum) {
if (isNaN(attrs.maximum)) {
return -1;
}
}
break;
}
}
}
there are many ways to do this, this probably being the least efficient lol but using your example, it will do the job.
also, this isn't really a backbone.js problem per say...but general js

Resources