date-test262.test.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*
  2. * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75:
  3. * - test/built-ins/Date/prototype/setTime/arg-to-number.js
  4. * - test/built-ins/Date/prototype/setTime/arg-to-number-err.js
  5. * - test/built-ins/Date/prototype/setTime/new-value-time-clip.js
  6. * - test/built-ins/Date/prototype/setMilliseconds/this-value-valid-date.js
  7. * - test/built-ins/Date/prototype/setUTCMilliseconds/this-value-valid-date.js
  8. * - test/built-ins/Date/prototype/setSeconds/this-value-valid-date-sec.js
  9. * - test/built-ins/Date/prototype/setSeconds/this-value-valid-date-ms.js
  10. * - test/built-ins/Date/prototype/setUTCSeconds/this-value-valid-date-sec.js
  11. * - test/built-ins/Date/prototype/setUTCSeconds/this-value-valid-date-ms.js
  12. * - test/built-ins/Date/prototype/setMinutes/this-value-valid-date.js
  13. * - test/built-ins/Date/prototype/setUTCMinutes/this-value-valid-date.js
  14. * - test/built-ins/Date/prototype/setHours/this-value-valid-date-hour.js
  15. * - test/built-ins/Date/prototype/setHours/this-value-valid-date-min.js
  16. * - test/built-ins/Date/prototype/setHours/this-value-valid-date-sec.js
  17. * - test/built-ins/Date/prototype/setHours/this-value-valid-date-ms.js
  18. * - test/built-ins/Date/prototype/setUTCHours/this-value-valid-date-hour.js
  19. * - test/built-ins/Date/prototype/setUTCHours/this-value-valid-date-min.js
  20. * - test/built-ins/Date/prototype/setUTCHours/this-value-valid-date-sec.js
  21. * - test/built-ins/Date/prototype/setUTCHours/this-value-valid-date-ms.js
  22. * - test/built-ins/Date/prototype/setDate/this-value-valid-date.js
  23. * - test/built-ins/Date/prototype/setUTCDate/date-value-read-before-tonumber-when-date-is-valid.js
  24. * - test/built-ins/Date/prototype/setMonth/this-value-valid-date-month.js
  25. * - test/built-ins/Date/prototype/setMonth/this-value-valid-date-date.js
  26. * - test/built-ins/Date/prototype/setUTCMonth/this-value-valid-date-month.js
  27. * - test/built-ins/Date/prototype/setUTCMonth/this-value-valid-date-date.js
  28. * - test/built-ins/Date/prototype/setFullYear/this-value-valid-date-year.js
  29. * - test/built-ins/Date/prototype/setFullYear/this-value-valid-date-month.js
  30. * - test/built-ins/Date/prototype/setFullYear/this-value-valid-date-date.js
  31. * - test/built-ins/Date/prototype/setFullYear/this-value-invalid-date.js
  32. * - test/built-ins/Date/prototype/setFullYear/arg-year-to-number-err.js
  33. * - test/built-ins/Date/prototype/setFullYear/arg-month-to-number-err.js
  34. * - test/built-ins/Date/prototype/setFullYear/arg-date-to-number-err.js
  35. * - test/built-ins/Date/prototype/setUTCFullYear/date-value-read-before-tonumber-when-date-is-invalid.js
  36. * - test/built-ins/Date/prototype/setMonth/arg-coercion-order.js
  37. * - test/language/expressions/addition/S11.6.1_A2.2_T2.js
  38. *
  39. * Copyright (C) 2016 the V8 project authors. All rights reserved.
  40. * Copyright (C) 2021 Kevin Gibbons. All rights reserved.
  41. * Copyright (C) 2024 André Bargull. All rights reserved.
  42. * Copyright 2009 the Sputnik authors. All rights reserved.
  43. * Test262 portions are governed by the BSD license in LICENSE.test262.
  44. */
  45. import { describe, expect, test } from "bun:test"
  46. import { Effect } from "effect"
  47. import { CodeMode } from "../src/index.js"
  48. const value = async (code: string) => {
  49. const result = await Effect.runPromise(CodeMode.execute({ code, tools: {} }))
  50. if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
  51. return result.value
  52. }
  53. describe("Date setter Test262 parity", () => {
  54. test("UTC setters mutate every supported component and return the new time", async () => {
  55. expect(
  56. await value(`
  57. const create = () => new Date(Date.UTC(2000, 0, 2, 3, 4, 5, 6))
  58. const result = []
  59. let date = create()
  60. result.push([date.setTime(1234) === date.getTime(), date.getTime()])
  61. date = create()
  62. result.push([date.setUTCMilliseconds(10) === date.getTime(), date.getUTCMilliseconds()])
  63. date = create()
  64. result.push([date.setUTCSeconds(10, 11) === date.getTime(), date.getUTCSeconds(), date.getUTCMilliseconds()])
  65. date = create()
  66. result.push([date.setUTCMinutes(10, 11, 12) === date.getTime(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds()])
  67. date = create()
  68. result.push([date.setUTCHours(10, 11, 12, 13) === date.getTime(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds()])
  69. date = create()
  70. result.push([date.setUTCDate(10) === date.getTime(), date.getUTCDate()])
  71. date = create()
  72. result.push([date.setUTCMonth(6, 10) === date.getTime(), date.getUTCMonth(), date.getUTCDate()])
  73. date = create()
  74. result.push([date.setUTCFullYear(2020, 6, 10) === date.getTime(), date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()])
  75. return result
  76. `),
  77. ).toEqual([
  78. [true, 1234],
  79. [true, 10],
  80. [true, 10, 11],
  81. [true, 10, 11, 12],
  82. [true, 10, 11, 12, 13],
  83. [true, 10],
  84. [true, 6, 10],
  85. [true, 2020, 6, 10],
  86. ])
  87. })
  88. test("local setters mutate every supported component", async () => {
  89. expect(
  90. await value(`
  91. const create = () => new Date(2000, 0, 2, 3, 4, 5, 6)
  92. const result = []
  93. let date = create()
  94. date.setMilliseconds(10)
  95. result.push(date.getMilliseconds())
  96. date = create()
  97. date.setSeconds(10, 11)
  98. result.push([date.getSeconds(), date.getMilliseconds()])
  99. date = create()
  100. date.setMinutes(10, 11, 12)
  101. result.push([date.getMinutes(), date.getSeconds(), date.getMilliseconds()])
  102. date = create()
  103. date.setHours(10, 11, 12, 13)
  104. result.push([date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()])
  105. date = create()
  106. date.setDate(10)
  107. result.push(date.getDate())
  108. date = create()
  109. date.setMonth(6, 10)
  110. result.push([date.getMonth(), date.getDate()])
  111. date = create()
  112. date.setFullYear(2020, 6, 10)
  113. result.push([date.getFullYear(), date.getMonth(), date.getDate()])
  114. return result
  115. `),
  116. ).toEqual([10, [10, 11], [10, 11, 12], [10, 11, 12, 13], 10, [6, 10], [2020, 6, 10]])
  117. })
  118. test("setter arguments use numeric coercion in source order", async () => {
  119. expect(
  120. await value(`
  121. const calls = []
  122. const date = new Date(0)
  123. const returned = date.setUTCSeconds(
  124. { valueOf: () => { calls.push("seconds"); return "2" } },
  125. { valueOf: () => { calls.push("milliseconds"); return true } },
  126. )
  127. const fallback = new Date(0)
  128. fallback.setTime({ valueOf: () => ({}), toString: () => "3" })
  129. return [returned === date.getTime(), date.getUTCSeconds(), date.getUTCMilliseconds(), fallback.getTime(), calls]
  130. `),
  131. ).toEqual([true, 2, 1, 3, ["seconds", "milliseconds"]])
  132. })
  133. test("setters snapshot the Date before coercing arguments", async () => {
  134. expect(
  135. await value(`
  136. const date = new Date(Date.UTC(2000, 5, 15, 1, 2, 3, 4))
  137. date.setUTCFullYear({
  138. valueOf: () => {
  139. date.setTime(Date.UTC(2020, 11, 20))
  140. return 2001
  141. },
  142. })
  143. const invalid = new Date(NaN)
  144. invalid.setUTCFullYear({ valueOf: () => { invalid.setTime(0); return 1 } })
  145. const effects = []
  146. const staysInvalid = new Date(NaN)
  147. const result = staysInvalid.setUTCMonth(
  148. { valueOf: () => { effects.push("month"); return 0 } },
  149. { valueOf: () => { effects.push("date"); return 1 } },
  150. )
  151. return [
  152. date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),
  153. invalid.getUTCFullYear(), invalid.getUTCMonth(), invalid.getUTCDate(),
  154. Number.isNaN(result), Number.isNaN(staysInvalid.getTime()), effects,
  155. ]
  156. `),
  157. ).toEqual([2001, 5, 15, 1, 0, 1, true, true, ["month", "date"]])
  158. })
  159. test("setFullYear recovers an invalid local Date", async () => {
  160. expect(
  161. await value(`
  162. const year = new Date(NaN)
  163. const yearResult = year.setFullYear(2016)
  164. const month = new Date(NaN)
  165. const monthResult = month.setFullYear(2016, 6)
  166. const day = new Date(NaN)
  167. const dayResult = day.setFullYear(2016, 6, 7)
  168. return [
  169. yearResult === year.getTime(), year.getFullYear(), year.getMonth(), year.getDate(),
  170. monthResult === month.getTime(), month.getFullYear(), month.getMonth(), month.getDate(),
  171. dayResult === day.getTime(), day.getFullYear(), day.getMonth(), day.getDate(),
  172. ]
  173. `),
  174. ).toEqual([true, 2016, 0, 1, true, 2016, 6, 1, true, 2016, 6, 7])
  175. })
  176. test("abrupt argument coercion stops later coercion and preserves the Date", async () => {
  177. expect(
  178. await value(`
  179. const calls = []
  180. const failure = { valueOf: () => { throw new Error("stop") } }
  181. const counter = { valueOf: () => { calls.push("counter"); return 1 } }
  182. const results = []
  183. const time = new Date(0)
  184. try { time.setTime(failure) } catch (error) { results.push(error.message) }
  185. results.push(time.getTime())
  186. const year = new Date(0)
  187. try { year.setFullYear(failure, counter, counter) } catch (error) { results.push(error.message) }
  188. results.push(year.getTime())
  189. const month = new Date(0)
  190. try { month.setFullYear(0, failure, counter) } catch (error) { results.push(error.message) }
  191. results.push(month.getTime())
  192. const day = new Date(0)
  193. try { day.setFullYear(0, 0, failure) } catch (error) { results.push(error.message) }
  194. results.push(day.getTime(), calls)
  195. return results
  196. `),
  197. ).toEqual(["stop", 0, "stop", 0, "stop", 0, "stop", 0, []])
  198. })
  199. test("omitted arguments, explicit undefined, extra arguments, and TimeClip match native behavior", async () => {
  200. expect(
  201. await value(`
  202. const calls = []
  203. const omittedOptional = new Date(0)
  204. const explicitUndefined = new Date(0)
  205. const noArgument = new Date(0)
  206. const extra = new Date(0)
  207. const clipped = new Date(0)
  208. const badPrimitive = new Date(0)
  209. let conversionError = ""
  210. try {
  211. badPrimitive.setTime({ valueOf: () => ({}), toString: () => ({}) })
  212. } catch (error) {
  213. conversionError = error.name
  214. }
  215. return [
  216. omittedOptional.setUTCSeconds(1),
  217. Number.isNaN(explicitUndefined.setUTCSeconds(1, undefined)),
  218. Number.isNaN(noArgument.setTime()),
  219. extra.setTime(1, { valueOf: () => { calls.push("extra"); return 2 } }),
  220. calls,
  221. Number.isNaN(clipped.setTime(8.64e15 + 1)),
  222. Number.isNaN(clipped.getTime()),
  223. conversionError,
  224. ]
  225. `),
  226. ).toEqual([1000, true, true, 1, [], true, true, "TypeError"])
  227. })
  228. })
  229. describe("Date default primitive Test262 parity", () => {
  230. test("addition and loose equality use the Date string primitive", async () => {
  231. expect(
  232. await value(`
  233. const date = new Date(0)
  234. const text = String(date)
  235. return [
  236. date + date === text + text,
  237. date + 0 === text + "0",
  238. date + true === text + "true",
  239. date == text,
  240. text == date,
  241. date == Number(date),
  242. Number(date) == date,
  243. date != Number(date),
  244. date == date,
  245. date == new Date(0),
  246. date == null,
  247. ]
  248. `),
  249. ).toEqual([true, true, true, true, true, false, false, true, true, false, false])
  250. })
  251. })