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

fix: throw error on muted resolution rejection during autoplay #7293

Merged
merged 1 commit into from
Jun 30, 2021

Conversation

roman-bc-dev
Copy link
Contributor

@roman-bc-dev roman-bc-dev commented Jun 29, 2021

Description

When autoplay is set to any or muted and muted() returns false, video.js attempts the following workflow:

  1. Call play(). If resolved, trigger autoplay-success
  2. On rejection, save original muted() value and set it to true
  3. Call play() again. If resolved, trigger autoplay-success
  4. On a second rejection, we reset muted() back to the saved value
  5. Ideally, the following .then method in the promise chain would be skipped and the autoplay-failure event would be triggered in the final .catch.

Since we .catch at multiple points along the promise chain, it's possible for step 4 to occur while still resolving to the following .then method, incorrectly triggering an autoplay-success event, instead of the expected autoplay-failure.

Specific Changes proposed

  1. Rethrow an error after the muted() value is reset in step 4, causing the following .next method to be skipped and the autoplay-failure event to be triggered in the final .catch in the promise chain.
  2. Add a try-catch to the mock "rejected" promise used in the autoplay.test suite to synchronize promise chains. This fixed an issue where the new error was being swallowed up by QUnit, which interrupted the final .then().catch() methods and generated false failures.
  3. Amended a function name and added some comments for clarity.

Requirements Checklist

  • Feature implemented / Bug fixed
  • If necessary, more likely in a feature request than a bug fix
    • Change has been verified in an actual browser (Chrome, Firefox, IE)
    • Unit Tests updated or fixed
    • Docs/guides updated
    • Example created (starter template on JSBin)
  • Reviewed by Two Core Contributors

@welcome
Copy link

welcome bot commented Jun 29, 2021

💖 Thanks for opening this pull request! 💖

Things that will help get your PR across the finish line:

  • Run npm run lint -- --errors locally to catch formatting errors earlier.
  • Include tests when adding/changing behavior.
  • Include screenshots and animated GIFs whenever possible.

We get a lot of pull requests on this repo, so please be patient and we will get back to you as soon as we can.

@codecov
Copy link

codecov bot commented Jun 29, 2021

Codecov Report

Merging #7293 (d2241ca) into main (a221be1) will increase coverage by 0.00%.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #7293   +/-   ##
=======================================
  Coverage   79.44%   79.45%           
=======================================
  Files         115      115           
  Lines        7264     7266    +2     
  Branches     1745     1746    +1     
=======================================
+ Hits         5771     5773    +2     
  Misses       1493     1493           
Impacted Files Coverage Δ
src/js/player.js 86.51% <100.00%> (+0.01%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a221be1...d2241ca. Read the comment docs.

Copy link
Contributor

@brandonocasey brandonocasey left a comment

Choose a reason for hiding this comment

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

Code looks good. Do we need another test for this case?

@gkatsev
Copy link
Member

gkatsev commented Jun 30, 2021

The test does catch this. Without wrapping the fn() call in our rejectPromise fake, it fails spectacularly.

fn();
} catch (err) {
return this;
}
return this;
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe this (line 46) should return this.resolvePromise here instead of this? I believe that way if a catch() throws it will trigger the next catch, and if it doesn't throw it will trigger the next then().

Copy link
Member

Choose a reason for hiding this comment

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

Returning this.resolvePromise here changes the expectations of the rejectPromise, this may end up requiring a lot of other test changes that may not be worth it right now.

return mutedPromise.catch(restoreMuted);
return mutedPromise.catch(err => {
restoreMuted();
throw new Error(`Rejection at manualAutoplay. Restoring muted value. ${err ? err : ''}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

If I remove this line that throws the error, I would expect these tests to fail since the promise on line 1479 should resolve and result in an 'autoplay-success' event and increment this.counts.success rather than this.counts.failure, however the test still passes which suggests it isn't capturing the test case exactly. It might be necessary to modify this.rejectPromise for these tests. I've shared one idea in another comment but I'm not positive it will work.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe what we need is a separate promise fake for these two tests?

Copy link
Member

Choose a reason for hiding this comment

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

I tried it locally, and I think it might be too complicated to get it in now.

Copy link
Contributor

Choose a reason for hiding this comment

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

In that case how about adding a TODO comment to address the false positive issue with these two tests?

Copy link
Member

Choose a reason for hiding this comment

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

Spent some time playing around with sinon's promises, and probably needs a complete rewrite of the test to use that.
I'll create an issue for this.

Copy link
Member

Choose a reason for hiding this comment

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

@gkatsev gkatsev mentioned this pull request Jun 30, 2021
@gkatsev gkatsev merged commit f9fb1d3 into videojs:main Jun 30, 2021
@welcome
Copy link

welcome bot commented Jun 30, 2021

Congrats on merging your first pull request! 🎉🎉🎉

@roman-bc-dev roman-bc-dev deleted the autoplay-err branch June 30, 2021 17:26
edirub pushed a commit to edirub/video.js that referenced this pull request Jun 8, 2023
…js#7293)

Previously, when autoplay was set to any or muted, we would accidentally swallow the autoplay rejection when we reset the muted state back to what it was. Instead, we want to re-throw the error.
To get it working, we also had to update our tests to try/catch in our fake promise.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants