promise-test262.test.ts 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510
  1. /*
  2. * Portions adapted from Test262 at revision 250f204f23a9249ff204be2baec29600faae7b75.
  3. * Every test names its upstream source; test.failing cases are executable conformance
  4. * targets for intended Promise behavior that CodeMode does not implement yet.
  5. *
  6. * Copyright 2014 Cubane Canada, Inc. All rights reserved.
  7. * Copyright 2015 Microsoft Corporation. All rights reserved.
  8. * Copyright 2016 Microsoft, Inc. All rights reserved.
  9. * Copyright 2017 Caitlin Potter. All rights reserved.
  10. * Copyright (C) 2016-2020 the V8 project authors. All rights reserved.
  11. * Copyright (C) 2018-2020 Rick Waldron. All rights reserved.
  12. * Copyright (C) 2019 Leo Balter. All rights reserved.
  13. * Test262 portions are governed by the BSD license in LICENSE.test262.
  14. */
  15. import { describe, expect, test } from "bun:test"
  16. import { Effect } from "effect"
  17. import { CodeMode } from "../src/index.js"
  18. const execute = (code: string) => Effect.runPromise(CodeMode.execute({ code, tools: {}, limits: { timeoutMs: 1_000 } }))
  19. const value = async (code: string) => {
  20. const result = await execute(code)
  21. if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`)
  22. return result.value
  23. }
  24. describe("Test262 Promise statics", () => {
  25. test("statics are callable and return promises", async () => {
  26. // Sources:
  27. // test/built-ins/Promise/all/S25.4.4.1_A1.1_T1.js
  28. // test/built-ins/Promise/allSettled/is-function.js
  29. // test/built-ins/Promise/allSettled/returns-promise.js
  30. // test/built-ins/Promise/race/S25.4.4.3_A1.1_T1.js
  31. // test/built-ins/Promise/resolve/S25.4.4.5_A1.1_T1.js
  32. // test/built-ins/Promise/reject/S25.4.4.4_A1.1_T1.js
  33. expect(
  34. await value(`
  35. const values = [
  36. Promise.all([]),
  37. Promise.allSettled([]),
  38. Promise.race([undefined]),
  39. Promise.resolve(),
  40. Promise.reject(),
  41. ]
  42. const callable = [
  43. typeof Promise.all,
  44. typeof Promise.allSettled,
  45. typeof Promise.race,
  46. typeof Promise.resolve,
  47. typeof Promise.reject,
  48. ]
  49. try { await values[4] } catch {}
  50. return [callable, values.map((item) => item instanceof Promise)]
  51. `),
  52. ).toEqual([
  53. ["function", "function", "function", "function", "function"],
  54. [true, true, true, true, true],
  55. ])
  56. })
  57. test("Promise.all returns fresh arrays for empty and settled inputs", async () => {
  58. // Sources:
  59. // test/built-ins/Promise/all/S25.4.4.1_A2.1_T1.js
  60. // test/built-ins/Promise/all/S25.4.4.1_A2.3_T1.js
  61. // test/built-ins/Promise/all/S25.4.4.1_A2.3_T2.js
  62. // test/built-ins/Promise/all/S25.4.4.1_A2.3_T3.js
  63. // test/built-ins/Promise/all/S25.4.4.1_A7.1_T1.js
  64. expect(
  65. await value(`
  66. const input = []
  67. const emptyPromise = Promise.all(input)
  68. const empty = await emptyPromise
  69. const onePromise = Promise.all([Promise.resolve(3)])
  70. const one = await onePromise
  71. return [
  72. emptyPromise instanceof Promise,
  73. empty instanceof Array,
  74. empty.length,
  75. empty !== input,
  76. onePromise instanceof Promise,
  77. one instanceof Array,
  78. one.length,
  79. one[0],
  80. ]
  81. `),
  82. ).toEqual([true, true, 0, true, true, true, 1, 3])
  83. })
  84. test("Promise.all adopts values and preserves input order and identity", async () => {
  85. // Sources:
  86. // test/built-ins/Promise/all/resolve-non-thenable.js
  87. // test/built-ins/Promise/all/S25.4.4.1_A8.2_T1.js
  88. // test/built-ins/Promise/all/S25.4.4.1_A8.2_T2.js
  89. const result = await value(`
  90. const first = { id: 1 }
  91. const second = { id: 2 }
  92. const values = await Promise.all([Promise.resolve(3), first, Promise.resolve(second)])
  93. const observe = async (promise) => {
  94. try { await promise; return "fulfilled" } catch (reason) { return reason }
  95. }
  96. return [
  97. values.length,
  98. values[0],
  99. values[1] === first,
  100. values[2] === second,
  101. await observe(Promise.all([Promise.reject(1), Promise.resolve(2)])),
  102. await observe(Promise.all([Promise.resolve(1), Promise.reject(2)])),
  103. ]
  104. `)
  105. expect(result).toEqual([3, 3, true, true, 1, 2])
  106. })
  107. test("Promise.allSettled returns fresh arrays and ordered outcome records", async () => {
  108. // Sources:
  109. // test/built-ins/Promise/allSettled/resolves-empty-array.js
  110. // test/built-ins/Promise/allSettled/resolves-to-array.js
  111. // test/built-ins/Promise/allSettled/resolved-all-fulfilled.js
  112. // test/built-ins/Promise/allSettled/resolved-all-rejected.js
  113. // test/built-ins/Promise/allSettled/resolved-all-mixed.js
  114. // test/built-ins/Promise/allSettled/resolve-non-thenable.js
  115. expect(
  116. await value(`
  117. const input = []
  118. const empty = await Promise.allSettled(input)
  119. const reason = { id: 4 }
  120. const object = { id: 5 }
  121. const outcomes = await Promise.allSettled([
  122. Promise.resolve(1),
  123. Promise.reject(2),
  124. 3,
  125. Promise.reject(reason),
  126. object,
  127. ])
  128. return [
  129. empty instanceof Array,
  130. empty.length,
  131. empty !== input,
  132. outcomes,
  133. outcomes[4].value === object,
  134. outcomes.map((item) => Object.keys(item)),
  135. ]
  136. `),
  137. ).toEqual([
  138. true,
  139. 0,
  140. true,
  141. [
  142. { status: "fulfilled", value: 1 },
  143. { status: "rejected", reason: 2 },
  144. { status: "fulfilled", value: 3 },
  145. { status: "rejected", reason: { id: 4 } },
  146. { status: "fulfilled", value: { id: 5 } },
  147. ],
  148. true,
  149. [
  150. ["status", "value"],
  151. ["status", "reason"],
  152. ["status", "value"],
  153. ["status", "reason"],
  154. ["status", "value"],
  155. ],
  156. ])
  157. })
  158. test("Promise.race preserves fulfillment, rejection, and iterable order", async () => {
  159. // Sources:
  160. // test/built-ins/Promise/race/S25.4.4.3_A6.2_T1.js
  161. // test/built-ins/Promise/race/S25.4.4.3_A7.1_T1.js
  162. // test/built-ins/Promise/race/S25.4.4.3_A7.2_T1.js
  163. // test/built-ins/Promise/race/S25.4.4.3_A7.3_T1.js
  164. // test/built-ins/Promise/race/S25.4.4.3_A7.3_T2.js
  165. expect(
  166. await value(`
  167. const observe = async (promise) => {
  168. try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] }
  169. }
  170. return await Promise.all([
  171. observe(Promise.race([23])),
  172. observe(Promise.race([Promise.reject(7)])),
  173. observe(Promise.race([Promise.resolve(1), Promise.resolve(2)])),
  174. observe(Promise.race([Promise.reject(3), Promise.resolve(4)])),
  175. ])
  176. `),
  177. ).toEqual([
  178. ["fulfilled", 23],
  179. ["rejected", 7],
  180. ["fulfilled", 1],
  181. ["rejected", 3],
  182. ])
  183. })
  184. test("combinators consume supported string iterables", async () => {
  185. // Sources:
  186. // test/built-ins/Promise/all/iter-arg-is-string-resolve.js
  187. // test/built-ins/Promise/allSettled/iter-arg-is-string-resolve.js
  188. // test/built-ins/Promise/race/iter-arg-is-string-resolve.js
  189. expect(
  190. await value(`
  191. return [
  192. await Promise.all("abc"),
  193. await Promise.allSettled("ab"),
  194. await Promise.race("abc"),
  195. ]
  196. `),
  197. ).toEqual([
  198. ["a", "b", "c"],
  199. [
  200. { status: "fulfilled", value: "a" },
  201. { status: "fulfilled", value: "b" },
  202. ],
  203. "a",
  204. ])
  205. })
  206. test("Promise.resolve adopts values and preserves CodeMode-promise identity", async () => {
  207. // Sources:
  208. // test/built-ins/Promise/resolve/S25.4.4.5_A2.1_T1.js
  209. // test/built-ins/Promise/resolve/resolve-non-obj.js
  210. // test/built-ins/Promise/resolve/resolve-non-thenable.js
  211. expect(
  212. await value(`
  213. const object = { id: 1 }
  214. const promise = Promise.resolve(1)
  215. return [
  216. await Promise.resolve(23),
  217. await Promise.resolve(Promise.resolve(24)),
  218. (await Promise.resolve(object)) === object,
  219. [promise].includes(Promise.resolve(promise)),
  220. ]
  221. `),
  222. ).toEqual([23, 24, true, true])
  223. })
  224. test("Promise.reject preserves primitive and object reasons", async () => {
  225. // Sources:
  226. // test/built-ins/Promise/reject/S25.4.4.4_A2.1_T1.js
  227. const result = await value(`
  228. const object = { reason: true }
  229. const reasons = [undefined, null, false, true, 0, "", 42, object]
  230. const observe = async (reason) => {
  231. try { await Promise.reject(reason); return false } catch (caught) { return caught === reason }
  232. }
  233. return await Promise.all(reasons.map(observe))
  234. `)
  235. expect(result).toEqual([true, true, true, true, true, true, true, true])
  236. })
  237. test("Promise.all resolves duplicate members into every slot", async () => {
  238. // Sources:
  239. // test/built-ins/Promise/all/invoke-resolve-on-promises-every-iteration-of-promise.js
  240. // test/built-ins/Promise/all/invoke-resolve-on-values-every-iteration-of-promise.js
  241. // (adapted: CodeMode has no observable Promise.resolve hook, so per-iteration
  242. // handling of a repeated member is asserted through the resolved slots)
  243. expect(
  244. await value(`
  245. const settled = Promise.resolve(3)
  246. const computed = (async () => "computed")()
  247. return [
  248. await Promise.all([settled, settled, settled]),
  249. await Promise.all([computed, "plain", computed]),
  250. ]
  251. `),
  252. ).toEqual([
  253. [3, 3, 3],
  254. ["computed", "plain", "computed"],
  255. ])
  256. })
  257. test("Promise.allSettled records duplicate members independently", async () => {
  258. // Source: test/built-ins/Promise/allSettled/invoke-resolve-on-promises-every-iteration-of-promise.js
  259. // (adapted: per-iteration handling of a repeated member is asserted through the
  260. // outcome records instead of a Promise.resolve hook)
  261. expect(
  262. await value(`
  263. const good = Promise.resolve(1)
  264. const bad = Promise.reject(2)
  265. return await Promise.allSettled([good, bad, good, bad])
  266. `),
  267. ).toEqual([
  268. { status: "fulfilled", value: 1 },
  269. { status: "rejected", reason: 2 },
  270. { status: "fulfilled", value: 1 },
  271. { status: "rejected", reason: 2 },
  272. ])
  273. })
  274. test("combinators adopt members that settled before the call", async () => {
  275. // Sources:
  276. // test/built-ins/Promise/all/reject-immed.js
  277. // test/built-ins/Promise/allSettled/reject-immed.js
  278. // test/built-ins/Promise/race/reject-immed.js
  279. // (adapted: immediately-rejecting thenables become CodeMode promises that settled,
  280. // and were even observed, before the combinator call)
  281. expect(
  282. await value(`
  283. const fulfilled = Promise.resolve("done")
  284. const rejected = Promise.reject("failed")
  285. try { await rejected } catch {}
  286. const observe = async (promise) => {
  287. try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] }
  288. }
  289. return [
  290. await observe(Promise.all([fulfilled, rejected])),
  291. await Promise.allSettled([rejected, fulfilled]),
  292. await observe(Promise.race([rejected, fulfilled])),
  293. ]
  294. `),
  295. ).toEqual([
  296. ["rejected", "failed"],
  297. [
  298. { status: "rejected", reason: "failed" },
  299. { status: "fulfilled", value: "done" },
  300. ],
  301. ["rejected", "failed"],
  302. ])
  303. })
  304. test("combinator results follow input order, not settlement order", async () => {
  305. // Sources:
  306. // test/built-ins/Promise/all/resolve-non-thenable.js
  307. // test/built-ins/Promise/allSettled/resolved-all-mixed.js
  308. // (adapted: members are created, and therefore settle, in reverse of input order;
  309. // deferred settlement is not expressible without host-async work in this corpus)
  310. expect(
  311. await value(`
  312. const third = Promise.resolve("c")
  313. const failing = (async () => { throw "b" })()
  314. try { await failing } catch {}
  315. const second = (async () => "b")()
  316. const first = Promise.resolve("a")
  317. return [
  318. await Promise.all([first, second, third]),
  319. await Promise.allSettled([first, failing, third]),
  320. ]
  321. `),
  322. ).toEqual([
  323. ["a", "b", "c"],
  324. [
  325. { status: "fulfilled", value: "a" },
  326. { status: "rejected", reason: "b" },
  327. { status: "fulfilled", value: "c" },
  328. ],
  329. ])
  330. })
  331. test("Promise.race ignores a rejected loser once the first contender wins", async () => {
  332. // Source: test/built-ins/Promise/race/reject-ignored-immed.js
  333. // (adapted: the losing rejection comes from an async function instead of a thenable;
  334. // the exact-equality check also asserts the loser leaves no unhandled-rejection warning)
  335. expect(
  336. await execute(`
  337. const loser = (async () => { throw "lost" })()
  338. return await Promise.race([Promise.resolve("won"), loser])
  339. `),
  340. ).toEqual({ ok: true, value: "won", toolCalls: [] })
  341. })
  342. test("Promise.race([]) returns a promise whose CodeMode failure is catchable", async () => {
  343. // Sources:
  344. // test/built-ins/Promise/race/S25.4.4.3_A2.1_T1.js
  345. // test/built-ins/Promise/race/S25.4.4.3_A5.1_T1.js
  346. // (adapted: upstream requires Promise.race([]) to never settle; CodeMode intentionally
  347. // rejects with a catchable diagnostic instead of hanging, so this asserts CodeMode
  348. // divergence rather than the spec never-settles behavior)
  349. expect(
  350. await value(`
  351. const empty = Promise.race([])
  352. try {
  353. await empty
  354. return "settled"
  355. } catch (error) {
  356. return [empty instanceof Promise, error instanceof Error]
  357. }
  358. `),
  359. ).toEqual([true, true])
  360. })
  361. test("Promise.resolve passes the same CodeMode promise through nested chains", async () => {
  362. // Source: test/built-ins/Promise/resolve/S25.4.4.5_A2.2_T1.js
  363. // (adapted: no executor construction, and identity is observed with Array includes
  364. // because promises are not comparable data values in CodeMode)
  365. expect(
  366. await value(`
  367. const promise = Promise.resolve({ id: 1 })
  368. return [
  369. [promise].includes(Promise.resolve(promise)),
  370. [promise].includes(Promise.resolve(Promise.resolve(promise))),
  371. (await Promise.resolve(Promise.resolve(promise))).id,
  372. ]
  373. `),
  374. ).toEqual([true, true, 1])
  375. })
  376. test("Promise.resolve of a rejected promise preserves identity and reason", async () => {
  377. // Source: test/built-ins/Promise/resolve/S25.4.4.5_A2.3_T1.js
  378. // (adapted: the source promise is already rejected instead of rejected later)
  379. expect(
  380. await value(`
  381. const rejected = Promise.reject("oops")
  382. const adopted = Promise.resolve(rejected)
  383. const identity = [rejected].includes(adopted)
  384. try {
  385. await adopted
  386. return "fulfilled"
  387. } catch (reason) {
  388. return [identity, reason]
  389. }
  390. `),
  391. ).toEqual([true, "oops"])
  392. })
  393. test("Promise.reject uses a promise reason without flattening it", async () => {
  394. // Sources:
  395. // test/built-ins/Promise/reject-via-fn-immed.js
  396. // test/built-ins/Promise/reject-via-fn-deferred.js
  397. // (adapted: the promise reason goes through Promise.reject instead of executor reject)
  398. expect(
  399. await value(`
  400. const observe = async (reason) => {
  401. try {
  402. await Promise.reject(reason)
  403. return "fulfilled"
  404. } catch (caught) {
  405. const identity = [reason].includes(caught)
  406. try { return [identity, caught instanceof Promise, await caught] }
  407. catch (inner) { return [identity, caught instanceof Promise, "rethrew " + inner] }
  408. }
  409. }
  410. return [await observe(Promise.resolve(1)), await observe(Promise.reject("inner"))]
  411. `),
  412. ).toEqual([
  413. [true, true, 1],
  414. [true, true, "rethrew inner"],
  415. ])
  416. })
  417. })
  418. describe("Test262 async functions and await", () => {
  419. test("declaration, expression, and arrow forms return promises", async () => {
  420. // Sources:
  421. // test/language/statements/async-function/declaration-returns-promise.js
  422. // test/language/expressions/async-function/expression-returns-promise.js
  423. // test/language/expressions/async-arrow-function/arrow-returns-promise.js
  424. expect(
  425. await value(`
  426. async function declaration() { return 1 }
  427. const expression = async function() { return 2 }
  428. const arrow = async () => 3
  429. const promises = [declaration(), expression(), arrow()]
  430. return [promises.map((item) => item instanceof Promise), await Promise.all(promises)]
  431. `),
  432. ).toEqual([
  433. [true, true, true],
  434. [1, 2, 3],
  435. ])
  436. })
  437. test("async bodies adopt returns and reject throws before and after await", async () => {
  438. // Sources:
  439. // test/language/statements/async-function/evaluation-body.js
  440. // test/language/statements/async-function/evaluation-body-that-returns.js
  441. // test/language/statements/async-function/evaluation-body-that-returns-after-await.js
  442. // test/language/statements/async-function/evaluation-body-that-throws.js
  443. // test/language/statements/async-function/evaluation-body-that-throws-after-await.js
  444. expect(
  445. await value(`
  446. const order = []
  447. const plain = async () => { order.push("body"); return 42 }
  448. const afterAwait = async () => { await Promise.resolve(); return 43 }
  449. const throwsBefore = async () => { throw 1 }
  450. const throwsAfter = async () => { await Promise.resolve(); throw 2 }
  451. const observe = async (promise) => {
  452. try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] }
  453. }
  454. const first = plain()
  455. return [
  456. order,
  457. await observe(first),
  458. await observe(afterAwait()),
  459. await observe(throwsBefore()),
  460. await observe(throwsAfter()),
  461. ]
  462. `),
  463. ).toEqual([["body"], ["fulfilled", 42], ["fulfilled", 43], ["rejected", 1], ["rejected", 2]])
  464. })
  465. test("default-parameter throws reject instead of escaping the call", async () => {
  466. // Source: test/language/statements/async-function/evaluation-default-that-throws.js
  467. expect(
  468. await value(`
  469. const fail = () => { throw new Error("default") }
  470. const run = async (value = fail()) => value
  471. let returned = false
  472. try {
  473. const promise = run()
  474. returned = promise instanceof Promise
  475. await promise
  476. return [returned, "fulfilled"]
  477. } catch (error) {
  478. return [returned, error.message]
  479. }
  480. `),
  481. ).toEqual([true, "default"])
  482. })
  483. test("async try/finally completion records override earlier completion", async () => {
  484. // Sources: the try-{return,throw,reject}-finally-{return,throw,reject}.js matrix under
  485. // test/language/statements/async-function, test/language/expressions/async-function,
  486. // and test/language/expressions/async-arrow-function.
  487. expect(
  488. await value(`
  489. const observe = async (promise) => {
  490. try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] }
  491. }
  492. const returnReturn = async () => { try { return "early" } finally { return await Promise.resolve("override") } }
  493. const returnThrow = async () => { try { return "early" } finally { throw "override" } }
  494. const returnReject = async () => { try { return "early" } finally { await Promise.reject("override") } }
  495. const throwReturn = async () => { try { throw "early" } finally { return await Promise.resolve("override") } }
  496. const throwThrow = async () => { try { throw "early" } finally { throw "override" } }
  497. const throwReject = async () => { try { throw "early" } finally { await Promise.reject("override") } }
  498. const rejectReturn = async () => { try { await Promise.reject("early") } finally { return await Promise.resolve("override") } }
  499. const rejectThrow = async () => { try { await Promise.reject("early") } finally { throw "override" } }
  500. const rejectReject = async () => { try { await Promise.reject("early") } finally { await Promise.reject("override") } }
  501. return await Promise.all([
  502. observe(returnReturn()), observe(returnThrow()), observe(returnReject()),
  503. observe(throwReturn()), observe(throwThrow()), observe(throwReject()),
  504. observe(rejectReturn()), observe(rejectThrow()), observe(rejectReject()),
  505. ])
  506. `),
  507. ).toEqual([
  508. ["fulfilled", "override"],
  509. ["rejected", "override"],
  510. ["rejected", "override"],
  511. ["fulfilled", "override"],
  512. ["rejected", "override"],
  513. ["rejected", "override"],
  514. ["fulfilled", "override"],
  515. ["rejected", "override"],
  516. ["rejected", "override"],
  517. ])
  518. })
  519. test("await preserves an object whose then property is not callable", async () => {
  520. // Source: test/language/expressions/await/await-awaits-thenable-not-callable.js
  521. expect(
  522. await value(`
  523. const thenable = { then: 42 }
  524. return (await thenable) === thenable
  525. `),
  526. ).toBe(true)
  527. })
  528. test("await returns non-promise operands unchanged", async () => {
  529. // Source: test/language/expressions/await/await-non-promise.js
  530. // (adapted: only value pass-through is asserted here; the spec tick ordering around
  531. // await of non-promises is covered by the failing interleaving test below)
  532. expect(
  533. await value(`
  534. const object = { id: 1 }
  535. const array = [1, 2]
  536. return [
  537. await 1,
  538. await "text",
  539. await true,
  540. (await null) === null,
  541. (await undefined) === undefined,
  542. (await object) === object,
  543. (await array) === array,
  544. ]
  545. `),
  546. ).toEqual([1, "text", true, true, true, true, true])
  547. })
  548. })
  549. describe("Test262 expected Promise conformance", () => {
  550. for (const name of ["all", "allSettled", "race", "any"] as const) {
  551. test(`Promise.${name} rejects invalid input with TypeError`, async () => {
  552. // Sources:
  553. // test/built-ins/Promise/all/S25.4.4.1_A3.1_T1.js
  554. // test/built-ins/Promise/all/S25.4.4.1_A3.1_T2.js
  555. // test/built-ins/Promise/allSettled/iter-arg-is-number-reject.js
  556. // test/built-ins/Promise/race/iter-arg-is-number-reject.js
  557. // test/built-ins/Promise/any/iter-arg-is-number-reject.js
  558. expect(
  559. await value(`
  560. try {
  561. const promise = Promise.${name}(42)
  562. const returned = promise instanceof Promise
  563. await promise
  564. return [returned, "fulfilled"]
  565. } catch (error) {
  566. return [true, error.name]
  567. }
  568. `),
  569. ).toEqual([true, "TypeError"])
  570. })
  571. }
  572. test("Promise.all consumes sparse positions as undefined", async () => {
  573. // Source: test/built-ins/Array/from/from-array.js (array iterator hole behavior)
  574. expect(
  575. await value(`
  576. const input = []
  577. input[1] = 1
  578. const result = await Promise.all(input)
  579. return [result.length, result[0] === undefined, result[1]]
  580. `),
  581. ).toEqual([2, true, 1])
  582. })
  583. test("Promise.allSettled consumes sparse positions as undefined", async () => {
  584. // Source: test/built-ins/Array/from/from-array.js (array iterator hole behavior)
  585. expect(
  586. await value(`
  587. const input = []
  588. input[1] = 1
  589. const result = await Promise.allSettled(input)
  590. return [result.length, result[0].status, result[0].value === undefined, result[1]]
  591. `),
  592. ).toEqual([2, "fulfilled", true, { status: "fulfilled", value: 1 }])
  593. })
  594. test("Promise.race consumes a sparse first position as undefined", async () => {
  595. // Source: test/built-ins/Array/from/from-array.js (array iterator hole behavior)
  596. expect(
  597. await value(`
  598. const input = []
  599. input[1] = 1
  600. return (await Promise.race(input)) === undefined
  601. `),
  602. ).toBe(true)
  603. })
  604. test("Promise.any consumes a sparse first position as an undefined fulfillment", async () => {
  605. // Source: test/built-ins/Array/from/from-array.js (array iterator hole behavior)
  606. expect(
  607. await value(`
  608. const input = []
  609. input[1] = Promise.reject("loses")
  610. return (await Promise.any(input)) === undefined
  611. `),
  612. ).toBe(true)
  613. })
  614. test("Promise.all settles after reactions attached to its inputs", async () => {
  615. // Sources:
  616. // test/built-ins/Promise/all/S25.4.4.1_A7.2_T1.js
  617. // test/built-ins/Promise/all/S25.4.4.1_A8.1_T1.js
  618. expect(
  619. await value(`
  620. const sequence = [1]
  621. const input = Promise.resolve(1)
  622. const aggregate = Promise.all([input])
  623. aggregate.then(() => sequence.push(4))
  624. input.then(() => sequence.push(3)).then(() => sequence.push(5))
  625. sequence.push(2)
  626. await aggregate
  627. await Promise.resolve()
  628. return sequence
  629. `),
  630. ).toEqual([1, 2, 3, 4, 5])
  631. })
  632. test("Promise.allSettled settles after reactions attached to its inputs", async () => {
  633. // Sources:
  634. // test/built-ins/Promise/allSettled/resolved-sequence.js
  635. // test/built-ins/Promise/allSettled/resolved-sequence-extra-ticks.js
  636. // test/built-ins/Promise/allSettled/resolved-sequence-mixed.js
  637. // test/built-ins/Promise/allSettled/resolved-sequence-with-rejections.js
  638. expect(
  639. await value(`
  640. const sequence = [1]
  641. const input = Promise.resolve(1)
  642. const aggregate = Promise.allSettled([input])
  643. aggregate.then(() => sequence.push(4))
  644. input.then(() => sequence.push(3)).then(() => sequence.push(5))
  645. sequence.push(2)
  646. await aggregate
  647. await Promise.resolve()
  648. return sequence
  649. `),
  650. ).toEqual([1, 2, 3, 4, 5])
  651. })
  652. test("Promise.race settles in a reaction after its winning input", async () => {
  653. // Sources:
  654. // test/built-ins/Promise/race/S25.4.4.3_A6.1_T1.js
  655. // test/built-ins/Promise/race/resolved-sequence-extra-ticks.js
  656. expect(
  657. await value(`
  658. const sequence = [1]
  659. const race = Promise.race([1])
  660. race.then(() => sequence.push(4))
  661. Promise.resolve().then(() => sequence.push(3)).then(() => sequence.push(5))
  662. sequence.push(2)
  663. await race
  664. await Promise.resolve()
  665. return sequence
  666. `),
  667. ).toEqual([1, 2, 3, 4, 5])
  668. })
  669. test("then reactions route and propagate fulfillment and rejection", async () => {
  670. // Sources:
  671. // test/built-ins/Promise/prototype/then/prfm-fulfilled.js
  672. // test/built-ins/Promise/prototype/then/prfm-rejected.js
  673. // test/built-ins/Promise/prototype/then/rxn-handler-identity.js
  674. // test/built-ins/Promise/prototype/then/rxn-handler-thrower.js
  675. // test/built-ins/Promise/prototype/then/rxn-handler-fulfilled-return-normal.js
  676. // test/built-ins/Promise/prototype/then/rxn-handler-fulfilled-return-abrupt.js
  677. // test/built-ins/Promise/prototype/then/rxn-handler-rejected-return-normal.js
  678. // test/built-ins/Promise/prototype/then/rxn-handler-rejected-return-abrupt.js
  679. expect(
  680. await value(`
  681. const observe = async (promise) => {
  682. try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] }
  683. }
  684. return await Promise.all([
  685. observe(Promise.resolve(1).then((value) => value + 1)),
  686. observe(Promise.reject(2).then(undefined, (reason) => reason + 1)),
  687. observe(Promise.resolve(3).then(undefined)),
  688. observe(Promise.reject(4).then(undefined)),
  689. observe(Promise.resolve(5).then(() => { throw 6 })),
  690. observe(Promise.reject(7).then(undefined, () => { throw 8 })),
  691. ])
  692. `),
  693. ).toEqual([
  694. ["fulfilled", 2],
  695. ["fulfilled", 3],
  696. ["fulfilled", 3],
  697. ["rejected", 4],
  698. ["rejected", 6],
  699. ["rejected", 8],
  700. ])
  701. })
  702. test("then reactions preserve breadth-first queue order", async () => {
  703. // Source: test/built-ins/Promise/prototype/then/S25.4.4_A1.1_T1.js
  704. expect(
  705. await value(`
  706. const sequence = [1]
  707. const promise = Promise.resolve()
  708. const first = promise.then(() => sequence.push(3)).then(() => sequence.push(5)).then(() => sequence.push(7))
  709. const second = promise.then(() => sequence.push(4)).then(() => sequence.push(6)).then(() => sequence.push(8))
  710. sequence.push(2)
  711. await Promise.all([first, second])
  712. return sequence
  713. `),
  714. ).toEqual([1, 2, 3, 4, 5, 6, 7, 8])
  715. })
  716. test("then rejects direct self-resolution for fulfilled and rejected sources", async () => {
  717. // Sources:
  718. // test/built-ins/Promise/prototype/then/resolve-settled-fulfilled-self.js
  719. // test/built-ins/Promise/prototype/then/resolve-settled-rejected-self.js
  720. // test/built-ins/Promise/prototype/then/resolve-pending-fulfilled-self.js
  721. // test/built-ins/Promise/prototype/then/resolve-pending-rejected-self.js
  722. expect(
  723. await value(`
  724. const observe = async (promise) => {
  725. try { await promise; return "fulfilled" } catch (reason) { return reason.name }
  726. }
  727. let fulfilled
  728. let rejected
  729. fulfilled = Promise.resolve().then(() => fulfilled)
  730. rejected = Promise.reject().then(undefined, () => rejected)
  731. return await Promise.all([observe(fulfilled), observe(rejected)])
  732. `),
  733. ).toEqual(["TypeError", "TypeError"])
  734. })
  735. test("catch delegates rejection handling and preserves fulfillment", async () => {
  736. // Sources:
  737. // test/built-ins/Promise/prototype/catch/S25.4.5.1_A2.1_T1.js
  738. // test/built-ins/Promise/prototype/catch/S25.4.5.1_A3.1_T1.js
  739. // test/built-ins/Promise/prototype/catch/S25.4.5.1_A3.1_T2.js
  740. expect(
  741. await value(`
  742. return [
  743. await Promise.resolve(1).catch(() => 2),
  744. await Promise.reject(3).catch((reason) => reason + 1),
  745. ]
  746. `),
  747. ).toEqual([1, 4])
  748. })
  749. test("finally preserves or replaces the original settlement", async () => {
  750. // Sources:
  751. // test/built-ins/Promise/prototype/finally/resolution-value-no-override.js
  752. // test/built-ins/Promise/prototype/finally/rejection-reason-no-fulfill.js
  753. // test/built-ins/Promise/prototype/finally/rejection-reason-override-with-throw.js
  754. expect(
  755. await value(`
  756. const observe = async (promise) => {
  757. try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] }
  758. }
  759. return await Promise.all([
  760. observe(Promise.resolve(1).finally(() => 2)),
  761. observe(Promise.reject(3).finally(() => 4)),
  762. observe(Promise.reject(5).finally(() => { throw 6 })),
  763. ])
  764. `),
  765. ).toEqual([
  766. ["fulfilled", 1],
  767. ["rejected", 3],
  768. ["rejected", 6],
  769. ])
  770. })
  771. test("then ignores non-callable handlers", async () => {
  772. // Sources:
  773. // test/built-ins/Promise/prototype/then/S25.4.5.3_A4.1_T1.js
  774. // test/built-ins/Promise/prototype/then/S25.4.5.3_A4.1_T2.js
  775. // test/built-ins/Promise/prototype/then/S25.4.5.3_A5.1_T1.js
  776. // test/built-ins/Promise/prototype/then/S25.4.5.3_A5.2_T1.js
  777. // (adapted: only non-callable handlers are probed; callables that are not plain
  778. // functions, such as tool references, intentionally throw in CodeMode)
  779. expect(
  780. await value(`
  781. const observe = async (promise) => {
  782. try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] }
  783. }
  784. return await Promise.all([
  785. observe(Promise.resolve(1).then(2)),
  786. observe(Promise.resolve(4).then(null, null)),
  787. observe(Promise.resolve(5).then({}, "x")),
  788. observe(Promise.reject(3).then(null, "x")),
  789. observe(Promise.reject(6).then(7, {})),
  790. ])
  791. `),
  792. ).toEqual([
  793. ["fulfilled", 1],
  794. ["fulfilled", 4],
  795. ["fulfilled", 5],
  796. ["rejected", 3],
  797. ["rejected", 6],
  798. ])
  799. })
  800. test("finally waits for a returned promise and preserves or replaces settlement", async () => {
  801. // Sources:
  802. // test/built-ins/Promise/prototype/finally/resolved-observable-then-calls.js
  803. // test/built-ins/Promise/prototype/finally/rejected-observable-then-calls.js
  804. // test/built-ins/Promise/prototype/finally/resolution-value-no-override.js
  805. expect(
  806. await value(`
  807. const observe = async (promise) => {
  808. try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] }
  809. }
  810. const order = []
  811. const cleanup = async () => {
  812. await Promise.resolve()
  813. order.push("cleanup")
  814. }
  815. const settled = await Promise.resolve("kept").finally(() => cleanup())
  816. order.push("settled:" + settled)
  817. return [
  818. await observe(Promise.resolve(1).finally(() => Promise.resolve(99))),
  819. order,
  820. await observe(Promise.resolve(2).finally(() => Promise.reject(3))),
  821. await observe(Promise.reject(4).finally(() => Promise.resolve(99))),
  822. ]
  823. `),
  824. ).toEqual([
  825. ["fulfilled", 1],
  826. ["cleanup", "settled:kept"],
  827. ["rejected", 3],
  828. ["rejected", 4],
  829. ])
  830. })
  831. test("then adopts a returned rejected promise", async () => {
  832. // Sources:
  833. // test/built-ins/Promise/prototype/then/rxn-handler-fulfilled-return-abrupt.js
  834. // test/built-ins/Promise/resolve/resolve-promise.js
  835. // (adapted: the fulfillment handler returns an already-rejected promise instead of
  836. // throwing, and the rejection handler recovers with a fulfilled promise)
  837. expect(
  838. await value(`
  839. const observe = async (promise) => {
  840. try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] }
  841. }
  842. return await Promise.all([
  843. observe(Promise.resolve(1).then(() => Promise.reject("bad"))),
  844. observe(Promise.reject(2).then(undefined, () => Promise.resolve("ok"))),
  845. ])
  846. `),
  847. ).toEqual([
  848. ["rejected", "bad"],
  849. ["fulfilled", "ok"],
  850. ])
  851. })
  852. test("independent reactions on one source each observe the same settlement", async () => {
  853. // Source: test/built-ins/Promise/prototype/then/S25.4.4_A2.1_T1.js
  854. // (adapted: the multiple-reactions family is asserted through the values every
  855. // reaction returns instead of a shared completion counter)
  856. expect(
  857. await value(`
  858. const fulfilled = Promise.resolve(7)
  859. const rejected = Promise.reject(8)
  860. return await Promise.all([
  861. fulfilled.then((value) => "first:" + value),
  862. fulfilled.then((value) => "second:" + value),
  863. rejected.catch((reason) => "first:" + reason),
  864. rejected.catch((reason) => "second:" + reason),
  865. ])
  866. `),
  867. ).toEqual(["first:7", "second:7", "first:8", "second:8"])
  868. })
  869. test("await always resumes in a later reaction and interleaves async functions", async () => {
  870. // Sources:
  871. // test/language/expressions/await/async-await-interleaved.js
  872. // test/language/expressions/await/await-non-promise.js
  873. expect(
  874. await value(`
  875. const sequence = []
  876. const first = async () => { sequence.push("first:1"); await 0; sequence.push("first:2") }
  877. const second = async () => { sequence.push("second:1"); await 0; sequence.push("second:2") }
  878. await Promise.all([first(), second()])
  879. return sequence
  880. `),
  881. ).toEqual(["first:1", "second:1", "first:2", "second:2"])
  882. })
  883. test("an async function rejects when it resolves with its own promise", async () => {
  884. // Adapted from the self-resolution requirement represented by:
  885. // test/built-ins/Promise/resolve-self.js
  886. // test/built-ins/Promise/resolve/S25.4.4.5_A4.1_T1.js
  887. expect(
  888. await value(`
  889. let promise
  890. const run = async () => {
  891. await Promise.resolve()
  892. return promise
  893. }
  894. promise = run()
  895. try {
  896. await promise
  897. return "fulfilled"
  898. } catch (error) {
  899. return error.name
  900. }
  901. `),
  902. ).toBe("TypeError")
  903. })
  904. test("Promise.resolve recursively assimilates callable thenables", async () => {
  905. // Source: test/built-ins/Promise/resolve/resolve-thenable.js
  906. expect(
  907. await value(`
  908. const value = { id: 1 }
  909. const nested = { then: (resolve) => resolve(value) }
  910. const thenable = { then: (resolve) => resolve(nested) }
  911. return (await Promise.resolve(thenable)) === value
  912. `),
  913. ).toBe(true)
  914. })
  915. test("Promise combinators assimilate callable thenable inputs", async () => {
  916. // Sources:
  917. // test/built-ins/Promise/all/reject-immed.js
  918. // test/built-ins/Promise/all/reject-ignored-immed.js
  919. // test/built-ins/Promise/allSettled/reject-ignored-immed.js
  920. // test/built-ins/Promise/race/resolve-thenable.js
  921. expect(
  922. await value(`
  923. const fulfills = { then: (resolve) => resolve(1) }
  924. const rejects = { then: (_, reject) => reject(2) }
  925. const resolvesFirst = { then: (resolve, reject) => { resolve(3); reject(4) } }
  926. const observe = async (promise) => {
  927. try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason] }
  928. }
  929. return [
  930. await observe(Promise.all([fulfills, rejects])),
  931. await Promise.allSettled([fulfills, resolvesFirst]),
  932. await observe(Promise.race([rejects])),
  933. ]
  934. `),
  935. ).toEqual([
  936. ["rejected", 2],
  937. [
  938. { status: "fulfilled", value: 1 },
  939. { status: "fulfilled", value: 3 },
  940. ],
  941. ["rejected", 2],
  942. ])
  943. })
  944. test("await assimilates callable thenables", async () => {
  945. // Source: test/language/expressions/await/await-awaits-thenables.js
  946. expect(
  947. await value(`
  948. const thenable = { then: (resolve) => resolve(42) }
  949. return await thenable
  950. `),
  951. ).toBe(42)
  952. })
  953. test("await rejects when a callable thenable throws", async () => {
  954. // Source: test/language/expressions/await/await-awaits-thenables-that-throw.js
  955. expect(
  956. await value(`
  957. const error = { id: 1 }
  958. const thenable = { then: () => { throw error } }
  959. try {
  960. await thenable
  961. return false
  962. } catch (caught) {
  963. return caught === error
  964. }
  965. `),
  966. ).toBe(true)
  967. })
  968. test("thenable resolution is deferred and settles only once", async () => {
  969. // Sources:
  970. // test/built-ins/Promise/resolve/S25.Promise_resolve_foreign_thenable_2.js
  971. // test/built-ins/Promise/exception-after-resolve-in-thenable-job.js
  972. expect(
  973. await value(`
  974. const sequence = []
  975. const thenable = {
  976. then: (resolve, reject) => {
  977. sequence.push("then")
  978. resolve(1)
  979. reject(2)
  980. throw 3
  981. }
  982. }
  983. const promise = Promise.resolve(thenable)
  984. sequence.push("after resolve")
  985. const result = await promise
  986. sequence.push("after await")
  987. return [result, sequence]
  988. `),
  989. ).toEqual([1, ["after resolve", "then", "after await"]])
  990. })
  991. test("the first thenable rejection wins over later resolution and throws", async () => {
  992. // Source: test/built-ins/Promise/exception-after-resolve-in-thenable-job.js
  993. expect(
  994. await value(`
  995. const thenable = {
  996. then: (resolve, reject) => {
  997. reject("first")
  998. resolve("second")
  999. throw "third"
  1000. }
  1001. }
  1002. try {
  1003. await thenable
  1004. return "fulfilled"
  1005. } catch (reason) {
  1006. return reason
  1007. }
  1008. `),
  1009. ).toBe("first")
  1010. })
  1011. test("constructors, reactions, finally, and async returns assimilate thenables", async () => {
  1012. // Sources:
  1013. // test/built-ins/Promise/resolve-thenable-immed.js
  1014. // test/built-ins/Promise/prototype/then/resolve-settled-fulfilled-thenable.js
  1015. // test/built-ins/Promise/prototype/finally/resolved-observable-then-calls.js
  1016. const thenable = (value: string) => `({ then: (resolve) => resolve(${JSON.stringify(value)}) })`
  1017. expect(
  1018. await value(`
  1019. const fromAsync = async () => ${thenable("async")}
  1020. const cleanup = []
  1021. return await Promise.all([
  1022. new Promise((resolve) => resolve(${thenable("constructor")})),
  1023. Promise.resolve().then(() => ${thenable("reaction")}),
  1024. Promise.resolve("kept").finally(() => {
  1025. cleanup.push("ran")
  1026. return ${thenable("ignored")}
  1027. }),
  1028. fromAsync(),
  1029. ]).then((values) => [values, cleanup])
  1030. `),
  1031. ).toEqual([["constructor", "reaction", "kept", "async"], ["ran"]])
  1032. })
  1033. test("finally settles after its cleanup thenable reactions", async () => {
  1034. // Source: test/built-ins/Promise/prototype/finally/resolved-observable-then-calls-PromiseResolve.js
  1035. expect(
  1036. await value(`
  1037. const sequence = []
  1038. const cleanup = { then: (resolve) => { sequence.push("then"); resolve() } }
  1039. const result = Promise.resolve("kept").finally(() => cleanup)
  1040. result.then(() => sequence.push("finally"))
  1041. Promise.resolve()
  1042. .then(() => sequence.push("tick1"))
  1043. .then(() => sequence.push("tick2"))
  1044. .then(() => sequence.push("tick3"))
  1045. .then(() => sequence.push("tick4"))
  1046. await result
  1047. sequence.push("await")
  1048. return sequence
  1049. `),
  1050. ).toEqual(["tick1", "then", "tick2", "tick3", "tick4", "finally", "await"])
  1051. })
  1052. })
  1053. describe("Test262 Promise.any", () => {
  1054. test("is a callable static that returns a promise", async () => {
  1055. // Sources:
  1056. // test/built-ins/Promise/any/is-function.js
  1057. // test/built-ins/Promise/any/returns-promise.js
  1058. expect(
  1059. await value(`
  1060. const promise = Promise.any([1])
  1061. return [typeof Promise.any, promise instanceof Promise, await promise]
  1062. `),
  1063. ).toEqual(["function", true, 1])
  1064. })
  1065. test("fulfills with the first fulfilled member, ignoring rejections", async () => {
  1066. // Sources:
  1067. // test/built-ins/Promise/any/resolved-sequence-mixed.js
  1068. // test/built-ins/Promise/any/resolved-sequence-with-rejections.js
  1069. // test/built-ins/Promise/any/reject-ignored-immed.js
  1070. expect(
  1071. await value(`
  1072. return [
  1073. await Promise.any([Promise.reject("a"), Promise.resolve(1), Promise.resolve(2)]),
  1074. await Promise.any([Promise.reject("a"), "plain", Promise.reject("b")]),
  1075. ]
  1076. `),
  1077. ).toEqual([1, "plain"])
  1078. })
  1079. test("a fulfillment wins over a later rejection of another member", async () => {
  1080. // Sources:
  1081. // test/built-ins/Promise/any/resolve-ignores-late-rejection.js
  1082. // test/built-ins/Promise/any/resolve-ignores-late-rejection-deferred.js
  1083. expect(
  1084. await value(`
  1085. let rejectLate
  1086. const late = new Promise((_, reject) => { rejectLate = reject })
  1087. const result = await Promise.any([late, Promise.resolve("won")])
  1088. rejectLate("too late")
  1089. return result
  1090. `),
  1091. ).toBe("won")
  1092. })
  1093. test("rejects with an AggregateError carrying the reasons in input order", async () => {
  1094. // Sources:
  1095. // test/built-ins/Promise/any/reject-all-mixed.js
  1096. // test/built-ins/Promise/any/reject-immed.js
  1097. // test/built-ins/Promise/any/reject-deferred.js
  1098. expect(
  1099. await value(`
  1100. let rejectLate
  1101. const late = new Promise((_, reject) => { rejectLate = reject })
  1102. const aggregate = Promise.any([Promise.reject("first"), late, Promise.reject("third")])
  1103. rejectLate("second")
  1104. try {
  1105. await aggregate
  1106. return "fulfilled"
  1107. } catch (error) {
  1108. return {
  1109. isAggregate: error instanceof AggregateError,
  1110. isError: error instanceof Error,
  1111. name: error.name,
  1112. message: error.message,
  1113. errors: error.errors,
  1114. }
  1115. }
  1116. `),
  1117. ).toEqual({
  1118. isAggregate: true,
  1119. isError: true,
  1120. name: "AggregateError",
  1121. message: "All promises were rejected",
  1122. errors: ["first", "second", "third"],
  1123. })
  1124. })
  1125. test("rejects an empty input with an empty AggregateError", async () => {
  1126. // Source: test/built-ins/Promise/any/iter-arg-is-empty-iterable-reject.js
  1127. expect(
  1128. await value(`
  1129. try {
  1130. await Promise.any([])
  1131. return "fulfilled"
  1132. } catch (error) {
  1133. return [error instanceof AggregateError, error.errors.length]
  1134. }
  1135. `),
  1136. ).toEqual([true, 0])
  1137. })
  1138. test("consumes a string input as its characters", async () => {
  1139. // Source: test/built-ins/Promise/any/iter-arg-is-string-resolve.js
  1140. expect(await value(`return await Promise.any("abc")`)).toBe("a")
  1141. })
  1142. test("rejects an empty string input with an empty AggregateError", async () => {
  1143. // Source: test/built-ins/Promise/any/iter-arg-is-empty-string-reject.js
  1144. expect(
  1145. await value(`
  1146. try {
  1147. await Promise.any("")
  1148. return "fulfilled"
  1149. } catch (error) {
  1150. return [error instanceof AggregateError, error.errors.length]
  1151. }
  1152. `),
  1153. ).toEqual([true, 0])
  1154. })
  1155. test("fulfills with the first member that does not reject", async () => {
  1156. // Sources:
  1157. // test/built-ins/Promise/any/resolve-from-reject-catch.js
  1158. // test/built-ins/Promise/any/resolve-from-resolve-reject-catch.js
  1159. expect(
  1160. await value(`
  1161. return await Promise.any([
  1162. Promise.reject("a"),
  1163. new Promise((resolve, reject) => reject("b")),
  1164. Promise.all([Promise.reject("c")]),
  1165. Promise.resolve(Promise.reject("d").catch((reason) => reason)),
  1166. ])
  1167. `),
  1168. ).toBe("d")
  1169. })
  1170. test("settles after reactions attached to its inputs", async () => {
  1171. // Source: test/built-ins/Promise/any/resolved-sequence.js
  1172. expect(
  1173. await value(`
  1174. const sequence = [1]
  1175. const input = Promise.resolve(1)
  1176. const aggregate = Promise.any([input])
  1177. aggregate.then(() => sequence.push(4))
  1178. input.then(() => sequence.push(3)).then(() => sequence.push(5))
  1179. sequence.push(2)
  1180. await aggregate
  1181. await Promise.resolve()
  1182. return sequence
  1183. `),
  1184. ).toEqual([1, 2, 3, 4, 5])
  1185. })
  1186. })
  1187. describe("Test262 AggregateError", () => {
  1188. test("constructs from an errors collection and an optional message", async () => {
  1189. // Sources:
  1190. // test/built-ins/AggregateError/errors-iterabletolist.js
  1191. // test/built-ins/AggregateError/message-undefined-no-prop.js
  1192. expect(
  1193. await value(`
  1194. const input = ["x", "y"]
  1195. const withMessage = new AggregateError(input, "msg")
  1196. const bare = new AggregateError([])
  1197. return [
  1198. withMessage.name,
  1199. withMessage.message,
  1200. withMessage.errors,
  1201. withMessage.errors !== input,
  1202. withMessage instanceof AggregateError,
  1203. withMessage instanceof Error,
  1204. bare.message,
  1205. bare.errors,
  1206. ]
  1207. `),
  1208. ).toEqual(["AggregateError", "msg", ["x", "y"], true, true, true, "", []])
  1209. })
  1210. test("rejects a non-collection errors argument with TypeError", async () => {
  1211. // Source: test/built-ins/AggregateError/errors-iterabletolist-failures.js
  1212. expect(
  1213. await value(`
  1214. try {
  1215. new AggregateError(42)
  1216. return "constructed"
  1217. } catch (error) {
  1218. return error.name
  1219. }
  1220. `),
  1221. ).toBe("TypeError")
  1222. })
  1223. test("is callable without new", async () => {
  1224. // Source: test/built-ins/AggregateError/newtarget-is-undefined.js
  1225. expect(
  1226. await value(`
  1227. const error = AggregateError(["x"], "m")
  1228. return [error instanceof AggregateError, error instanceof Error, error.name, error.message, error.errors]
  1229. `),
  1230. ).toEqual([true, true, "AggregateError", "m", ["x"]])
  1231. })
  1232. test("coerces a non-string message to a string", async () => {
  1233. // Source: test/built-ins/AggregateError/message-method-prop-cast.js (value coercion only; the
  1234. // upstream object-with-toString case is omitted because CodeMode has no user toString dispatch)
  1235. expect(
  1236. await value(`
  1237. return [
  1238. new AggregateError([], 42).message,
  1239. new AggregateError([], false).message,
  1240. new AggregateError([], true).message,
  1241. new AggregateError([], null).message,
  1242. ]
  1243. `),
  1244. ).toEqual(["42", "false", "true", "null"])
  1245. })
  1246. })
  1247. describe("Test262 Promise constructor", () => {
  1248. test("constructs a promise, handing the executor callable resolve/reject", async () => {
  1249. // Sources:
  1250. // test/built-ins/Promise/constructor.js
  1251. // test/built-ins/Promise/exec-args.js
  1252. expect(
  1253. await value(`
  1254. let observed
  1255. const promise = new Promise((resolve, reject) => {
  1256. observed = [typeof resolve, typeof reject]
  1257. resolve("done")
  1258. })
  1259. return [promise instanceof Promise, observed, await promise]
  1260. `),
  1261. ).toEqual([true, ["function", "function"], "done"])
  1262. })
  1263. test("a missing or non-callable executor is a TypeError", async () => {
  1264. // Source: test/built-ins/Promise/executor-not-callable.js
  1265. expect(
  1266. await value(`
  1267. const outcomes = []
  1268. for (const make of [() => new Promise(), () => new Promise(1), () => new Promise({})]) {
  1269. try {
  1270. make()
  1271. outcomes.push("constructed")
  1272. } catch (error) {
  1273. outcomes.push(error.name)
  1274. }
  1275. }
  1276. return outcomes
  1277. `),
  1278. ).toEqual(["TypeError", "TypeError", "TypeError"])
  1279. })
  1280. test("resolves immediately or later through an escaping resolver", async () => {
  1281. // Sources:
  1282. // test/built-ins/Promise/resolve-non-thenable-immed.js
  1283. // test/built-ins/Promise/resolve-non-thenable-deferred.js
  1284. // test/built-ins/Promise/create-resolving-functions-resolve.js
  1285. expect(
  1286. await value(`
  1287. let settle
  1288. const deferred = new Promise((resolve) => { settle = resolve })
  1289. const immediate = new Promise((resolve) => resolve("now"))
  1290. settle("later")
  1291. return [await immediate, await deferred]
  1292. `),
  1293. ).toEqual(["now", "later"])
  1294. })
  1295. test("rejects through reject and through an abrupt executor completion", async () => {
  1296. // Sources:
  1297. // test/built-ins/Promise/reject-via-fn-immed.js
  1298. // test/built-ins/Promise/reject-via-abrupt.js
  1299. expect(
  1300. await value(`
  1301. const observe = async (promise) => {
  1302. try { return ["fulfilled", await promise] } catch (reason) { return ["rejected", reason.message ?? reason] }
  1303. }
  1304. return [
  1305. await observe(new Promise((_, reject) => reject("nope"))),
  1306. await observe(new Promise(() => { throw new Error("boom") })),
  1307. ]
  1308. `),
  1309. ).toEqual([
  1310. ["rejected", "nope"],
  1311. ["rejected", "boom"],
  1312. ])
  1313. })
  1314. test("only the first settlement counts", async () => {
  1315. // Sources:
  1316. // test/built-ins/Promise/reject-ignored-via-fn-immed.js
  1317. // test/built-ins/Promise/resolve-ignored-via-fn-immed.js
  1318. expect(
  1319. await value(`
  1320. return [
  1321. await new Promise((resolve, reject) => { resolve("first"); reject("second"); resolve("third") }),
  1322. await new Promise((resolve) => { resolve(resolve("inner") === undefined ? "unreached" : "also unreached") }),
  1323. ]
  1324. `),
  1325. ).toEqual(["first", "inner"])
  1326. })
  1327. test("escaped resolvers keep first-settle-wins in both directions", async () => {
  1328. // Sources:
  1329. // test/built-ins/Promise/reject-ignored-via-fn-deferred.js
  1330. // test/built-ins/Promise/resolve-ignored-via-fn-deferred.js
  1331. expect(
  1332. await value(`
  1333. let resolveRejected, rejectRejected
  1334. const rejected = new Promise((resolve, reject) => { resolveRejected = resolve; rejectRejected = reject })
  1335. rejectRejected("first")
  1336. const lateResolve = resolveRejected("late")
  1337. let resolveFulfilled, rejectFulfilled
  1338. const fulfilled = new Promise((resolve, reject) => { resolveFulfilled = resolve; rejectFulfilled = reject })
  1339. resolveFulfilled()
  1340. const lateReject = rejectFulfilled(new Promise(() => {}))
  1341. try {
  1342. await rejected
  1343. return "fulfilled"
  1344. } catch (reason) {
  1345. return [reason, lateResolve === undefined, (await fulfilled) === undefined, lateReject === undefined]
  1346. }
  1347. `),
  1348. ).toEqual(["first", true, true, true])
  1349. })
  1350. test("a queued reaction chain observes a later rejection through a handler-less then", async () => {
  1351. // Sources:
  1352. // test/built-ins/Promise/reject-via-fn-immed-queue.js
  1353. // test/built-ins/Promise/reject-via-fn-deferred-queue.js
  1354. // test/built-ins/Promise/reject-via-abrupt-queue.js
  1355. expect(
  1356. await value(`
  1357. const observe = (promise) => promise.then(() => "wrong").then(() => "also wrong", (reason) => "caught:" + reason)
  1358. let reject
  1359. const deferred = new Promise((_, r) => { reject = r })
  1360. const chained = observe(deferred)
  1361. reject("boom")
  1362. return [
  1363. await observe(new Promise((_, r) => r("immed"))),
  1364. await chained,
  1365. await observe(new Promise(() => { throw "abrupt" })),
  1366. ]
  1367. `),
  1368. ).toEqual(["caught:immed", "caught:boom", "caught:abrupt"])
  1369. })
  1370. test("an exception after resolve is ignored", async () => {
  1371. // Source: test/built-ins/Promise/exception-after-resolve-in-executor.js
  1372. expect(await value(`return await new Promise((resolve) => { resolve("kept"); throw new Error("dropped") })`)).toBe(
  1373. "kept",
  1374. )
  1375. })
  1376. test("resolving with a promise adopts its settlement", async () => {
  1377. // Sources:
  1378. // test/built-ins/Promise/resolve-thenable-immed.js (promise-adoption portion)
  1379. // test/built-ins/Promise/all/S25.4.4.1_A2.3_T1.js (resolution adoption semantics)
  1380. expect(
  1381. await value(`
  1382. const adoptedValue = await new Promise((resolve) => resolve(Promise.resolve("adopted")))
  1383. try {
  1384. await new Promise((resolve) => resolve(Promise.reject("bad")))
  1385. return [adoptedValue, "fulfilled"]
  1386. } catch (reason) {
  1387. return [adoptedValue, reason]
  1388. }
  1389. `),
  1390. ).toEqual(["adopted", "bad"])
  1391. })
  1392. test("resolving with the promise itself rejects with TypeError", async () => {
  1393. // Source: test/built-ins/Promise/resolve-self.js
  1394. expect(
  1395. await value(`
  1396. let settle
  1397. const promise = new Promise((resolve) => { settle = resolve })
  1398. settle(promise)
  1399. try {
  1400. await promise
  1401. return "fulfilled"
  1402. } catch (error) {
  1403. return error.name
  1404. }
  1405. `),
  1406. ).toBe("TypeError")
  1407. })
  1408. test("executor runs synchronously before the constructor returns", async () => {
  1409. // Source: test/built-ins/Promise/executor-call-context-strict.js (synchronous Call(executor) step)
  1410. expect(
  1411. await value(`
  1412. const sequence = []
  1413. sequence.push("before")
  1414. new Promise((resolve) => { sequence.push("executor"); resolve() })
  1415. sequence.push("after")
  1416. return sequence
  1417. `),
  1418. ).toEqual(["before", "executor", "after"])
  1419. })
  1420. test("calling Promise without new throws TypeError", async () => {
  1421. // Source: test/built-ins/Promise/undefined-newtarget.js
  1422. expect(
  1423. await value(`
  1424. try {
  1425. Promise(() => {})
  1426. return "called"
  1427. } catch (error) {
  1428. return error.name
  1429. }
  1430. `),
  1431. ).toBe("TypeError")
  1432. })
  1433. })