Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add start and end hooks to the fireEvent helper #1185

Merged
merged 39 commits into from
Apr 4, 2022

Conversation

shamrt
Copy link
Contributor

@shamrt shamrt commented Jan 14, 2022

REVIEWER NOTE: Use of Hide whitespace option recommended.

Following suggestions in #1182 and #1183, the following PR:

  • converts the fireEvent helper to a Promise in order to support the hooks system
  • adds start and end hooks to the fireEvent helper
  • further adds start:[eventType] and end:[eventType] hooks to the fireEvent helper for finer control
  • "promisifies" all functions that use the fireEvent helper
  • adds internal test helpers for asserting the event hooks are accessible
  • adds tests for the new hooks in all relevant DOM helpers

Shane Martin added 30 commits January 13, 2022 10:23
@scalvert scalvert self-requested a review January 14, 2022 22:12
@scalvert
Copy link
Contributor

Thanks for doing this, @shamrt. Would you mind taking a look at the failing CI?

Error: addon-test-support/@ember/test-helpers/dom/tab.ts(233,11): error TS7030: Not all code paths return a value.

@scalvert
Copy link
Contributor

In broad strokes this changes seems really great. One issue is I think there may be unrelated changes included in this PR, which makes it a tad more difficult to review. Is there anything we can pull out as a separate, discrete change?

@shamrt
Copy link
Contributor Author

shamrt commented Jan 15, 2022

Is there anything we can pull out as a separate, discrete change?

I'd say there are a couple of changes that aren't necessary to this PR, such as:

  • switches from let to const
  • refactors to tests not strictly related to the new hooks

I'll revert those changes. If you can think of any others, please let me know.

})
.then(({ focusTarget, previousFocusedElement }) => {
if (!focusTarget) {
throw new Error('There was a previously focused element');
Copy link
Contributor Author

@shamrt shamrt Jan 15, 2022

Choose a reason for hiding this comment

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

Throwing an error here (and later catching) in order to mimic the behaviour previously handled by a return statement.

@shamrt
Copy link
Contributor Author

shamrt commented Jan 15, 2022

Alright, I've reverted a few things. Hopefully that tightens up the PR a bit.

@shamrt
Copy link
Contributor Author

shamrt commented Jan 18, 2022

Checking in to see whether I've covered everything for this PR. I think it's ready for re-review. Thanks!

@scalvert
Copy link
Contributor

@shamrt I'll take another look. Thanks so much for simplifying!

Comment on lines +100 to +101
.then(() => runHooks('fireEvent', 'start', element))
.then(() => runHooks(`fireEvent:${eventType}`, 'start', element))
Copy link
Contributor

Choose a reason for hiding this comment

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

I like the flexibility this provides. It's a nice composition of general to specific!

)
.then(() => settled());
})
.catch(() => {});
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we not let the exception propagate and fail the test if an exception is thrown?

Copy link
Contributor Author

@shamrt shamrt Jan 19, 2022

Choose a reason for hiding this comment

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

I mention in the comment above that the thrown error is a stand-in for the earlier return statement (see L62 of original code). Given we simply want to short-circuit the Promise chain, I didn't see any purpose in processing or propagating the error.

Additionally, given this function is being executed in a nested Promise within the focus function (see L155 of new code), the parent Promise chain doesn't know whether the child Promise resolved or was rejected (AFAIK).

This should result in the same behaviour we had when this function was synchronous. It doesn't look or feel great, but it gets the job done.

That said, I'm open to suggestions!

@scalvert
Copy link
Contributor

Overall this looks really good. It's crazy how much simpler the code would be with async/await...

@shamrt
Copy link
Contributor Author

shamrt commented Jan 31, 2022

@rwjblue Any chance you'll have time to review this PR this week? Thanks!

@mansona
Copy link
Member

mansona commented Apr 7, 2022

Hey folks 👋 I'm just pinging here in case we didn't see my issue from yesterday: #1210

I don't know how widespread this is going to be but if a lot of people are going to hit this problem it might be good to move on it sooner rather than later. Let me know your thoughts 👍

@shamrt
Copy link
Contributor Author

shamrt commented Apr 8, 2022

Hi @mansona. I'm very sorry this happened!

I'm taking a look at the issue today, which I've been able to replicate. Hopefully I can come up with a patch for you.

@mansona
Copy link
Member

mansona commented Apr 11, 2022

