Skip to content

Commit c2d153a

Browse files
authored
Merge pull request #1351 from darkdread/bugfix/equal-null-object
Fix equality checking for null type
2 parents 6d7b5dc + 320c8ce commit c2d153a

File tree

3 files changed

+157
-188
lines changed

3 files changed

+157
-188
lines changed

src/util.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ export const equal = (a: any, b: any, seen = []): boolean => {
117117
b = new Uint8Array(b);
118118
}
119119

120+
// typeof null is 'object'. If either is null, check if they are equal.
121+
if ((a === null || b === null) && a !== b) {
122+
return false;
123+
}
124+
120125
// If this point has been reached, a and b are either arrays or objects.
121126

122127
if (a instanceof Array) {

test/unit/util-suite.js

Lines changed: 0 additions & 188 deletions
This file was deleted.

test/unit/util.test.mjs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import 'mocha';
2+
import { expect } from 'chai';
3+
import * as util from '../../build/util.js';
4+
5+
function stringToArrayBuffer(str) {
6+
const buf = new ArrayBuffer(str.length * 2);
7+
const view = new Uint16Array(buf);
8+
for (let i = 0, c = str.length; i < c; i++) {
9+
view[i] = str.charCodeAt(i);
10+
}
11+
return buf;
12+
}
13+
14+
describe('util', () => {
15+
describe('path checking', () => {
16+
it('checks if path is a folder', () => {
17+
expect(util.isFolder('/')).to.equal(true);
18+
expect(util.isFolder('/foo/')).to.equal(true);
19+
expect(util.isFolder('/foo//')).to.equal(true);
20+
expect(util.isFolder('/foo/b ar/')).to.equal(true);
21+
expect(util.isFolder('/foo')).to.equal(false);
22+
expect(util.isFolder('/%2F')).to.equal(false);
23+
expect(util.isFolder('/foo/%2F')).to.equal(false);
24+
expect(util.isFolder('/foo/ ')).to.equal(false);
25+
});
26+
27+
it('checks if path is a document', () => {
28+
expect(util.isDocument('/')).to.equal(false);
29+
expect(util.isDocument('/foo/')).to.equal(false);
30+
expect(util.isDocument('/foo//')).to.equal(false);
31+
expect(util.isDocument('/foo/b ar/')).to.equal(false);
32+
expect(util.isDocument('/foo')).to.equal(true);
33+
expect(util.isDocument('/%2F')).to.equal(true);
34+
expect(util.isDocument('/foo/%2F')).to.equal(true);
35+
expect(util.isDocument('/foo/ ')).to.equal(true);
36+
});
37+
});
38+
39+
describe('path encoding', () => {
40+
it('encodes quotes in paths', () => {
41+
expect(util.cleanPath("Capture d'écran")).to.equal('Capture%20d%27%C3%A9cran');
42+
expect(util.cleanPath('So they said "hey"')).to.equal('So%20they%20said%20%22hey%22');
43+
});
44+
});
45+
46+
describe('equality and cloning', () => {
47+
it('compares objects deeply', () => {
48+
const deepClone = util.deepClone;
49+
const equal = util.equal;
50+
const obj = { str: 'a', i: 0, b: true, obj: { str: 'a' } };
51+
const obj2 = deepClone(obj);
52+
53+
expect(equal(obj, obj2)).to.equal(true);
54+
obj.nested = obj2;
55+
expect(equal(obj, obj2)).to.equal(false);
56+
57+
const buf1 = stringToArrayBuffer('foo');
58+
const buf2 = stringToArrayBuffer('foo');
59+
const buf3 = stringToArrayBuffer('bar');
60+
61+
expect(equal(obj, deepClone(obj))).to.equal(true);
62+
expect(equal(buf1, buf2)).to.equal(true);
63+
expect(equal(buf1, buf3)).to.equal(false);
64+
65+
const arr1 = [ stringToArrayBuffer('foo'), function() { return 1; } ];
66+
const arr2 = [ stringToArrayBuffer('foo'), function() { return 1; } ];
67+
const arr3 = [ stringToArrayBuffer('bar'), function() { return 1; } ];
68+
const arr4 = [ stringToArrayBuffer('foo'), function() { return 0; } ];
69+
70+
expect(equal(arr1, arr2)).to.equal(true);
71+
expect(equal(arr1, arr3)).to.equal(false);
72+
expect(equal(arr1, arr4)).to.equal(false);
73+
74+
expect(equal(null, null)).to.equal(true);
75+
expect(equal(undefined, null)).to.equal(false);
76+
expect(equal(null, {key: "value"})).to.equal(false);
77+
expect(equal({key: "value"}, null)).to.equal(false);
78+
expect(equal({key: null}, {key: undefined})).to.equal(false);
79+
expect(equal({key: null}, {key: null})).to.equal(true);
80+
});
81+
82+
it('clones objects deeply', () => {
83+
const deepClone = util.deepClone;
84+
const obj = { str: 'a', i: 0, b: true };
85+
const cloned = deepClone(obj);
86+
87+
expect(cloned).to.deep.equal(obj);
88+
obj.nested = cloned;
89+
const cloned2 = deepClone(obj);
90+
expect(cloned2).to.deep.equal(obj);
91+
});
92+
});
93+
94+
describe('path utilities', () => {
95+
it('generates paths from root', () => {
96+
const pathsFromRoot = util.pathsFromRoot;
97+
const p1 = '/';
98+
const p2 = '/a/b/c/d/e';
99+
const p3 = '/a/b/c';
100+
const p4 = '/a/b//';
101+
const p5 = '//';
102+
const p6 = '/a/b/c d/e/';
103+
const p7 = '/foo';
104+
105+
expect(pathsFromRoot(p1)).to.deep.equal([p1]);
106+
expect(pathsFromRoot(p2)).to.deep.equal([p2, '/a/b/c/d/', '/a/b/c/', '/a/b/', '/a/', '/']);
107+
expect(pathsFromRoot(p3)).to.deep.equal([p3, '/a/b/', '/a/', '/']);
108+
expect(pathsFromRoot(p4)).to.deep.equal([p4, '/a/b/', '/a/', '/']);
109+
expect(pathsFromRoot(p5)).to.deep.equal([p5, '/']);
110+
expect(pathsFromRoot(p6)).to.deep.equal([p6, '/a/b/c d/', '/a/b/', '/a/', '/']);
111+
expect(pathsFromRoot(p7)).to.deep.equal([p7, '/']);
112+
});
113+
});
114+
115+
describe('localStorage utilities', () => {
116+
it('detects when localStorage is unavailable', () => {
117+
const QuotaExceededError = function(message) {
118+
this.name = 'QuotaExceededError';
119+
this.message = message;
120+
};
121+
QuotaExceededError.prototype = new Error();
122+
123+
global.localStorage = {
124+
setItem: function(key, value) {
125+
throw new QuotaExceededError('DOM exception 22');
126+
}
127+
};
128+
129+
expect(util.localStorageAvailable()).to.equal(false);
130+
});
131+
132+
it('retrieves JSON from localStorage', () => {
133+
global.localStorage = {
134+
getItem: function() {
135+
return '{ "foo": "bar" }';
136+
}
137+
};
138+
139+
expect(util.getJSONFromLocalStorage('somekey')).to.deep.equal({ foo: 'bar' });
140+
});
141+
142+
it('returns null when key is missing', () => {
143+
global.localStorage = {
144+
getItem: function() {
145+
return null;
146+
}
147+
};
148+
149+
expect(util.getJSONFromLocalStorage('somekey')).to.equal(null);
150+
});
151+
});
152+
});

0 commit comments

Comments
 (0)