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