Skip to content

[await-dictionary] add runtime keyed Promise combinator tests#4932

Merged
ptomato merged 6 commits intotc39:mainfrom
danialasaria:dasaria/await-dictionary-keyed-runtime-tests
Apr 7, 2026
Merged

[await-dictionary] add runtime keyed Promise combinator tests#4932
ptomato merged 6 commits intotc39:mainfrom
danialasaria:dasaria/await-dictionary-keyed-runtime-tests

Conversation

@danialasaria
Copy link
Copy Markdown
Contributor

@danialasaria danialasaria commented Feb 18, 2026

Summary

This PR adds the first runtime/behavioral coverage for the await-dictionary proposal’s Promise keyed combinators:

  • Promise.allKeyed
  • Promise.allSettledKeyed

It follows the initial boilerplate-only PR and focuses on high-signal success/failure semantics and ordering behavior.

What this PR covers

Shared coverage for both methods

For both Promise.allKeyed and Promise.allSettledKeyed, this PR adds tests for:

  • rejecting when promises is not an Object (primitive matrix: undefined, null, number, string, boolean, symbol)
  • throwing when invoked with a non-constructor receiver (NewPromiseCapability path)
  • rejecting when the constructor’s resolve is non-callable (GetPromiseResolve / IfAbruptRejectPromise path)
  • resolving {} to an empty null-prototype result object
  • ignoring non-enumerable own properties
  • preserving output key order from property enumeration order (not settlement order)

Promise.allKeyed-specific

  • rejection from a synchronously rejected input promise
  • rejection from an asynchronously rejected input promise

Promise.allSettledKeyed-specific

  • all-fulfilled keyed results with { status: "fulfilled", value }
  • all-rejected keyed results with { status: "rejected", reason }
  • mixed fulfilled/rejected keyed results with correct per-key shapes

Notes

  • All tests are feature-gated with features: [await-dictionary].
  • New tests mirror existing naming/style patterns used in Promise/all and Promise/allSettled.
  • These tests intentionally validate keyed combinator result-object behavior (including null-prototype output), which differs from array-based combinators.

What this PR does not cover

This PR intentionally leaves some deeper edge paths for follow-up work, including:

  • constructor-throws / malformed promise capability variants
  • promiseResolve throwing or returning a non-thenable
  • exotic object abrupt completions in [[OwnPropertyKeys]] / [[GetOwnProperty]]
  • synchronous-thenable remainingElementsCount step-8 edge behavior
  • additional low-level closure/idempotency stress cases

References

Cover shared failure/success behavior for Promise.allKeyed and Promise.allSettledKeyed, including non-object rejection, non-constructor context, key ordering, and allSettled result-shape outcomes.
@danialasaria danialasaria force-pushed the dasaria/await-dictionary-keyed-runtime-tests branch from d6a6c31 to 50702d4 Compare February 23, 2026 16:22
…nt coverage

- Change key-order-preserved settle sequence to 2nd, 3rd, 1st to avoid false
  passes from reverse-order implementations
- Use Reflect.ownKeys instead of Object.keys in empty-object assertions for
  exhaustive own-key verification
- Add separate BigInt rejection tests gated behind BigInt feature flag
@danialasaria danialasaria force-pushed the dasaria/await-dictionary-keyed-runtime-tests branch from 607531a to 589f4da Compare February 26, 2026 19:20
@danialasaria danialasaria marked this pull request as ready for review March 2, 2026 17:00
@danialasaria danialasaria requested a review from a team as a code owner March 2, 2026 17:00
@danialasaria danialasaria marked this pull request as draft March 4, 2026 18:31
- prototype-keys-ignored: inherited enumerable properties are excluded
  ([[OwnPropertyKeys]] returns only own keys)
- symbol-keys: enumerable symbol-keyed properties are included in the
  result alongside string keys
- arg-is-function: functions with enumerable own properties are accepted
  as valid object arguments; built-in non-enumerable properties excluded
@danialasaria danialasaria force-pushed the dasaria/await-dictionary-keyed-runtime-tests branch from fd62c35 to 6c64e5d Compare March 4, 2026 19:04
@danialasaria danialasaria marked this pull request as ready for review March 4, 2026 19:30
Copy link
Copy Markdown
Contributor

@ptomato ptomato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Just a few minor comments.

For future reference, the tests are more readable when using async helpers. (And would avoid the $DONE called twice situation) No need to go back and change these unless you really want to, though.

- Adopt asyncHelpers.js across all 23 async test files
- Replace manual .then($DONE, $DONE) with asyncTest wrapper
- Convert TypeError rejection tests to assert.throwsAsync for
  more robust error checking
- Fixes double-$DONE bug in resolve-not-callable-reject-with-typeerror
@danialasaria danialasaria force-pushed the dasaria/await-dictionary-keyed-runtime-tests branch from 6d6ab23 to 89ed7e3 Compare April 2, 2026 20:10
@danialasaria
Copy link
Copy Markdown
Contributor Author

Thanks for the review @ptomato! Converted all async tests to use asyncTest / assert.throwsAsync from asyncHelpers.js -- fixes the double-$DONE issue and should be easier to read going forward. null and undefined should have been already covered in the chain (lines 28-29).

@@ -0,0 +1,36 @@
// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was this file supposed to be named reject-immediately?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either one works for me, we usually don't sweat the filenames too much as long as they are descriptive (and not spec references like "13.1.9.step5.js" or "fail-RequireInternalSlot-step2.js")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im happy either way too. Mostly just checking it wasn't a typo.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a typo! Went with the shorter form, but happy to rename if there's a preference.


var sym = Symbol('s');
var input = { str: Promise.resolve(1) };
input[sym] = Promise.resolve(2);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to also have a non-enumerable symbol. Then we've covered all 4 combinations of string/symbols and enumerable/non-enumerable.

Copy link
Copy Markdown
Contributor Author

@danialasaria danialasaria Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea--added a non-enumerable symbol to both symbol-keys.js files so we cover all 4 cases.

Copy link
Copy Markdown
Contributor

@ptomato ptomato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍, thanks!

We can merge after addressing Ashley's comment, or merge now and leave it for a followup, whatever you prefer.

Covers all 4 combinations of string/symbol and enumerable/non-enumerable.
@danialasaria
Copy link
Copy Markdown
Contributor Author

👍, thanks!

We can merge after addressing Ashley's comment, or merge now and leave it for a followup, whatever you prefer.

Addressed! Should be good to merge now

@ptomato ptomato merged commit b9a76eb into tc39:main Apr 7, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants