Projekt

Obecné

Profil

Stáhnout (7.28 KB) Statistiky
| Větev: | Revize:
1 3a515b92 cagy
'use strict';
2
3
var hasSymbols = require('has-symbols/shams')();
4
var forEach = require('for-each');
5
6
module.exports = function (assign, t) {
7
	t.test('error cases', function (st) {
8
		st['throws'](function () { assign(null); }, TypeError, 'target must be an object');
9
		st['throws'](function () { assign(undefined); }, TypeError, 'target must be an object');
10
		st['throws'](function () { assign(null, {}); }, TypeError, 'target must be an object');
11
		st['throws'](function () { assign(undefined, {}); }, TypeError, 'target must be an object');
12
		st.end();
13
	});
14
15
	t.test('non-object target, no sources', function (st) {
16
		var bool = assign(true);
17
		st.equal(typeof bool, 'object', 'bool is object');
18
		st.equal(Boolean.prototype.valueOf.call(bool), true, 'bool coerces to `true`');
19
20
		var number = assign(1);
21
		st.equal(typeof number, 'object', 'number is object');
22
		st.equal(Number.prototype.valueOf.call(number), 1, 'number coerces to `1`');
23
24
		var string = assign('1');
25
		st.equal(typeof string, 'object', 'number is object');
26
		st.equal(String.prototype.valueOf.call(string), '1', 'number coerces to `"1"`');
27
28
		st.end();
29
	});
30
31
	t.test('non-object target, with sources', function (st) {
32
		var signal = {};
33
34
		st.test('boolean', function (st2) {
35
			var bool = assign(true, { a: signal });
36
			st2.equal(typeof bool, 'object', 'bool is object');
37
			st.equal(Boolean.prototype.valueOf.call(bool), true, 'bool coerces to `true`');
38
			st2.equal(bool.a, signal, 'source properties copied');
39
			st2.end();
40
		});
41
42
		st.test('number', function (st2) {
43
			var number = assign(1, { a: signal });
44
			st2.equal(typeof number, 'object', 'number is object');
45
			st2.equal(Number.prototype.valueOf.call(number), 1, 'number coerces to `1`');
46
			st2.equal(number.a, signal, 'source properties copied');
47
			st2.end();
48
		});
49
50
		st.test('string', function (st2) {
51
			var string = assign('1', { a: signal });
52
			st2.equal(typeof string, 'object', 'number is object');
53
			st2.equal(String.prototype.valueOf.call(string), '1', 'number coerces to `"1"`');
54
			st2.equal(string.a, signal, 'source properties copied');
55
			st2.end();
56
		});
57
58
		st.end();
59
	});
60
61
	t.test('non-object sources', function (st) {
62
		st.deepEqual(assign({ a: 1 }, null, { b: 2 }), { a: 1, b: 2 }, 'ignores null source');
63
		st.deepEqual(assign({ a: 1 }, { b: 2 }, undefined), { a: 1, b: 2 }, 'ignores undefined source');
64
		st.end();
65
	});
66
67
	t.test('returns the modified target object', function (st) {
68
		var target = {};
69
		var returned = assign(target, { a: 1 });
70
		st.equal(returned, target, 'returned object is the same reference as the target object');
71
		st.end();
72
	});
73
74
	t.test('has the right length', function (st) {
75
		st.equal(assign.length, 2, 'length is 2 => 2 required arguments');
76
		st.end();
77
	});
78
79
	t.test('merge two objects', function (st) {
80
		var target = { a: 1 };
81
		var returned = assign(target, { b: 2 });
82
		st.deepEqual(returned, { a: 1, b: 2 }, 'returned object has properties from both');
83
		st.end();
84
	});
85
86
	t.test('works with functions', function (st) {
87
		var target = function () {};
88
		target.a = 1;
89
		var returned = assign(target, { b: 2 });
90
		st.equal(target, returned, 'returned object is target');
91
		st.equal(returned.a, 1);
92
		st.equal(returned.b, 2);
93
		st.end();
94
	});
95
96
	t.test('works with primitives', function (st) {
97
		var target = 2;
98
		var source = { b: 42 };
99
		var returned = assign(target, source);
100
		st.equal(Object.prototype.toString.call(returned), '[object Number]', 'returned is object form of number primitive');
101
		st.equal(Number(returned), target, 'returned and target have same valueOf');
102
		st.equal(returned.b, source.b);
103
		st.end();
104
	});
105
106
	t.test('merge N objects', function (st) {
107
		var target = { a: 1 };
108
		var source1 = { b: 2 };
109
		var source2 = { c: 3 };
110
		var returned = assign(target, source1, source2);
111
		st.deepEqual(returned, { a: 1, b: 2, c: 3 }, 'returned object has properties from all sources');
112
		st.end();
113
	});
114
115
	t.test('only iterates over own keys', function (st) {
116
		var Foo = function () {};
117
		Foo.prototype.bar = true;
118
		var foo = new Foo();
119
		foo.baz = true;
120
		var target = { a: 1 };
121
		var returned = assign(target, foo);
122
		st.equal(returned, target, 'returned object is the same reference as the target object');
123
		st.deepEqual(target, { a: 1, baz: true }, 'returned object has only own properties from both');
124
		st.end();
125
	});
126
127
	t.test('includes enumerable symbols, after keys', { skip: !hasSymbols }, function (st) {
128
		var visited = [];
129
		var obj = {};
130
		Object.defineProperty(obj, 'a', { enumerable: true, get: function () { visited.push('a'); return 42; } });
131
		var symbol = Symbol('enumerable');
132
		Object.defineProperty(obj, symbol, {
133
			enumerable: true,
134
			get: function () { visited.push(symbol); return Infinity; }
135
		});
136
		var nonEnumSymbol = Symbol('non-enumerable');
137
		Object.defineProperty(obj, nonEnumSymbol, {
138
			enumerable: false,
139
			get: function () { visited.push(nonEnumSymbol); return -Infinity; }
140
		});
141
		var target = assign({}, obj);
142
		st.deepEqual(visited, ['a', symbol], 'key is visited first, then symbol');
143
		st.equal(target.a, 42, 'target.a is 42');
144
		st.equal(target[symbol], Infinity, 'target[symbol] is Infinity');
145
		st.notEqual(target[nonEnumSymbol], -Infinity, 'target[nonEnumSymbol] is not -Infinity');
146
		st.end();
147
	});
148
149
	t.test('does not fail when symbols are not present', function (st) {
150
		var getSyms;
151
		if (hasSymbols) {
152
			getSyms = Object.getOwnPropertySymbols;
153
			delete Object.getOwnPropertySymbols;
154
		}
155
156
		var visited = [];
157
		var obj = {};
158
		Object.defineProperty(obj, 'a', { enumerable: true, get: function () { visited.push('a'); return 42; } });
159
		var keys = ['a'];
160
		if (hasSymbols) {
161
			var symbol = Symbol('sym');
162
			Object.defineProperty(obj, symbol, {
163
				enumerable: true,
164
				get: function () { visited.push(symbol); return Infinity; }
165
			});
166
			keys.push(symbol);
167
		}
168
		var target = assign({}, obj);
169
		st.deepEqual(visited, keys, 'assign visits expected keys');
170
		st.equal(target.a, 42, 'target.a is 42');
171
172
		if (hasSymbols) {
173
			st.equal(target[symbol], Infinity);
174
175
			Object.getOwnPropertySymbols = getSyms;
176
		}
177
		st.end();
178
	});
179
180
	t.test('preserves correct property enumeration order', function (st) {
181
		var str = 'abcdefghijklmnopqrst';
182
		var letters = {};
183
		forEach(str.split(''), function (letter) {
184
			letters[letter] = letter;
185
		});
186
187
		var n = 5;
188
		st.comment('run the next test ' + n + ' times');
189
		var object = assign({}, letters);
190
		var actual = '';
191
		for (var k in object) {
192
			actual += k;
193
		}
194
		for (var i = 0; i < n; ++i) {
195
			st.equal(actual, str, 'property enumeration order should be followed');
196
		}
197
		st.end();
198
	});
199
200
	t.test('checks enumerability and existence, in case of modification during [[Get]]', { skip: !Object.defineProperty }, function (st) {
201
		var targetBvalue = {};
202
		var targetCvalue = {};
203
		var target = { b: targetBvalue, c: targetCvalue };
204
		var source = {};
205
		Object.defineProperty(source, 'a', {
206
			enumerable: true,
207
			get: function () {
208
				delete this.b;
209
				Object.defineProperty(this, 'c', { enumerable: false });
210
				return 'a';
211
			}
212
		});
213
		var sourceBvalue = {};
214
		var sourceCvalue = {};
215
		source.b = sourceBvalue;
216
		source.c = sourceCvalue;
217
		var result = assign(target, source);
218
		st.equal(result, target, 'sanity check: result is === target');
219
		st.equal(result.b, targetBvalue, 'target key not overwritten by deleted source key');
220
		st.equal(result.c, targetCvalue, 'target key not overwritten by non-enumerable source key');
221
222
		st.end();
223
	});
224
};