Bug 1213731 - Rework Youtube puppeteer to work with both youtube and embedded youtube. r?maja_zf draft
authorBryce Van Dyk <bvandyk@mozilla.com>
Tue, 30 Aug 2016 16:25:01 +1200
changeset 407277 b2abd5fc6a32fa899c20c5c8892d81f2ae7ecffb
parent 404651 bd7645928990649c84609d3f531e803c2d41f269
child 407278 0b3cba6f1834d0ff9b96f7a0f4f0bf2d64199710
push id27918
push userbvandyk@mozilla.com
push dateTue, 30 Aug 2016 06:57:24 +0000
reviewersmaja_zf
bugs1213731
milestone51.0a1
Bug 1213731 - Rework Youtube puppeteer to work with both youtube and embedded youtube. r?maja_zf Rework the Youtube puppeteer to look up player and video element based on class names, instead of ID. This means that the tests can work with embedded players. This has the benefit that we can use youtube embedded links (youtube.com/embedded/<videoId>), which do not suffer from auto play related issues (auto play jumping to another video). MozReview-Commit-ID: 9UFyL7di6gH
dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py
--- a/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py
+++ b/dom/media/test/external/external_media_tests/media_utils/youtube_puppeteer.py
@@ -10,19 +10,26 @@ from marionette import Marionette
 from marionette_driver import By, expected, Wait
 from marionette_driver.errors import TimeoutException, NoSuchElementException
 from video_puppeteer import VideoPuppeteer, VideoException
 from external_media_tests.utils import verbose_until
 
 
 class YouTubePuppeteer(VideoPuppeteer):
     """
-    Wrapper around a YouTube #movie_player element.
+    Wrapper around a YouTube .html5-video-player element.
 
-    Partial reference: https://developers.google.com/youtube/js_api_reference.
+    Can be used with youtube videos or youtube videos at embedded URLS. E.g.
+    both https://www.youtube.com/watch?v=AbAACm1IQE0 and
+    https://www.youtube.com/embed/AbAACm1IQE0 should work.
+
+    Using an embedded video has the advantage of not auto-playing more videos
+    while a test is running.
+
+    Partial reference: https://developers.google.com/youtube/iframe_api_reference.
     This reference is useful for site-specific features such as interacting
     with ads, or accessing YouTube's debug data.
     """
 
     _yt_player_state = {
         'UNSTARTED': -1,
         'ENDED': 0,
         'PLAYING': 1,
@@ -32,24 +39,26 @@ class YouTubePuppeteer(VideoPuppeteer):
     }
     _yt_player_state_name = {v: k for k, v in _yt_player_state.items()}
     _time_pattern = re.compile('(?P<minute>\d+):(?P<second>\d+)')
 
     def __init__(self, marionette, url, **kwargs):
         self.player = None
         super(YouTubePuppeteer,
               self).__init__(marionette, url,
-                             video_selector='#movie_player video',
+                             video_selector='.html5-video-player video',
                              **kwargs)
         wait = Wait(self.marionette, timeout=30)
         with self.marionette.using_context(Marionette.CONTEXT_CONTENT):
             verbose_until(wait, self,
-                          expected.element_present(By.ID, 'movie_player'))
-            self.player = self.marionette.find_element(By.ID, 'movie_player')
-            self.marionette.execute_script("log('#movie_player "
+                          expected.element_present(By.CLASS_NAME,
+                                                   'html5-video-player'))
+            self.player = self.marionette.find_element(By.CLASS_NAME,
+                                                       'html5-video-player')
+            self.marionette.execute_script("log('.html5-video-player "
                                            "element obtained');")
         # When an ad is playing, self.player_duration indicates the duration
         # of the spliced-in ad stream, not the duration of the main video, so
         # we attempt to skip the ad first.
         for attempt in range(5):
             sleep(1)
             self.process_ad()
             if (self.ad_inactive and self.duration and not
@@ -112,31 +121,31 @@ class YouTubePuppeteer(VideoPuppeteer):
                 return loads(text)
             except ValueError:
                 self.marionette.log('Error loading json: DebugText',
                                     level='DEBUG')
 
     def execute_yt_script(self, script):
         """
         Execute JS script in content context with access to video element and
-        YouTube #movie_player element.
+        YouTube .html5-video-player element.
 
         :param script: script to be executed.
 
         :return: value returned by script
         """
         with self.marionette.using_context(Marionette.CONTEXT_CONTENT):
             return self.marionette.execute_script(script,
                                                   script_args=[self.video,
                                                                self.player])
 
     @property
     def playback_quality(self):
         """
-        Please see https://developers.google.com/youtube/js_api_reference#Playback_quality
+        Please see https://developers.google.com/youtube/iframe_api_reference#Playback_quality
         for valid values.
 
         :return: A string with a valid value returned via YouTube.
         """
         return self.execute_yt_script('return arguments[1].'
                                       'wrappedJSObject.getPlaybackQuality();')
 
     @property
@@ -171,29 +180,29 @@ class YouTubePuppeteer(VideoPuppeteer):
         return self.execute_yt_script('return arguments[1].'
                                       'wrappedJSObject.getVideoUrl();')
 
     @property
     def player_state(self):
         """
 
         :return: The YouTube state of the video. See
-         https://developers.google.com/youtube/js_api_reference#getPlayerState
+         https://developers.google.com/youtube/iframe_api_reference#getPlayerState
          for valid values.
         """
         state = self.execute_yt_script('return arguments[1].'
                                        'wrappedJSObject.getPlayerState();')
         return state
 
     @property
     def player_unstarted(self):
         """
         This and the following properties are based on the
         player.getPlayerState() call
-        (https://developers.google.com/youtube/js_api_reference#Playback_status)
+        (https://developers.google.com/youtube/iframe_api_reference#Playback_status)
 
         :return: True if the video has not yet started.
         """
         return self.player_state == self._yt_player_state['UNSTARTED']
 
     @property
     def player_ended(self):
         """
@@ -235,17 +244,17 @@ class YouTubePuppeteer(VideoPuppeteer):
         return self.player_state == self._yt_player_state['CUED']
 
     @property
     def ad_state(self):
         """
         Get state of current ad.
 
         :return: Returns one of the constants listed in
-         https://developers.google.com/youtube/js_api_reference#Playback_status
+         https://developers.google.com/youtube/iframe_api_reference#Playback_status
          for an ad.
 
         """
         # Note: ad_state is sometimes not accurate, due to some sort of lag?
         return self.execute_yt_script('return arguments[1].'
                                       'wrappedJSObject.getAdState();')
 
     @property
@@ -345,17 +354,17 @@ class YouTubePuppeteer(VideoPuppeteer):
         """
         if self.ad_playing:
             self.marionette.log('Waiting while ad plays')
             sleep(10)
         else:
             # no ad playing
             return False
         if self.ad_skippable:
-            selector = '#movie_player .videoAdUiSkipContainer'
+            selector = '.html5-video-player .videoAdUiSkipContainer'
             wait = Wait(self.marionette, timeout=30)
             try:
                 with self.marionette.using_context(Marionette.CONTEXT_CONTENT):
                     wait.until(expected.element_displayed(By.CSS_SELECTOR,
                                                           selector))
                     ad_button = self.marionette.find_element(By.CSS_SELECTOR,
                                                              selector)
                     ad_button.click()
@@ -373,17 +382,17 @@ class YouTubePuppeteer(VideoPuppeteer):
         :return: ad duration in seconds, if currently displayed in player
         """
         if not (self.ad_playing or self.player_measure_progress() == 0):
             return None
         # If the ad is not Flash...
         if (self.ad_playing and self.video_src.startswith('mediasource') and
                 self.duration):
             return self.duration
-        selector = '#movie_player .videoAdUiAttribution'
+        selector = '.html5-media-player .videoAdUiAttribution'
         wait = Wait(self.marionette, timeout=5)
         try:
             with self.marionette.using_context(Marionette.CONTEXT_CONTENT):
                 wait.until(expected.element_present(By.CSS_SELECTOR,
                                                     selector))
                 countdown = self.marionette.find_element(By.CSS_SELECTOR,
                                                          selector)
                 ad_time = self._time_pattern.search(countdown.text)
@@ -459,28 +468,28 @@ class YouTubePuppeteer(VideoPuppeteer):
             return False
 
     def __str__(self):
         messages = [super(YouTubePuppeteer, self).__str__()]
         if self.player:
             player_state = self._yt_player_state_name[self.player_state]
             ad_state = self._yt_player_state_name[self.ad_state]
             messages += [
-                '#movie_player: {',
+                '.html5-media-player: {',
                 '\tvideo id: {0},'.format(self.movie_id),
                 '\tvideo_title: {0}'.format(self.movie_title),
                 '\tcurrent_state: {0},'.format(player_state),
                 '\tad_state: {0},'.format(ad_state),
                 '\tplayback_quality: {0},'.format(self.playback_quality),
                 '\tcurrent_time: {0},'.format(self.player_current_time),
                 '\tduration: {0},'.format(self.player_duration),
                 '}'
             ]
         else:
-            messages += ['\t#movie_player: None']
+            messages += ['\t.html5-media-player: None']
         return '\n'.join(messages)
 
 
 def playback_started(yt):
     """
     Check whether playback has started.
 
     :param yt: YouTubePuppeteer