How to compare two json responses and see the differences in Postman? - comparison

I need to create tests in Postman where I compare two responses for two different requests and I want to see what is (if there is anything) the difference between them.
Case is that I can get json response which can contain anything, then I need to check if on different environment the same request gave the same response.
Right now I do it that way:
In first request I save responsee:
pm.globals.set('response', pm.response.json());
In second request I compare response with saved one with:
pm.test('Should have identical responses as previous', () => {
pm.expect(pm.response.json()).to.deep.equal(pm.globals.get('response'));
});
But in this case I just see if there is any difference, so I have to go through a lot of lines each time to find what was wrong.
What I need to get is when I have first response like:
[
{
color: "red",
value: "#f00"
},
{
color: "green",
value: "#0f0"
}
]
And second like:
[
{
color: "red",
value: "#f00"
},
{
color: "green",
value: "#0f2"
}
]
I want to get info in run results like:
there is difference in line: value: "#0f2"
or
in first response there was value: "#0f0" and in second there is value: "#0f2"
Is it even possible to do?

Ok, the solution that I worked with:
In first request I saved response with
pm.globals.set('respa', pm.response.json());
In second I used function to find differences:
function diff(obj1, obj2) {
const result = {};
if (Object.is(obj1, obj2)) {
return undefined;
}
if (!obj2 || typeof obj2 !== 'object') {
return obj2;
}
Object.keys(obj1).concat(Object.keys(obj2)).forEach(key => {
if (obj2[key] !== obj1[key] && !Object.is(obj1[key], obj2[key])) {
result[key] = obj2[key];
}
if (typeof obj2[key] === 'object' && typeof obj1[key] === 'object') {
const value = diff(obj1[key], obj2[key]);
if (value !== undefined) {
result[key] = value;
}
}
});
return result;
}
And then also in second response I've added 'if' which create test when there is a difference in responses, and that test is named as difference, and also it's saved in console as json.
pm.globals.set('respb', pm.response.json());
if (!Object.is(pm.globals.get('respb'), pm.globals.get('respa'))) {
const result = diff(pm.globals.get('respb'), pm.globals.get('res12'));
console.log(result);
pm.test('Difference' + JSON.stringify(result), () => {
pm.expect(0).to.equal(pm.globals.get(1));
});
}

Related

Single sign on failing with LinkedIn account to a microsoft website

We are seeing an issue with users unable to access our production and PPE apps via LinkedIn sign in. The redirection is not happening to specified redirect URL once users provides user name and password. The network trace shows login is successful but not going to redirect URL. This has been working last 4 years or so and suddenly started failing in both environments from yesterday.
Bummer. Something went wrong
We tried verifying the network trace and a support case is raised to LinkedIn with recording. Finally we are redirected to raise the issue here.
I had the same issue and found that it was caused by using JSON.stringify to "overload" the state parameter with other parameters. In my case, I add other parameters in the following way:
providerCfg.auth_params.state = JSON.stringify({
state: providerCfg.auth_params.state,
redirectPageUrl,
redirectParams,
userTypeBit,
isLogin
})
const authUrl = new URL(providerCfg.auth_url)
Object.entries(providerCfg.auth_params).forEach(([key, val]) => {
authUrl.searchParams.append(key, encodeURIComponent(val))
})
return buildURL(providerCfg.auth_url, providerCfg.auth_params)
When I removed the call to JSON.stringify and just passed in a state parameter, the oauth flow worked correctly. Obviously, the other parameters that I passed in were important so I created my own functions to serialize and deserialize the values. The code below works well for anything other than deeply nested objects. You will need to update the metaDataCfg based on your own requirements.
const META_STRING_DELIMITER = '|'
const serializeBasicObject = (targetObj) => {
if (!targetObj) {
return ''
}
return Object.entries(targetObj).reduce((objString, [key, val]) => {
const param = `${key}=${val || ''}`
if (!objString.length) {
return param
}
return `${objString}${META_STRING_DELIMITER}${param}`
}, '')
}
const deserializeBasicObject = (targetStr) => {
if (!targetStr) {
return ''
}
const keyValPairs = targetStr.split(META_STRING_DELIMITER)
return keyValPairs.reduce((targetObj, keyValPair) => {
const splitIdx = keyValPair.indexOf('=')
const key = keyValPair.slice(0, splitIdx)
targetObj[key] = keyValPair.slice(splitIdx + 1, keyValPair.length)
return targetObj
}, {})
}
const metaDataCfg = {
state: {},
redirectPageUrl: {},
redirectParams: {
serialize: serializeBasicObject,
deserialize: deserializeBasicObject
},
userTypeBit: { deserialize: Number },
isLogin: { deserialize: dataUtil.getBoolean }
}
const getMetaString = (metaData) => {
return Object.entries(metaDataCfg).reduce((metaString, [metaDataKey, cfg]) => {
const val = (cfg.serialize) ? cfg.serialize(metaData[metaDataKey]) : metaData[metaDataKey]
const param = `${metaDataKey}=${dataUtil.isNil(val) ? '' : val}`
if (!metaString.length) {
return param
}
return `${metaString}${META_STRING_DELIMITER}${param}`
}, '')
}
export const getDataFromMetaString = (metaString) => {
const params = metaString.split(META_STRING_DELIMITER)
const data = params.reduce((metaData, param) => {
const splitIdx = param.indexOf('=')
const key = param.slice(0, splitIdx)
let val = param.slice(splitIdx + 1, param.length)
if (dataUtil.isNil(val) || !val.length) {
return metaData
}
const deserializer = metaDataCfg[key].deserialize
if (deserializer && val) {
val = deserializer(val)
}
metaData[key] = val
return metaData
}, {})
return data
}

Angular UI-grid not sorting by date

I am using UI-grid, and I have a bunch of JS date objects like so:
"dob": new Date('1942-11-19')
I want to be able to filter the column by date when you click the "sort ascending/descending" buttons. As such, I have set the colDef up like so:
{
field: 'dob'
, displayName: 'D.O.B.'
, width: '130'
, editableCellTemplate: '<div><form name="inputForm"><input type="INPUT_TYPE" ng-class="\'colt\' + col.uid" ui-grid-editor ng-model="MODEL_COL_FIELD" style="border-bottom-color: #74B3CE; border-bottom-width: 2px;"></form></div>'
, headerCellClass: $scope.highlightFilteredHeader
, cellTemplate: '<div class="ui-grid-cell-contents" >{{grid.getCellValue(row, col)| date:\'MM-dd-yyyy\'}}</div>'
, cellFilter: 'date'
, type: 'date'
},
however, the column simply does not sort correctly. I even tried to set up a function to sort it from an external button like so:
function mostRecent(){
console.log('clicked mostRecent');
$scope.gridApi.grid.sortColumn(
$scope.gridApi.grid.getColumn('dob'), uiGridConstants.DESC
);
$scope.gridApi.grid.notifyDataChange(uiGridConstants.dataChange.ALL); //this line updates the rest of the columns accordingly
};
But it also causes a mish-mush sort that is not correct. Does anyone know what the issue is? I thought it might have to do with my cellTemplate, but after removing the template, there wasn't a difference...
Yes you are right, ui-grid doesn't support sorting of Date type columns.
However you can define a sortingAlgorithm in the columnDef.
Here is how your column definition should look like:
...
columnDefinition.sortingAlgorithm = function (firstDateString, secondDateString) {
var dateFormat = 'YYYY-MM-DD';
return function (firstDateString, secondDateString, dateFormat) {
if (!firstDateString && !secondDateString) {
return 0;
}
if (!firstDateString) {
return 1;
}
if (!secondDateString) {
return -1;
}
var firstDate = $window.moment(firstDateString, dateFormat);
if (!firstDate.isValid()) {
throw new Error('Invalid date: ', firstDateString);
}
var secondDate = $window.moment(secondDateString, dateFormat);
if (!firstDate.isValid()) {
throw new Error('Invalid date: ', secondDateString);
}
if (firstDate.isSame(secondDate)) {
return 0;
} else {
return firstDate.isBefore(secondDate) ? -1 : 1;
}
};
};
...
Please note that in this example Moment.js is used. It is a very useful library so you might probably find also another place in your project where to use it.
$scope.gridOptions = {
data: 'gridData',
columnDefs: [
{field: 'name', displayName: 'Name'},
{field:'age',
displayName:'Birth Date',
sortFn: function (aDate, bDate) {
var a=new Date(aDate);
var b=new Date(bDate);
if (a < b) {
return -1;
}
else if (a > b) {
return 1;
}
else {
return 0;
}
}
}]
};
Try this
http://plnkr.co/edit/0VD3X5YvuNSWAZlig95X?p=info
reference :
https://github.com/angular-ui/ui-grid/issues/222
You can define the Sorting Algorithm for the date fields in UI Grid like below
columnDefs: [
{
field: 'DateFrom', displayName: 'From',
sortingAlgorithm: function (aDate, bDate, rowA, rowB, direction) {
var a = new Date(moment(aDate, "DD-MM-YYYY").format("YYYY-MM-DD"));
//here DD-MM-YYYY is the current format in which the dates are returned
var b = new Date(moment(bDate, "DD-MM-YYYY").format("YYYY-MM-DD"));
if (a < b) {
return -1;
}
else if (a > b) {
return 1;
}
else {
return 0;
}
}
}
]
We can sort the ui-grid column containing date field in a simplest way.
Make use of cellTemplate in this way:
{
name: "Date",
field: 'date',
cellTemplate:'<div>{{row.entity.date | date:"dd/MM/yyyy"}}</div>'
},
So, you can choose any format for date, for eg. date:"dd-MM" etc.

Throw error in Mongoid

Lets say i have this code:
map = %Q{
function() {
emit(this.name, { likes: this.likes });
}
}
reduce = %Q{
function(key, values) {
var result = { likes: 0 };
values.forEach(function(value) {
if(value.likes < 0){
#{Rails.logger.error "likes are negativ" }
}
result.likes += value.likes;
});
return result;
}
}
Band.where(:likes.gt => 100).map_reduce(map, reduce).out(inline: true)
As you can see I want to record an error if the value.likes are negativ:
#{Rails.logger.error "likes are negativ" }
But this Code is executed each time I run the aggregate and not when the likes are negativ.
What can I do to throw an error in the aggregate statement?
Lets just analyze the code. Firstly:
map = %Q{
function() {
emit(this.name, { likes: this.likes });
}
}
Here a string is assigned to a variable map. Please not the %Q{} is just another way of writing "". Former is another syntax to easily define strings which have a double quote. e.g.
# pretty
%Q{He said "You are awesome"}
# not so pretty
"He said \"You are awesome\""
Next there is:
reduce = %Q{
function(key, values) {
var result = { likes: 0 };
values.forEach(function(value) {
if(value.likes < 0){
#{Rails.logger.error "likes are negative" }
}
result.likes += value.likes;
});
return result;
}
}
Here another string is assigned to a variable reduce. #{Rails.logger.error "likes are negative" } is just a regular string interpolation logging an error and returning true. So above code is equivalent to:
Rails.logger.error "likes are negative"
reduce = %Q{
function(key, values) {
var result = { likes: 0 };
values.forEach(function(value) {
if(value.likes < 0){
true
}
result.likes += value.likes;
});
return result;
}
}
You see why the logging statement is executed every time.
Next there is:
Band.where(:likes.gt => 100).map_reduce(map, reduce).out(inline: true)
This is just a simple statement using mongoid to execute a map-reduce command on the mongo server, passing map and reduce functions constructed earlier.
Note that in above code, intention is to execute ruby code in a javascript reduce function. However that is not possible, as reduce function is being executed on mongodb server and cannot execute the logging statement.
One way to handle the situation could be to reduce to a hash like {likes: 0, negative_likes: 0}, incrementing negative_likes conditionally and logging error on receiving result.
PS: it might be a better idea to use aggregation framework instead of map-reduce.

Override Minimum length string of Select2

Select2 Jquery Plugin
I was having hard time how to override the default message for minimum length input in jquery Select2.
by default the plugin gives the following message.
Default Text
Please enter 1 more characters
My requirement was to show, the following text
Required Text
Enter 1 Character
please share the solution.
Thanks.
The accepted answer does not work for Select2 v4. Expanding on the comment by #IsaacKleinman, the way to override the default messages for an individual Select2 instance is through the language property:
var opts = {
language: {
inputTooShort: function(args) {
// args.minimum is the minimum required length
// args.input is the user-typed text
return "Type more stuff";
},
inputTooLong: function(args) {
// args.maximum is the maximum allowed length
// args.input is the user-typed text
return "You typed too much";
},
errorLoading: function() {
return "Error loading results";
},
loadingMore: function() {
return "Loading more results";
},
noResults: function() {
return "No results found";
},
searching: function() {
return "Searching...";
},
maximumSelected: function(args) {
// args.maximum is the maximum number of items the user may select
return "Error loading results";
}
}
};
$('#mySelect').select2(opts);
To override the functions globally, call the set function on the defaults (according to the docs):
$.fn.select2.defaults.set("key", "value")
However, in our code we do it like this:
$.fn.select2.defaults.defaults['language'].searching = function(){
return 'Custom searching message'
};
I don't know why we don't follow the docs, but it works.
Solution
Here is the solution that i have found out.
Prior to v4
Initialize
$("input[name='cont_responsible'],input[name='corr_responsible'],input[name='prev_responsible'],input[name='pfmea_responsible']").select2({
minimumInputLength: 1,
formatInputTooShort: function () {
return "Enter 1 Character";
},
});
Note
Do not forget to add this code in your document. ready function.
$(document).ready(function () {
});
I shared my solution, any better solutions are welcome.
Thanks.
Using v4 and onwards
The following worked for V4. #Isaac Kleinman
language: { inputTooShort: function () { return ''; } },
You can try this on version 4.0 or higher
you can see reference for answer frome this link :
issues reference
$("#select2").select2({
minimumInputLength: 1,
language: {
inputTooShort: function() {
return 'Please Add More Text';
}
}
});
If you are using django-select2, just add attributes to your form in forms.py:
widget=BookSelect2Widget(
attrs={'data-minimum-input-length': 1}
)
Override the function behaviour like below
$.fn.select2.defaults = $.extend($.fn.select2.defaults, {
formatMatches: function(matches) {
return matches + $filter('translate')('label.matches.found');
},
formatNoMatches: function() {
return $filter('translate')('noMatches.found');
},
formatInputTooShort: function(input, min) {
var n = min - input.length;
return $filter('translate')('label.please.enter ') + n + $filter('translate')(' more.characters') + (n == 1 ? "" : "s");
},
formatInputTooLong: function(input, max) {
var n = input.length - max;
return $filter('translate')('please.delete ') + n + $filter('translate')('')('delete.characters') + (n == 1 ? "" : "s");
},
formatSelectionTooBig: function(limit) {
return $filter('translate')('select.only') + limit + $filter('translate')('select.item ') + (limit == 1 ? "" : "s");
},
formatLoadMore: function(pageNumber) {
return $filter('translate')('load.results');
},
formatSearching: function() {
return $filter('translate')('label.search');
}
});
}

jQuery UI Spinner with letters A-Z or other custom range

Is there a way to customize jQuery UI spinner, so that A-Z letters (or any custom range) is possible?
Yes, this is possible. Here's a simple example using A-Z, adapted from the provided time example:
$.widget("ui.alphaspinner", $.ui.spinner, {
options: {
min: 65,
max: 90
},
_parse: function(value) {
if (typeof value === "string") {
return value.charCodeAt(0);
}
return value;
},
_format: function(value) {
return String.fromCharCode(value);
}
});
Usage:
$("#my-input").alphaspinner();
Example: http://jsfiddle.net/4nwTc/1/
The above example creates a new widget called alphaspinner that inherits from spinner. You can do this for just one spinner with the following:
$(function() {
var spinner = $("#alpha-spinner").spinner({
min: 65,
max: 90
}).data("spinner");
spinner._parse = function (value) {
if (typeof value === "string") {
return value.charCodeAt(0);
}
return value;
};
spinner._format = function (value) {
return String.fromCharCode(value);
}
});​
Example: http://jsfiddle.net/4nwTc/2/
I built up on Andrews code and built a spinner widget that takes a string array for input.
You can see the solution here.

Resources