1 | 'use strict';
|
---|
2 |
|
---|
3 | var test = require('tape');
|
---|
4 | var v = require('es-value-fixtures');
|
---|
5 | var forEach = require('for-each');
|
---|
6 | var inspect = require('object-inspect');
|
---|
7 | var hasOwn = require('hasown');
|
---|
8 | var hasPropertyDescriptors = require('has-property-descriptors')();
|
---|
9 | var getOwnPropertyDescriptors = require('object.getownpropertydescriptors');
|
---|
10 | var ownKeys = require('reflect.ownkeys');
|
---|
11 |
|
---|
12 | var defineDataProperty = require('../');
|
---|
13 |
|
---|
14 | test('defineDataProperty', function (t) {
|
---|
15 | t.test('argument validation', function (st) {
|
---|
16 | forEach(v.primitives, function (nonObject) {
|
---|
17 | st['throws'](
|
---|
18 | // @ts-expect-error
|
---|
19 | function () { defineDataProperty(nonObject, 'key', 'value'); },
|
---|
20 | TypeError,
|
---|
21 | 'throws on non-object input: ' + inspect(nonObject)
|
---|
22 | );
|
---|
23 | });
|
---|
24 |
|
---|
25 | forEach(v.nonPropertyKeys, function (nonPropertyKey) {
|
---|
26 | st['throws'](
|
---|
27 | // @ts-expect-error
|
---|
28 | function () { defineDataProperty({}, nonPropertyKey, 'value'); },
|
---|
29 | TypeError,
|
---|
30 | 'throws on non-PropertyKey input: ' + inspect(nonPropertyKey)
|
---|
31 | );
|
---|
32 | });
|
---|
33 |
|
---|
34 | forEach(v.nonBooleans, function (nonBoolean) {
|
---|
35 | if (nonBoolean !== null) {
|
---|
36 | st['throws'](
|
---|
37 | // @ts-expect-error
|
---|
38 | function () { defineDataProperty({}, 'key', 'value', nonBoolean); },
|
---|
39 | TypeError,
|
---|
40 | 'throws on non-boolean nonEnumerable: ' + inspect(nonBoolean)
|
---|
41 | );
|
---|
42 |
|
---|
43 | st['throws'](
|
---|
44 | // @ts-expect-error
|
---|
45 | function () { defineDataProperty({}, 'key', 'value', false, nonBoolean); },
|
---|
46 | TypeError,
|
---|
47 | 'throws on non-boolean nonWritable: ' + inspect(nonBoolean)
|
---|
48 | );
|
---|
49 |
|
---|
50 | st['throws'](
|
---|
51 | // @ts-expect-error
|
---|
52 | function () { defineDataProperty({}, 'key', 'value', false, false, nonBoolean); },
|
---|
53 | TypeError,
|
---|
54 | 'throws on non-boolean nonConfigurable: ' + inspect(nonBoolean)
|
---|
55 | );
|
---|
56 | }
|
---|
57 | });
|
---|
58 |
|
---|
59 | st.end();
|
---|
60 | });
|
---|
61 |
|
---|
62 | t.test('normal data property', function (st) {
|
---|
63 | /** @type {Record<PropertyKey, string>} */
|
---|
64 | var obj = { existing: 'existing property' };
|
---|
65 | st.ok(hasOwn(obj, 'existing'), 'has initial own property');
|
---|
66 | st.equal(obj.existing, 'existing property', 'has expected initial value');
|
---|
67 |
|
---|
68 | var res = defineDataProperty(obj, 'added', 'added property');
|
---|
69 | st.equal(res, void undefined, 'returns `undefined`');
|
---|
70 | st.ok(hasOwn(obj, 'added'), 'has expected own property');
|
---|
71 | st.equal(obj.added, 'added property', 'has expected value');
|
---|
72 |
|
---|
73 | defineDataProperty(obj, 'existing', 'new value');
|
---|
74 | st.ok(hasOwn(obj, 'existing'), 'still has expected own property');
|
---|
75 | st.equal(obj.existing, 'new value', 'has new expected value');
|
---|
76 |
|
---|
77 | defineDataProperty(obj, 'explicit1', 'new value', false);
|
---|
78 | st.ok(hasOwn(obj, 'explicit1'), 'has expected own property (explicit enumerable)');
|
---|
79 | st.equal(obj.explicit1, 'new value', 'has new expected value (explicit enumerable)');
|
---|
80 |
|
---|
81 | defineDataProperty(obj, 'explicit2', 'new value', false, false);
|
---|
82 | st.ok(hasOwn(obj, 'explicit2'), 'has expected own property (explicit writable)');
|
---|
83 | st.equal(obj.explicit2, 'new value', 'has new expected value (explicit writable)');
|
---|
84 |
|
---|
85 | defineDataProperty(obj, 'explicit3', 'new value', false, false, false);
|
---|
86 | st.ok(hasOwn(obj, 'explicit3'), 'has expected own property (explicit configurable)');
|
---|
87 | st.equal(obj.explicit3, 'new value', 'has new expected value (explicit configurable)');
|
---|
88 |
|
---|
89 | st.end();
|
---|
90 | });
|
---|
91 |
|
---|
92 | t.test('loose mode', { skip: !hasPropertyDescriptors }, function (st) {
|
---|
93 | var obj = { existing: 'existing property' };
|
---|
94 |
|
---|
95 | defineDataProperty(obj, 'added', 'added value 1', true, null, null, true);
|
---|
96 | st.deepEqual(
|
---|
97 | getOwnPropertyDescriptors(obj),
|
---|
98 | {
|
---|
99 | existing: {
|
---|
100 | configurable: true,
|
---|
101 | enumerable: true,
|
---|
102 | value: 'existing property',
|
---|
103 | writable: true
|
---|
104 | },
|
---|
105 | added: {
|
---|
106 | configurable: true,
|
---|
107 | enumerable: !hasPropertyDescriptors,
|
---|
108 | value: 'added value 1',
|
---|
109 | writable: true
|
---|
110 | }
|
---|
111 | },
|
---|
112 | 'in loose mode, obj still adds property 1'
|
---|
113 | );
|
---|
114 |
|
---|
115 | defineDataProperty(obj, 'added', 'added value 2', false, true, null, true);
|
---|
116 | st.deepEqual(
|
---|
117 | getOwnPropertyDescriptors(obj),
|
---|
118 | {
|
---|
119 | existing: {
|
---|
120 | configurable: true,
|
---|
121 | enumerable: true,
|
---|
122 | value: 'existing property',
|
---|
123 | writable: true
|
---|
124 | },
|
---|
125 | added: {
|
---|
126 | configurable: true,
|
---|
127 | enumerable: true,
|
---|
128 | value: 'added value 2',
|
---|
129 | writable: !hasPropertyDescriptors
|
---|
130 | }
|
---|
131 | },
|
---|
132 | 'in loose mode, obj still adds property 2'
|
---|
133 | );
|
---|
134 |
|
---|
135 | defineDataProperty(obj, 'added', 'added value 3', false, false, true, true);
|
---|
136 | st.deepEqual(
|
---|
137 | getOwnPropertyDescriptors(obj),
|
---|
138 | {
|
---|
139 | existing: {
|
---|
140 | configurable: true,
|
---|
141 | enumerable: true,
|
---|
142 | value: 'existing property',
|
---|
143 | writable: true
|
---|
144 | },
|
---|
145 | added: {
|
---|
146 | configurable: !hasPropertyDescriptors,
|
---|
147 | enumerable: true,
|
---|
148 | value: 'added value 3',
|
---|
149 | writable: true
|
---|
150 | }
|
---|
151 | },
|
---|
152 | 'in loose mode, obj still adds property 3'
|
---|
153 | );
|
---|
154 |
|
---|
155 | st.end();
|
---|
156 | });
|
---|
157 |
|
---|
158 | t.test('non-normal data property, ES3', { skip: hasPropertyDescriptors }, function (st) {
|
---|
159 | /** @type {Record<PropertyKey, string>} */
|
---|
160 | var obj = { existing: 'existing property' };
|
---|
161 |
|
---|
162 | st['throws'](
|
---|
163 | function () { defineDataProperty(obj, 'added', 'added value', true); },
|
---|
164 | SyntaxError,
|
---|
165 | 'nonEnumerable throws a Syntax Error'
|
---|
166 | );
|
---|
167 |
|
---|
168 | st['throws'](
|
---|
169 | function () { defineDataProperty(obj, 'added', 'added value', false, true); },
|
---|
170 | SyntaxError,
|
---|
171 | 'nonWritable throws a Syntax Error'
|
---|
172 | );
|
---|
173 |
|
---|
174 | st['throws'](
|
---|
175 | function () { defineDataProperty(obj, 'added', 'added value', false, false, true); },
|
---|
176 | SyntaxError,
|
---|
177 | 'nonWritable throws a Syntax Error'
|
---|
178 | );
|
---|
179 |
|
---|
180 | st.deepEqual(
|
---|
181 | ownKeys(obj),
|
---|
182 | ['existing'],
|
---|
183 | 'obj still has expected keys'
|
---|
184 | );
|
---|
185 | st.equal(obj.existing, 'existing property', 'obj still has expected values');
|
---|
186 |
|
---|
187 | st.end();
|
---|
188 | });
|
---|
189 |
|
---|
190 | t.test('new non-normal data property, ES5+', { skip: !hasPropertyDescriptors }, function (st) {
|
---|
191 | /** @type {Record<PropertyKey, string>} */
|
---|
192 | var obj = { existing: 'existing property' };
|
---|
193 |
|
---|
194 | defineDataProperty(obj, 'nonEnum', null, true);
|
---|
195 | defineDataProperty(obj, 'nonWrit', null, false, true);
|
---|
196 | defineDataProperty(obj, 'nonConf', null, false, false, true);
|
---|
197 |
|
---|
198 | st.deepEqual(
|
---|
199 | getOwnPropertyDescriptors(obj),
|
---|
200 | {
|
---|
201 | existing: {
|
---|
202 | configurable: true,
|
---|
203 | enumerable: true,
|
---|
204 | value: 'existing property',
|
---|
205 | writable: true
|
---|
206 | },
|
---|
207 | nonEnum: {
|
---|
208 | configurable: true,
|
---|
209 | enumerable: false,
|
---|
210 | value: null,
|
---|
211 | writable: true
|
---|
212 | },
|
---|
213 | nonWrit: {
|
---|
214 | configurable: true,
|
---|
215 | enumerable: true,
|
---|
216 | value: null,
|
---|
217 | writable: false
|
---|
218 | },
|
---|
219 | nonConf: {
|
---|
220 | configurable: false,
|
---|
221 | enumerable: true,
|
---|
222 | value: null,
|
---|
223 | writable: true
|
---|
224 | }
|
---|
225 | },
|
---|
226 | 'obj has expected property descriptors'
|
---|
227 | );
|
---|
228 |
|
---|
229 | st.end();
|
---|
230 | });
|
---|
231 |
|
---|
232 | t.test('existing non-normal data property, ES5+', { skip: !hasPropertyDescriptors }, function (st) {
|
---|
233 | // test case changing an existing non-normal property
|
---|
234 |
|
---|
235 | /** @type {Record<string, null | string>} */
|
---|
236 | var obj = {};
|
---|
237 | Object.defineProperty(obj, 'nonEnum', { configurable: true, enumerable: false, value: null, writable: true });
|
---|
238 | Object.defineProperty(obj, 'nonWrit', { configurable: true, enumerable: true, value: null, writable: false });
|
---|
239 | Object.defineProperty(obj, 'nonConf', { configurable: false, enumerable: true, value: null, writable: true });
|
---|
240 |
|
---|
241 | st.deepEqual(
|
---|
242 | getOwnPropertyDescriptors(obj),
|
---|
243 | {
|
---|
244 | nonEnum: {
|
---|
245 | configurable: true,
|
---|
246 | enumerable: false,
|
---|
247 | value: null,
|
---|
248 | writable: true
|
---|
249 | },
|
---|
250 | nonWrit: {
|
---|
251 | configurable: true,
|
---|
252 | enumerable: true,
|
---|
253 | value: null,
|
---|
254 | writable: false
|
---|
255 | },
|
---|
256 | nonConf: {
|
---|
257 | configurable: false,
|
---|
258 | enumerable: true,
|
---|
259 | value: null,
|
---|
260 | writable: true
|
---|
261 | }
|
---|
262 | },
|
---|
263 | 'obj initially has expected property descriptors'
|
---|
264 | );
|
---|
265 |
|
---|
266 | defineDataProperty(obj, 'nonEnum', 'new value', false);
|
---|
267 | defineDataProperty(obj, 'nonWrit', 'new value', false, false);
|
---|
268 | st['throws'](
|
---|
269 | function () { defineDataProperty(obj, 'nonConf', 'new value', false, false, false); },
|
---|
270 | TypeError,
|
---|
271 | 'can not alter a nonconfigurable property'
|
---|
272 | );
|
---|
273 |
|
---|
274 | st.deepEqual(
|
---|
275 | getOwnPropertyDescriptors(obj),
|
---|
276 | {
|
---|
277 | nonEnum: {
|
---|
278 | configurable: true,
|
---|
279 | enumerable: true,
|
---|
280 | value: 'new value',
|
---|
281 | writable: true
|
---|
282 | },
|
---|
283 | nonWrit: {
|
---|
284 | configurable: true,
|
---|
285 | enumerable: true,
|
---|
286 | value: 'new value',
|
---|
287 | writable: true
|
---|
288 | },
|
---|
289 | nonConf: {
|
---|
290 | configurable: false,
|
---|
291 | enumerable: true,
|
---|
292 | value: null,
|
---|
293 | writable: true
|
---|
294 | }
|
---|
295 | },
|
---|
296 | 'obj ends up with expected property descriptors'
|
---|
297 | );
|
---|
298 |
|
---|
299 | st.end();
|
---|
300 | });
|
---|
301 |
|
---|
302 | t.test('frozen object, ES5+', { skip: !hasPropertyDescriptors }, function (st) {
|
---|
303 | var frozen = Object.freeze({ existing: true });
|
---|
304 |
|
---|
305 | st['throws'](
|
---|
306 | function () { defineDataProperty(frozen, 'existing', 'new value'); },
|
---|
307 | TypeError,
|
---|
308 | 'frozen object can not modify an existing property'
|
---|
309 | );
|
---|
310 |
|
---|
311 | st['throws'](
|
---|
312 | function () { defineDataProperty(frozen, 'new', 'new property'); },
|
---|
313 | TypeError,
|
---|
314 | 'frozen object can not add a new property'
|
---|
315 | );
|
---|
316 |
|
---|
317 | st.end();
|
---|
318 | });
|
---|
319 |
|
---|
320 | t.test('sealed object, ES5+', { skip: !hasPropertyDescriptors }, function (st) {
|
---|
321 | var sealed = Object.seal({ existing: true });
|
---|
322 | st.deepEqual(
|
---|
323 | Object.getOwnPropertyDescriptor(sealed, 'existing'),
|
---|
324 | {
|
---|
325 | configurable: false,
|
---|
326 | enumerable: true,
|
---|
327 | value: true,
|
---|
328 | writable: true
|
---|
329 | },
|
---|
330 | 'existing value on sealed object has expected descriptor'
|
---|
331 | );
|
---|
332 |
|
---|
333 | defineDataProperty(sealed, 'existing', 'new value');
|
---|
334 |
|
---|
335 | st.deepEqual(
|
---|
336 | Object.getOwnPropertyDescriptor(sealed, 'existing'),
|
---|
337 | {
|
---|
338 | configurable: false,
|
---|
339 | enumerable: true,
|
---|
340 | value: 'new value',
|
---|
341 | writable: true
|
---|
342 | },
|
---|
343 | 'existing value on sealed object has changed descriptor'
|
---|
344 | );
|
---|
345 |
|
---|
346 | st['throws'](
|
---|
347 | function () { defineDataProperty(sealed, 'new', 'new property'); },
|
---|
348 | TypeError,
|
---|
349 | 'sealed object can not add a new property'
|
---|
350 | );
|
---|
351 |
|
---|
352 | st.end();
|
---|
353 | });
|
---|
354 |
|
---|
355 | t.test('nonextensible object, ES5+', { skip: !hasPropertyDescriptors }, function (st) {
|
---|
356 | var nonExt = Object.preventExtensions({ existing: true });
|
---|
357 |
|
---|
358 | st.deepEqual(
|
---|
359 | Object.getOwnPropertyDescriptor(nonExt, 'existing'),
|
---|
360 | {
|
---|
361 | configurable: true,
|
---|
362 | enumerable: true,
|
---|
363 | value: true,
|
---|
364 | writable: true
|
---|
365 | },
|
---|
366 | 'existing value on non-extensible object has expected descriptor'
|
---|
367 | );
|
---|
368 |
|
---|
369 | defineDataProperty(nonExt, 'existing', 'new value', true);
|
---|
370 |
|
---|
371 | st.deepEqual(
|
---|
372 | Object.getOwnPropertyDescriptor(nonExt, 'existing'),
|
---|
373 | {
|
---|
374 | configurable: true,
|
---|
375 | enumerable: false,
|
---|
376 | value: 'new value',
|
---|
377 | writable: true
|
---|
378 | },
|
---|
379 | 'existing value on non-extensible object has changed descriptor'
|
---|
380 | );
|
---|
381 |
|
---|
382 | st['throws'](
|
---|
383 | function () { defineDataProperty(nonExt, 'new', 'new property'); },
|
---|
384 | TypeError,
|
---|
385 | 'non-extensible object can not add a new property'
|
---|
386 | );
|
---|
387 |
|
---|
388 | st.end();
|
---|
389 | });
|
---|
390 |
|
---|
391 | t.end();
|
---|
392 | });
|
---|