From: Lady Date: Sat, 11 Nov 2023 02:55:58 +0000 (-0500) Subject: Test constructability, name, length in string.js X-Git-Url: https://git.ladys.computer/Pisces/commitdiff_plain/5f5b986d80f54cca184a03a7f5ddb17a19abacbf?ds=inline Test constructability, name, length in string.js --- diff --git a/string.js b/string.js index edf1881..774261d 100644 --- a/string.js +++ b/string.js @@ -10,6 +10,7 @@ import { bind, call, + createArrowFunction, createCallableFunction, identity, } from "./function.js"; @@ -302,6 +303,13 @@ export const { })(); export const { + /** + * Returns an iterator over the codepoints in the string representation + * of the provided value according to the algorithm of + * `String::[Symbol.iterator]`. + */ + characters, + /** * Returns an iterator over the code units in the string * representation of the provided value. @@ -330,6 +338,9 @@ export const { */ scalarValueString, } = (() => { + const generateCharacters = function* (character) { + yield character; + }; const generateCodeUnits = function* (ucsCharacter) { yield getCodeUnit(ucsCharacter, 0); }; @@ -341,6 +352,10 @@ export const { : 0xFFFD; }; + const charactersIterator = stringIteratorFunction( + generateCharacters, + "String Character Iterator", + ); const codeUnitsIterator = arrayIteratorFunction( generateCodeUnits, "String Code Unit Iterator", @@ -369,6 +384,7 @@ export const { }; return { + characters: ($) => charactersIterator(`${$}`), codeUnits: ($) => codeUnitsIterator(`${$}`), codepoints: ($) => codepointsIterator(`${$}`), scalarValues: ($) => scalarValuesIterator(`${$}`), @@ -380,16 +396,6 @@ export const { }; })(); -/** - * Returns an iterator over the codepoints in the string representation - * of the provided value according to the algorithm of - * `String::[Symbol.iterator]`. - */ -export const characters = createCallableFunction( - stringPrototype[ITERATOR], - "characters", -); - /** * Returns the character at the provided position in the string * representation of the provided value according to the algorithm of @@ -405,12 +411,38 @@ export const getCharacter = ($, pos) => { /** * Returns the code unit at the provided position in the string * representation of the provided value according to the algorithm of - * `String::charAt`. + * `String::charAt`, except that out‐of‐bounds values return undefined + * in place of nan. */ -export const getCodeUnit = createCallableFunction( - stringPrototype.charCodeAt, - "getCodeUnit", -); +export const { + getCodeUnit, + + /** + * Returns the result of catenating the string representations of the + * provided values, returning a new string according to the algorithm + * of `String::concat`. + * + * ※ If no arguments are given, this function returns the empty + * string. This is different behaviour than if an explicit undefined + * first argument is given, in which case the resulting string will + * begin with `"undefined"`. + */ + stringCatenate, +} = (() => { + const { charCodeAt, concat } = String.prototype; + const { isNaN: isNan } = Number; + + return { + getCodeUnit: ($, n) => { + const codeUnit = call(charCodeAt, $, [n]); + return isNan(codeUnit) ? undefined : codeUnit; + }, + stringCatenate: defineOwnProperties( + (...args) => call(concat, "", args), + { name: { value: "stringCatenate" }, length: { value: 2 } }, + ), + }; +})(); /** * Returns the codepoint at the provided position in the string @@ -419,7 +451,7 @@ export const getCodeUnit = createCallableFunction( */ export const getCodepoint = createCallableFunction( stringPrototype.codePointAt, - "getCodepoint", + { name: "getCodepoint" }, ); /** @@ -429,7 +461,7 @@ export const getCodepoint = createCallableFunction( */ export const getFirstSubstringIndex = createCallableFunction( stringPrototype.indexOf, - "getFirstSubstringIndex", + { name: "getFirstSubstringIndex" }, ); /** @@ -439,7 +471,7 @@ export const getFirstSubstringIndex = createCallableFunction( */ export const getLastSubstringIndex = createCallableFunction( stringPrototype.lastIndexOf, - "getLastSubstringIndex", + { name: "getLastSubstringIndex" }, ); /** @@ -451,34 +483,79 @@ export const getLastSubstringIndex = createCallableFunction( */ export const join = (() => { const { join: arrayJoin } = arrayPrototype; - const join = ($, separator = ",") => - call(arrayJoin, [...$], [`${separator}`]); + const join = ($, separator) => + call( + arrayJoin, + [...$], + [separator === undefined ? "," : `${separator}`], + ); return join; })(); -export const { - /** - * Returns a string created from the raw value of the tagged template - * literal. - * - * ※ This is an alias for `String.raw`. - */ - raw: rawString, +/** + * Returns a string created from the raw value of the tagged template + * literal. + * + * ※ This is effectively an alias for `String.raw`. + */ +export const rawString = createArrowFunction(String.raw, { + name: "rawString", +}); +export const { /** * Returns a string created from the provided code units. * - * ※ This is an alias for `String.fromCharCode`. - */ - fromCharCode: stringFromCodeUnits, - - /** - * Returns a string created from the provided codepoints. + * ※ This is effectively an alias for `String.fromCharCode`, but + * with the same error behaviour as `String.fromCodePoint`. * - * ※ This is an alias for `String.fromCodePoint`. + * ☡ This function throws an error if provided with an argument which + * is not an integral number from 0 to FFFF₁₆ inclusive. */ - fromCodePoint: stringFromCodepoints, -} = String; + stringFromCodeUnits, +} = (() => { + const { fromCharCode } = String; + const { isInteger: isIntegralNumber } = Number; + + return { + stringFromCodeUnits: defineOwnProperties( + (...codeUnits) => { + for (let index = 0; index < codeUnits.length; ++index) { + // Iterate over each provided code unit and throw if it is + // out of range. + const nextCU = +codeUnits[index]; + if ( + !isIntegralNumber(nextCU) || nextCU < 0 || nextCU > 0xFFFF + ) { + // The code unit is not an integral number between 0 and + // 0xFFFF. + throw new RangeError( + `Piscēs: Code unit out of range: ${nextCU}.`, + ); + } else { + // The code unit is acceptable. + /* do nothing */ + } + } + return call(fromCharCode, undefined, codeUnits); + }, + { name: { value: "stringFromCodeUnits" }, length: { value: 1 } }, + ), + }; +})(); + +/** + * Returns a string created from the provided codepoints. + * + * ※ This is effectively an alias for `String.fromCodePoint`. + * + * ☡ This function throws an error if provided with an argument which + * is not an integral number from 0 to 10FFFF₁₆ inclusive. + */ +export const stringFromCodepoints = createArrowFunction( + String.fromCodePoint, + { name: "stringFromCodepoints" }, +); /** * Returns the result of splitting the provided value on A·S·C·I·I @@ -503,16 +580,6 @@ export const splitOnCommas = ($) => ",", ); -/** - * Returns the result of catenating the string representations of the - * provided values, returning a new string according to the algorithm - * of `String::concat`. - */ -export const stringCatenate = createCallableFunction( - stringPrototype.concat, - "stringCatenate", -); - /** * Returns whether the string representation of the provided value ends * with the provided search string according to the algorithm of @@ -520,7 +587,7 @@ export const stringCatenate = createCallableFunction( */ export const stringEndsWith = createCallableFunction( stringPrototype.endsWith, - "stringEndsWith", + { name: "stringEndsWith" }, ); /** @@ -530,7 +597,7 @@ export const stringEndsWith = createCallableFunction( */ export const stringIncludes = createCallableFunction( stringPrototype.includes, - "stringIncludes", + { name: "stringIncludes" }, ); /** @@ -540,7 +607,7 @@ export const stringIncludes = createCallableFunction( */ export const stringMatch = createCallableFunction( stringPrototype.match, - "stringMatch", + { name: "stringMatch" }, ); /** @@ -550,16 +617,16 @@ export const stringMatch = createCallableFunction( */ export const stringMatchAll = createCallableFunction( stringPrototype.matchAll, - "stringMatchAll", + { name: "stringMatchAll" }, ); /** * Returns the normalized form of the string representation of the - * provided value according to the algorithm of `String::matchAll`. + * provided value according to the algorithm of `String::normalize`. */ export const stringNormalize = createCallableFunction( stringPrototype.normalize, - "stringNormalize", + { name: "stringNormalize" }, ); /** @@ -569,7 +636,7 @@ export const stringNormalize = createCallableFunction( */ export const stringPadEnd = createCallableFunction( stringPrototype.padEnd, - "stringPadEnd", + { name: "stringPadEnd" }, ); /** @@ -579,7 +646,7 @@ export const stringPadEnd = createCallableFunction( */ export const stringPadStart = createCallableFunction( stringPrototype.padStart, - "stringPadStart", + { name: "stringPadStart" }, ); /** @@ -589,7 +656,7 @@ export const stringPadStart = createCallableFunction( */ export const stringRepeat = createCallableFunction( stringPrototype.repeat, - "stringRepeat", + { name: "stringRepeat" }, ); /** @@ -599,7 +666,7 @@ export const stringRepeat = createCallableFunction( */ export const stringReplace = createCallableFunction( stringPrototype.replace, - "stringReplace", + { name: "stringReplace" }, ); /** @@ -609,7 +676,7 @@ export const stringReplace = createCallableFunction( */ export const stringReplaceAll = createCallableFunction( stringPrototype.replaceAll, - "stringReplaceAll", + { name: "stringReplaceAll" }, ); /** @@ -619,7 +686,7 @@ export const stringReplaceAll = createCallableFunction( */ export const stringSearch = createCallableFunction( stringPrototype.search, - "stringSearch", + { name: "stringSearch" }, ); /** @@ -628,7 +695,7 @@ export const stringSearch = createCallableFunction( */ export const stringSlice = createCallableFunction( stringPrototype.slice, - "stringSlice", + { name: "stringSlice" }, ); /** @@ -638,7 +705,7 @@ export const stringSlice = createCallableFunction( */ export const stringSplit = createCallableFunction( stringPrototype.split, - "stringSplit", + { name: "stringSplit" }, ); /** @@ -648,18 +715,20 @@ export const stringSplit = createCallableFunction( */ export const stringStartsWith = createCallableFunction( stringPrototype.startsWith, - "stringStartsWith", + { name: "stringStartsWith" }, ); /** - * Returns the `[[StringData]]` of the provided value. + * Returns the value of the provided string. + * + * ※ This is effectively an alias for the `String::valueOf`. * - * ☡ This function will throw if the provided object does not have a - * `[[StringData]]` internal slot. + * ☡ This function throws if the provided argument is not a string and + * does not have a `[[StringData]]` slot. */ export const stringValue = createCallableFunction( stringPrototype.valueOf, - "stringValue", + { name: "stringValue" }, ); /** diff --git a/string.test.js b/string.test.js index 066770b..4f257f9 100644 --- a/string.test.js +++ b/string.test.js @@ -20,21 +20,50 @@ import { import { asciiLowercase, asciiUppercase, + characters, codepoints, codeUnits, getCharacter, + getCodepoint, + getCodeUnit, + getFirstSubstringIndex, + getLastSubstringIndex, join, Matcher, + rawString, scalarValues, scalarValueString, splitOnASCIIWhitespace, splitOnCommas, + stringCatenate, + stringEndsWith, + stringFromCodepoints, + stringFromCodeUnits, + stringIncludes, + stringMatch, + stringMatchAll, + stringNormalize, + stringPadEnd, + stringPadStart, + stringRepeat, + stringReplace, + stringReplaceAll, + stringSearch, + stringSlice, + stringSplit, + stringStartsWith, + stringValue, stripAndCollapseASCIIWhitespace, stripLeadingAndTrailingASCIIWhitespace, + substring, toString, } from "./string.js"; describe("Matcher", () => { + it("[[Call]] throws an error", () => { + assertThrows(() => Matcher("")); + }); + it("[[Construct]] accepts a string first argument", () => { assert(new Matcher("")); }); @@ -66,6 +95,18 @@ describe("Matcher", () => { assertThrows(() => new Matcher("", undefined, "failure")); }); + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(Matcher.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(Matcher.name, "Matcher"); + }); + }); + describe("::dotAll", () => { it("[[Get]] returns true when the dotAll flag is present", () => { assertStrictEquals(new Matcher(/(?:)/su).dotAll, true); @@ -74,6 +115,30 @@ describe("Matcher", () => { it("[[Get]] returns false when the dotAll flag is not present", () => { assertStrictEquals(new Matcher(/(?:)/u).dotAll, false); }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "dotAll", + ).get.length, + 0, + ); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "dotAll", + ).get.name, + "get dotAll", + ); + }); + }); }); describe("::exec", () => { @@ -125,6 +190,18 @@ describe("Matcher", () => { null, ); }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(Matcher.prototype.exec.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(Matcher.prototype.exec.name, "exec"); + }); + }); }); describe("::global", () => { @@ -135,6 +212,30 @@ describe("Matcher", () => { it("[[Get]] returns false when the global flag is not present", () => { assertStrictEquals(new Matcher(/(?:)/u).global, false); }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "global", + ).get.length, + 0, + ); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "global", + ).get.name, + "get global", + ); + }); + }); }); describe("::hasIndices", () => { @@ -145,6 +246,30 @@ describe("Matcher", () => { it("[[Get]] returns false when the hasIndices flag is not present", () => { assertStrictEquals(new Matcher(/(?:)/u).hasIndices, false); }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "hasIndices", + ).get.length, + 0, + ); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "hasIndices", + ).get.name, + "get hasIndices", + ); + }); + }); }); describe("::ignoreCase", () => { @@ -155,6 +280,30 @@ describe("Matcher", () => { it("[[Get]] returns false when the ignoreCase flag is not present", () => { assertStrictEquals(new Matcher(/(?:)/u).ignoreCase, false); }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "ignoreCase", + ).get.length, + 0, + ); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "ignoreCase", + ).get.name, + "get ignoreCase", + ); + }); + }); }); describe("::multiline", () => { @@ -165,6 +314,30 @@ describe("Matcher", () => { it("[[Get]] returns false when the multiline flag is not present", () => { assertStrictEquals(new Matcher(/(?:)/u).multiline, false); }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "multiline", + ).get.length, + 0, + ); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "multiline", + ).get.name, + "get multiline", + ); + }); + }); }); describe("::source", () => { @@ -172,6 +345,30 @@ describe("Matcher", () => { assertStrictEquals(new Matcher("").source, "(?:)"); assertStrictEquals(new Matcher(/.*/su).source, ".*"); }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "source", + ).get.length, + 0, + ); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "source", + ).get.name, + "get source", + ); + }); + }); }); describe("::sticky", () => { @@ -182,11 +379,35 @@ describe("Matcher", () => { it("[[Get]] returns false when the sticky flag is not present", () => { assertStrictEquals(new Matcher(/(?:)/u).sticky, false); }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "sticky", + ).get.length, + 0, + ); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "sticky", + ).get.name, + "get sticky", + ); + }); + }); }); describe("::toString", () => { - it("[[Get]] does not throw an error", () => { - new Matcher(/(?:)/u).toString(); + it("[[Call]] returns the string source", () => { + assertStrictEquals(new Matcher(/(?:)/u).toString(), "/(?:)/u"); }); }); @@ -194,6 +415,30 @@ describe("Matcher", () => { it("[[Get]] returns true when the unicode flag is present", () => { assertStrictEquals(new Matcher(/(?:)/u).unicode, true); }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "unicode", + ).get.length, + 0, + ); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + Object.getOwnPropertyDescriptor( + Matcher.prototype, + "unicode", + ).get.name, + "get unicode", + ); + }); + }); }); describe("~", () => { @@ -274,12 +519,96 @@ describe("asciiLowercase", () => { it("[[Call]] lowercases (just) A·S·C·I·I letters", () => { assertStrictEquals(asciiLowercase("aBſÆss FtɁɂß"), "abſÆss ftɁɂß"); }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new asciiLowercase("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(asciiLowercase.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(asciiLowercase.name, "asciiLowercase"); + }); + }); }); describe("asciiUppercase", () => { it("[[Call]] uppercases (just) A·S·C·I·I letters", () => { assertStrictEquals(asciiUppercase("aBſÆss FtɁɂß"), "ABſÆSS FTɁɂß"); }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new asciiUppercase("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(asciiUppercase.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(asciiUppercase.name, "asciiUppercase"); + }); + }); +}); + +describe("characters", () => { + it("[[Call]] returns an iterable", () => { + assertStrictEquals( + typeof characters("")[Symbol.iterator], + "function", + ); + }); + + it("[[Call]] returns an iterator", () => { + assertStrictEquals(typeof characters("").next, "function"); + }); + + it("[[Call]] returns a string character iterator", () => { + assertStrictEquals( + characters("")[Symbol.toStringTag], + "String Character Iterator", + ); + }); + + it("[[Call]] iterates over the characters", () => { + assertEquals([ + ...characters("Ii🎙\uDFFF\uDD96\uD83C\uD800🆗☺"), + ], [ + "I", + "i", + "🎙", + "\uDFFF", + "\uDD96", + "\uD83C", + "\uD800", + "🆗", + "☺", + ]); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new characters("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(characters.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(characters.name, "characters"); + }); + }); }); describe("codeUnits", () => { @@ -318,6 +647,22 @@ describe("codeUnits", () => { 0x263A, ]); }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new codeUnits("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(codeUnits.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(codeUnits.name, "codeUnits"); + }); + }); }); describe("codepoints", () => { @@ -354,6 +699,22 @@ describe("codepoints", () => { 0x263A, ]); }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new codepoints("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(codepoints.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(codepoints.name, "codepoints"); + }); + }); }); describe("getCharacter", () => { @@ -366,8 +727,155 @@ describe("getCharacter", () => { }); it("[[Call]] returns undefined for an out‐of‐bounds index", () => { - assertStrictEquals(getCharacter("Ii🎙🆗☺", -1), void {}); - assertStrictEquals(getCharacter("Ii🎙🆗☺", 7), void {}); + assertStrictEquals(getCharacter("Ii🎙🆗☺", -1), undefined); + assertStrictEquals(getCharacter("Ii🎙🆗☺", 7), undefined); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new getCharacter("a", 0)); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(getCharacter.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(getCharacter.name, "getCharacter"); + }); + }); +}); + +describe("getCodeUnit", () => { + it("[[Call]] returns the code unit at the provided position", () => { + assertStrictEquals(getCodeUnit("Ii🎙🆗☺", 4), 0xD83C); + }); + + it("[[Call]] returns a low surrogate if the provided position splits a character", () => { + assertStrictEquals(getCodeUnit("Ii🎙🆗☺", 5), 0xDD97); + }); + + it("[[Call]] returns undefined for an out‐of‐bounds index", () => { + assertStrictEquals(getCodeUnit("Ii🎙🆗☺", -1), undefined); + assertStrictEquals(getCodeUnit("Ii🎙🆗☺", 7), undefined); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new getCodeUnit("a", 0)); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(getCodeUnit.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(getCodeUnit.name, "getCodeUnit"); + }); + }); +}); + +describe("getCodepoint", () => { + it("[[Call]] returns the character at the provided position", () => { + assertStrictEquals(getCodepoint("Ii🎙🆗☺", 4), 0x1F197); + }); + + it("[[Call]] returns a low surrogate if the provided position splits a character", () => { + assertStrictEquals(getCodepoint("Ii🎙🆗☺", 5), 0xDD97); + }); + + it("[[Call]] returns undefined for an out‐of‐bounds index", () => { + assertStrictEquals(getCodepoint("Ii🎙🆗☺", -1), undefined); + assertStrictEquals(getCodepoint("Ii🎙🆗☺", 7), undefined); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new getCodepoint("a", 0)); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(getCodepoint.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(getCodepoint.name, "getCodepoint"); + }); + }); +}); + +describe("getFirstSubstringIndex", () => { + it("[[Call]] returns the index of the first match", () => { + assertStrictEquals(getFirstSubstringIndex("Ii🎙🆗☺🆗", "🆗"), 4); + }); + + it("[[Call]] returns −1 if no match is found", () => { + assertStrictEquals(getFirstSubstringIndex("Ii🎙🆗☺🆗", "🆗🆖"), -1); + }); + + it("[[Call]] returns 0 when provided with an empty string", () => { + assertStrictEquals(getFirstSubstringIndex("Ii🎙🆗☺🆗", ""), 0); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new getFirstSubstringIndex("", "")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(getFirstSubstringIndex.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + getFirstSubstringIndex.name, + "getFirstSubstringIndex", + ); + }); + }); +}); + +describe("getLastSubstringIndex", () => { + it("[[Call]] returns the index of the first match", () => { + assertStrictEquals(getLastSubstringIndex("Ii🎙🆗☺🆗", "🆗"), 7); + }); + + it("[[Call]] returns −1 if no match is found", () => { + assertStrictEquals(getLastSubstringIndex("Ii🎙🆗☺🆗", "🆖🆗"), -1); + }); + + it("[[Call]] returns the length when provided with an empty string", () => { + assertStrictEquals( + getLastSubstringIndex("Ii🎙🆗☺🆗", ""), + "Ii🎙🆗☺🆗".length, + ); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new getLastSubstringIndex("", "")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(getLastSubstringIndex.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + getLastSubstringIndex.name, + "getLastSubstringIndex", + ); + }); }); }); @@ -386,6 +894,44 @@ describe("join", () => { "☂☂☂", ); }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new join([])); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(join.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(join.name, "join"); + }); + }); +}); + +describe("rawString", () => { + it("[[Call]] acts like String.raw", () => { + assertStrictEquals(rawString`\nraw${" string"}`, "\\nraw string"); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new rawString(["string"])); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(rawString.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(rawString.name, "rawString"); + }); + }); }); describe("scalarValueString", () => { @@ -395,6 +941,22 @@ describe("scalarValueString", () => { "Ii🎙\uFFFD\uFFFD\uFFFD\uFFFD🆗☺", ); }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new scalarValueString("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(scalarValueString.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(scalarValueString.name, "scalarValueString"); + }); + }); }); describe("scalarValues", () => { @@ -431,6 +993,22 @@ describe("scalarValues", () => { 0x263A, ]); }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new scalarValues("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(scalarValues.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(scalarValues.name, "scalarValues"); + }); + }); }); describe("splitOnASCIIWhitespace", () => { @@ -499,6 +1077,25 @@ describe("splitOnASCIIWhitespace", () => { ["🅰️", "🅱️", "🆎", "🅾️"], ); }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new splitOnASCIIWhitespace("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(splitOnASCIIWhitespace.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + splitOnASCIIWhitespace.name, + "splitOnASCIIWhitespace", + ); + }); + }); }); describe("splitOnCommas", () => { @@ -544,20 +1141,655 @@ describe("splitOnCommas", () => { ["", "", "", ""], ); }); -}); -describe("stripAndCollapseASCIIWhitespace", () => { - it("[[Call]] collapses mixed inner whitespace", () => { - assertEquals( - stripAndCollapseASCIIWhitespace("🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️"), - "🅰️ 🅱️ 🆎 🅾️", - ); + it("[[Construct]] throws an error", () => { + assertThrows(() => new splitOnCommas("")); }); - it("[[Call]] trims leading and trailing whitespace", () => { - assertStrictEquals( - stripAndCollapseASCIIWhitespace( - "\f\r\n\r\n \n\t\f 🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️\n\f", + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(splitOnCommas.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(splitOnCommas.name, "splitOnCommas"); + }); + }); +}); + +describe("stringCatenate", () => { + it("[[Call]] catenates the values", () => { + assertStrictEquals(stringCatenate("the", " values"), "the values"); + }); + + it("[[Call]] returns an empty string when called with no values", () => { + assertStrictEquals(stringCatenate(), ""); + }); + + it('[[Call]] uses "undefined" when explicitly provided undefined', () => { + assertStrictEquals( + stringCatenate(undefined, undefined), + "undefinedundefined", + ); + }); + + it('[[Call]] uses "null" when provided null', () => { + assertStrictEquals(stringCatenate(null, null), "nullnull"); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringCatenate()); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringCatenate.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringCatenate.name, "stringCatenate"); + }); + }); +}); + +describe("stringEndsWith", () => { + it("[[Call]] returns whether the string ends with the thing", () => { + assertStrictEquals( + stringEndsWith("very success", " success"), + true, + ); + assertStrictEquals(stringEndsWith("very fail", " success"), false); + }); + + it("[[Call]] accepts an offset", () => { + assertStrictEquals( + stringEndsWith("very successful", " success", 12), + true, + ); + }); + + it("[[Call]] returns true for an empty string test", () => { + assertStrictEquals(stringEndsWith("", ""), true); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringEndsWith("", "")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringEndsWith.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringEndsWith.name, "stringEndsWith"); + }); + }); +}); + +describe("stringFromCodeUnits", () => { + it("[[Call]] makes the string", () => { + assertStrictEquals( + stringFromCodeUnits(0xD83C, 0xDD97), + "🆗", + ); + }); + + it("[[Call]] throws with non‐integral arguments", () => { + assertThrows(() => stringFromCodeUnits(NaN)); + assertThrows(() => stringFromCodeUnits(Infinity)); + assertThrows(() => stringFromCodeUnits(0.1)); + }); + + it("[[Call]] throws with arguments out of range", () => { + assertThrows(() => stringFromCodeUnits(-1)); + assertThrows(() => stringFromCodeUnits(0x10000)); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringFromCodeUnits([])); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringFromCodeUnits.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + stringFromCodeUnits.name, + "stringFromCodeUnits", + ); + }); + }); +}); + +describe("stringFromCodepoints", () => { + it("[[Call]] makes the string", () => { + assertStrictEquals(stringFromCodepoints(0x1F197), "🆗"); + }); + + it("[[Call]] throws with non‐integral arguments", () => { + assertThrows(() => stringFromCodepoints(NaN)); + assertThrows(() => stringFromCodepoints(Infinity)); + assertThrows(() => stringFromCodepoints(0.1)); + }); + + it("[[Call]] throws with arguments out of range", () => { + assertThrows(() => stringFromCodepoints(-1)); + assertThrows(() => stringFromCodepoints(0x110000)); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringFromCodepoints([])); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringFromCodepoints.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + stringFromCodepoints.name, + "stringFromCodepoints", + ); + }); + }); +}); + +describe("stringIncludes", () => { + it("[[Call]] returns whether the string includes the thing", () => { + assertStrictEquals( + stringIncludes("very success full", " success "), + true, + ); + assertStrictEquals( + stringIncludes("very fail full", " success "), + false, + ); + }); + + it("[[Call]] accepts an offset", () => { + assertStrictEquals( + stringIncludes("maybe success full", " success ", 4), + true, + ); + assertStrictEquals( + stringIncludes("maybe success full", " success ", 5), + true, + ); + assertStrictEquals( + stringIncludes("maybe success full", " success ", 6), + false, + ); + }); + + it("[[Call]] returns true for an empty string test", () => { + assertStrictEquals(stringIncludes("", ""), true); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringIncludes("", "")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringIncludes.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringIncludes.name, "stringIncludes"); + }); + }); +}); + +describe("stringMatch", () => { + it("[[Call]] does the match akin to String::match", () => { + assertEquals( + [...stringMatch("very success full", /([sc]+[ue]?)+/)], + ["success", "ss"], + ); + assertEquals( + [...stringMatch("very success full", /([sc]+)[ue]?/g)], + ["su", "cce", "ss"], + ); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringMatch("", /(?:)/)); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringMatch.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringMatch.name, "stringMatch"); + }); + }); +}); + +describe("stringMatchAll", () => { + it("[[Call]] does the match akin to String::matchAll", () => { + assertEquals( + [...stringMatchAll("very success full", /([sc]+)[ue]?/g)].map(( + match, + ) => [...match]), + [["su", "s"], ["cce", "cc"], ["ss", "ss"]], + ); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringMatchAll("", /(?:)/g)); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringMatchAll.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringMatchAll.name, "stringMatchAll"); + }); + }); +}); + +describe("stringNormalize", () => { + it("[[Call]] normalizes the string properly", () => { + assertStrictEquals(stringNormalize("ẛ", "NFC"), "\u1E9B"); + assertStrictEquals(stringNormalize("ẛ", "NFD"), "\u017F\u0307"); + assertStrictEquals(stringNormalize("ẛ", "NFKC"), "\u1E61"); + assertStrictEquals(stringNormalize("ẛ", "NFKD"), "\u0073\u0307"); + }); + + it("[[Call]] assumes NFC", () => { + assertStrictEquals(stringNormalize("\u017F\u0307"), "\u1E9B"); + }); + + it("[[Call]] throws with an invalid form", () => { + assertThrows(() => stringNormalize("", "NFB")); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringNormalize("", "NFC")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringNormalize.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringNormalize.name, "stringNormalize"); + }); + }); +}); + +describe("stringPadEnd", () => { + it("[[Call]] pads the end of the string", () => { + assertStrictEquals(stringPadEnd("xx", 3), "xx "); + assertStrictEquals(stringPadEnd("xx", 3, "o"), "xxo"); + assertStrictEquals(stringPadEnd("", 3, "xo"), "xox"); + assertStrictEquals(stringPadEnd("xx", 3, ""), "xx"); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringPadEnd("", 1)); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringPadEnd.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringPadEnd.name, "stringPadEnd"); + }); + }); +}); + +describe("stringPadStart", () => { + it("[[Call]] pads the start of the string", () => { + assertStrictEquals(stringPadStart("xx", 3), " xx"); + assertStrictEquals(stringPadStart("xx", 3, "o"), "oxx"); + assertStrictEquals(stringPadStart("", 3, "xo"), "xox"); + assertStrictEquals(stringPadStart("xx", 3, ""), "xx"); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringPadStart("", 1)); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringPadStart.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringPadStart.name, "stringPadStart"); + }); + }); +}); + +describe("stringRepeat", () => { + it("[[Call]] repeats the string", () => { + assertStrictEquals(stringRepeat("xx", 3), "xxxxxx"); + assertStrictEquals(stringRepeat("", 3), ""); + assertStrictEquals(stringRepeat("xx", 0), ""); + }); + + it("[[Call]] throws for negative repititions", () => { + assertThrows(() => stringRepeat("", -1)); + }); + + it("[[Call]] throws for infinite repititions", () => { + assertThrows(() => stringRepeat("", Infinity)); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringRepeat("", 1)); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringRepeat.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringRepeat.name, "stringRepeat"); + }); + }); +}); + +describe("stringReplace", () => { + it("[[Call]] does the replacement akin to String::replace", () => { + assertStrictEquals( + stringReplace("it’s a failure", "failure", "success"), + "it’s a success", + ); + assertStrictEquals( + stringReplace( + "very success full", + /([sc]+)[ue]?/, + ($) => $.length, + ), + "very 2ccess full", + ); + assertStrictEquals( + stringReplace( + "very success full", + /([sc]+)[ue]?/g, + (...$s) => + `${$s[0].length}`.repeat($s[1].length) + + $s[0].substring($s[1].length), + ), + "very 2u33e22 full", + ); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringReplace("", /(?:)/, "")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringReplace.length, 3); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringReplace.name, "stringReplace"); + }); + }); +}); + +describe("stringReplaceAll", () => { + it("[[Call]] does the match akin to String::replaceAll", () => { + assertStrictEquals( + stringReplaceAll("it’s a failure failure", "failure", "success"), + "it’s a success success", + ); + assertStrictEquals( + stringReplaceAll( + "very success full", + /([sc]+)[ue]?/g, + (...$s) => + `${$s[0].length}`.repeat($s[1].length) + + $s[0].substring($s[1].length), + ), + "very 2u33e22 full", + ); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringReplaceAll("", /(?:)/g)); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringReplaceAll.length, 3); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringReplaceAll.name, "stringReplaceAll"); + }); + }); +}); + +describe("stringSearch", () => { + it("[[Call]] does the search akin to String::search", () => { + assertStrictEquals( + stringSearch("very success full", /([sc]+)[ue]?/), + 5, + ); + assertStrictEquals( + stringSearch("very fail full", /([sc]+)[ue]?/), + -1, + ); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringSearch("", /(?:)/)); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringSearch.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringSearch.name, "stringSearch"); + }); + }); +}); + +describe("stringSlice", () => { + it("[[Call]] slices the string akin to String::search", () => { + assertStrictEquals( + stringSlice("very success full", 5, 12), + "success", + ); + assertStrictEquals( + stringSlice("very success full", -12, -5), + "success", + ); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringSlice("", 0, 0)); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringSlice.length, 3); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringSlice.name, "stringSlice"); + }); + }); +}); + +describe("stringSplit", () => { + it("[[Call]] splits the string akin to String::split", () => { + assertEquals(stringSplit("success", ""), [ + "s", + "u", + "c", + "c", + "e", + "s", + "s", + ]); + assertEquals(stringSplit("success", /(?<=[aeiou])(?=[^aeiou])/), [ + "su", + "cce", + "ss", + ]); + assertEquals(stringSplit("success", "failure"), ["success"]); + }); + + it("[[Call]] recognizes a limit", () => { + assertEquals(stringSplit("success", "", 4), ["s", "u", "c", "c"]); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringSplit("", "")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringSplit.length, 3); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringSplit.name, "stringSplit"); + }); + }); +}); + +describe("stringStartsWith", () => { + it("[[Call]] returns whether the string starts with the thing", () => { + assertStrictEquals( + stringStartsWith("success is had", "success "), + true, + ); + assertStrictEquals( + stringStartsWith("no success is had", "success "), + false, + ); + }); + + it("[[Call]] accepts an offset", () => { + assertStrictEquals( + stringStartsWith("much success is had", "success ", 5), + true, + ); + }); + + it("[[Call]] returns true for an empty string test", () => { + assertStrictEquals(stringEndsWith("", ""), true); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringStartsWith("", "")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringStartsWith.length, 2); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringStartsWith.name, "stringStartsWith"); + }); + }); +}); + +describe("stringStartsWith", () => { + it("[[Call]] returns the string value of a string literal", () => { + assertStrictEquals(stringValue("success"), "success"); + }); + + it("[[Call]] returns the string value of a string object", () => { + const string = new String("success"); + Object.defineProperties(string, { + toString: { value: () => "failure" }, + valueOf: { value: () => "failure" }, + }); + assertStrictEquals(stringValue(string), "success"); + }); + + it("[[Call]] throws for non‐strings", () => { + assertThrows(() => stringValue(Object.create(String.prototype))); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stringValue("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringValue.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(stringValue.name, "stringValue"); + }); + }); +}); + +describe("stripAndCollapseASCIIWhitespace", () => { + it("[[Call]] collapses mixed inner whitespace", () => { + assertEquals( + stripAndCollapseASCIIWhitespace("🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️"), + "🅰️ 🅱️ 🆎 🅾️", + ); + }); + + it("[[Call]] trims leading and trailing whitespace", () => { + assertStrictEquals( + stripAndCollapseASCIIWhitespace( + "\f\r\n\r\n \n\t\f 🅰️\f \t\n🅱️\r\n\r🆎\n\f🅾️\n\f", ), "🅰️ 🅱️ 🆎 🅾️", ); @@ -576,6 +1808,25 @@ describe("stripAndCollapseASCIIWhitespace", () => { "a\u202F\u205F\xa0\v\0\bb", ); }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stripAndCollapseASCIIWhitespace("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stripAndCollapseASCIIWhitespace.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + stripAndCollapseASCIIWhitespace.name, + "stripAndCollapseASCIIWhitespace", + ); + }); + }); }); describe("stripLeadingAndTrailingASCIIWhitespace", () => { @@ -610,6 +1861,57 @@ describe("stripLeadingAndTrailingASCIIWhitespace", () => { "a b", ); }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new stripLeadingAndTrailingASCIIWhitespace("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals( + stripLeadingAndTrailingASCIIWhitespace.length, + 1, + ); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + stripLeadingAndTrailingASCIIWhitespace.name, + "stripLeadingAndTrailingASCIIWhitespace", + ); + }); + }); +}); + +describe("substring", () => { + it("[[Call]] returns the substring", () => { + assertStrictEquals( + substring("success", 0), + "success", + ); + assertStrictEquals( + substring("very success full", 5, 12), + "success", + ); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new substring("", 0)); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(substring.length, 3); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(substring.name, "substring"); + }); + }); }); describe("toString", () => { @@ -627,4 +1929,20 @@ describe("toString", () => { it("[[Call]] throws when provided a symbol", () => { assertThrows(() => toString(Symbol())); }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new toString("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(toString.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals(toString.name, "toString"); + }); + }); });