| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- /*
- * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
- * - test/built-ins/Object/groupBy/evenOdd.js
- * - test/built-ins/Object/groupBy/groupLength.js
- * - test/built-ins/Object/groupBy/callback-arg.js
- * - test/built-ins/Object/groupBy/string.js
- * - test/built-ins/Object/groupBy/emptyList.js
- * - test/built-ins/Object/groupBy/toPropertyKey.js
- * - test/built-ins/Object/groupBy/invalid-property-key.js
- * - test/built-ins/Object/groupBy/invalid-iterable.js
- * - test/built-ins/Object/groupBy/invalid-callback.js
- * - test/built-ins/Object/groupBy/callback-throws.js
- * - test/built-ins/Object/groupBy/null-prototype.js
- * - test/built-ins/Map/groupBy/evenOdd.js
- * - test/built-ins/Map/groupBy/groupLength.js
- * - test/built-ins/Map/groupBy/callback-arg.js
- * - test/built-ins/Map/groupBy/string.js
- * - test/built-ins/Map/groupBy/emptyList.js
- * - test/built-ins/Map/groupBy/toPropertyKey.js
- * - test/built-ins/Map/groupBy/negativeZero.js
- * - test/built-ins/Map/groupBy/invalid-iterable.js
- * - test/built-ins/Map/groupBy/invalid-callback.js
- * - test/built-ins/Map/groupBy/callback-throws.js
- * - test/built-ins/Map/groupBy/map-instance.js
- *
- * Copyright (c) 2023 Ecma International. All rights reserved.
- * Test262 portions are governed by the BSD license in LICENSE.test262.
- * Invalid-iterable cases use a plain object, equivalent to the upstream object's absent
- * Symbol.iterator; CodeMode does not support symbol-keyed properties.
- */
- import { describe, expect, test } from "bun:test"
- import { Effect } from "effect"
- import { CodeMode } from "../src/index.js"
- const execute = (code: string) => Effect.runPromise(CodeMode.execute({ code, tools: {} }))
- const value = async (code: string) => {
- const result = await execute(code)
- if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
- return result.value
- }
- describe("Object.groupBy Test262 parity", () => {
- test("groups values by coerced property keys", async () => {
- expect(
- await value(`
- const values = [1, 2, 3]
- const grouped = Object.groupBy(values, (value) => value % 2 === 0 ? "even" : "odd")
- const stringable = { toString: () => 1 }
- const coerced = Object.groupBy([1, "1", stringable], (value) => value)
- const lengths = Object.groupBy(["hello", "test", "world"], (value) => value.length)
- return [
- Object.keys(grouped), grouped.even, grouped.odd, grouped.toString === undefined,
- Object.keys(coerced), coerced[1].length,
- Object.keys(lengths), lengths[5], lengths[4],
- ]
- `),
- ).toEqual([["odd", "even"], [2], [1, 3], true, ["1"], 3, ["4", "5"], ["hello", "world"], ["test"]])
- })
- test("passes each value and index to the callback", async () => {
- expect(
- await value(`
- const values = [-0, 0, 1, 2, 3]
- const seen = []
- Object.groupBy(values, (value, index, ...extra) => {
- seen.push([value === values[index], index, extra.length])
- return null
- })
- return seen
- `),
- ).toEqual([
- [true, 0, 0],
- [true, 1, 0],
- [true, 2, 0],
- [true, 3, 0],
- [true, 4, 0],
- ])
- })
- test("supports strings and empty collections", async () => {
- expect(
- await value(`
- const grouped = Object.groupBy("🥰💩🙏😈", (char) => char < "🙏" ? "before" : "after")
- const empty = Object.groupBy([], () => { throw new Error("not called") })
- return [Object.keys(grouped), grouped.before, grouped.after, Object.keys(empty)]
- `),
- ).toEqual([["after", "before"], ["💩", "😈"], ["🥰", "🙏"], []])
- })
- test("rejects invalid inputs and propagates callback and key-coercion failures", async () => {
- expect(
- await value(`
- const messages = []
- try { Object.groupBy({}, () => { throw new Error("not called") }) } catch (error) { messages.push(error.name) }
- for (const callback of [null, undefined, {}]) {
- try { Object.groupBy([], callback) } catch (error) { messages.push(error.name) }
- }
- try { Object.groupBy([1], () => { throw new Error("callback") }) } catch (error) { messages.push(error.message) }
- try {
- Object.groupBy([1], () => ({
- toString: () => { throw new Error("property key") },
- }))
- } catch (error) { messages.push(error.message) }
- return messages
- `),
- ).toEqual(["TypeError", "TypeError", "TypeError", "TypeError", "callback", "property key"])
- })
- })
- describe("Map.groupBy Test262 parity", () => {
- test("returns a Map with identity-preserving groups", async () => {
- expect(
- await value(`
- const stringable = { toString: () => 1 }
- const grouped = Map.groupBy([1, "1", stringable], (value) => value)
- const parity = Map.groupBy([1, 2, 3], (value) => value % 2 === 0 ? "even" : "odd")
- const lengths = Map.groupBy(["hello", "test", "world"], (value) => value.length)
- return [
- grouped instanceof Map,
- grouped.size,
- grouped.get(1),
- grouped.get("1"),
- grouped.has(stringable),
- grouped.keys().length,
- parity.get("even"),
- parity.get("odd"),
- lengths.keys(),
- lengths.get(5),
- lengths.get(4),
- ]
- `),
- ).toEqual([true, 3, [1], ["1"], true, 3, [2], [1, 3], [5, 4], ["hello", "world"], ["test"]])
- })
- test("normalizes negative zero and passes each value and index", async () => {
- expect(
- await value(`
- const values = [-0, 0, 1, 2, 3]
- const seen = []
- const grouped = Map.groupBy(values, (value, index, ...extra) => {
- seen.push([value === values[index], index, extra.length])
- return value
- })
- return [grouped.size, grouped.get(0), seen]
- `),
- ).toEqual([
- 4,
- [-0, 0],
- [
- [true, 0, 0],
- [true, 1, 0],
- [true, 2, 0],
- [true, 3, 0],
- [true, 4, 0],
- ],
- ])
- })
- test("supports strings and empty collections", async () => {
- expect(
- await value(`
- const grouped = Map.groupBy("🥰💩🙏😈", (char) => char < "🙏" ? "before" : "after")
- const empty = Map.groupBy([], () => { throw new Error("not called") })
- return [grouped.keys(), grouped.get("before"), grouped.get("after"), empty.size]
- `),
- ).toEqual([["after", "before"], ["💩", "😈"], ["🥰", "🙏"], 0])
- })
- test("rejects invalid inputs and propagates callback failures", async () => {
- expect(
- await value(`
- const results = []
- try { Map.groupBy({}, () => { throw new Error("not called") }) } catch (error) { results.push(error.name) }
- for (const callback of [null, undefined, {}]) {
- try { Map.groupBy([], callback) } catch (error) { results.push(error.name) }
- }
- try { Map.groupBy([1], () => { throw new Error("callback") }) }
- catch (error) { results.push(error.message) }
- return results
- `),
- ).toEqual(["TypeError", "TypeError", "TypeError", "TypeError", "callback"])
- })
- })
|