Commit 66e2d062 by Christine Lytwynec

Merge pull request #12060 from edx/clytwynec/ac-367

Clytwynec/ac 367
parents 0c319efb 6e93cee2
......@@ -58,6 +58,7 @@ jscover.log
jscover.log.*
.tddium*
common/test/data/test_unicode/static/
test_root/courses/
django-pyfs
### Installation artifacts
......
......@@ -219,6 +219,17 @@ class AutoAuthEnabledTestCase(UrlResetMixin, TestCase):
self.assertTrue(response.url.endswith(url_pattern)) # pylint: disable=no-member
def test_redirect_to_specified(self):
# Create user and redirect to specified url
url_pattern = '/u/test#about_me'
response = self._auto_auth({
'username': 'test',
'redirect_to': url_pattern,
'staff': 'true',
}, status_code=302)
self.assertTrue(response.url.endswith(url_pattern)) # pylint: disable=no-member
def _auto_auth(self, params=None, status_code=None, **kwargs):
"""
Make a request to the auto-auth end-point and check
......
......@@ -1907,8 +1907,9 @@ def auto_auth(request):
* `course_id`: Enroll the student in the course with `course_id`
* `roles`: Comma-separated list of roles to grant the student in the course with `course_id`
* `no_login`: Define this to create the user but not login
* `redirect`: Set to "true" will redirect to course if course_id is defined, otherwise it will redirect to dashboard
* `redirect`: Set to "true" will redirect to the `redirect_to` value if set, or
course home page if course_id is defined, otherwise it will redirect to dashboard
* `redirect_to`: will redirect to to this url
If username, email, or password are not provided, use
randomly generated credentials.
"""
......@@ -1924,6 +1925,7 @@ def auto_auth(request):
is_staff = request.GET.get('staff', None)
is_superuser = request.GET.get('superuser', None)
course_id = request.GET.get('course_id', None)
redirect_to = request.GET.get('redirect_to', None)
# mode has to be one of 'honor'/'professional'/'verified'/'audit'/'no-id-professional'/'credit'
enrollment_mode = request.GET.get('enrollment_mode', 'honor')
......@@ -1932,7 +1934,7 @@ def auto_auth(request):
if course_id:
course_key = CourseLocator.from_string(course_id)
role_names = [v.strip() for v in request.GET.get('roles', '').split(',') if v.strip()]
redirect_when_done = request.GET.get('redirect', '').lower() == 'true'
redirect_when_done = request.GET.get('redirect', '').lower() == 'true' or redirect_to
login_when_done = 'no_login' not in request.GET
form = AccountCreationForm(
......@@ -1997,8 +1999,11 @@ def auto_auth(request):
# Provide the user with a valid CSRF token
# then return a 200 response unless redirect is true
if redirect_when_done:
# Redirect to specific page if specified
if redirect_to:
redirect_url = redirect_to
# Redirect to course info page if course_id is known
if course_id:
elif course_id:
try:
# redirect to course info page in LMS
redirect_url = reverse(
......
[run]
data_file = reports/pa11ycrawler/.coverage
source =
lms
cms
common/djangoapps
common/lib
openedx/core/djangoapps
**/mako_lms/
**/mako_cms/
omit =
lms/envs/*
cms/envs/*
common/djangoapps/terrain/*
common/djangoapps/*/migrations/*
openedx/core/djangoapps/*/migrations/*
*/test*
*/management/*
*/urls*
*/wsgi*
lms/djangoapps/*/migrations/*
cms/djangoapps/*/migrations/*
parallel = True
[report]
ignore_errors = True
include =
**/views/*.py
**/views.py
[html]
title = pa11ycrawler Coverage Report
directory = reports/pa11ycrawler/cover
[xml]
output = reports/pa11ycrawler/coverage.xml
......@@ -14,6 +14,8 @@
"devDependencies": {
"jshint": "^2.7.0",
"edx-custom-a11y-rules": "edx/edx-custom-a11y-rules",
"pa11y": "3.6.0",
"pa11y-reporter-1.0-json": "1.0.2",
"plato": "1.2.2"
}
}
......@@ -3,7 +3,7 @@ Run acceptance tests that use the bok-choy framework
http://bok-choy.readthedocs.org/en/latest/
"""
from paver.easy import task, needs, cmdopts, sh
from pavelib.utils.test.suites.bokchoy_suite import BokChoyTestSuite
from pavelib.utils.test.suites.bokchoy_suite import BokChoyTestSuite, Pa11yCrawler
from pavelib.utils.envs import Env
from pavelib.utils.test.utils import check_firefox_version
from optparse import make_option
......@@ -24,6 +24,7 @@ BOKCHOY_OPTS = [
('extra_args=', 'e', 'adds as extra args to the test command'),
('default_store=', 's', 'Default modulestore'),
('test_dir=', 'd', 'Directory for finding tests (relative to common/test/acceptance)'),
('imports_dir=', 'i', 'Directory containing (un-archived) courses to be imported'),
('num_processes=', 'n', 'Number of test threads (for multiprocessing)'),
('verify_xss', 'x', 'Run XSS vulnerability tests'),
make_option("--verbose", action="store_const", const=2, dest="verbosity"),
......@@ -53,6 +54,7 @@ def parse_bokchoy_opts(options):
'extra_args': getattr(options, 'extra_args', ''),
'pdb': getattr(options, 'pdb', False),
'test_dir': getattr(options, 'test_dir', 'tests'),
'imports_dir': getattr(options, 'imports_dir', None),
'save_screenshots': getattr(options, 'save_screenshots', False),
}
......@@ -115,30 +117,50 @@ def test_a11y(options):
@task
@needs('pavelib.prereqs.install_prereqs')
@cmdopts([
('test_spec=', 't', 'Specific test to run'),
('fasttest', 'a', 'Skip some setup'),
('imports_dir=', 'd', 'Directory containing (un-archived) courses to be imported'),
('default_store=', 's', 'Default modulestore'),
make_option("--verbose", action="store_const", const=2, dest="verbosity"),
make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"),
make_option("-v", "--verbosity", action="count", dest="verbosity"),
])
@cmdopts(BOKCHOY_OPTS)
def perf_report_bokchoy(options):
"""
Generates a har file for with page performance info.
"""
opts = {
'test_spec': getattr(options, 'test_spec', None),
'fasttest': getattr(options, 'fasttest', False),
'default_store': getattr(options, 'default_store', os.environ.get('DEFAULT_STORE', 'split')),
'imports_dir': getattr(options, 'imports_dir', None),
'verbosity': getattr(options, 'verbosity', 2),
'test_dir': 'performance',
}
opts = parse_bokchoy_opts(options)
opts['test_dir'] = 'performance'
run_bokchoy(**opts)
@task
@needs('pavelib.prereqs.install_prereqs')
@cmdopts(BOKCHOY_OPTS + [
('with-html', 'w', 'Include html reports'),
make_option('--course-key', help='Course key for test course'),
make_option(
"--skip-fetch",
action="store_false",
dest="should_fetch_course",
help='Course key for test course',
),
])
def pa11ycrawler(options):
"""
Runs pa11ycrawler against the demo-test-course to generates accessibility
reports. (See https://github.com/edx/demo-test-course)
Note: Like the bok-choy tests, this can be used with the `serversonly`
flag to get an environment running. The setup for this is the same as
for bok-choy tests, only test course is imported as well.
"""
opts = parse_bokchoy_opts(options)
opts['report_dir'] = Env.PA11YCRAWLER_REPORT_DIR
opts['coveragerc'] = Env.PA11YCRAWLER_COVERAGERC
opts['should_fetch_course'] = getattr(options, 'should_fetch_course', None)
opts['course_key'] = getattr(options, 'course-key', None)
test_suite = Pa11yCrawler('a11y_crawler', **opts)
test_suite.run()
if getattr(options, 'with_html', False):
test_suite.generate_html_reports()
def run_bokchoy(**opts):
"""
Runs BokChoyTestSuite with the given options.
......@@ -197,3 +219,14 @@ def a11y_coverage():
Env.BOK_CHOY_A11Y_REPORT_DIR,
Env.BOK_CHOY_A11Y_COVERAGERC
)
@task
def pa11ycrawler_coverage():
"""
Generate coverage reports for bok-choy tests
"""
parse_coverage(
Env.PA11YCRAWLER_REPORT_DIR,
Env.PA11YCRAWLER_COVERAGERC
)
......@@ -4,9 +4,11 @@ Run just this test with: paver test_lib -t pavelib/paver_tests/test_paver_bok_ch
"""
import os
import unittest
from mock import patch, call
from test.test_support import EnvironmentVarGuard
from paver.easy import BuildFailure
from pavelib.utils.test.suites import BokChoyTestSuite
from pavelib.utils.test.suites import BokChoyTestSuite, Pa11yCrawler
REPO_DIR = os.getcwd()
......@@ -167,3 +169,63 @@ class TestPaverBokChoyCmd(unittest.TestCase):
suite = BokChoyTestSuite('', num_processes=2, verbosity=3)
with self.assertRaises(BuildFailure):
BokChoyTestSuite.verbosity_processes_string(suite)
class TestPaverPa11yCrawlerCmd(unittest.TestCase):
"""
Paver pa11ycrawler command test cases. Most of the functionality is
inherited from BokChoyTestSuite, so those tests aren't duplicated.
"""
def setUp(self):
super(TestPaverPa11yCrawlerCmd, self).setUp()
# Mock shell commands
mock_sh = patch('pavelib.utils.test.suites.bokchoy_suite.sh')
self._mock_sh = mock_sh.start()
# Cleanup mocks
self.addCleanup(mock_sh.stop)
def _expected_command(self, report_dir, start_urls):
"""
Returns the expected command to run pa11ycrawler.
"""
expected_statement = (
'pa11ycrawler run {start_urls} '
'--pa11ycrawler-allowed-domains=localhost '
'--pa11ycrawler-reports-dir={report_dir} '
'--pa11ycrawler-deny-url-matcher=logout '
'--pa11y-reporter="1.0-json" '
'--depth-limit=6 '
).format(
start_urls=start_urls,
report_dir=report_dir,
)
return expected_statement
def test_default(self):
suite = Pa11yCrawler('')
self.assertEqual(
suite.cmd,
self._expected_command(suite.pa11y_report_dir, suite.start_urls)
)
def test_get_test_course(self):
suite = Pa11yCrawler('')
suite.get_test_course()
self._mock_sh.assert_has_calls([
call(
'wget {targz} -O {dir}demo_course.tar.gz'.format(targz=suite.tar_gz_file, dir=suite.imports_dir)),
call(
'tar zxf {dir}demo_course.tar.gz -C {dir}'.format(dir=suite.imports_dir)),
])
def test_generate_html_reports(self):
suite = Pa11yCrawler('')
suite.generate_html_reports()
self._mock_sh.assert_has_calls([
call(
'pa11ycrawler json-to-html --pa11ycrawler-reports-dir={}'.format(suite.pa11y_report_dir)),
])
......@@ -37,6 +37,9 @@ class Env(object):
"lib" / "custom_a11y_rules.js"
)
PA11YCRAWLER_REPORT_DIR = REPORT_DIR / "pa11ycrawler"
PA11YCRAWLER_COVERAGERC = BOK_CHOY_DIR / ".pa11ycrawlercoveragerc"
# If set, put reports for run in "unique" directories.
# The main purpose of this is to ensure that the reports can be 'slurped'
# in the main jenkins flow job without overwriting the reports from other
......
......@@ -6,4 +6,4 @@ from .nose_suite import NoseTestSuite, SystemTestSuite, LibTestSuite
from .python_suite import PythonTestSuite
from .js_suite import JsTestSuite
from .acceptance_suite import AcceptanceTestSuite
from .bokchoy_suite import BokChoyTestSuite
from .bokchoy_suite import BokChoyTestSuite, Pa11yCrawler
......@@ -2,9 +2,11 @@
Class used for defining and running Bok Choy acceptance test suite
"""
from time import sleep
from urllib import urlencode
from common.test.acceptance.fixtures.course import CourseFixture, FixtureError
from path import Path as path
from paver.easy import sh, BuildFailure
from pavelib.utils.test.suites.suite import TestSuite
from pavelib.utils.envs import Env
......@@ -162,6 +164,26 @@ class BokChoyTestSuite(TestSuite):
# load data in db_fixtures
self.load_data()
# load courses if self.imports_dir is set
self.load_courses()
# Ensure the test servers are available
msg = colorize('green', "Confirming servers are running...")
print msg
bokchoy_utils.start_servers(self.default_store, self.coveragerc)
def load_courses(self):
"""
Loads courses from self.imports_dir.
Note: self.imports_dir is the directory that contains the directories
that have courses in them. For example, if the course is located in
`test_root/courses/test-example-course/`, self.imports_dir should be
`test_root/courses/`.
"""
msg = colorize('green', "Importing courses from {}...".format(self.imports_dir))
print msg
if self.imports_dir:
sh(
"DEFAULT_STORE={default_store}"
......@@ -171,11 +193,6 @@ class BokChoyTestSuite(TestSuite):
)
)
# Ensure the test servers are available
msg = colorize('green', "Confirming servers are running...")
print msg
bokchoy_utils.start_servers(self.default_store, self.coveragerc)
def load_data(self):
"""
Loads data into database from db_fixtures
......@@ -241,3 +258,113 @@ class BokChoyTestSuite(TestSuite):
cmd = (" ").join(cmd)
return cmd
class Pa11yCrawler(BokChoyTestSuite):
"""
Sets up test environment with mega-course loaded, and runs pa11ycralwer
against it.
"""
def __init__(self, *args, **kwargs):
super(Pa11yCrawler, self).__init__(*args, **kwargs)
self.course_key = kwargs.get('course_key', "course-v1:edX+Test101+course")
if self.imports_dir:
# If imports_dir has been specified, assume the files are
# already there -- no need to fetch them from github. This
# allows someome to crawl a different course. They are responsible
# for putting it, un-archived, in the directory.
self.should_fetch_course = False
else:
# Otherwise, obey `--skip-fetch` command and use the default
# test course. Note that the fetch will also be skipped when
# using `--fast`.
self.should_fetch_course = kwargs.get('should_fetch_course', not self.fasttest)
self.imports_dir = path('test_root/courses/')
self.pa11y_report_dir = os.path.join(self.report_dir, 'pa11ycrawler_reports')
self.tar_gz_file = "https://github.com/edx/demo-test-course/archive/master.tar.gz"
self.start_urls = []
auto_auth_params = {
"redirect": 'true',
"staff": 'true',
"course_id": self.course_key,
}
cms_params = urlencode(auto_auth_params)
self.start_urls.append("\"http://localhost:8031/auto_auth?{}\"".format(cms_params))
sequence_url = "/api/courses/v1/blocks/?{}".format(
urlencode({
"course_id": self.course_key,
"depth": "all",
"all_blocks": "true",
})
)
auto_auth_params.update({'redirect_to': sequence_url})
lms_params = urlencode(auto_auth_params)
self.start_urls.append("\"http://localhost:8003/auto_auth?{}\"".format(lms_params))
def __enter__(self):
if self.should_fetch_course:
self.get_test_course()
super(Pa11yCrawler, self).__enter__()
def get_test_course(self):
"""
Fetches the test course.
"""
self.imports_dir.makedirs_p()
zipped_course = self.imports_dir + 'demo_course.tar.gz'
msg = colorize('green', "Fetching the test course from github...")
print msg
sh(
'wget {tar_gz_file} -O {zipped_course}'.format(
tar_gz_file=self.tar_gz_file,
zipped_course=zipped_course,
)
)
msg = colorize('green', "Uncompressing the test course...")
print msg
sh(
'tar zxf {zipped_course} -C {courses_dir}'.format(
zipped_course=zipped_course,
courses_dir=self.imports_dir,
)
)
def generate_html_reports(self):
"""
Runs pa11ycrawler json-to-html
"""
cmd_str = (
'pa11ycrawler json-to-html --pa11ycrawler-reports-dir={report_dir}'
).format(report_dir=self.pa11y_report_dir)
sh(cmd_str)
@property
def cmd(self):
"""
Runs pa11ycrawler as staff user against the test course.
"""
cmd_str = (
'pa11ycrawler run {start_urls} '
'--pa11ycrawler-allowed-domains={allowed_domains} '
'--pa11ycrawler-reports-dir={report_dir} '
'--pa11ycrawler-deny-url-matcher={dont_go_here} '
'--pa11y-reporter="{reporter}" '
'--depth-limit={depth} '
).format(
start_urls=self.start_urls,
allowed_domains='localhost',
report_dir=self.pa11y_report_dir,
reporter="1.0-json",
dont_go_here="logout",
depth="6",
)
return cmd_str
......@@ -67,6 +67,7 @@ git+https://github.com/edx/rfc6266.git@v0.0.5-edx#egg=rfc6266==0.0.5-edx
# Used for testing
git+https://github.com/edx/lettuce.git@0.2.20.002#egg=lettuce==0.2.20.002
git+https://github.com/edx/pa11ycrawler.git@0.0.1#egg=pa11ycrawler
# Our libraries:
git+https://github.com/edx/XBlock.git@xblock-0.4.8#egg=XBlock==0.4.8
......
#!/usr/bin/env bash
set -e
echo "Setting up for accessibility tests..."
source scripts/jenkins-common.sh
echo "Running pa11ycrawler against test course..."
paver pa11ycrawler
echo "Generating coverage report..."
paver pa11ycrawler_coverage
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment