Commit ed6a2086 by Matjaz Gregoric

Merge pull request #10 from open-craft/show-private-results

Add ability for course staff to always view the results
parents 0b99e802 b37deed5
language: python
python:
- "2.7"
before_install:
- "export DISPLAY=:99"
- "sh -e /etc/init.d/xvfb start"
install:
- "sh install_test_deps.sh"
- "python setup.py develop"
script: pep8 poll --max-line-length=120 && pylint poll && python run_tests.py --with-coverage --cover-package=poll
notifications:
email: false
...@@ -229,3 +229,11 @@ The resulting events look like this for polls: ...@@ -229,3 +229,11 @@ The resulting events look like this for polls:
{"username": "staff", "host": "precise64", "event_source": "server", "event_type": "xblock.survey.submitted", "context": {"course_user_tags": {}, "user_id": 1, "org_id": "JediAcademy", "module": {"display_name": "Survey"}, "course_id": "JediAcademy/FW301/2015", "path": "/courses/JediAcademy/FW301/2015/xblock/i4x:;_;_JediAcademy;_FW301;_survey;_e4975240b6c64a1e988bad86ea917070/handler/vote"}, "time": "2015-01-12T19:13:13.115038+00:00", "ip": "10.0.2.2", "event": {"url_name": "e4975240b6c64a1e988bad86ea917070", "choices": {"enjoy": "Y", "learn": "M", "recommend": "N"}}, "agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:34.0) Gecko/20100101 Firefox/34.0", "page": "x_module"} {"username": "staff", "host": "precise64", "event_source": "server", "event_type": "xblock.survey.submitted", "context": {"course_user_tags": {}, "user_id": 1, "org_id": "JediAcademy", "module": {"display_name": "Survey"}, "course_id": "JediAcademy/FW301/2015", "path": "/courses/JediAcademy/FW301/2015/xblock/i4x:;_;_JediAcademy;_FW301;_survey;_e4975240b6c64a1e988bad86ea917070/handler/vote"}, "time": "2015-01-12T19:13:13.115038+00:00", "ip": "10.0.2.2", "event": {"url_name": "e4975240b6c64a1e988bad86ea917070", "choices": {"enjoy": "Y", "learn": "M", "recommend": "N"}}, "agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:34.0) Gecko/20100101 Firefox/34.0", "page": "x_module"}
{"username": "staff", "host": "precise64", "event_source": "server", "event_type": "xblock.survey.view_results", "context": {"course_user_tags": {}, "user_id": 1, "org_id": "JediAcademy", "module": {"display_name": "Survey"}, "course_id": "JediAcademy/FW301/2015", "path": "/courses/JediAcademy/FW301/2015/xblock/i4x:;_;_JediAcademy;_FW301;_survey;_e4975240b6c64a1e988bad86ea917070/handler/get_results"}, "time": "2015-01-12T19:13:13.513909+00:00", "ip": "10.0.2.2", "event": {}, "agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:34.0) Gecko/20100101 Firefox/34.0", "page": "x_module"} {"username": "staff", "host": "precise64", "event_source": "server", "event_type": "xblock.survey.view_results", "context": {"course_user_tags": {}, "user_id": 1, "org_id": "JediAcademy", "module": {"display_name": "Survey"}, "course_id": "JediAcademy/FW301/2015", "path": "/courses/JediAcademy/FW301/2015/xblock/i4x:;_;_JediAcademy;_FW301;_survey;_e4975240b6c64a1e988bad86ea917070/handler/get_results"}, "time": "2015-01-12T19:13:13.513909+00:00", "ip": "10.0.2.2", "event": {}, "agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:34.0) Gecko/20100101 Firefox/34.0", "page": "x_module"}
## Viewing the Results
When running inside the edX LMS, course staff members have the ability to view results without voting.
If you want to grant members of other groups ability to view the results, you can configure the group
names in the django settings using the `XBLOCK_POLL_EXTRA_VIEW_GROUPS` setting, for example:
XBLOCK_POLL_EXTRA_VIEW_GROUPS = ['poll_staff']
# Installs xblock-sdk and dependencies needed to run the tests suite.
# Run this script inside a fresh virtual environment.
pip install -e git://github.com/edx/xblock-sdk.git#egg=xblock-sdk
pip install -r $VIRTUAL_ENV/src/xblock-sdk/requirements.txt
pip install -r $VIRTUAL_ENV/src/xblock-sdk/test-requirements.txt
python setup.py develop
...@@ -34,6 +34,16 @@ from xblock.fragment import Fragment ...@@ -34,6 +34,16 @@ from xblock.fragment import Fragment
from xblockutils.publish_event import PublishEventMixin from xblockutils.publish_event import PublishEventMixin
from xblockutils.resources import ResourceLoader from xblockutils.resources import ResourceLoader
HAS_EDX_ACCESS = False
try:
# pylint: disable=import-error
from django.conf import settings
from courseware.access import has_access
from api_manager.models import GroupProfile
HAS_EDX_ACCESS = True
except ImportError:
pass
class ResourceMixin(object): class ResourceMixin(object):
loader = ResourceLoader(__name__) loader = ResourceLoader(__name__)
...@@ -166,6 +176,25 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin): ...@@ -166,6 +176,25 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin):
return True return True
return False return False
def can_view_private_results(self):
"""
Checks to see if the user has permissions to view private results.
This only works inside the LMS.
"""
if HAS_EDX_ACCESS and hasattr(self.runtime, 'user') and hasattr(self.runtime, 'course_id'):
# Course staff users have permission to view results.
if has_access(self.runtime.user, 'staff', self, self.runtime.course_id):
return True
else:
# Check if user is member of a group that is explicitly granted
# permission to view the results through django configuration.
group_names = getattr(settings, 'XBLOCK_POLL_EXTRA_VIEW_GROUPS', [])
if group_names:
group_ids = self.runtime.user.groups.values_list('id', flat=True)
return GroupProfile.objects.filter(group_id__in=group_ids, name__in=group_names).exists()
else:
return False
@staticmethod @staticmethod
def get_max_submissions(data, result, private_results): def get_max_submissions(data, result, private_results):
""" """
...@@ -192,6 +221,8 @@ class PollBlock(PollBase): ...@@ -192,6 +221,8 @@ class PollBlock(PollBase):
Poll XBlock. Allows a teacher to poll users, and presents the results so Poll XBlock. Allows a teacher to poll users, and presents the results so
far of the poll to the user when finished. far of the poll to the user when finished.
""" """
# pylint: disable=too-many-instance-attributes
display_name = String(default='Poll') display_name = String(default='Poll')
question = String(default='What is your favorite color?') question = String(default='What is your favorite color?')
# This will be converted into an OrderedDict. # This will be converted into an OrderedDict.
...@@ -305,6 +336,7 @@ class PollBlock(PollBase): ...@@ -305,6 +336,7 @@ class PollBlock(PollBase):
'can_vote': self.can_vote(), 'can_vote': self.can_vote(),
'max_submissions': self.max_submissions, 'max_submissions': self.max_submissions,
'submissions_count': self.submissions_count, 'submissions_count': self.submissions_count,
'can_view_private_results': self.can_view_private_results(),
}) })
if self.choice: if self.choice:
...@@ -346,7 +378,7 @@ class PollBlock(PollBase): ...@@ -346,7 +378,7 @@ class PollBlock(PollBase):
@XBlock.json_handler @XBlock.json_handler
def get_results(self, data, suffix=''): def get_results(self, data, suffix=''):
if self.private_results: if self.private_results and not self.can_view_private_results():
detail, total = {}, None detail, total = {}, None
else: else:
self.publish_event_from_dict(self.event_namespace + '.view_results', {}) self.publish_event_from_dict(self.event_namespace + '.view_results', {})
...@@ -448,13 +480,18 @@ class PollBlock(PollBase): ...@@ -448,13 +480,18 @@ class PollBlock(PollBase):
""" """
<poll tally="{'long': 20, 'short': 29, 'not_saying': 15, 'longer' : 35}" <poll tally="{'long': 20, 'short': 29, 'not_saying': 15, 'longer' : 35}"
question="## How long have you been studying with us?" question="## How long have you been studying with us?"
answers='[["longt", {"label": "A very long time", "img": null}], ["short", {"label": "Not very long", "img": null}], ["not_saying", {"label": "I shall not say", "img": null}], ["longer", {"label": "Longer than you", "img": null}]]' answers='[["longt", {"label": "A very long time", "img": null}],
["short", {"label": "Not very long", "img": null}],
["not_saying", {"label": "I shall not say", "img": null}],
["longer", {"label": "Longer than you", "img": null}]]'
feedback="### Thank you&#10;&#10;for being a valued student."/> feedback="### Thank you&#10;&#10;for being a valued student."/>
"""), """),
] ]
class SurveyBlock(PollBase): class SurveyBlock(PollBase):
# pylint: disable=too-many-instance-attributes
display_name = String(default='Survey') display_name = String(default='Survey')
# The display name affects how the block is labeled in the studio, # The display name affects how the block is labeled in the studio,
# but either way we want it to say 'Poll' by default on the page. # but either way we want it to say 'Poll' by default on the page.
...@@ -512,6 +549,7 @@ class SurveyBlock(PollBase): ...@@ -512,6 +549,7 @@ class SurveyBlock(PollBase):
'can_vote': self.can_vote(), 'can_vote': self.can_vote(),
'submissions_count': self.submissions_count, 'submissions_count': self.submissions_count,
'max_submissions': self.max_submissions, 'max_submissions': self.max_submissions,
'can_view_private_results': self.can_view_private_results(),
}) })
return self.create_fragment( return self.create_fragment(
...@@ -554,7 +592,7 @@ class SurveyBlock(PollBase): ...@@ -554,7 +592,7 @@ class SurveyBlock(PollBase):
tally = [] tally = []
questions = OrderedDict(self.markdown_items(self.questions)) questions = OrderedDict(self.markdown_items(self.questions))
default_answers = OrderedDict([(answer, 0) for answer, __ in self.answers]) default_answers = OrderedDict([(answer, 0) for answer, __ in self.answers])
choices = self.choices choices = self.choices or {}
total = 0 total = 0
self.clean_tally() self.clean_tally()
source_tally = self.tally source_tally = self.tally
...@@ -585,7 +623,7 @@ class SurveyBlock(PollBase): ...@@ -585,7 +623,7 @@ class SurveyBlock(PollBase):
highest = 0 highest = 0
top_index = None top_index = None
for index, answer in enumerate(question['answers']): for index, answer in enumerate(question['answers']):
if answer['key'] == choices[question['key']]: if answer['key'] == choices.get(question['key']):
answer['choice'] = True answer['choice'] = True
# Find the most popular choice. # Find the most popular choice.
if answer['count'] > highest: if answer['count'] > highest:
...@@ -595,6 +633,7 @@ class SurveyBlock(PollBase): ...@@ -595,6 +633,7 @@ class SurveyBlock(PollBase):
answer['percent'] = round(answer['count'] / float(total) * 100) answer['percent'] = round(answer['count'] / float(total) * 100)
except ZeroDivisionError: except ZeroDivisionError:
answer['percent'] = 0 answer['percent'] = 0
if top_index is not None:
question['answers'][top_index]['top'] = True question['answers'][top_index]['top'] = True
return tally, total return tally, total
...@@ -661,7 +700,7 @@ class SurveyBlock(PollBase): ...@@ -661,7 +700,7 @@ class SurveyBlock(PollBase):
@XBlock.json_handler @XBlock.json_handler
def get_results(self, data, suffix=''): def get_results(self, data, suffix=''):
if self.private_results: if self.private_results and not self.can_view_private_results():
detail, total = {}, None detail, total = {}, None
else: else:
self.publish_event_from_dict(self.event_namespace + '.view_results', {}) self.publish_event_from_dict(self.event_namespace + '.view_results', {})
...@@ -791,9 +830,16 @@ class SurveyBlock(PollBase): ...@@ -791,9 +830,16 @@ class SurveyBlock(PollBase):
"""), """),
("Survey Functions", ("Survey Functions",
""" """
<survey tally='{"q1": {"sa": 5, "a": 5, "n": 3, "d": 2, "sd": 5}, "q2": {"sa": 3, "a": 2, "n": 3, "d": 10, "sd": 2}, "q3": {"sa": 2, "a": 7, "n": 1, "d": 4, "sd": 6}, "q4": {"sa": 1, "a": 2, "n": 8, "d": 4, "sd": 5}}' <survey tally='{"q1": {"sa": 5, "a": 5, "n": 3, "d": 2, "sd": 5},
questions='[["q1", {"label": "I feel like this test will pass.", "img": null}], ["q2", {"label": "I like testing software", "img": null}], ["q3", {"label": "Testing is not necessary", "img": null}], ["q4", {"label": "I would fake a test result to get software deployed.", "img": null}]]' "q2": {"sa": 3, "a": 2, "n": 3, "d": 10, "sd": 2},
answers='[["sa", "Strongly Agree"], ["a", "Agree"], ["n", "Neutral"], ["d", "Disagree"], ["sd", "Strongly Disagree"]]' "q3": {"sa": 2, "a": 7, "n": 1, "d": 4, "sd": 6},
"q4": {"sa": 1, "a": 2, "n": 8, "d": 4, "sd": 5}}'
questions='[["q1", {"label": "I feel like this test will pass.", "img": null}],
["q2", {"label": "I like testing software", "img": null}],
["q3", {"label": "Testing is not necessary", "img": null}],
["q4", {"label": "I would fake a test result to get software deployed.", "img": null}]]'
answers='[["sa", "Strongly Agree"], ["a", "Agree"], ["n", "Neutral"],
["d", "Disagree"], ["sd", "Strongly Disagree"]]'
feedback="### Thank you&#10;&#10;for running the tests."/> feedback="### Thank you&#10;&#10;for running the tests."/>
""") """)
] ]
...@@ -211,3 +211,8 @@ th.survey-answer { ...@@ -211,3 +211,8 @@ th.survey-answer {
.poll-submissions-count { .poll-submissions-count {
font-weight: bold; font-weight: bold;
} }
.view-results-button-wrapper {
text-align: right;
cursor: pointer;
}
<script id="poll-results-template" type="text/html"> <script id="poll-results-template" type="text/html">
<h3 class="poll-header">{{display_name}}</h3> <h3 class="poll-header">{{display_name}}</h3>
<div class="poll-question-container">{{{question}}}</div> <div class="poll-question-container">{{{question}}}</div>
<ul class="poll-answers-results"> <ul class="poll-answers-results poll-results">
{{#each tally}} {{#each tally}}
<li class="poll-result"> <li class="poll-result">
<div class="poll-result-input-container"> <div class="poll-result-input-container">
......
<script id="survey-results-template" type="text/html"> <script id="survey-results-template" type="text/html">
<h3 class="poll-header">{{block_name}}</h3> <h3 class="poll-header">{{block_name}}</h3>
<table class="survey-table"> <table class="survey-table poll-results">
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
......
...@@ -40,5 +40,9 @@ ...@@ -40,5 +40,9 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% if can_view_private_results %}
<div class="view-results-button-wrapper"><a class="view-results-button">View results</a></div>
{% endif %}
{% endif %} {% endif %}
</div> </div>
...@@ -47,5 +47,9 @@ ...@@ -47,5 +47,9 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% if can_view_private_results %}
<div class="view-results-button-wrapper"><a class="view-results-button">View results</a></div>
{% endif %}
{% endif %} {% endif %}
</div> </div>
...@@ -10,10 +10,12 @@ function PollUtil (runtime, element, pollType) { ...@@ -10,10 +10,12 @@ function PollUtil (runtime, element, pollType) {
this.submit = $('input[type=button]', element); this.submit = $('input[type=button]', element);
this.answers = $('input[type=radio]', element); this.answers = $('input[type=radio]', element);
this.resultsTemplate = Handlebars.compile($("#" + pollType + "-results-template", element).html()); this.resultsTemplate = Handlebars.compile($("#" + pollType + "-results-template", element).html());
this.viewResultsButton = $('.view-results-button', element);
this.viewResultsButton.click(this.getResults);
// If the submit button doesn't exist, the user has already // If the submit button doesn't exist, the user has already
// selected a choice. Render results instead of initializing machinery. // selected a choice. Render results instead of initializing machinery.
if (! self.submit.length) { if (! self.submit.length) {
self.getResults({'success': true}); self.onSubmit({'success': true});
return false; return false;
} }
var max_submissions = parseInt($('.poll-max-submissions', element).text()); var max_submissions = parseInt($('.poll-max-submissions', element).text());
...@@ -42,7 +44,7 @@ function PollUtil (runtime, element, pollType) { ...@@ -42,7 +44,7 @@ function PollUtil (runtime, element, pollType) {
type: "POST", type: "POST",
url: self.voteUrl, url: self.voteUrl,
data: JSON.stringify({"choice": choice}), data: JSON.stringify({"choice": choice}),
success: self.getResults success: self.onSubmit
}); });
}); });
// If the user has already reached their maximum submissions, all inputs should be disabled. // If the user has already reached their maximum submissions, all inputs should be disabled.
...@@ -73,7 +75,7 @@ function PollUtil (runtime, element, pollType) { ...@@ -73,7 +75,7 @@ function PollUtil (runtime, element, pollType) {
type: "POST", type: "POST",
url: self.voteUrl, url: self.voteUrl,
data: JSON.stringify(self.surveyChoices()), data: JSON.stringify(self.surveyChoices()),
success: self.getResults success: self.onSubmit
}) })
}); });
// If the user has refreshed the page, they may still have an answer // If the user has refreshed the page, they may still have an answer
...@@ -86,7 +88,7 @@ function PollUtil (runtime, element, pollType) { ...@@ -86,7 +88,7 @@ function PollUtil (runtime, element, pollType) {
var choices = {}; var choices = {};
self.answers.each(function(index, el) { self.answers.each(function(index, el) {
el = $(el); el = $(el);
choices[el.prop('name')] = $(self.checkedElement(el)).val(); choices[el.prop('name')] = $(self.checkedElement(el), element).val();
}); });
return choices; return choices;
}; };
...@@ -111,7 +113,7 @@ function PollUtil (runtime, element, pollType) { ...@@ -111,7 +113,7 @@ function PollUtil (runtime, element, pollType) {
} }
}; };
this.getResults = function (data) { this.onSubmit = function (data) {
// Fetch the results from the server and render them. // Fetch the results from the server and render them.
if (!data['success']) { if (!data['success']) {
alert(data['errors'].join('\n')); alert(data['errors'].join('\n'));
...@@ -135,6 +137,11 @@ function PollUtil (runtime, element, pollType) { ...@@ -135,6 +137,11 @@ function PollUtil (runtime, element, pollType) {
return; return;
} }
// Used if results are not private, to show the user how other students voted. // Used if results are not private, to show the user how other students voted.
self.getResults();
};
this.getResults = function () {
// Used if results are not private, to show the user how other students voted.
$.ajax({ $.ajax({
// Semantically, this would be better as GET, but we can use helper // Semantically, this would be better as GET, but we can use helper
// functions with POST. // functions with POST.
......
[REPORTS]
reports=no
[FORMAT]
max-line-length=120
[MESSAGES CONTROL]
disable=
abstract-class-not-used,
broad-except,
cyclic-import,
fixme,
invalid-name,
locally-disabled,
maybe-no-member,
missing-docstring,
protected-access,
star-args,
too-few-public-methods,
too-many-public-methods,
too-many-ancestors,
too-many-arguments,
too-many-locals,
unused-argument
[SIMILARITIES]
min-similarity-lines=8
...@@ -3,4 +3,4 @@ markdown ...@@ -3,4 +3,4 @@ markdown
-e git+https://github.com/edx-solutions/xblock-utils.git#egg=xblock-utils -e git+https://github.com/edx-solutions/xblock-utils.git#egg=xblock-utils
-e git://github.com/nosedjango/nosedjango.git@ed7d7f9aa969252ff799ec159f828eaa8c1cbc5a#egg=nosedjango-dev -e git://github.com/nosedjango/nosedjango.git@ed7d7f9aa969252ff799ec159f828eaa8c1cbc5a#egg=nosedjango-dev
ddt ddt
mock
#!/usr/bin/env python
"""
Run tests for the Poll XBlock
This script is required to run our selenium tests inside the xblock-sdk workbench
because the workbench SDK's settings file is not inside any python module.
"""
import os
import sys
import workbench
if __name__ == "__main__":
# Find the location of the XBlock SDK. Note: it must be installed in development mode.
# ('python setup.py develop' or 'pip install -e')
xblock_sdk_dir = os.path.dirname(os.path.dirname(workbench.__file__))
sys.path.append(xblock_sdk_dir)
# Use the workbench settings file:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
# Configure a range of ports in case the default port of 8081 is in use
os.environ.setdefault("DJANGO_LIVE_TEST_SERVER_ADDRESS", "localhost:8081-8099")
from django.core.management import execute_from_command_line
args = sys.argv[1:]
paths = [arg for arg in args if arg[0] != '-']
if not paths:
paths = ["tests/"]
options = [arg for arg in args if arg not in paths]
execute_from_command_line([sys.argv[0], "test"] + paths + options)
...@@ -53,6 +53,8 @@ setup( ...@@ -53,6 +53,8 @@ setup(
'XBlock', 'XBlock',
'markdown', 'markdown',
'xblock-utils', 'xblock-utils',
'ddt',
'mock',
], ],
dependency_links=['http://github.com/edx-solutions/xblock-utils/tarball/master#egg=xblock-utils'], dependency_links=['http://github.com/edx-solutions/xblock-utils/tarball/master#egg=xblock-utils'],
entry_points={ entry_points={
......
...@@ -23,11 +23,29 @@ ...@@ -23,11 +23,29 @@
from ddt import ddt, unpack, data from ddt import ddt, unpack, data
from selenium.common.exceptions import NoSuchElementException from selenium.common.exceptions import NoSuchElementException
from poll.poll import PollBase
from base_test import PollBaseTest, DEFAULT_SURVEY_NAMES, DEFAULT_POLL_NAMES from base_test import PollBaseTest, DEFAULT_SURVEY_NAMES, DEFAULT_POLL_NAMES
scenarios = ('Survey Private', DEFAULT_SURVEY_NAMES), ('Poll Private', DEFAULT_POLL_NAMES) scenarios = ('Survey Private', DEFAULT_SURVEY_NAMES), ('Poll Private', DEFAULT_POLL_NAMES)
def stub_view_permission(can_view):
"""
Patches the 'can_view_private_results' function to return specified answer.
"""
def stub_view_permissions_decorator(test_fn):
def test_patched(self, page_name, names):
original = PollBase.can_view_private_results
try:
PollBase.can_view_private_results = lambda self: can_view
test_fn(self, page_name, names)
finally:
PollBase.can_view_private_results = original
return test_patched
return stub_view_permissions_decorator
@ddt @ddt
class TestPrivateResults(PollBaseTest): class TestPrivateResults(PollBaseTest):
""" """
...@@ -55,7 +73,7 @@ class TestPrivateResults(PollBaseTest): ...@@ -55,7 +73,7 @@ class TestPrivateResults(PollBaseTest):
self.do_submit(names) self.do_submit(names)
# No results should be showing. # No results should be showing.
self.assertNotIn(self.browser.find_element_by_css_selector('div.poll-block').get_attribute('innerHTML'), 'poll-top-choice') self.assertRaises(NoSuchElementException, self.browser.find_element_by_css_selector, '.poll-results')
self.assertRaises(NoSuchElementException, self.browser.find_element_by_css_selector, '.poll-footnote') self.assertRaises(NoSuchElementException, self.browser.find_element_by_css_selector, '.poll-footnote')
@unpack @unpack
...@@ -83,3 +101,27 @@ class TestPrivateResults(PollBaseTest): ...@@ -83,3 +101,27 @@ class TestPrivateResults(PollBaseTest):
self.assertFalse(self.browser.find_element_by_css_selector('.poll-feedback').is_displayed()) self.assertFalse(self.browser.find_element_by_css_selector('.poll-feedback').is_displayed())
self.do_submit(names) self.do_submit(names)
self.assertTrue(self.browser.find_element_by_css_selector('.poll-feedback').is_displayed()) self.assertTrue(self.browser.find_element_by_css_selector('.poll-feedback').is_displayed())
@unpack
@data(*scenarios)
@stub_view_permission(False)
def test_results_button_visibility_without_permission(self, page_name, names):
self.go_to_page(page_name)
self.assertRaises(NoSuchElementException, self.browser.find_element_by_css_selector, '.view-results-button')
@unpack
@data(*scenarios)
@stub_view_permission(True)
def test_results_button_visibility_with_permission(self, page_name, names):
self.go_to_page(page_name)
self.browser.find_element_by_css_selector('.view-results-button')
@unpack
@data(*scenarios)
@stub_view_permission(True)
def test_results_button(self, page_name, names):
self.go_to_page(page_name)
button = self.browser.find_element_by_css_selector('a.view-results-button')
button.click()
self.wait_until_exists('.poll-results')
self.wait_until_exists('.poll-footnote')
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