diff --git a/odoo/addons/base/tests/test_http_case.py b/odoo/addons/base/tests/test_http_case.py index b0323091a24..85b09f3a8a3 100644 --- a/odoo/addons/base/tests/test_http_case.py +++ b/odoo/addons/base/tests/test_http_case.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.tests.common import HttpCase, tagged -from odoo.tools import mute_logger, logging +from odoo.tests.common import HttpCase, tagged, ChromeBrowser +from odoo.tools import config, logging from unittest.mock import patch @tagged('-at_install', 'post_install') @@ -47,5 +47,23 @@ class TestHttpCase(HttpCase): if text == 'test successful': continue self.assertEqual(text, "Object(custom=Object, value=1, description='dummy')") - console_log_count +=1 + console_log_count += 1 self.assertEqual(console_log_count, 1) + + +@tagged('-at_install', 'post_install') +class TestChromeBrowser(HttpCase): + def setUp(self): + super().setUp() + screencasts_dir = config['screencasts'] or config['screenshots'] + with patch.dict('odoo.tools.config.options', {'screencasts': screencasts_dir, 'screenshots': config['screenshots']}): + self.browser = ChromeBrowser(self) + self.addCleanup(self.browser.stop) + + def test_screencasts(self): + self.browser.start_screencast() + self.browser.navigate_to('about:blank') + self.browser._wait_ready() + code = "setTimeout(() => console.log('test successful'), 2000); setInterval(() => document.body.innerText = (new Date()).getTime(), 100);" + self.browser._wait_code_ok(code, 10) + self.browser._save_screencast() diff --git a/odoo/tests/common.py b/odoo/tests/common.py index efda46aa811..f569cbca658 100644 --- a/odoo/tests/common.py +++ b/odoo/tests/common.py @@ -957,9 +957,11 @@ class ChromeBrowser: def stop(self): if hasattr(self, 'ws'): self._websocket_send('Page.stopScreencast') - if self.screencasts_dir and os.path.isdir(self.screencasts_frames_dir): + if self.screencasts_dir: + screencasts_frames_dir = self.screencasts_frames_dir self.screencasts_dir = None - shutil.rmtree(self.screencasts_frames_dir) + if os.path.isdir(screencasts_frames_dir): + shutil.rmtree(screencasts_frames_dir, ignore_errors=True) self._websocket_request('Page.stopLoading') self._websocket_request('Runtime.evaluate', params={'expression': """ @@ -1345,6 +1347,8 @@ which leads to stray network requests and inconsistencies.""" wait() def _handle_screencast_frame(self, sessionId, data, metadata): + if not self.screencasts_frames_dir: + return self._websocket_send('Page.screencastFrameAck', params={'sessionId': sessionId}) if not self.screencasts_dir: return @@ -1391,6 +1395,8 @@ which leads to stray network requests and inconsistencies.""" self._logger.debug('No screencast frames to encode') return None + self.stop_screencast() + for f in self.screencast_frames: with open(f['file_path'], 'rb') as b64_file: frame = base64.decodebytes(b64_file.read()) @@ -1418,7 +1424,11 @@ which leads to stray network requests and inconsistencies.""" duration = end_time - self.screencast_frames[i]['timestamp'] concat_file.write("file '%s'\nduration %s\n" % (frame_file_path, duration)) concat_file.write("file '%s'" % frame_file_path) # needed by the concat plugin - r = subprocess.run([ffmpeg_path, '-intra', '-f', 'concat','-safe', '0', '-i', concat_script_path, '-pix_fmt', 'yuv420p', outfile]) + try: + subprocess.run([ffmpeg_path, '-f', 'concat', '-safe', '0', '-i', concat_script_path, '-pix_fmt', 'yuv420p', '-g', '0', outfile], check=True) + except subprocess.CalledProcessError: + self._logger.error('Failed to encode screencast.') + return self._logger.log(25, 'Screencast in: %s', outfile) else: outfile = outfile.strip('.mp4') @@ -1429,6 +1439,9 @@ which leads to stray network requests and inconsistencies.""" assert self.screencasts_dir self._websocket_send('Page.startScreencast') + def stop_screencast(self): + self._websocket_send('Page.stopScreencast') + def set_cookie(self, name, value, path, domain): params = {'name': name, 'value': value, 'path': path, 'domain': domain} self._websocket_request('Network.setCookie', params=params) @@ -1440,7 +1453,8 @@ which leads to stray network requests and inconsistencies.""" self._websocket_request('Network.deleteCookies', params=params) return - def _wait_ready(self, ready_code, timeout=60): + def _wait_ready(self, ready_code=None, timeout=60): + ready_code = ready_code or "document.readyState === 'complete'" self._logger.info('Evaluate ready code "%s"', ready_code) start_time = time.time() result = None @@ -1809,7 +1823,6 @@ class HttpCase(TransactionCase): # Needed because tests like test01.js (qunit tests) are passing a ready # code = "" - ready = ready or "document.readyState === 'complete'" self.assertTrue(browser._wait_ready(ready), 'The ready "%s" code was always falsy' % ready) error = False