ES6StringAPIs.js 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. // ES6 String API extensions tests -- verifies the API shape and basic functionality
  6. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  7. function verifyThrowsIfRegExpSearchString(functionName) {
  8. var f = String.prototype[functionName].bind("abc");
  9. var re = /./;
  10. assert.throws(f.bind(undefined, re), TypeError, "Regular RegExp");
  11. helpers.withPropertyDeleted(RegExp.prototype, Symbol.match, function () {
  12. assert.throws(f.bind(undefined, re), TypeError, "Regular RegExp without Symbol.match property");
  13. })
  14. re = 1;
  15. assert.doesNotThrow(f.bind(undefined, re), "Not an Object (Number)");
  16. re = {};
  17. assert.doesNotThrow(f.bind(undefined, re), "Object without Symbol.match property");
  18. re = {
  19. [Symbol.match]: true
  20. };
  21. assert.throws(f.bind(undefined, re), TypeError, "Object with Boolean Symbol.match property");
  22. re = {
  23. [Symbol.match]: 'coerced to true'
  24. };
  25. assert.throws(f.bind(undefined, re), TypeError, "Object with Symbol.match property coerced to 'true'");
  26. re = {
  27. [Symbol.match]: null
  28. };
  29. assert.doesNotThrow(f.bind(undefined, re), "Object with Symbol.match property coerced to 'false'");
  30. }
  31. var tests = [
  32. {
  33. name: "Array prototype and String prototype should have spec defined built-ins with correct lengths",
  34. body: function () {
  35. assert.isTrue(Array.prototype.hasOwnProperty('find'), "Array.prototype should have a find method");
  36. assert.isTrue(Array.prototype.hasOwnProperty('findIndex'), "Array.prototype should have a findIndex method");
  37. assert.isTrue(String.prototype.hasOwnProperty('repeat'), "String.prototype should have a repeat method");
  38. assert.isTrue(String.prototype.hasOwnProperty('startsWith'), "String.prototype should have a startsWith method");
  39. assert.isTrue(String.prototype.hasOwnProperty('endsWith'), "String.prototype should have a endsWith method");
  40. assert.isTrue(String.prototype.hasOwnProperty('includes'), "String.prototype should have a includes method");
  41. assert.isTrue(Array.prototype.find.length === 1, "find method takes two arguments but the second is optional and the spec says the length must be 1");
  42. assert.isTrue(Array.prototype.findIndex.length === 1, "findIndex method takes two arguments but the second is optional and the spec says the length must be 1");
  43. assert.isTrue(String.prototype.repeat.length === 1, "repeat method takes zero arguments");
  44. assert.isTrue(String.prototype.startsWith.length === 1, "startsWith method takes two arguments but the second is optional and the spec says the length must be 1");
  45. assert.isTrue(String.prototype.endsWith.length === 1, "endsWith method takes two arguments but the second is optional and the spec says the length must be 1");
  46. assert.isTrue(String.prototype.includes.length === 1, "includes method takes two arguments but the second is optional and the spec says the length must be 1");
  47. }
  48. },
  49. {
  50. name: "find takes a predicate and applies it to each element in the array in ascending order until the predicate returns true, then find returns that element",
  51. body: function () {
  52. assert.throws(function () { Array.prototype.find.call(); }, TypeError, "find throws TypeError if it is given no arguments", "Array.prototype.find: 'this' is null or undefined");
  53. assert.throws(function () { Array.prototype.find.call(undefined); }, TypeError, "find throws TypeError if its this argument is undefined", "Array.prototype.find: 'this' is null or undefined");
  54. assert.throws(function () { Array.prototype.find.call(undefined, function () { return true; }, {}); }, TypeError, "find throws TypeError if its this argument is undefined even if given further arguments", "Array.prototype.find: 'this' is null or undefined");
  55. assert.throws(function () { Array.prototype.find.call(null); }, TypeError, "find throws TypeError if its this argument is null", "Array.prototype.find: 'this' is null or undefined");
  56. assert.throws(function () { Array.prototype.find.call(null, function () { return true; }, {}); }, TypeError, "find throws TypeError if its this argument is null even if given further arguments", "Array.prototype.find: 'this' is null or undefined");
  57. var arr = [ 1, 2, 3 ];
  58. // Test missing/invalid predicate argument
  59. assert.throws(function () { arr.find(); }, TypeError, "find throws TypeError if it is given no predicate argument", "Array.prototype.find: argument is not a Function object");
  60. assert.throws(function () { arr.find({}); }, TypeError, "find throws TypeError if it is given a non-Function object predicate argument", "Array.prototype.find: argument is not a Function object");
  61. var fakeArrWithLengthGetter = {
  62. __proto__: Array.prototype,
  63. get length () { throw new Error('getter called'); }
  64. };
  65. assert.throws(function () { fakeArrWithLengthGetter.find(); }, Error, "find gets length property, calling getter method, before checking for a valid predicate function argument", "getter called");
  66. // Test simple predicates matching each element
  67. assert.areEqual( 1, arr.find(function (v, i, a) { return true; }), "Simple predicate always returns true, should find first item");
  68. assert.areEqual( 1, arr.find(function (v, i, a) { if (i >= 1) { assert.fail("shouldn't visit index > 0"); } return v === 1; }), "Simple predicate matching first element, should find first element");
  69. assert.areEqual( 2, arr.find(function (v, i, a) { if (i >= 2) { assert.fail("shouldn't visit index > 1"); } return v === 2; }), "Simple predicate matching second element, should find second element");
  70. assert.areEqual( 3, arr.find(function (v, i, a) { if (i >= 3) { assert.fail("shouldn't visit index > 2"); } return v === 3; }), "Simple predicate matching third element, should find third element");
  71. assert.areEqual(undefined, arr.find(function (v, i, a) { if (i >= 3) { assert.fail("shouldn't visit index > 2"); } return v === 4; }), "Simple predicate matching non-existent element, should not find any element");
  72. assert.areEqual( 2, arr.find(function (v, i, a) { if (i >= 2) { assert.fail("shouldn't visit index > 1"); } return v === 2 || v === 3; }), "Simple predicate matching two elements, second and third, should find first of them, i.e. the second element");
  73. // Test adding elements in predicate function
  74. assert.areEqual(undefined, arr.find(function (v, i, a) { if (i >= 3) { assert.fail("shouldn't visit index > 2 (initial array length"); } a.push(v + 3); return false; }), "Elements added after find has started shouldn't be included in the search");
  75. assert.areEqual([ 1, 2, 3, 4, 5, 6 ], arr, "Three more elements should be added to end of arr");
  76. // Test deleting elements in predicate function that have not been visited
  77. function f(v, i, a) {
  78. if (i % 2 === 1) {
  79. assert.areEqual(undefined, v, "odd indices should be undefined since they were deleted");
  80. } else {
  81. delete a[i+1];
  82. }
  83. return false;
  84. };
  85. assert.areEqual(undefined, arr.find(f), "Elements deleted before being visited after find has started shouldn't be included in the search");
  86. assert.areEqual( 1, arr[0], "Odd elements should be deleted [0]");
  87. assert.areEqual(undefined, arr[1], "Odd elements should be deleted [1]");
  88. assert.areEqual( 3, arr[2], "Odd elements should be deleted [2]");
  89. assert.areEqual(undefined, arr[3], "Odd elements should be deleted [3]");
  90. assert.areEqual( 5, arr[4], "Odd elements should be deleted [4]");
  91. assert.areEqual(undefined, arr[5], "Odd elements should be deleted [5]");
  92. // Test deleting elements in predicate function that have already been visited
  93. assert.areEqual(undefined, arr.find(function (v, i, a) { delete a[i]; return false; }), "Elements deleted after being visited has no effect on the search");
  94. assert.areEqual(undefined, arr[0], "All elements should be deleted [0]");
  95. assert.areEqual(undefined, arr[1], "All elements should be deleted [1]");
  96. assert.areEqual(undefined, arr[2], "All elements should be deleted [2]");
  97. assert.areEqual(undefined, arr[3], "All elements should be deleted [3]");
  98. assert.areEqual(undefined, arr[4], "All elements should be deleted [4]");
  99. assert.areEqual(undefined, arr[5], "All elements should be deleted [5]");
  100. assert.areEqual(6, arr.length, "arr length is still 6");
  101. // Test thisArg argument
  102. var thisArg = { };
  103. assert.areEqual(undefined, arr.find(function (v, i, a) { if (this !== thisArg) { assert.fail("this is not what was passed to second parameter of find()"); } return false; }, thisArg), "Argument passed as the optional second parameter of find should become the this value in the predicate");
  104. // Test and ensure Array.prototype.find calls ToLength
  105. // checks lower bound (negative -> zero)
  106. // upper bound ( pow(2,53)-1 ) cannot be tested in reasonable time
  107. var arr = { '0': 1, '1': 2, '2': 3, length: -1 };
  108. assert.areEqual(undefined, Array.prototype.find.call(arr, function (v, i, a) {assert.fail("shouldn't visit any element when length is less than zero"); return true;}), "find should use ToLength function that clamps length between 0 and pow(2,53)-1");
  109. }
  110. },
  111. {
  112. name: "findIndex takes a predicate and applies it to each element in the array in ascending order until the predicate returns true, then findIndex returns the index of that element",
  113. body: function () {
  114. assert.throws(function () { Array.prototype.findIndex.call(); }, TypeError, "findIndex throws TypeError if it is given no arguments", "Array.prototype.findIndex: 'this' is null or undefined");
  115. assert.throws(function () { Array.prototype.findIndex.call(undefined); }, TypeError, "findIndex throws TypeError if its this argument is undefined", "Array.prototype.findIndex: 'this' is null or undefined");
  116. assert.throws(function () { Array.prototype.findIndex.call(undefined, function () { return true; }, {}); }, TypeError, "findIndex throws TypeError if its this argument is undefined even if given further arguments", "Array.prototype.findIndex: 'this' is null or undefined");
  117. assert.throws(function () { Array.prototype.findIndex.call(null); }, TypeError, "findIndex throws TypeError if its this argument is null", "Array.prototype.findIndex: 'this' is null or undefined");
  118. assert.throws(function () { Array.prototype.findIndex.call(null, function () { return true; }, {}); }, TypeError, "findIndex throws TypeError if its this argument is null even if given further arguments", "Array.prototype.findIndex: 'this' is null or undefined");
  119. var arr = [ 1, 2, 3 ];
  120. // Test missing/invalid predicate argument
  121. assert.throws(function () { arr.findIndex(); }, TypeError, "findIndex throws TypeError if it is given no predicate argument", "Array.prototype.findIndex: argument is not a Function object");
  122. assert.throws(function () { arr.findIndex({}); }, TypeError, "findIndex throws TypeError if it is given a non-Function object predicate argument", "Array.prototype.findIndex: argument is not a Function object");
  123. var fakeArrWithLengthGetter = {
  124. __proto__: Array.prototype,
  125. get length () { throw new Error('getter called'); }
  126. };
  127. assert.throws(function () { fakeArrWithLengthGetter.findIndex(); }, Error, "findIndex gets length property, calling getter method, before checking for a valid predicate function argument", "getter called");
  128. // Test simple predicates matching each element
  129. assert.areEqual( 0, arr.findIndex(function (v, i, a) { return true; }), "Simple predicate always returns true, should find first item");
  130. assert.areEqual( 0, arr.findIndex(function (v, i, a) { if (i >= 1) { assert.fail("shouldn't visit index > 0"); } return v === 1; }), "Simple predicate matching first element, should find first element");
  131. assert.areEqual( 1, arr.findIndex(function (v, i, a) { if (i >= 2) { assert.fail("shouldn't visit index > 1"); } return v === 2; }), "Simple predicate matching second element, should find second element");
  132. assert.areEqual( 2, arr.findIndex(function (v, i, a) { if (i >= 3) { assert.fail("shouldn't visit index > 2"); } return v === 3; }), "Simple predicate matching third element, should find third element");
  133. assert.areEqual(-1, arr.findIndex(function (v, i, a) { if (i >= 3) { assert.fail("shouldn't visit index > 2"); } return v === 4; }), "Simple predicate matching non-existent element, should not find any element");
  134. assert.areEqual( 1, arr.findIndex(function (v, i, a) { if (i >= 2) { assert.fail("shouldn't visit index > 1"); } return v === 2 || v === 3; }), "Simple predicate matching two elements, second and third, should find first of them, i.e. the second element");
  135. // Test adding elements in predicate function
  136. assert.areEqual(-1, arr.findIndex(function (v, i, a) { if (i >= 3) { assert.fail("shouldn't visit index > 2 (initial array length"); } a.push(v + 3); return false; }), "Elements added after findIndex has started shouldn't be included in the search");
  137. assert.areEqual([ 1, 2, 3, 4, 5, 6 ], arr, "Three more elements should be added to end of arr");
  138. // Test deleting elements in predicate function that have not been visited
  139. function f(v, i, a) {
  140. if (i % 2 === 1) {
  141. assert.areEqual(undefined, v, "odd indices should be undefined since they were deleted");
  142. } else {
  143. delete a[i+1];
  144. }
  145. return false;
  146. }
  147. assert.areEqual( -1, arr.findIndex(f), "Elements deleted before being visited after findIndex has started shouldn't be included in the search");
  148. assert.areEqual( 1, arr[0], "Odd elements should be deleted [0]");
  149. assert.areEqual(undefined, arr[1], "Odd elements should be deleted [1]");
  150. assert.areEqual( 3, arr[2], "Odd elements should be deleted [2]");
  151. assert.areEqual(undefined, arr[3], "Odd elements should be deleted [3]");
  152. assert.areEqual( 5, arr[4], "Odd elements should be deleted [4]");
  153. assert.areEqual(undefined, arr[5], "Odd elements should be deleted [5]");
  154. // Test deleting elements in predicate function that have already been visited
  155. assert.areEqual( -1, arr.findIndex(function (v, i, a) { delete a[i]; return false; }), "Elements deleted after being visited has no effect on the search");
  156. assert.areEqual(undefined, arr[0], "All elements should be deleted [0]");
  157. assert.areEqual(undefined, arr[1], "All elements should be deleted [1]");
  158. assert.areEqual(undefined, arr[2], "All elements should be deleted [2]");
  159. assert.areEqual(undefined, arr[3], "All elements should be deleted [3]");
  160. assert.areEqual(undefined, arr[4], "All elements should be deleted [4]");
  161. assert.areEqual(undefined, arr[5], "All elements should be deleted [5]");
  162. assert.areEqual(6, arr.length, "arr length is still 6");
  163. // Test thisArg argument
  164. var thisArg = { };
  165. assert.areEqual(-1, arr.findIndex(function (v, i, a) { if (this !== thisArg) { assert.fail("this is not what was passed to second parameter of findIndex()"); } return false; }, thisArg), "Argument passed as the optional second parameter of findIndex should become the this value in the predicate");
  166. // Test and ensure Array.prototype.findIndex calls ToLength
  167. // checks lower bound (negative -> zero)
  168. // upper bound ( pow(2,53)-1 ) cannot be tested in reasonable time
  169. var arr = { '0': 1, '1': 2, '2': 3, length: -3 };
  170. assert.areEqual(-1, Array.prototype.findIndex.call(arr, function (v, i, a) {assert.fail("shouldn't visit any element when length is less than zero"); return true;}), "findIndex should use ToLength function that clamps length between 0 and pow(2,53)-1");
  171. }
  172. },
  173. {
  174. name: "find and findIndex do not skip 'holes' in arrays and array-likes",
  175. body: function () {
  176. var arr = [,,,,,];
  177. var count = 0;
  178. arr.find(function () { count++; return false; });
  179. assert.areEqual(arr.length, count, "find calls its callback for every element up to the array's length even if those elements are undefined");
  180. count = 0;
  181. arr.findIndex(function () { count++; return false; });
  182. assert.areEqual(arr.length, count, "findIndex calls its callback for every element up to the array's length even if those elements are undefined");
  183. arr = { length: 5, 0: undefined, 1: undefined, 3: undefined };
  184. count = 0;
  185. Array.prototype.find.call(arr, function () { count++; return false; });
  186. assert.areEqual(arr.length, count, "find calls its callback for every element up to the array-like's length even if those elements do not exist on the array-like");
  187. count = 0;
  188. Array.prototype.findIndex.call(arr, function () { count++; return false; });
  189. assert.areEqual(arr.length, count, "findIndex calls its callback for every element up to the array-like's length even if those elements do not exist on the array-like");
  190. }
  191. },
  192. {
  193. name: "repeat takes a string and number and returns a string that is the given string repeated the given number of times",
  194. body: function () {
  195. assert.throws(function () { String.prototype.repeat.call(); }, TypeError, "repeat throws TypeError if it is given no arguments", "String.prototype.repeat: 'this' is null or undefined");
  196. assert.throws(function () { String.prototype.repeat.call(undefined); }, TypeError, "repeat throws TypeError if its this argument is undefined", "String.prototype.repeat: 'this' is null or undefined");
  197. assert.throws(function () { String.prototype.repeat.call(undefined, "", 0); }, TypeError, "repeat throws TypeError if its this argument is undefined even if given further arguments", "String.prototype.repeat: 'this' is null or undefined");
  198. assert.throws(function () { String.prototype.repeat.call(null); }, TypeError, "repeat throws TypeError if its this argument is null", "String.prototype.repeat: 'this' is null or undefined");
  199. assert.throws(function () { String.prototype.repeat.call(null, "", 0); }, TypeError, "repeat throws TypeError if its this argument is null even if given further arguments", "String.prototype.repeat: 'this' is null or undefined");
  200. var s;
  201. s = "";
  202. assert.areEqual("", s.repeat(0), "empty string repeated zero times is the empty string");
  203. assert.areEqual("", s.repeat(NaN), "empty string: NaN converts to zero so produces the empty string");
  204. assert.areEqual("", s.repeat(1), "empty string repeated once is the empty string");
  205. assert.areEqual("", s.repeat(2), "empty string repeated twice is the empty string");
  206. assert.areEqual("", s.repeat(50), "empty string repeated fifty times is the empty string");
  207. s = "a";
  208. assert.areEqual("", s.repeat(0), "single character string repeated zero times is the empty string");
  209. assert.areEqual("", s.repeat(NaN), "single character string: NaN converts to zero so produces the empty string");
  210. assert.areEqual("a", s.repeat(1), "single character string repeated once is itself");
  211. assert.areEqual("aa", s.repeat(2), "single character string repeated twice is a two character string");
  212. assert.areEqual("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", s.repeat(50), "single character string repeated fifty times is a 50 character string");
  213. s = "abc";
  214. assert.areEqual("", s.repeat(0), "multi-character string repeated zero times is the empty string");
  215. assert.areEqual("", s.repeat(NaN), "multi-character string: NaN converts to zero so produces the empty string");
  216. assert.areEqual("abc", s.repeat(1), "multi-character string repeated once is itself");
  217. assert.areEqual("abcabc", s.repeat(2), "3 character string repeated twice is a six character string");
  218. assert.areEqual("abcabcabcabcabcabcabcabcabcabc", s.repeat(10), "3 character string repeated ten times is a 30 character string");
  219. assert.throws(function () { s.repeat(-1); }, RangeError, "negative repeat counts are out of range", "String.prototype.repeat: argument out of range");
  220. assert.throws(function () { s.repeat(-Infinity); }, RangeError, "negative infinite repeat count is out of range", "String.prototype.repeat: argument out of range");
  221. assert.throws(function () { s.repeat(Infinity); }, RangeError, "infinite repeat count is out of range", "String.prototype.repeat: argument out of range");
  222. assert.areEqual("\0\0\0\0", "\0".repeat(4), "null character embedded in string is repeated");
  223. assert.areEqual("a\0ba\0ba\0b", "a\0b".repeat(3), "null character embedded in string mixed with normal characters is repeated");
  224. assert.areEqual("\0abc\0\0abc\0\0abc\0", "\0abc\0".repeat(3), "null character embedded in string surrounding normal characters is repeated");
  225. }
  226. },
  227. {
  228. name: "startsWith returns true if the given search string matches the substring of the given string starting at the given position",
  229. body: function () {
  230. assert.throws(function () { String.prototype.startsWith.call(); }, TypeError, "startsWith throws TypeError if it is given no arguments", "String.prototype.startsWith: 'this' is null or undefined");
  231. assert.throws(function () { String.prototype.startsWith.call(undefined); }, TypeError, "startsWith throws TypeError if its this argument is undefined", "String.prototype.startsWith: 'this' is null or undefined");
  232. assert.throws(function () { String.prototype.startsWith.call(undefined, "", 0); }, TypeError, "startsWith throws TypeError if its this argument is undefined even if given further arguments", "String.prototype.startsWith: 'this' is null or undefined");
  233. assert.throws(function () { String.prototype.startsWith.call(null); }, TypeError, "startsWith throws TypeError if its this argument is null", "String.prototype.startsWith: 'this' is null or undefined");
  234. assert.throws(function () { String.prototype.startsWith.call(null, "", 0); }, TypeError, "startsWith throws TypeError if its this argument is null even if given further arguments", "String.prototype.startsWith: 'this' is null or undefined");
  235. var s;
  236. s = "";
  237. assert.isTrue(s.startsWith(""), "the empty string starts with the empty string");
  238. assert.isFalse(s.startsWith("anything"), "the empty string does not start with any non-empty string");
  239. assert.isTrue(s.startsWith("", 1), "the search position is clipped to exist within the string and thus the the empty string starts with the empty string for any given position argument");
  240. assert.isTrue(s.startsWith("", Infinity), "the empty string starts with the empty string even if given starting position is Infinity, since the starting position is clipped");
  241. s = "abcdefghijklmnopqrstuvwxyz";
  242. assert.isTrue(s.startsWith(""), "a non-empty string starts with the empty string");
  243. assert.isTrue(s.startsWith("a"), "single character prefix substring matches the beginning of the string");
  244. assert.isTrue(s.startsWith("abc"), "prefix substring matches the beginning of the string");
  245. assert.isTrue(s.startsWith("abcdefghijklm"), "long prefix substring string matches the beginning of the string");
  246. assert.isTrue(s.startsWith("abcdefghijklmnopqrstuvwxyz"), "identical string matches the beginning of the string");
  247. assert.isFalse(s.startsWith("bcd"), "non-prefix substring does not match beginning of the string");
  248. assert.isFalse(s.startsWith("mnopqrstuv"), "long non-prefix substring does not match beginning of the string");
  249. assert.isFalse(s.startsWith("xyz"), "suffix substring does not match beginning of the string");
  250. assert.isFalse(s.startsWith("abczzz"), "non-substring with partial prefix match does not match beginning of the string");
  251. assert.isTrue(s.startsWith("", 3), "a non-empty string starts with the empty string at any position");
  252. assert.isTrue(s.startsWith("", 26), "a non-empty string starts with the empty string at its end");
  253. assert.isTrue(s.startsWith("", Infinity), "a non-empty string starts with the empty string at its end (Infinity clipped to end position)");
  254. assert.isTrue(s.startsWith("abcd", -Infinity), "a non-empty string starts with a prefix substring at its beginning (-Infinity clipped to start position)");
  255. assert.isTrue(s.startsWith("bcd", 1), "a non-empty string starts with a given substring at the position where that substring begins");
  256. assert.isTrue(s.startsWith("mnopqrstuv", 12), "a non-empty string starts with a given (long) substring at the position where that substring begins");
  257. assert.isTrue(s.startsWith("xyz", 23), "a non-empty string starts with a suffix substring at the position where the suffix begins");
  258. assert.isTrue(s.startsWith("z", 25), "a non-empty string starts with a single character suffix substring at the last position in the string");
  259. assert.isFalse(s.startsWith("z", 26), "a non-empty string does not start with a single suffix substring at the position past the string");
  260. s = "abc\0def";
  261. assert.isTrue(s.startsWith("abc\0def"), "string with embedded null character starts with itself");
  262. assert.isTrue(s.startsWith("abc\0d"), "string with embedded null character starts with prefix including null character");
  263. assert.isTrue(s.startsWith("abc\0"), "string with embedded null character starts with prefix including and ending with null character in search string");
  264. assert.isFalse(s.startsWith("abc\0abc"), "string with embedded null character does not start with string that is only different after null character");
  265. assert.isFalse(s.startsWith("def\0abc"), "string with embedded null character does not start with string that is only different before null character");
  266. assert.isTrue(s.startsWith("\0def", 3), "string with embedded null character starts with substring beginning with null character at corresponding starting position");
  267. var n = 12345;
  268. assert.isTrue(String.prototype.startsWith.call(n, "123"), "startsWith works even when its this argument is not a string object");
  269. assert.isFalse(String.prototype.startsWith.call(n, "45"), "startsWith works even when its this argument is not a string object");
  270. }
  271. },
  272. {
  273. name: "startsWith throws if searchString is a RegExp",
  274. body: verifyThrowsIfRegExpSearchString.bind(undefined, "startsWith")
  275. },
  276. {
  277. name: "endsWith returns true if the given search string matches the substring of the given string ending at the given position",
  278. body: function () {
  279. assert.throws(function () { String.prototype.endsWith.call(); }, TypeError, "endsWith throws TypeError if it is given no arguments", "String.prototype.endsWith: 'this' is null or undefined");
  280. assert.throws(function () { String.prototype.endsWith.call(undefined); }, TypeError, "endsWith throws TypeError if its this argument is undefined", "String.prototype.endsWith: 'this' is null or undefined");
  281. assert.throws(function () { String.prototype.endsWith.call(undefined, "", 0); }, TypeError, "endsWith throws TypeError if its this argument is undefined even if given further arguments", "String.prototype.endsWith: 'this' is null or undefined");
  282. assert.throws(function () { String.prototype.endsWith.call(null); }, TypeError, "endsWith throws TypeError if its this argument is null", "String.prototype.endsWith: 'this' is null or undefined");
  283. assert.throws(function () { String.prototype.endsWith.call(null, "", 0); }, TypeError, "endsWith throws TypeError if its this argument is null even if given further arguments", "String.prototype.endsWith: 'this' is null or undefined");
  284. var s;
  285. s = "";
  286. assert.isTrue(s.endsWith(""), "the empty string ends with the empty string");
  287. assert.isFalse(s.endsWith("anything"), "the empty string does not end with any non-empty string");
  288. assert.isTrue(s.endsWith("", -1), "the search position is clipped to exist within the string and thus the the empty string ends with the empty string for any given position argument");
  289. assert.isTrue(s.endsWith("", Infinity), "the empty string ends with the empty string even if given ending position is Infinity, since the ending position is clipped");
  290. s = "abcdefghijklmnopqrstuvwxyz";
  291. assert.isTrue(s.endsWith(""), "a non-empty string ends with the empty string");
  292. assert.isTrue(s.endsWith("z"), "single character suffix substring matches the beginning of the string");
  293. assert.isTrue(s.endsWith("xyz"), "suffix substring matches the beginning of the string");
  294. assert.isTrue(s.endsWith("nopqrstuvwxyz"), "long suffix substring string matches the beginning of the string");
  295. assert.isTrue(s.endsWith("abcdefghijklmnopqrstuvwxyz"), "identical string matches the beginning of the string");
  296. assert.isFalse(s.endsWith("wxy"), "non-suffix substring does not match beginning of the string");
  297. assert.isFalse(s.endsWith("mnopqrstuv"), "long non-suffix substring does not match beginning of the string");
  298. assert.isFalse(s.endsWith("abc"), "prefix substring does not match beginning of the string");
  299. assert.isFalse(s.endsWith("aaaxyz"), "non-substring with partial suffix match does not match beginning of the string");
  300. assert.isTrue(s.endsWith("", 23), "a non-empty string ends with the empty string at any position");
  301. assert.isTrue(s.endsWith("", 0), "a non-empty string ends with the empty string at its beginning");
  302. assert.isTrue(s.endsWith("wxyz", Infinity), "a non-empty string ends with a suffix substring at its end (Infinity clipped to end position)");
  303. assert.isTrue(s.endsWith("", -Infinity), "a non-empty string ends with the empty string at its beginning (-Infinity clipped to start position)");
  304. assert.isTrue(s.endsWith("wxy", 25), "a non-empty string ends with a given substring at the position where that substring ends");
  305. assert.isTrue(s.endsWith("mnopqrstuv", 22), "a non-empty string ends with a given (long) substring at the position where that substring ends");
  306. assert.isTrue(s.endsWith("abc", 3), "a non-empty string ends with a prefix substring at the position where the prefix ends");
  307. assert.isTrue(s.endsWith("a", 1), "a non-empty string ends with a single character prefix substring at the first position in the string");
  308. assert.isFalse(s.endsWith("a", 0), "a non-empty string does not end with a single prefix substring at the position past the beginning of the string");
  309. s = "abc\0def";
  310. assert.isTrue(s.endsWith("abc\0def"), "string with embedded null character ends with itself");
  311. assert.isTrue(s.endsWith("c\0def"), "string with embedded null character ends with prefix including null character");
  312. assert.isTrue(s.endsWith("\0def"), "string with embedded null character ends with prefix including and starting with null character in search string");
  313. assert.isFalse(s.endsWith("abc\0abc"), "string with embedded null character does not end with string that is only different after null character");
  314. assert.isFalse(s.endsWith("def\0abc"), "string with embedded null character does not end with string that is only different before null character");
  315. assert.isTrue(s.endsWith("abc\0", 4), "string with embedded null character ends with substring ending with null character at corresponding ending position");
  316. var n = 12345;
  317. assert.isTrue(String.prototype.endsWith.call(n, "345"), "endsWith works even when its this argument is not a string object");
  318. assert.isFalse(String.prototype.endsWith.call(n, "12"), "endsWith works even when its this argument is not a string object");
  319. }
  320. },
  321. {
  322. name: "endsWith throws if searchString is a RegExp",
  323. body: verifyThrowsIfRegExpSearchString.bind(undefined, "endsWith")
  324. },
  325. {
  326. name: "includes returns true if the given search string matches any substring of the given string",
  327. body: function () {
  328. assert.throws(function () { String.prototype.includes.call(); }, TypeError, "includes throws TypeError if it is given no arguments", "String.prototype.includes: 'this' is null or undefined");
  329. assert.throws(function () { String.prototype.includes.call(undefined); }, TypeError, "includes throws TypeError if its this argument is undefined", "String.prototype.includes: 'this' is null or undefined");
  330. assert.throws(function () { String.prototype.includes.call(undefined, "", 0); }, TypeError, "includes throws TypeError if its this argument is undefined even if given further arguments", "String.prototype.includes: 'this' is null or undefined");
  331. assert.throws(function () { String.prototype.includes.call(null); }, TypeError, "includes throws TypeError if its this argument is null", "String.prototype.includes: 'this' is null or undefined");
  332. assert.throws(function () { String.prototype.includes.call(null, "", 0); }, TypeError, "includes throws TypeError if its this argument is null even if given further arguments", "String.prototype.includes: 'this' is null or undefined");
  333. var s;
  334. s = "";
  335. assert.isTrue(s.includes(""), "the empty string includes the empty string");
  336. assert.isFalse(s.includes("anything"), "the empty string includes no non-empty strings");
  337. assert.isTrue(s.includes("", 1), "the search position is clipped to exist within the string and thus the empty string includes itself for any given position argument");
  338. assert.isTrue(s.includes("", Infinity), "the empty string includes the empty string even if given ending position is Infinity, since the ending position is clipped");
  339. s = "abcdefghijklmnopqrstuvwxyz";
  340. assert.isTrue(s.includes(""), "a non-empty string includes the empty string");
  341. assert.isTrue(s.includes("abc"), "substring found at the beginning of the string");
  342. assert.isTrue(s.includes("xyz"), "substring found at the end of the string");
  343. assert.isTrue(s.includes("z"), "substring found at the very end of the string");
  344. assert.isTrue(s.includes("ijklmno"), "substring found in the middle of the string");
  345. assert.isFalse(s.includes("abczzz"), "substring partially matches at the beginning of the string but is not a match");
  346. assert.isFalse(s.includes("xyzaaa"), "substring partially matches at the ending of the string but is not a match");
  347. assert.isFalse(s.includes("zaaa"), "substring partially matches at the very ending of the string but is not a match");
  348. assert.isFalse(s.includes("ijklxyz"), "substring partially matches in the middle of the string but is not a match");
  349. assert.isTrue(s.includes("", 26), "a non-empty string includes the empty string even at the end");
  350. assert.isTrue(s.includes("", Infinity), "a non-empty string includes the empty string even at the end (Infinity clipped to end position)");
  351. assert.isTrue(s.includes("abc", -Infinity), "a non-empty string includes a substring starting at the beginning (-Infinity clipped to start position)");
  352. assert.isFalse(s.includes("z", 26), "a non-empty string includes no non-empty string at the end");
  353. assert.isTrue(s.includes("z", 25), "starting at the last character, a string includes its last character");
  354. assert.isFalse(s.includes("y", 25), "starting at the last character, a string does not contain previous characters");
  355. assert.isFalse(s.includes("abc", 1), "a string does not contain a substring if the only occurrence begins before the given start position");
  356. assert.isTrue(s.includes("mnop", 5), "substring found in the middle of a string after the given start position");
  357. assert.isTrue(s.includes("efg", 4), "substring found in the middle of a string at the given start position");
  358. s = "abc\0def";
  359. assert.isTrue(s.includes("abc\0def"), "string with embedded null character includes itself");
  360. assert.isTrue(s.includes("abc\0d"), "string with embedded null character includes prefix including null character");
  361. assert.isTrue(s.includes("abc\0"), "string with embedded null character includes prefix including and ending with null character in search string");
  362. assert.isTrue(s.includes("\0def"), "string with embedded null character includes prefix including and starting with null character in search string");
  363. assert.isFalse(s.includes("abc\0abc"), "string with embedded null character does not contain string that is only different after null character");
  364. assert.isFalse(s.includes("def\0abc"), "string with embedded null character does not contain string that is only different before null character");
  365. assert.isTrue(s.includes("\0def", 3), "string with embedded null character includes with substring beginning with null character at corresponding starting position");
  366. var n = 12345;
  367. assert.isTrue(String.prototype.includes.call(n, "34"), "includes works even when its this argument is not a string object");
  368. assert.isFalse(String.prototype.includes.call(n, "7"), "includes works even when its this argument is not a string object");
  369. }
  370. },
  371. {
  372. name: "includes throws if searchString is a RegExp",
  373. body: verifyThrowsIfRegExpSearchString.bind(undefined, "includes")
  374. },
  375. {
  376. name: "String.fromCodePoint has correct shape",
  377. body: function() {
  378. assert.areEqual(1, String.fromCodePoint.length, "String.fromCodePoint.length === 1");
  379. }
  380. },
  381. ];
  382. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });