]> Lady’s Gitweb - Pisces/blob - collection.test.js
Add isArraylikeObject
[Pisces] / collection.test.js
1 // ♓🌟 Piscēs ∷ collection.test.js
2 // ====================================================================
3 //
4 // Copyright © 2022 Lady [@ Lady’s Computer].
5 //
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
9
10 import {
11 assertEquals,
12 assertSpyCall,
13 assertSpyCalls,
14 assertStrictEquals,
15 assertThrows,
16 describe,
17 it,
18 spy,
19 } from "./dev-deps.js";
20 import {
21 canonicalNumericIndexString,
22 findIndexedEntry,
23 isArrayIndexString,
24 isArraylikeObject,
25 isCollection,
26 isConcatSpreadable,
27 isIntegerIndexString,
28 lengthOfArraylike,
29 toIndex,
30 toLength,
31 } from "./collection.js";
32
33 describe("canonicalNumericIndexString", () => {
34 it("[[Call]] returns undefined for nonstrings", () => {
35 assertStrictEquals(canonicalNumericIndexString(1), void {});
36 });
37
38 it("[[Call]] returns undefined for noncanonical strings", () => {
39 assertStrictEquals(canonicalNumericIndexString(""), void {});
40 assertStrictEquals(canonicalNumericIndexString("01"), void {});
41 assertStrictEquals(
42 canonicalNumericIndexString("9007199254740993"),
43 void {},
44 );
45 });
46
47 it('[[Call]] returns -0 for "-0"', () => {
48 assertStrictEquals(canonicalNumericIndexString("-0"), -0);
49 });
50
51 it("[[Call]] returns the corresponding number for canonical strings", () => {
52 assertStrictEquals(canonicalNumericIndexString("0"), 0);
53 assertStrictEquals(canonicalNumericIndexString("-0.25"), -0.25);
54 assertStrictEquals(
55 canonicalNumericIndexString("9007199254740992"),
56 9007199254740992,
57 );
58 assertStrictEquals(canonicalNumericIndexString("NaN"), 0 / 0);
59 assertStrictEquals(canonicalNumericIndexString("Infinity"), 1 / 0);
60 assertStrictEquals(
61 canonicalNumericIndexString("-Infinity"),
62 -1 / 0,
63 );
64 });
65 });
66
67 describe("findIndexedEntry", () => {
68 it("[[Call]] returns undefined if no matching entry exists", () => {
69 assertStrictEquals(findIndexedEntry([], () => true), void {});
70 assertStrictEquals(findIndexedEntry([1], () => false), void {});
71 });
72
73 it("[[Call]] returns an entry for the first match", () => {
74 assertEquals(
75 findIndexedEntry([, true, false], ($) => $ ?? true),
76 [0, void {}],
77 );
78 assertEquals(
79 findIndexedEntry(["failure", "success"], ($) => $ == "success"),
80 [1, "success"],
81 );
82 });
83
84 it("[[Call]] works on arraylike objects", () => {
85 assertEquals(
86 findIndexedEntry({ 1: "success", length: 2 }, ($) => $),
87 [1, "success"],
88 );
89 assertEquals(
90 findIndexedEntry({ 1: "failure", length: 1 }, ($) => $),
91 void {},
92 );
93 });
94
95 it("[[Call]] only gets the value once", () => {
96 const get1 = spy(() => true);
97 findIndexedEntry({
98 get 1() {
99 return get1();
100 },
101 length: 2,
102 }, ($) => $);
103 assertSpyCalls(get1, 1);
104 });
105
106 it("[[Call]] passes the value, index, and this value to the callback", () => {
107 const arr = ["failure", "success", "success"];
108 const callback = spy(($) => $ === "success");
109 const thisArg = {};
110 findIndexedEntry(arr, callback, thisArg);
111 assertSpyCalls(callback, 2);
112 assertSpyCall(callback, 0, {
113 args: ["failure", 0, arr],
114 self: thisArg,
115 });
116 assertSpyCall(callback, 1, {
117 args: ["success", 1, arr],
118 self: thisArg,
119 });
120 });
121 });
122
123 describe("isArrayIndexString", () => {
124 it("[[Call]] returns false for nonstrings", () => {
125 assertStrictEquals(isArrayIndexString(1), false);
126 });
127
128 it("[[Call]] returns false for noncanonical strings", () => {
129 assertStrictEquals(isArrayIndexString(""), false);
130 assertStrictEquals(isArrayIndexString("01"), false);
131 assertStrictEquals(isArrayIndexString("9007199254740993"), false);
132 });
133
134 it("[[Call]] returns false for nonfinite numbers", () => {
135 assertStrictEquals(isArrayIndexString("NaN"), false);
136 assertStrictEquals(isArrayIndexString("Infinity"), false);
137 assertStrictEquals(isArrayIndexString("-Infinity"), false);
138 });
139
140 it("[[Call]] returns false for negative numbers", () => {
141 assertStrictEquals(isArrayIndexString("-0"), false);
142 assertStrictEquals(isArrayIndexString("-1"), false);
143 });
144
145 it("[[Call]] returns false for nonintegers", () => {
146 assertStrictEquals(isArrayIndexString("0.25"), false);
147 assertStrictEquals(isArrayIndexString("1.1"), false);
148 });
149
150 it("[[Call]] returns false for numbers greater than or equal to -1 >>> 0", () => {
151 assertStrictEquals(isArrayIndexString(String(-1 >>> 0)), false);
152 assertStrictEquals(
153 isArrayIndexString(String((-1 >>> 0) + 1)),
154 false,
155 );
156 });
157
158 it("[[Call]] returns true for array lengths less than -1 >>> 0", () => {
159 assertStrictEquals(isArrayIndexString("0"), true);
160 assertStrictEquals(
161 isArrayIndexString(String((-1 >>> 0) - 1)),
162 true,
163 );
164 });
165 });
166
167 describe("isArraylikeObject", () => {
168 it("[[Call]] returns false for primitives", () => {
169 assertStrictEquals(isArraylikeObject("failure"), false);
170 });
171
172 it("[[Call]] returns false if length throws", () => {
173 assertStrictEquals(
174 isArraylikeObject({
175 get length() {
176 throw void {};
177 },
178 }),
179 false,
180 );
181 });
182
183 it("[[Call]] returns false if length is not a number and cannot be converted to one", () => {
184 assertStrictEquals(isArraylikeObject({ length: 1n }), false);
185 });
186
187 it("[[Call]] returns true if length is convertable to a number", () => {
188 assertStrictEquals(isArraylikeObject({ length: -0 }), true);
189 assertStrictEquals(isArraylikeObject({ length: 1 }), true);
190 assertStrictEquals(isArraylikeObject({ length: -1.25 }), true);
191 assertStrictEquals(
192 isArraylikeObject({ length: 9007199254740992 }),
193 true,
194 );
195 assertStrictEquals(isArraylikeObject({ length: Infinity }), true);
196 assertStrictEquals(isArraylikeObject({ length: "success" }), true);
197 });
198 });
199
200 describe("isCollection", () => {
201 it("[[Call]] returns false for primitives", () => {
202 assertStrictEquals(isCollection("failure"), false);
203 });
204
205 it("[[Call]] returns false if length throws", () => {
206 assertStrictEquals(
207 isCollection({
208 get length() {
209 throw void {};
210 },
211 }),
212 false,
213 );
214 });
215
216 it("[[Call]] returns false if length is not an integer index and cannot be converted to one", () => {
217 assertStrictEquals(
218 isCollection({ length: -1, [Symbol.isConcatSpreadable]: true }),
219 false,
220 );
221 assertStrictEquals(
222 isCollection({
223 length: Infinity,
224 [Symbol.isConcatSpreadable]: true,
225 }),
226 false,
227 );
228 assertStrictEquals(
229 isCollection({
230 length: 9007199254740992,
231 [Symbol.isConcatSpreadable]: true,
232 }),
233 false,
234 );
235 });
236
237 it("[[Call]] returns true if length is an integer index and the object is concat spreadable", () => {
238 assertStrictEquals(
239 isCollection({ length: 1, [Symbol.isConcatSpreadable]: true }),
240 true,
241 );
242 assertStrictEquals(
243 isCollection({ length: 0, [Symbol.isConcatSpreadable]: true }),
244 true,
245 );
246 assertStrictEquals(
247 isCollection({
248 length: 9007199254740991,
249 [Symbol.isConcatSpreadable]: true,
250 }),
251 true,
252 );
253 });
254
255 it("[[Call]] returns true if length can be converted to an index without throwing an error and the object is concat spreadable", () => {
256 assertStrictEquals(
257 isCollection({ length: -0, [Symbol.isConcatSpreadable]: true }),
258 true,
259 );
260 assertStrictEquals(
261 isCollection({ length: NaN, [Symbol.isConcatSpreadable]: true }),
262 true,
263 );
264 });
265 });
266
267 describe("isConcatSpreadable", () => {
268 it("[[Call]] returns false for primitives", () => {
269 assertStrictEquals(isConcatSpreadable("failure"), false);
270 });
271
272 it("[[Call]] returns false if [Symbol.isConcatSpreadable] is null or false", () => {
273 assertStrictEquals(
274 isConcatSpreadable(
275 Object.assign([], { [Symbol.isConcatSpreadable]: null }),
276 ),
277 false,
278 );
279 assertStrictEquals(
280 isConcatSpreadable(
281 Object.assign([], { [Symbol.isConcatSpreadable]: false }),
282 ),
283 false,
284 );
285 });
286
287 it("[[Call]] returns true if [Symbol.isConcatSpreadable] is undefined and the object is an array", () => {
288 assertStrictEquals(
289 isConcatSpreadable(
290 Object.assign([], { [Symbol.isConcatSpreadable]: undefined }),
291 ),
292 true,
293 );
294 });
295
296 it("[[Call]] returns true if [Symbol.isConcatSpreadable] is true", () => {
297 assertStrictEquals(
298 isConcatSpreadable({ [Symbol.isConcatSpreadable]: true }),
299 true,
300 );
301 });
302 });
303
304 describe("isIntegerIndexString", () => {
305 it("[[Call]] returns false for nonstrings", () => {
306 assertStrictEquals(isIntegerIndexString(1), false);
307 });
308
309 it("[[Call]] returns false for noncanonical strings", () => {
310 assertStrictEquals(isIntegerIndexString(""), false);
311 assertStrictEquals(isIntegerIndexString("01"), false);
312 assertStrictEquals(
313 isIntegerIndexString("9007199254740993"),
314 false,
315 );
316 });
317
318 it("[[Call]] returns false for nonfinite numbers", () => {
319 assertStrictEquals(isIntegerIndexString("NaN"), false);
320 assertStrictEquals(isIntegerIndexString("Infinity"), false);
321 assertStrictEquals(isIntegerIndexString("-Infinity"), false);
322 });
323
324 it("[[Call]] returns false for negative numbers", () => {
325 assertStrictEquals(isIntegerIndexString("-0"), false);
326 assertStrictEquals(isIntegerIndexString("-1"), false);
327 });
328
329 it("[[Call]] returns false for nonintegers", () => {
330 assertStrictEquals(isIntegerIndexString("0.25"), false);
331 assertStrictEquals(isIntegerIndexString("1.1"), false);
332 });
333
334 it("[[Call]] returns false for numbers greater than or equal to 2 ** 53", () => {
335 assertStrictEquals(
336 isIntegerIndexString("9007199254740992"),
337 false,
338 );
339 });
340
341 it("[[Call]] returns true for safe canonical integer strings", () => {
342 assertStrictEquals(isIntegerIndexString("0"), true);
343 assertStrictEquals(isIntegerIndexString("9007199254740991"), true);
344 });
345 });
346
347 describe("lengthOfArraylike", () => {
348 it("[[Call]] returns the length", () => {
349 assertStrictEquals(
350 lengthOfArraylike({ length: 9007199254740991 }),
351 9007199254740991,
352 );
353 });
354
355 it("[[Call]] returns a non·nan result", () => {
356 assertStrictEquals(lengthOfArraylike({ length: NaN }), 0);
357 assertStrictEquals(lengthOfArraylike({ length: "failure" }), 0);
358 });
359
360 it("[[Call]] returns an integral result", () => {
361 assertStrictEquals(lengthOfArraylike({ length: 0.25 }), 0);
362 assertStrictEquals(lengthOfArraylike({ length: 1.1 }), 1);
363 });
364
365 it("[[Call]] returns a result greater than or equal to zero", () => {
366 assertStrictEquals(lengthOfArraylike({ length: -0 }), 0);
367 assertStrictEquals(lengthOfArraylike({ length: -1 }), 0);
368 assertStrictEquals(lengthOfArraylike({ length: -Infinity }), 0);
369 });
370
371 it("[[Call]] returns a result less than 2 ** 53", () => {
372 assertStrictEquals(
373 lengthOfArraylike({ length: 9007199254740992 }),
374 9007199254740991,
375 );
376 assertStrictEquals(
377 lengthOfArraylike({ length: Infinity }),
378 9007199254740991,
379 );
380 });
381 });
382
383 describe("toIndex", () => {
384 it("[[Call]] returns an index", () => {
385 assertStrictEquals(toIndex(9007199254740991), 9007199254740991);
386 });
387
388 it("[[Call]] returns zero for a zerolike result", () => {
389 assertStrictEquals(toIndex(NaN), 0);
390 assertStrictEquals(toIndex("failure"), 0);
391 assertStrictEquals(toIndex(-0), 0);
392 });
393
394 it("[[Call]] rounds down to the nearest integer", () => {
395 assertStrictEquals(toIndex(0.25), 0);
396 assertStrictEquals(toIndex(1.1), 1);
397 });
398
399 it("[[Call]] throws when provided a negative number", () => {
400 assertThrows(() => toIndex(-1));
401 assertThrows(() => toIndex(-Infinity));
402 });
403
404 it("[[Call]] throws when provided a number greater than or equal to 2 ** 53", () => {
405 assertThrows(() => toIndex(9007199254740992));
406 assertThrows(() => toIndex(Infinity));
407 });
408 });
409
410 describe("toLength", () => {
411 it("[[Call]] returns a length", () => {
412 assertStrictEquals(toLength(9007199254740991), 9007199254740991);
413 });
414
415 it("[[Call]] returns a non·nan result", () => {
416 assertStrictEquals(toLength(NaN), 0);
417 assertStrictEquals(toLength("failure"), 0);
418 });
419
420 it("[[Call]] returns an integral result", () => {
421 assertStrictEquals(toLength(0.25), 0);
422 assertStrictEquals(toLength(1.1), 1);
423 });
424
425 it("[[Call]] returns a result greater than or equal to zero", () => {
426 assertStrictEquals(toLength(-0), 0);
427 assertStrictEquals(toLength(-1), 0);
428 assertStrictEquals(toLength(-Infinity), 0);
429 });
430
431 it("[[Call]] returns a result less than 2 ** 53", () => {
432 assertStrictEquals(toLength(9007199254740992), 9007199254740991);
433 assertStrictEquals(toLength(Infinity), 9007199254740991);
434 });
435 });
This page took 0.087666 seconds and 5 git commands to generate.