Skip to content

[await-dictionary] add failure case and edge case tests#5041

Open
danialasaria wants to merge 2 commits intotc39:mainfrom
danialasaria:dasaria/await-dictionary-keyed-failure-edge-tests
Open

[await-dictionary] add failure case and edge case tests#5041
danialasaria wants to merge 2 commits intotc39:mainfrom
danialasaria:dasaria/await-dictionary-keyed-failure-edge-tests

Conversation

@danialasaria
Copy link
Copy Markdown
Contributor

@danialasaria danialasaria commented Apr 29, 2026

Summary

This PR adds the remaining failure-case and edge-case coverage for the await-dictionary proposal's Promise keyed combinators:

  • Promise.allKeyed
  • Promise.allSettledKeyed

It follows the runtime/behavioral tests PR (#4932) and completes the testing plan from #4886.

What this PR covers

NewPromiseCapability failures (sync throws)

  • Constructor body throws (ctx-ctor-throws.js)
  • Executor does not provide callable resolve/reject in various combinations (capability-executor-not-callable.js)

GetPromiseResolve failures (async rejections)

  • Get(constructor, "resolve") accessor throws (invoke-resolve-get-error-reject.js)
  • Calling the resolved promiseResolve function throws (invoke-resolve-error-reject.js)

Invoke(then) failures (async rejections)

  • Get(nextPromise, "then") accessor throws (invoke-then-get-error-reject.js)
  • Calling nextPromise.then(...) throws (invoke-then-error-reject.js)

allKeyed rejection ordering

  • Second promise to settle is rejected (reject-second.js)
  • Last promise to settle is rejected (reject-last.js)

Synchronous thenables (step 8 edge case)

  • Thenables that synchronously invoke onFulfilled during the loop, causing remainingElementsCount to reach zero before the loop exits (resolve-before-loop-exit.js)

Exotic object abrupt completions (Proxy)

  • [[OwnPropertyKeys]] throws (ownkeys-throws.js)
  • [[GetOwnProperty]] throws (getownproperty-throws.js)
  • [[GetOwnProperty]] returns undefined -- key is skipped (getownproperty-returns-undefined.js)

Property descriptor filtering

  • Non-enumerable keys are skipped (getownproperty-not-enumerable.js)

Notes

  • All async tests use asyncTest from asyncHelpers.js per reviewer feedback on [await-dictionary] add runtime keyed Promise combinator tests #4932
  • Sync tests use assert.throws(...)
  • All tests are feature-gated with features: [await-dictionary]; Proxy tests additionally gate on Proxy
  • Tests mirror existing patterns from Promise.all / Promise.allSettled test suites
  • capability-executor-not-callable includes poisoned resolve getters (matching Promise.all counterpart) to detect wrong evaluation order

What this PR does not cover

This completes all items from the testing plan (#4886).

References

Cover remaining items from the testing plan (tc39#4886):

- NewPromiseCapability: constructor throws, executor not callable
- GetPromiseResolve: Get accessor throws
- promiseResolve call throws during iteration
- allKeyed rejection ordering (2nd and last to settle)
- Synchronous thenables hitting remainingElementsCount step 8
- Exotic object: [[OwnPropertyKeys]] throws
- Exotic object: [[GetOwnProperty]] throws
- Exotic object: [[GetOwnProperty]] returns undefined (key skipped)
- capability-executor-not-callable: add Object.defineProperty poison on
  'resolve' to detect wrong evaluation order, add fn6 case (Number, String)
- invoke-then-error-reject: new test for Invoke(nextPromise, "then") throwing
- invoke-then-get-error-reject: new test for Get(nextPromise, "then") throwing
- resolve-before-loop-exit: switch Object.keys to Reflect.ownKeys
- getownproperty-not-enumerable: new test for non-enumerable keys being skipped

Made-with: Cursor
@danialasaria danialasaria force-pushed the dasaria/await-dictionary-keyed-failure-edge-tests branch from 578f02d to 6e2fa0c Compare April 29, 2026 18:05
@danialasaria danialasaria marked this pull request as ready for review April 29, 2026 18:11
@danialasaria danialasaria requested a review from a team as a code owner April 29, 2026 18:11
Copy link
Copy Markdown
Contributor

@acutmore acutmore left a comment

Choose a reason for hiding this comment

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

Could we also have a test that a custom constructor is being 'constructed' rather than 'called'.

If we can use new.target we can check like:

var error = new Test262Error();
function Constructor(executor) {
  if (new.target !== Constructor) {
    throw error
  }
}

We can also have assertions that validate the arguments passed to a custom constructor are correct: should only be one argument: the executor. And it should be a function with length 2.


Promise.allSettledKeyed.call(Constructor, input);

assert.sameValue(callCount, 1, "callCount after call to allSettledKeyed()");
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.

Could we either move the callCount to after the assertions, or have a separate count for the start and end of the callback. Otherwise technically the assertions could reject and they are swallowed by the function caller yet the test would pass (unless the harness has some tracking logic to guarantee a test fails if a harness assertion errors, rather than relying on the exception bubbling up)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants