-
Notifications
You must be signed in to change notification settings - Fork 256
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: content can play too early after contentupdate #318
Fix: content can play too early after contentupdate #318
Conversation
…e handler in preroll?, reset _pausedOnContentupdate flag on playing
@@ -666,6 +670,10 @@ const contribAdsPlugin = function(options) { | |||
if (player.paused()) { | |||
this.state = 'content-set'; | |||
} else { | |||
// If we get here we missed the 'play' event, so pause the player so | |||
// that content doesn't start playing immediately after the source change | |||
player.pause(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we may want to pause the player if it's not paused yet and then go to the same state content-set
.
If I understand correctly, the reason this else block will go to ads-ready?
is that there is an assumption that the play
event has been missed. So once the player is paused, we should be in the same state that causes the if clause to change state to content-set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From an offline conversation we came to the conclusion that the state transition to ads-ready?
is needed still.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think my main uncertainty is around postroll behavior. If the contentupdate
event happens after a postroll, do we have the correct behavior?
Also, this should add some unit tests! 😄
Once that's out of the way I'll be happy to re-review
test/test.ads.js
Outdated
@@ -729,6 +729,45 @@ QUnit.test('adsready in content-playback triggers readyforpreroll', function(ass | |||
assert.strictEqual(spy.getCall(0).args[0].type, 'readyforpreroll', 'readyforpreroll should have been triggered.'); | |||
}); | |||
|
|||
QUnit.test('contentupdate in preroll? transitions to content-set', function(assert) { | |||
assert.expect(2); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe all these tests should trigger loadstart
before play as well, to make sure the player is setup correctly
test/test.ads.js
Outdated
assert.expect(2); | ||
|
||
this.player.trigger('play'); | ||
this.player.trigger('adtimeout'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just do this.players.ads.skipLinearAdMode()
here, or explicitly do startLinearAdMode()
and endLinearAdMode()
right after.
test/test.ads.js
Outdated
assert.expect(4); | ||
|
||
this.player.trigger('play'); | ||
this.player.trigger('adtimeout'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same feedback for this line as the above test
src/plugin.js
Outdated
@@ -435,6 +436,9 @@ const contribAdsPlugin = function(options) { | |||
}, | |||
nopreroll() { | |||
this.state = 'content-playback'; | |||
}, | |||
contentupdate() { | |||
this.state = 'content-set'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a loadstart/contentupdate for the initial source. Is there any guarantee that this isn't that event? Specifically, if the play / adsready came in before loadstart.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I'm not sure there is any such guarantee. We could add a safeguard, something like:
contentupdate() {
// only change states if this is not the first loadstart/contentupdate
if (player.ads._hasThereBeenALoadStartDuringPlayerLife) {
this.state = 'content-set';
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll need to test it to make sure _hasThereBeenALoadStartDuringPlayerLife
is set, it's also set on loadstart so the order of event handlers matters here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In hindsight, this check won't have any value for subsequent videos in a playlist since _hasThereBeenALoadStartDuringPlayerLife
will be true for the life of the player after the first loadstart
, as the name suggests :P. Will need to find another way
test/test.ads.js
Outdated
this.player.trigger('adsready'); | ||
assert.strictEqual(this.player.ads.state, 'preroll?'); | ||
this.player.trigger('contentupdate'); | ||
assert.strictEqual(this.player.ads.state, 'content-set'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should avoid tests that assert the state. Firstly, they will break very shortly. Secondly, it is preferable to test things that indicate that the player is functioning correctly. For example, making sure it is paused if it should be paused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If that's the case, I'll remove these first two tests in their entirety since they are just testing that certain state transitions occur under the proper conditions. Is that what you would recommend in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, it does seem like the third test covers the functionality you've implemented here.
test/test.ads.js
Outdated
@@ -729,6 +729,46 @@ QUnit.test('adsready in content-playback triggers readyforpreroll', function(ass | |||
assert.strictEqual(spy.getCall(0).args[0].type, 'readyforpreroll', 'readyforpreroll should have been triggered.'); | |||
}); | |||
|
|||
QUnit.test('contentupdate in preroll? transitions to content-set', function(assert) { | |||
assert.expect(2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend avoiding using expect
unless your code has assertions in a loop. For simple procedural tests, the expect doesn't add value and requires maintenance if the test is updated.
test/test.ads.js
Outdated
sinon.spy(this.player, 'pause'); | ||
this.player.trigger('contentupdate'); | ||
assert.strictEqual(this.player.ads.state, 'ads-ready?'); | ||
assert.ok(this.player.pause.calledOnce, 'player was paused'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assertions on pause.calledOnce and _pausedOnContentUpdate are good, you can remove the assertions on the state. The test remains just as valuable and would survive the refactor.
@@ -729,6 +729,18 @@ QUnit.test('adsready in content-playback triggers readyforpreroll', function(ass | |||
assert.strictEqual(spy.getCall(0).args[0].type, 'readyforpreroll', 'readyforpreroll should have been triggered.'); | |||
}); | |||
|
|||
QUnit.test('contentupdate in content-playback transitions to ads-ready? and pauses player if not already paused', function(assert) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: remove state reference from test name
sinon.spy(this.player, 'pause'); | ||
this.player.trigger('contentupdate'); | ||
assert.ok(this.player.pause.calledOnce, 'player was paused'); | ||
assert.ok(this.player.ads._pausedOnContentupdate, '_pausedOnContentupdate is true'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we also test that the play happens?
@@ -666,6 +667,10 @@ const contribAdsPlugin = function(options) { | |||
if (player.paused()) { | |||
this.state = 'content-set'; | |||
} else { | |||
// If we get here we missed the 'play' event, so pause the player so | |||
// that content doesn't start playing immediately after the source change | |||
player.pause(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about if contentupdate for the initial source happens during content-playback? This can happen with autoplay.
This fix has been moved into the refactor. Closing. |
Description
This addresses a couple of issues surrounding contentupdates:
contentupdate
contentupdate
event handler in the 'preroll?' state, so if one occurs we remain in ‘preroll?’ through the source change. And because the ad timeout isn’t cleared, the eventualadtimeout
event causes a transition directly into ‘content-playback’, bypassing 'content-set' / 'ads-ready?' / 'ads-ready'EDIT: We are going to wait to address no.2 in the upcoming refactor
Specific Additions
contentupdate
occurs, pause the player if it is not already paused.contentupdate()
handler in 'preroll?' that changes state to 'content-set'As mentioned above: we'll hold off on no.2 until the refactor