Bug 1213731 - Rework Youtube puppeteer to work with both youtube and embedded youtube. r=maja_zf, a=test-only
authorBryce Van Dyk <bvandyk@mozilla.com>
Tue, 30 Aug 2016 16:25:01 +1200
changeset 348043 1624eb2ef70cc9b2fe719fbbb10e4deeb7863a1b
parent 348042 6d7d2556e60603fcb6031a87d8bdd4da265174b2
child 348044 c6766e0626d69cfc359588176e2e877656e1c32a
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmaja_zf, test-only
bugs1213731
milestone50.0a2
Bug 1213731 - Rework Youtube puppeteer to work with both youtube and embedded youtube. r=maja_zf, a=test-only 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