mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-01 21:22:14 +08:00
c629967c3d
commit423cf4f407
Author: Ben Gotow <ben@foundry376.com> Date: Fri Oct 11 22:33:53 2019 -0500 Replace belcard with homegrown VCard parser because our needs are minimal and the linux binary has relocation issues commit4ae19c0ed5
Author: Ben Gotow <ben@foundry376.com> Date: Fri Oct 11 10:38:26 2019 -0500 Skip building a few more belr components, still looking for relocation error cause commita7ec02a449
Author: Ben Gotow <ben@foundry376.com> Date: Thu Oct 10 22:16:15 2019 -0500 Fix windows SRV record lookups for contact directory autodiscovery commit318a31d279
Author: Ben Gotow <ben@foundry376.com> Date: Thu Oct 10 20:50:06 2019 -0500 Leave vcard_grammar unpacked so mailsync can find it at runtime commitbf7e43e37d
Author: Ben Gotow <ben@foundry376.com> Date: Thu Oct 10 20:43:01 2019 -0500 Fix bug in icon case sensitivity commitc283513653
Author: Ben Gotow <ben@foundry376.com> Date: Thu Oct 10 17:29:24 2019 -0500 Renew Windows Authenticode code signing cert (there goes $500…) commitd13235f65b
Author: Ben Gotow <ben@foundry376.com> Date: Wed Oct 9 23:25:34 2019 -0500 Bump mailsync to move belr dlls commit00ca6431df
Author: Ben Gotow <ben@foundry376.com> Date: Wed Oct 9 23:22:53 2019 -0500 Bump to xcode9 to fix odd C++11 error commit47903c99c4
Author: Ben Gotow <ben@foundry376.com> Date: Wed Oct 9 23:01:32 2019 -0500 Bump mailsync to build belr as a dll instead of a static lib commit21d645d4e7
Author: Ben Gotow <ben@foundry376.com> Date: Wed Oct 9 22:38:24 2019 -0500 Bump mailsync commit3f943031cb
Author: Ben Gotow <ben@foundry376.com> Date: Wed Oct 9 21:57:39 2019 -0500 Bump mailsync to fix windows libetpan failure commit8fb55ca0fc
Author: Ben Gotow <ben@foundry376.com> Date: Wed Oct 9 21:36:57 2019 -0500 Bump mailsync, add grammar commitb959c54e50
Author: Ben Gotow <ben@foundry376.com> Date: Wed Oct 9 21:20:13 2019 -0500 Bump mailsync for linux / win32 commitddb5229d67
Author: Ben Gotow <ben@foundry376.com> Date: Tue Oct 8 22:44:12 2019 -0500 Bump mailsync commitf80e1bc422
Author: Ben Gotow <ben@foundry376.com> Date: Tue Oct 8 11:11:39 2019 -0500 Fix LESS linter failures commit66dc60a731
Author: Ben Gotow <ben@foundry376.com> Date: Tue Oct 8 11:00:37 2019 -0500 Extend participant search to return / expand groups commit3bded91307
Author: Ben Gotow <ben@foundry376.com> Date: Tue Oct 8 03:18:11 2019 -0500 Add comments, etc commit4ede5446de
Author: Ben Gotow <ben@foundry376.com> Date: Tue Oct 8 02:38:50 2019 -0500 Google People API contacts CRUD alongside CardDav commit96c6a64e46
Author: Ben Gotow <ben@foundry376.com> Date: Mon Oct 7 14:00:34 2019 -0500 Build ContactBook concept to track which accounts have sync running commit1f6aab1083
Author: Ben Gotow <ben@foundry376.com> Date: Mon Oct 7 11:38:03 2019 -0500 Contact and contact group CRUD, address book menus commitb877c58d48
Author: Ben Gotow <ben@foundry376.com> Date: Sun Oct 6 16:32:33 2019 -0500 Editing contact names working commit761079304c
Author: Ben Gotow <ben@foundry376.com> Date: Mon Sep 30 15:42:59 2019 -0500 Improved styling of YYYYMMDD field commit71a567276b
Author: Ben Gotow <ben@foundry376.com> Date: Mon Sep 30 15:06:28 2019 -0500 UI for edit + move + delete contacts commitf1967dd603
Author: Ben Gotow <ben@foundry376.com> Date: Thu Sep 26 13:50:44 2019 -0500 Allow you to turn on / off the “Found in Mail” autocompletions commit0c2b0eb03b
Author: Ben Gotow <ben@foundry376.com> Date: Thu Sep 26 13:50:14 2019 -0500 Improve contacts window launch perf by lazy loading composer support, scanning less of fs for themes commit07abd6cb71
Author: Ben Gotow <ben@foundry376.com> Date: Thu Sep 26 04:36:10 2019 -0500 Support for CardDav contact display, better groups presentation commit0a9e166d79
Author: Ben Gotow <ben@foundry376.com> Date: Tue Sep 24 12:42:37 2019 -0500 Add hidden attribute to the model in prep for deletion of auto-created contacts commite6ce3b2af9
Author: Ben Gotow <ben@foundry376.com> Date: Tue Sep 24 12:12:52 2019 -0500 Initial pass at address book commit740d7e8655
Author: Ben Gotow <ben@foundry376.com> Date: Tue Sep 24 08:27:06 2019 -0500 Make headers of Preferences > Accounts consistent with General
636 lines
22 KiB
TypeScript
636 lines
22 KiB
TypeScript
/* eslint no-new-wrappers: 0 */
|
|
/* eslint no-new-object: 0 */
|
|
import * as Utils from '../../src/flux/models/utils';
|
|
import { Thread } from '../../src/flux/models/thread';
|
|
import { Contact } from '../../src/flux/models/contact';
|
|
|
|
class Foo {
|
|
static initClass() {
|
|
this.prototype.field = {
|
|
a: 1,
|
|
b: 2,
|
|
};
|
|
}
|
|
constructor(instanceVar) {
|
|
this.instanceVar = instanceVar;
|
|
}
|
|
method(stuff) {
|
|
this.stuff = stuff;
|
|
}
|
|
}
|
|
Foo.initClass();
|
|
|
|
class Bar extends Foo {
|
|
subMethod(stuff) {
|
|
this.moreStuff = stuff;
|
|
return this.method(stuff);
|
|
}
|
|
}
|
|
|
|
describe('Utils', function() {
|
|
describe('modelTypesReviver', function() {
|
|
beforeEach(function() {
|
|
this.testThread = new Thread({
|
|
id: 'local-1',
|
|
accountId: '1',
|
|
pluginMetadata: [],
|
|
participants: [
|
|
new Contact({
|
|
id: 'local-a',
|
|
name: 'Juan',
|
|
email: 'juan@mailspring.com',
|
|
accountId: '1',
|
|
}),
|
|
new Contact({ id: 'local-b', name: 'Ben', email: 'ben@mailspring.com', accountId: '1' }),
|
|
],
|
|
subject: 'Test 1234',
|
|
});
|
|
});
|
|
|
|
it('should serialize and de-serialize models correctly', function() {
|
|
const expectedString =
|
|
'[{"id":"local-1","aid":"1","metadata":[],"subject":"Test 1234","categories":[],"participants":[{"id":"local-a","aid":"1","name":"Juan","email":"juan@mailspring.com","__cls":"Contact"},{"id":"local-b","aid":"1","name":"Ben","email":"ben@mailspring.com","__cls":"Contact"}],"__cls":"Thread"}]';
|
|
|
|
const jsonString = JSON.stringify([this.testThread]);
|
|
expect(jsonString).toEqual(expectedString);
|
|
const revived = JSON.parse(jsonString, Utils.modelTypesReviver);
|
|
expect(revived).toEqual([this.testThread]);
|
|
});
|
|
|
|
it("should re-inflate Models in places they're not explicitly declared types", function() {
|
|
const b = { id: 'ThreadsToProcess', json: [this.testThread] };
|
|
const jsonString = JSON.stringify(b);
|
|
const expectedString =
|
|
'{"id":"ThreadsToProcess","json":[{"id":"local-1","aid":"1","metadata":[],"subject":"Test 1234","categories":[],"participants":[{"id":"local-a","aid":"1","name":"Juan","email":"juan@mailspring.com","__cls":"Contact"},{"id":"local-b","aid":"1","name":"Ben","email":"ben@mailspring.com","__cls":"Contact"}],"__cls":"Thread"}]}';
|
|
|
|
expect(jsonString).toEqual(expectedString);
|
|
const revived = JSON.parse(jsonString, Utils.modelTypesReviver);
|
|
expect(revived).toEqual(b);
|
|
expect(revived.json[0] instanceof Thread).toBe(true);
|
|
expect(revived.json[0].participants[0] instanceof Contact).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('deepClone', function() {
|
|
beforeEach(function() {
|
|
this.v1 = [1, 2, 3];
|
|
this.v2 = [4, 5, 6];
|
|
this.foo = new Foo(this.v1);
|
|
this.bar = new Bar(this.v2);
|
|
this.o2 = [
|
|
this.foo,
|
|
{ v1: this.v1, v2: this.v2, foo: this.foo, bar: this.bar, baz: 'baz', fn: Foo },
|
|
'abc',
|
|
];
|
|
this.o2.circular = this.o2;
|
|
this.o2Clone = Utils.deepClone(this.o2);
|
|
});
|
|
|
|
it('deep clones dates correctly', function() {
|
|
const d1 = new Date(2016, 1, 1);
|
|
const d2 = Utils.deepClone(d1);
|
|
expect(d2.valueOf()).toBe(d1.valueOf());
|
|
});
|
|
|
|
it('makes a deep clone', function() {
|
|
this.v1.push(4);
|
|
this.v2.push(7);
|
|
this.foo.stuff = 'stuff';
|
|
this.bar.subMethod('stuff');
|
|
expect(this.o2Clone[0].stuff).toBeUndefined();
|
|
expect(this.o2Clone[1].foo.stuff).toBeUndefined();
|
|
expect(this.o2Clone[1].bar.stuff).toBeUndefined();
|
|
expect(this.o2Clone[1].v1.length).toBe(3);
|
|
expect(this.o2Clone[1].v2.length).toBe(3);
|
|
expect(this.o2Clone[2]).toBe('abc');
|
|
});
|
|
|
|
it('does not deep clone the prototype', function() {
|
|
this.foo.field.a = 'changed under the hood';
|
|
expect(this.o2Clone[0].field.a).toBe('changed under the hood');
|
|
});
|
|
|
|
it('clones constructors properly', function() {
|
|
expect(new this.o2Clone[1].fn() instanceof Foo).toBe(true);
|
|
});
|
|
|
|
it('clones prototypes properly', function() {
|
|
expect(this.o2Clone[1].foo instanceof Foo).toBe(true);
|
|
expect(this.o2Clone[1].bar instanceof Bar).toBe(true);
|
|
});
|
|
|
|
it('can take a customizer to edit values as we clone', function() {
|
|
const clone = Utils.deepClone(this.o2, function(key, clonedValue) {
|
|
if (key === 'v2') {
|
|
clonedValue.push('custom value');
|
|
return clonedValue;
|
|
} else {
|
|
return clonedValue;
|
|
}
|
|
});
|
|
this.v2.push(7);
|
|
expect(clone[1].v2.length).toBe(4);
|
|
expect(clone[1].v2[3]).toBe('custom value');
|
|
});
|
|
});
|
|
|
|
// Pulled equality tests from underscore
|
|
// https://github.com/jashkenas/underscore/blob/master/test/objects.js
|
|
describe('isEqual', function() {
|
|
describe('custom behavior', function() {
|
|
it('makes functions always equal', function() {
|
|
const f1 = function() {};
|
|
const f2 = function() {};
|
|
expect(Utils.isEqual(f1, f2)).toBe(false);
|
|
expect(Utils.isEqual(f1, f2, { functionsAreEqual: true })).toBe(true);
|
|
});
|
|
|
|
it('can ignore keys in objects', function() {
|
|
const o1 = {
|
|
foo: 'bar',
|
|
arr: [1, 2, 3],
|
|
nest: { a: 'b', c: 1, ignoreMe: 5 },
|
|
ignoreMe: 123,
|
|
};
|
|
const o2 = {
|
|
foo: 'bar',
|
|
arr: [1, 2, 3],
|
|
nest: { a: 'b', c: 1, ignoreMe: 10 },
|
|
ignoreMe: 456,
|
|
};
|
|
|
|
expect(Utils.isEqual(o1, o2)).toBe(false);
|
|
expect(Utils.isEqual(o1, o2, { ignoreKeys: ['ignoreMe'] })).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('passes all underscore equality tests', function() {
|
|
const First = function() {
|
|
return (this.value = 1);
|
|
};
|
|
First.prototype.value = 1;
|
|
|
|
const Second = function() {
|
|
return (this.value = 1);
|
|
};
|
|
Second.prototype.value = 2;
|
|
|
|
const ok = val => expect(val).toBe(true);
|
|
|
|
// Basic equality and identity comparisons.
|
|
ok(Utils.isEqual(null, null), '`null` is equal to `null`');
|
|
ok(Utils.isEqual(), '`undefined` is equal to `undefined`');
|
|
|
|
ok(!Utils.isEqual(0, -0), '`0` is not equal to `-0`');
|
|
ok(!Utils.isEqual(-0, 0), 'Commutative equality is implemented for `0` and `-0`');
|
|
ok(!Utils.isEqual(null, undefined), '`null` is not equal to `undefined`');
|
|
ok(
|
|
!Utils.isEqual(undefined, null),
|
|
'Commutative equality is implemented for `null` and `undefined`'
|
|
);
|
|
|
|
// String object and primitive comparisons.
|
|
ok(Utils.isEqual('Curly', 'Curly'), 'Identical string primitives are equal');
|
|
ok(
|
|
Utils.isEqual(new String('Curly'), new String('Curly')),
|
|
'String objects with identical primitive values are equal'
|
|
);
|
|
ok(
|
|
Utils.isEqual(new String('Curly'), 'Curly'),
|
|
'String primitives and their corresponding object wrappers are equal'
|
|
);
|
|
ok(
|
|
Utils.isEqual('Curly', new String('Curly')),
|
|
'Commutative equality is implemented for string objects and primitives'
|
|
);
|
|
|
|
ok(!Utils.isEqual('Curly', 'Larry'), 'String primitives with different values are not equal');
|
|
ok(
|
|
!Utils.isEqual(new String('Curly'), new String('Larry')),
|
|
'String objects with different primitive values are not equal'
|
|
);
|
|
ok(
|
|
!Utils.isEqual(new String('Curly'), {
|
|
toString() {
|
|
return 'Curly';
|
|
},
|
|
}),
|
|
'String objects and objects with a custom `toString` method are not equal'
|
|
);
|
|
|
|
// Number object and primitive comparisons.
|
|
ok(Utils.isEqual(75, 75), 'Identical number primitives are equal');
|
|
ok(
|
|
Utils.isEqual(new Number(75), new Number(75)),
|
|
'Number objects with identical primitive values are equal'
|
|
);
|
|
ok(
|
|
Utils.isEqual(75, new Number(75)),
|
|
'Number primitives and their corresponding object wrappers are equal'
|
|
);
|
|
ok(
|
|
Utils.isEqual(new Number(75), 75),
|
|
'Commutative equality is implemented for number objects and primitives'
|
|
);
|
|
ok(!Utils.isEqual(new Number(0), -0), '`new Number(0)` and `-0` are not equal');
|
|
ok(
|
|
!Utils.isEqual(0, new Number(-0)),
|
|
'Commutative equality is implemented for `new Number(0)` and `-0`'
|
|
);
|
|
|
|
ok(
|
|
!Utils.isEqual(new Number(75), new Number(63)),
|
|
'Number objects with different primitive values are not equal'
|
|
);
|
|
ok(
|
|
!Utils.isEqual(new Number(63), {
|
|
valueOf() {
|
|
return 63;
|
|
},
|
|
}),
|
|
'Number objects and objects with a `valueOf` method are not equal'
|
|
);
|
|
|
|
// Comparisons involving `NaN`.
|
|
ok(Utils.isEqual(NaN, NaN), '`NaN` is equal to `NaN`');
|
|
ok(Utils.isEqual(new Object(NaN), NaN), 'Object(`NaN`) is equal to `NaN`');
|
|
ok(!Utils.isEqual(61, NaN), 'A number primitive is not equal to `NaN`');
|
|
ok(!Utils.isEqual(new Number(79), NaN), 'A number object is not equal to `NaN`');
|
|
ok(!Utils.isEqual(Infinity, NaN), '`Infinity` is not equal to `NaN`');
|
|
|
|
// Boolean object and primitive comparisons.
|
|
ok(Utils.isEqual(true, true), 'Identical boolean primitives are equal');
|
|
ok(
|
|
Utils.isEqual(new Boolean(), new Boolean()),
|
|
'Boolean objects with identical primitive values are equal'
|
|
);
|
|
ok(
|
|
Utils.isEqual(true, new Boolean(true)),
|
|
'Boolean primitives and their corresponding object wrappers are equal'
|
|
);
|
|
ok(
|
|
Utils.isEqual(new Boolean(true), true),
|
|
'Commutative equality is implemented for booleans'
|
|
);
|
|
ok(
|
|
!Utils.isEqual(new Boolean(true), new Boolean()),
|
|
'Boolean objects with different primitive values are not equal'
|
|
);
|
|
|
|
// Common type coercions.
|
|
ok(!Utils.isEqual(new Boolean(false), true), '`new Boolean(false)` is not equal to `true`');
|
|
ok(!Utils.isEqual('75', 75), 'String and number primitives with like values are not equal');
|
|
ok(
|
|
!Utils.isEqual(new Number(63), new String(63)),
|
|
'String and number objects with like values are not equal'
|
|
);
|
|
ok(
|
|
!Utils.isEqual(75, '75'),
|
|
'Commutative equality is implemented for like string and number values'
|
|
);
|
|
ok(!Utils.isEqual(0, ''), 'Number and string primitives with like values are not equal');
|
|
ok(!Utils.isEqual(1, true), 'Number and boolean primitives with like values are not equal');
|
|
ok(
|
|
!Utils.isEqual(new Boolean(false), new Number(0)),
|
|
'Boolean and number objects with like values are not equal'
|
|
);
|
|
ok(
|
|
!Utils.isEqual(false, new String('')),
|
|
'Boolean primitives and string objects with like values are not equal'
|
|
);
|
|
ok(
|
|
!Utils.isEqual(12564504e5, new Date(2009, 9, 25)),
|
|
'Dates and their corresponding numeric primitive values are not equal'
|
|
);
|
|
|
|
// Dates.
|
|
ok(
|
|
Utils.isEqual(new Date(2009, 9, 25), new Date(2009, 9, 25)),
|
|
'Date objects referencing identical times are equal'
|
|
);
|
|
ok(
|
|
!Utils.isEqual(new Date(2009, 9, 25), new Date(2009, 11, 13)),
|
|
'Date objects referencing different times are not equal'
|
|
);
|
|
ok(
|
|
!Utils.isEqual(new Date(2009, 11, 13), {
|
|
getTime() {
|
|
return 12606876e5;
|
|
},
|
|
}),
|
|
'Date objects and objects with a `getTime` method are not equal'
|
|
);
|
|
ok(!Utils.isEqual(new Date('Curly'), new Date('Curly')), 'Invalid dates are not equal');
|
|
|
|
// Functions.
|
|
ok(
|
|
!Utils.isEqual(First, Second),
|
|
'Different functions with identical bodies and source code representations are not equal'
|
|
);
|
|
|
|
// RegExps.
|
|
ok(
|
|
Utils.isEqual(/(?:)/gim, /(?:)/gim),
|
|
'RegExps with equivalent patterns and flags are equal'
|
|
);
|
|
ok(Utils.isEqual(/(?:)/gi, /(?:)/gi), 'Flag order is not significant');
|
|
ok(
|
|
!Utils.isEqual(/(?:)/g, /(?:)/gi),
|
|
'RegExps with equivalent patterns and different flags are not equal'
|
|
);
|
|
ok(
|
|
!Utils.isEqual(/Moe/gim, /Curly/gim),
|
|
'RegExps with different patterns and equivalent flags are not equal'
|
|
);
|
|
ok(!Utils.isEqual(/(?:)/gi, /(?:)/g), 'Commutative equality is implemented for RegExps');
|
|
ok(
|
|
!Utils.isEqual(/Curly/g, {
|
|
source: 'Larry',
|
|
global: true,
|
|
ignoreCase: false,
|
|
multiline: false,
|
|
}),
|
|
'RegExps and RegExp-like objects are not equal'
|
|
);
|
|
|
|
// Empty arrays, array-like objects, and object literals.
|
|
ok(Utils.isEqual({}, {}), 'Empty object literals are equal');
|
|
ok(Utils.isEqual([], []), 'Empty array literals are equal');
|
|
ok(Utils.isEqual([{}], [{}]), 'Empty nested arrays and objects are equal');
|
|
ok(!Utils.isEqual({ length: 0 }, []), 'Array-like objects and arrays are not equal.');
|
|
ok(
|
|
!Utils.isEqual([], { length: 0 }),
|
|
'Commutative equality is implemented for array-like objects'
|
|
);
|
|
|
|
ok(!Utils.isEqual({}, []), 'Object literals and array literals are not equal');
|
|
ok(!Utils.isEqual([], {}), 'Commutative equality is implemented for objects and arrays');
|
|
|
|
// Arrays with primitive and object values.
|
|
ok(
|
|
Utils.isEqual([1, 'Larry', true], [1, 'Larry', true]),
|
|
'Arrays containing identical primitives are equal'
|
|
);
|
|
ok(
|
|
Utils.isEqual([/Moe/g, new Date(2009, 9, 25)], [/Moe/g, new Date(2009, 9, 25)]),
|
|
'Arrays containing equivalent elements are equal'
|
|
);
|
|
|
|
// Multi-dimensional arrays.
|
|
let a = [
|
|
new Number(47),
|
|
false,
|
|
'Larry',
|
|
/Moe/,
|
|
new Date(2009, 11, 13),
|
|
['running', 'biking', new String('programming')],
|
|
{ a: 47 },
|
|
];
|
|
let b = [
|
|
new Number(47),
|
|
false,
|
|
'Larry',
|
|
/Moe/,
|
|
new Date(2009, 11, 13),
|
|
['running', 'biking', new String('programming')],
|
|
{ a: 47 },
|
|
];
|
|
ok(
|
|
Utils.isEqual(a, b),
|
|
'Arrays containing nested arrays and objects are recursively compared'
|
|
);
|
|
|
|
// Overwrite the methods defined in ES 5.1 section 15.4.4.
|
|
a.forEach = a.map = a.filter = a.every = a.indexOf = a.lastIndexOf = a.some = a.reduce = a.reduceRight = null;
|
|
b.join = b.pop = b.reverse = b.shift = b.slice = b.splice = b.concat = b.sort = b.unshift = null;
|
|
|
|
// Array elements and properties.
|
|
ok(
|
|
Utils.isEqual(a, b),
|
|
'Arrays containing equivalent elements and different non-numeric properties are equal'
|
|
);
|
|
a.push('White Rocks');
|
|
ok(!Utils.isEqual(a, b), 'Arrays of different lengths are not equal');
|
|
a.push('East Boulder');
|
|
b.push('Gunbarrel Ranch', 'Teller Farm');
|
|
ok(
|
|
!Utils.isEqual(a, b),
|
|
'Arrays of identical lengths containing different elements are not equal'
|
|
);
|
|
|
|
// Sparse arrays.
|
|
ok(Utils.isEqual(Array(3), Array(3)), 'Sparse arrays of identical lengths are equal');
|
|
ok(
|
|
!Utils.isEqual(Array(3), Array(6)),
|
|
'Sparse arrays of different lengths are not equal when both are empty'
|
|
);
|
|
|
|
const sparse = [];
|
|
sparse[1] = 5;
|
|
ok(Utils.isEqual(sparse, [undefined, 5]), 'Handles sparse arrays as dense');
|
|
|
|
// Simple objects.
|
|
ok(
|
|
Utils.isEqual({ a: 'Curly', b: 1, c: true }, { a: 'Curly', b: 1, c: true }),
|
|
'Objects containing identical primitives are equal'
|
|
);
|
|
ok(
|
|
Utils.isEqual(
|
|
{ a: /Curly/g, b: new Date(2009, 11, 13) },
|
|
{ a: /Curly/g, b: new Date(2009, 11, 13) }
|
|
),
|
|
'Objects containing equivalent members are equal'
|
|
);
|
|
ok(
|
|
!Utils.isEqual({ a: 63, b: 75 }, { a: 61, b: 55 }),
|
|
'Objects of identical sizes with different values are not equal'
|
|
);
|
|
ok(
|
|
!Utils.isEqual({ a: 63, b: 75 }, { a: 61, c: 55 }),
|
|
'Objects of identical sizes with different property names are not equal'
|
|
);
|
|
ok(!Utils.isEqual({ a: 1, b: 2 }, { a: 1 }), 'Objects of different sizes are not equal');
|
|
ok(
|
|
!Utils.isEqual({ a: 1 }, { a: 1, b: 2 }),
|
|
'Commutative equality is implemented for objects'
|
|
);
|
|
ok(
|
|
!Utils.isEqual({ x: 1, y: undefined }, { x: 1, z: 2 }),
|
|
'Objects with identical keys and different values are not equivalent'
|
|
);
|
|
|
|
// `A` contains nested objects and arrays.
|
|
a = {
|
|
name: new String('Moe Howard'),
|
|
age: new Number(77),
|
|
stooge: true,
|
|
hobbies: ['acting'],
|
|
film: {
|
|
name: 'Sing a Song of Six Pants',
|
|
release: new Date(1947, 9, 30),
|
|
stars: [new String('Larry Fine'), 'Shemp Howard'],
|
|
minutes: new Number(16),
|
|
seconds: 54,
|
|
},
|
|
};
|
|
|
|
// `B` contains equivalent nested objects and arrays.
|
|
b = {
|
|
name: new String('Moe Howard'),
|
|
age: new Number(77),
|
|
stooge: true,
|
|
hobbies: ['acting'],
|
|
film: {
|
|
name: 'Sing a Song of Six Pants',
|
|
release: new Date(1947, 9, 30),
|
|
stars: [new String('Larry Fine'), 'Shemp Howard'],
|
|
minutes: new Number(16),
|
|
seconds: 54,
|
|
},
|
|
};
|
|
ok(Utils.isEqual(a, b), 'Objects with nested equivalent members are recursively compared');
|
|
|
|
// Instances.
|
|
ok(Utils.isEqual(new First(), new First()), 'Object instances are equal');
|
|
ok(
|
|
!Utils.isEqual(new First(), new Second()),
|
|
'Objects with different constructors and identical own properties are not equal'
|
|
);
|
|
ok(
|
|
!Utils.isEqual({ value: 1 }, new First()),
|
|
'Object instances and objects sharing equivalent properties are not equal'
|
|
);
|
|
ok(
|
|
!Utils.isEqual({ value: 2 }, new Second()),
|
|
'The prototype chain of objects should not be examined'
|
|
);
|
|
|
|
// Circular Arrays.
|
|
(a = []).push(a);
|
|
(b = []).push(b);
|
|
ok(Utils.isEqual(a, b), 'Arrays containing circular references are equal');
|
|
a.push(new String('Larry'));
|
|
b.push(new String('Larry'));
|
|
ok(
|
|
Utils.isEqual(a, b),
|
|
'Arrays containing circular references and equivalent properties are equal'
|
|
);
|
|
a.push('Shemp');
|
|
b.push('Curly');
|
|
ok(
|
|
!Utils.isEqual(a, b),
|
|
'Arrays containing circular references and different properties are not equal'
|
|
);
|
|
|
|
// More circular arrays #767.
|
|
a = ['everything is checked but', 'this', 'is not'];
|
|
a[1] = a;
|
|
b = ['everything is checked but', ['this', 'array'], 'is not'];
|
|
ok(
|
|
!Utils.isEqual(a, b),
|
|
'Comparison of circular references with non-circular references are not equal'
|
|
);
|
|
|
|
// Circular Objects.
|
|
a = { abc: null };
|
|
b = { abc: null };
|
|
a.abc = a;
|
|
b.abc = b;
|
|
ok(Utils.isEqual(a, b), 'Objects containing circular references are equal');
|
|
a.def = 75;
|
|
b.def = 75;
|
|
ok(
|
|
Utils.isEqual(a, b),
|
|
'Objects containing circular references and equivalent properties are equal'
|
|
);
|
|
a.def = new Number(75);
|
|
b.def = new Number(63);
|
|
ok(
|
|
!Utils.isEqual(a, b),
|
|
'Objects containing circular references and different properties are not equal'
|
|
);
|
|
|
|
// More circular objects #767.
|
|
a = { everything: 'is checked', but: 'this', is: 'not' };
|
|
a.but = a;
|
|
b = { everything: 'is checked', but: { that: 'object' }, is: 'not' };
|
|
ok(
|
|
!Utils.isEqual(a, b),
|
|
'Comparison of circular references with non-circular object references are not equal'
|
|
);
|
|
|
|
// Cyclic Structures.
|
|
a = [{ abc: null }];
|
|
b = [{ abc: null }];
|
|
(a[0].abc = a).push(a);
|
|
(b[0].abc = b).push(b);
|
|
ok(Utils.isEqual(a, b), 'Cyclic structures are equal');
|
|
a[0].def = 'Larry';
|
|
b[0].def = 'Larry';
|
|
ok(Utils.isEqual(a, b), 'Cyclic structures containing equivalent properties are equal');
|
|
a[0].def = new String('Larry');
|
|
b[0].def = new String('Curly');
|
|
ok(!Utils.isEqual(a, b), 'Cyclic structures containing different properties are not equal');
|
|
|
|
// Complex Circular References.
|
|
a = { foo: { b: { foo: { c: { foo: null } } } } };
|
|
b = { foo: { b: { foo: { c: { foo: null } } } } };
|
|
a.foo.b.foo.c.foo = a;
|
|
b.foo.b.foo.c.foo = b;
|
|
ok(
|
|
Utils.isEqual(a, b),
|
|
'Cyclic structures with nested and identically-named properties are equal'
|
|
);
|
|
|
|
// Chaining.
|
|
// NOTE: underscore doesn't support chaining
|
|
//
|
|
// ok(!Utils.isEqual(_({x: 1, y: undefined}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal')
|
|
//
|
|
// a = _({x: 1, y: 2}).chain()
|
|
// b = _({x: 1, y: 2}).chain()
|
|
// equal(Utils.isEqual(a.isEqual(b), _(true)), true, '`isEqual` can be chained')
|
|
|
|
// Objects without a `constructor` property
|
|
if (Object.create) {
|
|
a = Object.create(null, { x: { value: 1, enumerable: true } });
|
|
b = { x: 1 };
|
|
ok(Utils.isEqual(a, b), 'Handles objects without a constructor (e.g. from Object.create');
|
|
}
|
|
|
|
Foo = function() {
|
|
return (this.a = 1);
|
|
};
|
|
Foo.prototype.constructor = null;
|
|
|
|
const other = { a: 1 };
|
|
ok(!Utils.isEqual(new Foo(), other));
|
|
});
|
|
});
|
|
|
|
describe('subjectWithPrefix', function() {
|
|
it('should replace an existing Re:', () =>
|
|
expect(Utils.subjectWithPrefix('Re: Test Case', 'Fwd:')).toEqual('Fwd: Test Case'));
|
|
|
|
it('should replace an existing re:', () =>
|
|
expect(Utils.subjectWithPrefix('re: Test Case', 'Fwd:')).toEqual('Fwd: Test Case'));
|
|
|
|
it('should replace an existing Fwd:', () =>
|
|
expect(Utils.subjectWithPrefix('Fwd: Test Case', 'Re:')).toEqual('Re: Test Case'));
|
|
|
|
it('should replace an existing fwd:', () =>
|
|
expect(Utils.subjectWithPrefix('fwd: Test Case', 'Re:')).toEqual('Re: Test Case'));
|
|
|
|
it('should not replace Re: or Fwd: found embedded in the subject', function() {
|
|
expect(Utils.subjectWithPrefix('My questions are: 123', 'Fwd:')).toEqual(
|
|
'Fwd: My questions are: 123'
|
|
);
|
|
expect(Utils.subjectWithPrefix('My questions fwd: 123', 'Fwd:')).toEqual(
|
|
'Fwd: My questions fwd: 123'
|
|
);
|
|
});
|
|
|
|
it('should work if no existing prefix is present', () =>
|
|
expect(Utils.subjectWithPrefix('My questions', 'Fwd:')).toEqual('Fwd: My questions'));
|
|
});
|
|
});
|