From 668c476acbbe7cbe580a762b7647bea9b3d35c3b Mon Sep 17 00:00:00 2001 From: Valera Rozuvan Date: Wed, 2 Apr 2014 16:58:25 +0300 Subject: [PATCH] Add auto screenshot functionality. --- common/djangoapps/terrain/browser.py | 68 ++++++++++++++++++++ lms/djangoapps/courseware/features/common.py | 38 +++++++++++ lms/djangoapps/courseware/features/video.py | 1 - rakelib/tests.rake | 3 +- test_root/log/auto_screenshots/.gitkeep | 0 5 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 test_root/log/auto_screenshots/.gitkeep diff --git a/common/djangoapps/terrain/browser.py b/common/djangoapps/terrain/browser.py index 6edd11528c..6d7c2b3184 100644 --- a/common/djangoapps/terrain/browser.py +++ b/common/djangoapps/terrain/browser.py @@ -163,6 +163,16 @@ def reset_data(scenario): world.absorb({}, 'scenario_dict') +@before.each_scenario +def configure_screenshots(scenario): + """ + Before each scenario, turn off automatic screenshots. + + Args: str, scenario. Name of current scenario. + """ + world.auto_capture_screenshots = False + + @after.each_scenario def clear_data(scenario): world.spew('scenario_dict') @@ -184,6 +194,23 @@ def reset_databases(scenario): xmodule.modulestore.django.clear_existing_modulestores() +@world.absorb +def capture_screenshot(image_name): + """ + Capture a screenshot outputting it to a defined directory. + This function expects only the name of the file. It will generate + the full path of the output screenshot. + + If the name contains spaces, they ill be converted to underscores. + """ + output_dir = '{}/log/auto_screenshots'.format(settings.TEST_ROOT) + image_name = '{}/{}.png'.format(output_dir, image_name.replace(' ', '_')) + try: + world.browser.driver.save_screenshot(image_name) + except WebDriverException: + LOGGER.error("Could not capture a screenshot '{}'".format(image_name)) + + @after.each_scenario def screenshot_on_error(scenario): """ @@ -198,6 +225,47 @@ def screenshot_on_error(scenario): LOGGER.error('Could not capture a screenshot') +def capture_screenshot_for_step(step, when): + """ + Useful method for debugging acceptance tests that are run in Vagrant. + This method runs automatically before and after each step of an acceptance + test scenario. The variable: + + world.auto_capture_screenshots + + either enables or disabled the taking of screenshots. To change the + variable there is a convenient step defined: + + I (enable|disable) auto screenshots + + If you just want to capture a single screenshot at a desired point in code, + you should use the method: + + world.capture_screenshot("image_name") + """ + if world.auto_capture_screenshots: + scenario_num = step.scenario.feature.scenarios.index(step.scenario) + step_num = step.scenario.steps.index(step) + 1 + step_func_name = step.defined_at.function.func_name + image_name = "{prefix:03d}__{num}__{name}__{postfix}".format( + prefix=scenario_num, + num=step_num, + name=step_func_name, + postfix=when + ) + world.capture_screenshot(image_name) + + +@before.each_step +def before_each_step(step): + capture_screenshot_for_step(step, 'before') + + +@after.each_step +def after_each_step(step): + capture_screenshot_for_step(step, 'after') + + @after.harvest def teardown_browser(total): """ diff --git a/lms/djangoapps/courseware/features/common.py b/lms/djangoapps/courseware/features/common.py index 0c30044300..99501bfd5a 100644 --- a/lms/djangoapps/courseware/features/common.py +++ b/lms/djangoapps/courseware/features/common.py @@ -3,6 +3,8 @@ from __future__ import absolute_import +import time + from lettuce import world, step from lettuce.django import django_url from django.contrib.auth.models import User @@ -18,6 +20,42 @@ from logging import getLogger logger = getLogger(__name__) +@step('I (.*) capturing of screenshots before and after each step$') +def configure_screenshots_for_all_steps(_step, action): + """ + A step to be used in *.feature files. Enables/disables + automatic saving of screenshots before and after each step in a + scenario. + """ + action=action.strip() + if action == 'enable': + world.auto_capture_screenshots = True + elif action == 'disable': + world.auto_capture_screenshots = False + else: + raise ValueError('Parameter `action` should be one of "enable" or "disable".') + + +def capture_screenshot_before_after(func): + """ + A decorator that will take a screenshot before and after the applied + function is run. Use this if you do not want to capture screenshots + for each step in a scenario, but rather want to debug a single function. + """ + def inner(*args, **kwargs): + prefix=round(time.time() * 1000) + + world.capture_screenshot("{}_{}_{}".format( + prefix, func.func_name, 'before' + )) + ret_val=func(*args, **kwargs) + world.capture_screenshot("{}_{}_{}".format( + prefix, func.func_name, 'after' + )) + return ret_val + return inner + + @step(u'The course "([^"]*)" exists$') def create_course(_step, course): diff --git a/lms/djangoapps/courseware/features/video.py b/lms/djangoapps/courseware/features/video.py index 4b47c40580..aab009bee7 100644 --- a/lms/djangoapps/courseware/features/video.py +++ b/lms/djangoapps/courseware/features/video.py @@ -629,4 +629,3 @@ def click_on_the_caption(_step, index, expected_time): find_caption_line_by_data_index(int(index)).click() actual_time = elapsed_time() assert int(expected_time) == actual_time - diff --git a/rakelib/tests.rake b/rakelib/tests.rake index 2a5a84da8c..49aabcaea0 100644 --- a/rakelib/tests.rake +++ b/rakelib/tests.rake @@ -51,9 +51,10 @@ def run_tests(system, report_dir, test_id=nil, stop_on_failure=true) end task :clean_test_files do - desc "Clean fixture files used by tests and .pyc files" + desc "Clean fixture files used by tests, .pyc files, and automatic screenshots" sh("git clean -fqdx test_root/logs test_root/data test_root/staticfiles test_root/uploads") sh("find . -type f -name \"*.pyc\" -delete") + sh("rm -rf test_root/log/auto_screenshots/*") end task :clean_reports_dir => REPORT_DIR do diff --git a/test_root/log/auto_screenshots/.gitkeep b/test_root/log/auto_screenshots/.gitkeep new file mode 100644 index 0000000000..e69de29bb2