No worries, thanks for looking into it @shamrt 🎉

Can you expand a bit what the difference is? and how wide-reaching the regression is likely to be? I'm trying to figure out if this is going to be a widespread enough issue that we should really revert sooner rather than later and add the fix to an updated PR. I don't really know enough about the issue to be able to judge but maybe you have an idea, especially since you have been able to replicate the issue

@scalvert
Copy link
Contributor

@shamrt any more info on what the issue is?

@shamrt
Copy link
Contributor Author

shamrt commented Apr 11, 2022

I'm still digging into it at work. The apparent source is unclear, but I'll post progress either way tomorrow

@shamrt
Copy link
Contributor Author

shamrt commented Apr 12, 2022

TL;DR: I've spent a solid chunk of time trying to debug this issue, but have run up against a wall, and have been forced to time-box it at work.


Debug and reproduction steps:

  • Does update to @ember.test-helpers@2.6.0 result in breakage?
    • No
  • Does update to @ember.test-helpers@2.7.0 result in breakage?
    • Yes
  • Check whether it's a focus issue (__focus__ is executed during some clicks)
    • not a focus issue
  • Trace performed actions with browser debugger
    • looks like the library now performs a "double-click" on L710 with the click(document.body) strategy — once with mousedown dispatch, then with click dispatch
    • Open question: Does the Promisification negate the need for the mousedown and mouseup events in __click__?
      • Seems not
    • With old version of @ember/test-helpers (v2.4.2), only the click event performs an actual click event, disappearing the popover
      • selector: body
      • actions:
        • mousedown on body
        • blur on button:nth-child(2)
        • focusout on button:nth-child(2)
        • mouseup on body
        • click on body
      • result: one dialog disappears
    • in v2.7.0:
      • selector: body
      • actions:
        • mousedown on body (one dialog disappears)
        • blur on button:nth-child(2) (NOT RUN)
        • focusout on button:nth-child(2) (NOT RUN)
        • mouseup on body
        • click on body (second dialog disappears)
  • Step through code in browser to trace why __blur__ not being run in v2.7.0
    • previousFocusedElement is button:nth-child(2) and focusTarget is null, which performs blur and focusout events

    • Something in the mousedown event is causing the Dialog to close

    • Seems to be something running in backburner.js and executed by this._boundAutorunEnd (this._end) that closes the Dialog

    • Could be there's something added to Ember's event loop that causes an error or the "double-click" behaviour

    • Digging further, I've observed the following error, followed by a this.component.destroy() execution, which closes the Dialog:

       'Error
       at Backburner.schedule (http://localhost:7357/assets/vendor.js:60158:32)
       at schedule (http://localhost:7357/assets/vendor.js:42214:24)
       at scheduleDestroy (http://localhost:7357/assets/vendor.js:8880:29)
       at http://localhost:7357/assets/vendor.js:43613:75
       at iterate (http://localhost:7357/assets/vendor.js:43504:7)
       at destroy (http://localhost:7357/assets/vendor.js:43613:5)
       at iterate (http://localhost:7357/assets/vendor.js:43504:7)
       at destroy (http://localhost:7357/assets/vendor.js:43611:5)
       at iterate (http://localhost:7357/assets/vendor.js:43501:9)
       at destroy (http://localhost:7357/assets/vendor.js:43611:5)'
    • I don't know enough about the guts of Ember to be able to debug this, or understand why it's occurring.

  • Pull back from debugging, and instead simplify @ember/test-helpers implementation locally using async/await
    • Converted most helpers so comparison of the logic with @ember/test-helpers@2.6.0 is as straightforward as possible.
    • Run yarn prepack in @ember/test-helpers and link with ember-headlessui
    • Issue still present in ember-headlessui
    • Disable runHooks in fire-event.ts
    • Issue still present in ember-headlessui

I'm at a complete loss, at this point, as to why this issue has sprung up. It appears to be a legitimate edge case, but I'm flummoxed.


Side-question for the library maintainers: Given the library now only supports Ember 3.8, Node 10 supports async/await, and Node 10+ has been supported since Ember 3.1.3, is there a reason Node <8 support been hasn't dropped? Doing so would facilitate the move to async/await. I assume there are Ember 3.8 projects out there still using Node 8?

@shamrt
Copy link
Contributor Author

shamrt commented Apr 12, 2022

Here's a GIF of the issue when it occurs

ember-test-helpers-issue

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.

None yet

4 participants