diff --git a/addons/survey/tests/__init__.py b/addons/survey/tests/__init__.py index 12f614f03bc..b345311e36b 100644 --- a/addons/survey/tests/__init__.py +++ b/addons/survey/tests/__init__.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import common from . import test_survey +from . import test_survey_flow +from . import test_survey_invite from . import test_survey_ui diff --git a/addons/survey/tests/common.py b/addons/survey/tests/common.py new file mode 100644 index 00000000000..fbe03f3f24b --- /dev/null +++ b/addons/survey/tests/common.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from collections import Counter +from contextlib import contextmanager +from functools import partial + +from odoo.tests import common, new_test_user + +survey_new_test_user = partial(new_test_user, context={'mail_create_nolog': True, 'mail_create_nosubscribe': True, 'mail_notrack': True, 'no_reset_password': True}) + + +class SurveyCase(common.SavepointCase): + + def setUp(self): + super(SurveyCase, self).setUp() + + """ Some custom stuff to make the matching between questions and answers + :param dict _type_match: dict + key: question type + value: (answer type, answer field_name) + """ + self._type_match = { + 'free_text': ('free_text', 'value_free_text'), + 'textbox': ('text', 'value_text'), + 'numerical_box': ('number', 'value_number'), + 'date': ('date', 'value_date'), + 'simple_choice': ('suggestion', 'value_suggested'), # TDE: still unclear + 'multiple_choice': ('suggestion', 'value_suggested'), # TDE: still unclear + 'matrix': ('suggestion', ('value_suggested', 'value_suggested_row')), # TDE: still unclear + } + + """ Create test data: a survey with some pre-defined questions and various test users for ACL """ + self.survey_manager = survey_new_test_user( + self.env, name='Gustave Doré', login='survey_manager', email='survey.manager@example.com', + groups='survey.group_survey_manager,base.group_user' + ) + + self.survey_user = survey_new_test_user( + self.env, name='Lukas Peeters', login='survey_user', email='survey.user@example.com', + groups='survey.group_survey_user,base.group_user' + ) + + self.user_emp = survey_new_test_user( + self.env, name='Eglantine Employee', login='user_emp', email='employee@example.com', + groups='base.group_user' + ) + + self.user_portal = survey_new_test_user( + self.env, name='Patrick Portal', login='user_portal', email='portal@example.com', + groups='base.group_portal' + ) + + self.user_public = survey_new_test_user( + self.env, name='Pauline Public', login='user_public', email='public@example.com', + groups='base.group_public' + ) + + self.customer = self.env['res.partner'].create({ + 'name': 'Caroline Customer', + 'email': 'customer@example.com', + 'customer': True, + }) + + self.survey = self.env['survey.survey'].sudo(self.survey_manager).create({ + 'title': 'Test Survey', + 'auth_required': True, + 'users_can_go_back': False, + }) + self.page_0 = self.env['survey.page'].sudo(self.survey_manager).create({ + 'title': 'First page', + 'survey_id': self.survey.id, + }) + self.question_ft = self.env['survey.question'].sudo(self.survey_manager).create({ + 'question': 'Test Free Text', + 'page_id': self.page_0.id, + 'question_type': 'free_text', + }) + self.question_num = self.env['survey.question'].sudo(self.survey_manager).create({ + 'question': 'Test NUmerical Box', + 'page_id': self.page_0.id, + 'question_type': 'numerical_box', + }) + + @contextmanager + def sudo(self, user): + """ Quick sudo environment """ + old_uid = self.uid + try: + self.uid = user.id + self.env = self.env(user=self.uid) + yield + finally: + # back + self.uid = old_uid + self.env = self.env(user=self.uid) + + def assertAnswer(self, answer, state, page): + self.assertEqual(answer.state, state) + self.assertEqual(answer.last_displayed_page_id, page) + + def assertAnswerLines(self, page, answer, answer_data): + """ Check answer lines. + + :param dict answer_data: + key = question ID + value = {'value': [user input]} + """ + lines = answer.user_input_line_ids.filtered(lambda l: l.page_id == page) + answer_count = sum(len(user_input['value']) for user_input in answer_data.values()) + self.assertEqual(len(lines), answer_count) + for qid, user_input in answer_data.items(): + answer_lines = lines.filtered(lambda l: l.question_id.id == qid) + question = answer_lines[0].question_id # TDE note: might have several answers for a given question + if question.question_type == 'multiple_choice': + values = user_input['value'] + answer_fname = self._type_match[question.question_type][1] + self.assertEqual( + Counter(getattr(line, answer_fname).id for line in answer_lines), + Counter(values)) + elif question.question_type == 'simple_choice': + [value] = user_input['value'] + answer_fname = self._type_match[question.question_type][1] + self.assertEqual(getattr(answer_lines, answer_fname).id, value) + elif question.question_type == 'matrix': + [value_col, value_row] = user_input['value'] + answer_fname_col = self._type_match[question.question_type][1][0] + answer_fname_row = self._type_match[question.question_type][1][1] + self.assertEqual(getattr(answer_lines, answer_fname_col).id, value_col) + self.assertEqual(getattr(answer_lines, answer_fname_row).id, value_row) + else: + [value] = user_input['value'] + answer_fname = self._type_match[question.question_type][1] + self.assertEqual(getattr(answer_lines, answer_fname), value) + + def assertResponse(self, response, status_code, text_bits=None): + self.assertEqual(response.status_code, status_code) + for text in text_bits or []: + self.assertIn(text, response.text) + + def _add_question(self, page, name, qtype, **kwargs): + constr_mandatory = kwargs.pop('constr_mandatory', True) + constr_error_msg = kwargs.pop('constr_error_msg', 'TestError') + base_qvalues = { + 'page_id': page.id, + 'question': name, + 'question_type': qtype, + 'constr_mandatory': constr_mandatory, + 'constr_error_msg': constr_error_msg, + } + if qtype in ('simple_choice', 'multiple_choice'): + base_qvalues['labels_ids'] = [ + (0, 0, {'value': label['value'], 'quizz_mark': label.get('quizz_mark', 0)}) + for label in kwargs.pop('labels') + ] + elif qtype == 'matrix': + base_qvalues['matrix_subtype'] = kwargs.pop('matrix_subtype', 'simple') + base_qvalues['labels_ids'] = [ + (0, 0, {'value': label['value'], 'quizz_mark': label.get('quizz_mark', 0)}) + for label in kwargs.pop('labels') + ] + base_qvalues['labels_ids_2'] = [ + (0, 0, {'value': label['value'], 'quizz_mark': label.get('quizz_mark', 0)}) + for label in kwargs.pop('labels_2') + ] + else: + pass + base_qvalues.update(kwargs) + question = self.env['survey.question'].create(base_qvalues) + return question + + def _add_answer(self, survey, partner, **kwargs): + base_avals = { + 'survey_id': survey.id, + 'partner_id': partner.id if partner else False, + 'email': kwargs.pop('email', False), + 'input_type': 'manually', + } + base_avals.update(kwargs) + return self.env['survey.user_input'].create(base_avals) + + def _add_answer_line(self, question, answer, answer_value, **kwargs): + qtype = self._type_match.get(question.question_type, (False, False)) + answer_type = kwargs.pop('answer_type', qtype[0]) + answer_fname = kwargs.pop('answer_fname', qtype[1]) + + base_alvals = { + 'user_input_id': answer.id, + 'question_id': question.id, + 'skipped': False, + 'answer_type': answer_type, + } + base_alvals[answer_fname] = answer_value + base_alvals.update(kwargs) + return self.env['survey.user_input_line'].create(base_alvals) diff --git a/addons/survey/tests/test_survey.py b/addons/survey/tests/test_survey.py index 312a566e56a..0ce7c5d612d 100644 --- a/addons/survey/tests/test_survey.py +++ b/addons/survey/tests/test_survey.py @@ -2,269 +2,192 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import random -import re + from collections import Counter from itertools import product - from werkzeug import urls from odoo import _ from odoo.addons.http_routing.models.ir_http import slug -from odoo.exceptions import UserError -from odoo.tests.common import TransactionCase +from odoo.addons.survey.tests import common +from odoo.tests.common import users -class TestSurvey(TransactionCase): - def setUp(self): - super(TestSurvey, self).setUp() - User = self.env['res.users'].with_context({'no_reset_password': True}) - (group_survey_user, group_employee) = (self.ref('survey.group_survey_user'), self.ref('base.group_user')) - self.survey_manager = User.create({ - 'name': 'Gustave Doré', 'login': 'Gustav','email': 'gustav.dore@example.com', - 'groups_id': [(6, 0, [self.ref('survey.group_survey_manager'), group_survey_user, group_employee])]}) +class TestSurveyInternals(common.SurveyCase): - self.survey_user = User.create({ - 'name': 'Lukas Peeters', 'login': 'Lukas', 'email': 'lukas.petters@example.com', - 'groups_id': [(6, 0, [group_survey_user, group_employee])]}) - - self.user_public = User.create({ - 'name': 'Wout Janssens', 'login': 'Wout', 'email': 'wout.janssens@example.com', - 'groups_id': [(6, 0, [self.ref('base.group_public')])]}) - - self.survey1 = self.env['survey.survey'].sudo(self.survey_manager).create({'title': "S0", 'page_ids': [(0, 0, {'title': "P0"})]}) - self.page1 = self.survey1.page_ids[0] - - def test_00_create_minimal_survey(self): - question = self.env['survey.question'].sudo(self.survey_manager).create({'page_id': self.page1.id, 'question': 'Q0'}) - self.assertEqual(self.survey1.title, "S0", msg="Title of the survey is somehow modified.") - self.assertEqual(len(self.survey1.page_ids), 1, msg="Additional Pages are linked with the survey after creation.") - self.assertEqual(self.page1.title, "P0", msg="Title of the page is somehow modified.") - self.assertEqual(len(self.page1.question_ids), 1, msg="Additional questions are linked with the page after creation.") - self.assertEqual(question.question, "Q0", msg="Title of the Question is somehow modified.") - - def test_01_question_type_validation_save_line_function(self): + @users('survey_manager') + def test_answer_validation_mandatory(self): + """ For each type of question check that mandatory questions correctly check for complete answers """ for (question_type, text) in self.env['survey.question']._fields['question_type'].selection: - # Each question type must have validation function. - self.assertTrue(hasattr(self.env['survey.question'], 'validate_' + question_type), msg="Question must have a validation method in\ - the form of 'validate_' followed by the name of the type.") - - # Survey Input Lines must have validation function for each line. - self.assertTrue(hasattr(self.env['survey.user_input_line'], 'save_line_' + question_type), msg="Inputline must have Save method in \ - the form of 'save_line_' followed by the name of the type.") - - def test_02_question_answer_required(self): - for (question_type, text) in self.env['survey.question']._fields['question_type'].selection: - # Blank value of field is not accepted for mandatory questions. + kwargs = {} if question_type == 'multiple_choice': - question = self.env['survey.question'].sudo(self.survey_manager).create({ - 'page_id': self.page1.id, 'question': 'Q0', 'question_type': 'multiple_choice', - 'constr_mandatory': True, 'constr_error_msg': 'Error', - 'labels_ids': [(0, 0, {'value': "MChoice0", "quizz_mark": 0}), (0, 0, {'value': "MChoice1", "quizz_mark": 0})]}) - + kwargs['labels'] = [{'value': 'MChoice0'}, {'value': 'MChoice1'}] + elif question_type == 'simple_choice': + kwargs['labels'] = [] elif question_type == 'matrix': - question = self.env['survey.question'].sudo(self.survey_manager).create({ - 'page_id': self.page1.id, 'question': 'Q0', 'question_type': 'matrix', 'matrix_subtype': 'simple', - 'constr_mandatory': True, 'constr_error_msg': 'Error', - 'labels_ids': [(0, 0, {'value': "Column0", "quizz_mark": 0}), (0, 0, {'value': "Column1", "quizz_mark": 0})], - 'labels_ids_2': [(0, 0, {'value': "Row0", "quizz_mark": 0}), (0, 0, {'value': "Row1", "quizz_mark": 0})]}) + kwargs['labels'] = [{'value': 'Column0'}, {'value': 'Column1'}] + kwargs['labels_2'] = [{'value': 'Row0'}, {'value': 'Row1'}] + question = self._add_question(self.page_0, 'Q0', question_type, **kwargs) - else: - question = self.env['survey.question'].sudo(self.survey_manager).create({ - 'page_id': self.page1.id, 'question': 'Q0', 'question_type': question_type, 'constr_mandatory': True, 'constr_error_msg': 'Error'}) - answer_tag = '%s_%s_%s' % (self.survey1.id, self.page1.id, question.id) - self.assertDictEqual({answer_tag: "Error"}, question.validate_question({answer_tag: ''}, answer_tag), - msg=("Validation function for type %s is unable to generate error if it is mandatory and answer is blank." % question_type)) + answer_tag = '%s_%s_%s' % (self.survey.id, self.page_0.id, question.id) + self.assertDictEqual( + question.validate_question({answer_tag: ''}, answer_tag), + {answer_tag: 'TestError'} + ) - def test_03_question_textbox(self): - questions = [ - self.env['survey.question'].sudo(self.survey_manager).create({ - 'page_id': self.page1.id, 'question': 'Q0', 'question_type': 'textbox', 'validation_email': True}), - self.env['survey.question'].sudo(self.survey_manager).create({ - 'page_id': self.page1.id, 'question': 'Q1', 'question_type': 'textbox', 'validation_required': True, - 'validation_length_min': 2, 'validation_length_max': 8, 'validation_error_msg': "Error"})] + @users('survey_manager') + def test_answer_validation_date(self): + question = self._add_question( + self.page_0, 'Q0', 'date', validation_required=True, + validation_min_date='2015-03-20', validation_max_date='2015-03-25', validation_error_msg='ValidationError') + answer_tag = '%s_%s_%s' % (self.survey.id, self.page_0.id, question.id) - results = [('test @ testcom', _('This answer must be an email address')), ('t', 'Error')] - for i in range(len(questions)): - answer_tag = '%s_%s_%s' % (self.survey1.id, self.page1.id, questions[i].id) - self.assertEqual(questions[i].validate_question({answer_tag: results[i][0]}, answer_tag), {answer_tag: results[i][1]}, msg="\ - Validation function for textbox is unable to notify if answer is violating the validation rules") - - def test_04_question_numerical_box(self): - question = self.env['survey.question'].sudo(self.survey_manager).create({ - 'page_id': self.page1.id, 'question': 'Q0', 'question_type': 'numerical_box', 'validation_required': True, - 'validation_min_float_value': 2.1, 'validation_max_float_value': 3.0, 'validation_error_msg': "Error"}) - answer_tag = '%s_%s_%s' % (self.survey1.id, self.page1.id, question.id) - results = [('aaa', _('This is not a number')), ('4.5', 'Error'), ('0.1', 'Error')] - for i in range(len(results)): - self.assertEqual(question.validate_question({answer_tag: results[i][0]}, answer_tag), {answer_tag: results[i][1]}, msg="\ - Validation function for type numerical_box is unable to notify if answer is violating the validation rules") - - def test_05_question_date(self): - question = self.env['survey.question'].sudo(self.survey_manager).create({ - 'page_id': self.page1.id, 'question': 'Q0', 'question_type': 'date', 'validation_required': True, - 'validation_min_date': '2015-03-20', 'validation_max_date': '2015-03-25', 'validation_error_msg': "Error"}) - answer_tag = '%s_%s_%s' % (self.survey1.id, self.page1.id, question.id) - results = [('2015-55-10', _('This is not a date')), ('2015-03-19', 'Error'), ('2015-03-26', 'Error')] - for i in range(len(results)): - self.assertEqual(question.validate_question({answer_tag: results[i][0]}, answer_tag), {answer_tag: results[i][1]}, msg="\ - Validation function for type date is unable to notify if answer is violating the validation rules") - - def test_06_survey_sharing(self): - # Case-1: Executing action with correct data. - correct_survey = self.env['survey.survey'].sudo(self.survey_manager).create({ - 'title': "S0", 'stage_id': self.env['survey.stage'].search([('sequence', '=', 1)]).id, - 'page_ids': [(0, 0, {'title': "P0", 'question_ids': [(0, 0, {'question': "Q0", 'question_type': 'free_text'})]})]}) - action = correct_survey.action_send_survey() - template = self.env.ref('survey.email_template_survey', raise_if_not_found=False) - - ctx = dict( - self.env.context, - default_model='survey.survey', - default_res_id=correct_survey.id, - default_survey_id=correct_survey.id, - default_use_template=bool(template), - default_template_id=template and template.id or False, - default_composition_mode='comment', - notif_layout='mail.mail_notification_light', + self.assertEqual( + question.validate_question({answer_tag: 'Is Alfred an answer ?'}, answer_tag), + {answer_tag: _('This is not a date')} ) - self.assertDictEqual(action, { - 'type': 'ir.actions.act_window', - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'survey.invite', - 'target': 'new', - 'context': ctx, - }) + self.assertEqual( + question.validate_question({answer_tag: '2015-03-19'}, answer_tag), + {answer_tag: 'ValidationError'} + ) - # Case-2: Executing action with incorrect data. - surveys = [ - self.env['survey.survey'].sudo(self.survey_manager).create({ # Survey without any page or question. - 'title': "Test survey"}), - self.env['survey.survey'].sudo(self.survey_manager).create({ # Closed Survey. - 'title': "S0", 'stage_id': self.env['survey.stage'].search([('closed', '=', True)]).id, # Getting Closed stage id. - 'page_ids': [(0, 0, {'title': "P0", 'question_ids': [(0, 0, {'question': "Q0", 'question_type': 'free_text'})]})]})] - for survey in surveys: - self.assertRaises(UserError, survey.action_send_survey) + self.assertEqual( + question.validate_question({answer_tag: '2015-03-26'}, answer_tag), + {answer_tag: 'ValidationError'} + ) - def test_07_survey_email_message(self): - # Case-1: Executing send_mail with correct data. - partner = self.env['res.partner'].create({'name': 'Marie De Cock', 'email': 'marie.de.cock@gmail.com'}) - survey_mail_message = self.env['survey.invite'].sudo(self.survey_manager).create({ - 'survey_id': self.survey1.id, 'public': 'email_public_link', 'body': '__URL__', 'partner_ids': [(4, partner.id)]}) - survey_mail_message.send_mail() + self.assertEqual( + question.validate_question({answer_tag: '2015-03-25'}, answer_tag), + {} + ) - # Case-2: Executing send_mail with incorrect data. - mail_messages = [ - self.env['survey.invite'].sudo(self.survey_manager).create({ # Mail Message without __URL__ in body. - 'survey_id': self.survey1.id, 'public': 'email_public_link'}), - self.env['survey.invite'].sudo(self.survey_manager).create({ # Mail Message without recipents. - 'survey_id': self.survey1.id, 'public': 'email_public_link', 'body': "__URL__"})] - for message in mail_messages: - self.assertRaises(UserError, message.send_mail) + @users('survey_manager') + def test_answer_validation_numerical(self): + question = self._add_question( + self.page_0, 'Q0', 'numerical_box', validation_required=True, + validation_min_float_value=2.2, validation_max_float_value=3.3, validation_error_msg='ValidationError') + answer_tag = '%s_%s_%s' % (self.survey.id, self.page_0.id, question.id) - def test_08_survey_urls(self): - def validate_url(url): - """ Reference: https://github.com/django/django/blob/master/django/core/validators.py """ - url_regex = re.compile( - r'^https?://' # http:// or https:// - r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain... - r'localhost|' # localhost... - r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4 - r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6 - r'(?::\d+)?' # optional port - r'(?:/?|[/?]\S+)$', re.IGNORECASE) - return True if url_regex.match(url) else False + self.assertEqual( + question.validate_question({answer_tag: 'Is Alfred an answer ?'}, answer_tag), + {answer_tag: _('This is not a number')} + ) - base_url = self.env['ir.config_parameter'].get_param('web.base.url') - urltypes = {'public': 'start', 'print': 'print', 'result': 'results'} - for urltype, urltxt in urltypes.items(): - survey_url = getattr(self.survey1, urltype + '_url') - survey_url_relative = getattr(self.survey1.with_context({'relative_url': True}), urltype + '_url') - self.assertTrue(validate_url(survey_url)) - url = "survey/%s/%s" % (urltxt, slug(self.survey1)) - full_url = urls.url_join(base_url, url) - self.assertEqual(full_url, survey_url) - self.assertEqual('/' + url, survey_url_relative) - if urltype == 'public': - url_html = 'Click here to start survey' - self.assertEqual(url_html % full_url, getattr(self.survey1, urltype + '_url_html'), msg="Public URL is incorrect") - self.assertEqual(url_html % ('/' + url), getattr(self.survey1.with_context({'relative_url': True}), urltype + '_url_html'), msg="Public URL is incorrect.") + self.assertEqual( + question.validate_question({answer_tag: '2.0'}, answer_tag), + {answer_tag: 'ValidationError'} + ) - def test_09_answer_survey(self): - question = self.env['survey.question'].sudo(self.survey_manager).create({'page_id': self.page1.id, 'question': 'Q0'}) - input_portal = self.env['survey.user_input'].sudo(self.survey_user).create({ - 'survey_id': self.survey1.id, - 'partner_id': self.survey_user.partner_id.id, - 'user_input_line_ids': [(0, 0, { - 'skipped': False, 'answer_type': 'free_text', 'value_free_text': "Test Answer", - 'survey_id': self.survey1.id, 'question_id': question.id})]}) + self.assertEqual( + question.validate_question({answer_tag: '4.0'}, answer_tag), + {answer_tag: 'ValidationError'} + ) - input_public = self.env['survey.user_input'].sudo(self.user_public).create({ - 'survey_id': self.survey1.id, - 'partner_id': self.survey_user.partner_id.id, - 'user_input_line_ids': [(0, 0, { - 'skipped': False, 'answer_type': 'free_text', 'value_free_text': "Test Answer", - 'survey_id': self.survey1.id, 'question_id': question.id})]}) + self.assertEqual( + question.validate_question({answer_tag: '2.9'}, answer_tag), + {} + ) - answers = [input_portal.user_input_line_ids[0], input_public.user_input_line_ids[0]] - expected_values = {'answer_type': 'free_text', 'value_free_text': "Test Answer"} - for answer in answers: - for field, value in expected_values.items(): - self.assertEqual(answer[field], value, msg="Unable to answer the survey. Expected behaviour of %s is not proper." % (field)) + @users('survey_manager') + def test_answer_validation_textbox_email(self): + question = self._add_question(self.page_0, 'Q0', 'textbox', validation_email=True) + answer_tag = '%s_%s_%s' % (self.survey.id, self.page_0.id, question.id) - def test_10_survey_result_simple_multiple_choice(self): - question = self.env['survey.question'].sudo(self.survey_manager).create({ - 'page_id': self.page1.id, 'question': 'Q0', 'question_type': 'simple_choice', - 'labels_ids': [(0, 0, {'value': "Choice0", 'quizz_mark': 0}), (0, 0, {'value': "Choice1", 'quizz_mark': 0})]}) + self.assertEqual( + question.validate_question({answer_tag: 'not an email'}, answer_tag), + {answer_tag: _('This answer must be an email address')} + ) + + self.assertEqual( + question.validate_question({answer_tag: 'email@example.com'}, answer_tag), + {} + ) + + @users('survey_manager') + def test_answer_validation_textbox_length(self): + question = self._add_question( + self.page_0, 'Q0', 'textbox', validation_required=True, + validation_length_min=2, validation_length_max=8, validation_error_msg='ValidationError') + answer_tag = '%s_%s_%s' % (self.survey.id, self.page_0.id, question.id) + + self.assertEqual( + question.validate_question({answer_tag: 'l'}, answer_tag), + {answer_tag: 'ValidationError'} + ) + + self.assertEqual( + question.validate_question({answer_tag: 'waytoomuchlonganswer'}, answer_tag), + {answer_tag: 'ValidationError'} + ) + + self.assertEqual( + question.validate_question({answer_tag: 'valid'}, answer_tag), + {} + ) + + @users('survey_manager') + def test_result_data_simple_multiple_choice(self): + question = self._add_question( + self.page_0, 'Q0', 'simple_choice', + labels=[{'value': 'Choice0'}, {'value': 'Choice1'}] + ) for i in range(3): - self.env['survey.user_input'].sudo(self.user_public).create({'survey_id': self.survey1.id, 'user_input_line_ids': [(0, 0, { - 'question_id': question.id, - 'answer_type': 'suggestion', - 'value_suggested': random.choice(question.labels_ids.ids)})]}) + answer = self._add_answer(self.survey, False, email='public@example.com') + self._add_answer_line( + question, answer, random.choice(question.labels_ids.ids), + answer_type='suggestion', answer_fname='value_suggested') lines = [line.value_suggested.id for line in question.user_input_line_ids] answers = [{'text': label.value, 'count': lines.count(label.id), 'answer_id': label.id} for label in question.labels_ids] prp_result = self.env['survey.survey'].prepare_result(question)['answers'] - self.assertItemsEqual(prp_result, answers, msg="Statistics of simple, multiple choice questions are different from expectation") + self.assertItemsEqual(prp_result, answers) - def test_11_survey_result_matrix(self): - question = self.env['survey.question'].sudo(self.survey_manager).create({ - 'page_id': self.page1.id, 'question': 'Q0', 'question_type': 'matrix', 'matrix_subtype': 'simple', - 'labels_ids': [(0, 0, {'value': "Column0", "quizz_mark": 0}), (0, 0, {'value': "Column1", "quizz_mark": 0})], - 'labels_ids_2': [(0, 0, {'value': "Row0", "quizz_mark": 0}), (0, 0, {'value': "Row1", "quizz_mark": 0})]}) + @users('survey_manager') + def test_result_data_matrix(self): + question = self._add_question( + self.page_0, 'Q0', 'matrix', matrix_subtype='simple', + labels=[{'value': 'Column0'}, {'value': 'Column1'}], + labels_2=[{'value': 'Row0'}, {'value': 'Row1'}] + ) for i in range(3): - self.env['survey.user_input'].sudo(self.user_public).create({'survey_id': self.survey1.id, 'user_input_line_ids': [(0, 0, { - 'question_id': question.id, 'answer_type': 'suggestion', 'value_suggested': random.choice(question.labels_ids.ids), - 'value_suggested_row': random.choice(question.labels_ids_2.ids)})]}) + answer = self._add_answer(self.survey, False, email='public@example.com') + self._add_answer_line( + question, answer, random.choice(question.labels_ids.ids), + answer_type='suggestion', answer_fname='value_suggested', value_suggested_row=random.choice(question.labels_ids_2.ids) + ) lines = [(line.value_suggested_row.id, line.value_suggested.id) for line in question.user_input_line_ids] res = {} for i in product(question.labels_ids_2.ids, question.labels_ids.ids): res[i] = lines.count((i)) - self.assertEqual(self.env['survey.survey'].prepare_result(question)['result'], res, msg="Statistics of matrix type questions are different from expectations") + self.assertEqual(self.env['survey.survey'].prepare_result(question)['result'], res) - def test_12_survey_result_numeric_box(self): - question = self.env['survey.question'].sudo(self.survey_manager).create({'page_id': self.page1.id, 'question': 'Q0', 'question_type': 'numerical_box'}) + @users('survey_manager') + def test_result_data_numeric_box(self): + question = self._add_question(self.page_0, 'Q0', 'numerical_box') num = [float(n) for n in random.sample(range(1, 100), 3)] nsum = sum(num) for i in range(3): - self.env['survey.user_input'].sudo(self.user_public).create({'survey_id': self.survey1.id, 'user_input_line_ids': [(0, 0, { - 'question_id': question.id, 'answer_type': 'number', 'value_number': num[i]})]}) + answer = self._add_answer(self.survey, False, email='public@example.com') + self._add_answer_line(question, answer, num[i]) exresult = { 'average': round((nsum / len(num)), 2), 'max': round(max(num), 2), 'min': round(min(num), 2), 'sum': nsum, 'most_common': Counter(num).most_common(5)} result = self.env['survey.survey'].prepare_result(question) for key in exresult: - self.assertEqual(result[key], exresult[key], msg="Statistics of numeric box type questions are different from expectations") + self.assertEqual(result[key], exresult[key]) - def test_13_survey_actions(self): - self.env['survey.question'].sudo(self.survey_manager).create({'page_id': self.page1.id, 'question': 'Q0', 'question_type': 'numerical_box'}) - - actions = { - 'start': {'method': 'public', 'token': '/test', 'text': 'Start'}, - 'print': {'method': 'print', 'token': '/test', 'text': 'Print'}, - 'result': {'method': 'result', 'token': '', 'text': 'Results of the'}, - 'test': {'method': 'public', 'token': '/phantom', 'text': 'Results of the'}} - for action, val in actions.items(): - result = getattr(self.survey1.with_context({'survey_token': val['token'][1:]}), 'action_' + action + '_survey')() - url = getattr(self.survey1.with_context({'relative_url': True}), val['method'] + '_url') + val['token'] - self.assertEqual(result['url'], url) + @users('survey_manager') + def test_survey_urls(self): + base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + urltypes = {'public': 'start', 'print': 'print', 'result': 'results'} + for urltype, urltxt in urltypes.items(): + survey_url = getattr(self.survey, urltype + '_url') + survey_url_relative = getattr(self.survey.with_context({'relative_url': True}), urltype + '_url') + url = "survey/%s/%s" % (urltxt, slug(self.survey)) + full_url = urls.url_join(base_url, url) + self.assertEqual(full_url, survey_url) + self.assertEqual('/' + url, survey_url_relative) + if urltype == 'public': + url_html = 'Click here to start survey' + self.assertEqual(url_html % full_url, getattr(self.survey, urltype + '_url_html'), msg="Public URL is incorrect") + self.assertEqual(url_html % ('/' + url), getattr(self.survey.with_context({'relative_url': True}), urltype + '_url_html')) diff --git a/addons/survey/tests/test_survey_flow.py b/addons/survey/tests/test_survey_flow.py new file mode 100644 index 00000000000..12230d49194 --- /dev/null +++ b/addons/survey/tests/test_survey_flow.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import re + +from odoo.addons.survey.tests import common +from odoo.tests import tagged +from odoo.tests.common import HttpCase + + +@tagged('functional') +class TestSurveyFlow(common.SurveyCase, HttpCase): + + def _access_start(self, survey): + return self.url_open('/survey/start/%s' % survey.id) + + def _access_page(self, survey, token): + return self.url_open('/survey/fill/%s/%s' % (survey.id, token)) + + def _access_submit(self, survey, post_data): + return self.url_open('/survey/submit/%s' % survey.id, data=post_data) + + def _find_csrf_token(self, text): + csrf_token_re = re.compile("(input.+csrf_token.+value=\")([_a-zA-Z0-9]{51})", re.MULTILINE) + return csrf_token_re.search(text).groups()[1] + + def _format_submission_data(self, page, answer_data, additional_post_data): + post_data = {} + post_data['page_id'] = page.id + for question_id, answer_vals in answer_data.items(): + question = page.question_ids.filtered(lambda q: q.id == question_id) + if question.question_type == 'multiple_choice': + values = answer_vals['value'] + for value in values: + key = "%s_%s_%s_%s" % (page.survey_id.id, page.id, question.id, value) + post_data[key] = value + else: + [value] = answer_vals['value'] + key = "%s_%s_%s" % (page.survey_id.id, page.id, question.id) + post_data[key] = value + post_data.update(**additional_post_data) + return post_data + + def test_flow_public(self): + # Step: survey manager creates the survey + # -------------------------------------------------- + with self.sudo(self.survey_manager): + survey = self.env['survey.survey'].create({ + 'title': 'Public Survey for Tarte Al Djotte', + 'auth_required': False, + }) + + # First page is about customer data + page_0 = self.env['survey.page'].create({ + 'title': 'Page1: Your Data', + 'survey_id': survey.id, + }) + page0_q0 = self._add_question( + page_0, 'What is your name', 'free_text', + comments_allowed=False, + constr_mandatory=True, constr_error_msg='Please enter your name') + page0_q1 = self._add_question( + page_0, 'What is your age', 'numerical_box', + comments_allowed=False, + constr_mandatory=True, constr_error_msg='Please enter your name') + + # Second page is about tarte al djotte + page_1 = self.env['survey.page'].create({ + 'title': 'Page2: Tarte Al Djotte', + 'survey_id': survey.id, + }) + page1_q0 = self._add_question( + page_1, 'What do you like most in our tarte al djotte', 'multiple_choice', + labels=[{'value': 'The gras'}, + {'value': 'The bette'}, + {'value': 'The tout'}, + {'value': 'The regime is fucked up'}]) + + # fetch starting data to check only newly created data during this flow + answers = self.env['survey.user_input'].search([('survey_id', '=', survey.id)]) + answer_lines = self.env['survey.user_input_line'].search([('survey_id', '=', survey.id)]) + self.assertEqual(answers, self.env['survey.user_input']) + self.assertEqual(answer_lines, self.env['survey.user_input_line']) + + # Step: customer takes the survey + # -------------------------------------------------- + + # Customer opens start page + r = self._access_start(survey) + self.assertResponse(r, 200, [survey.title]) + + # -> this should have generated a new answer with a token + answers = self.env['survey.user_input'].search([('survey_id', '=', survey.id)]) + self.assertEqual(len(answers), 1) + answer_token = answers.token + self.assertTrue(answer_token) + self.assertAnswer(answers, 'new', self.env['survey.page']) + + # Customer begins survey with first page + r = self._access_page(survey, answer_token) + self.assertResponse(r, 200) + self.assertAnswer(answers, 'new', self.env['survey.page']) + csrf_token = self._find_csrf_token(r.text) + + # Customer submit first page answers + answer_data = { + page0_q0.id: {'value': ['Alfred Poilvache']}, + page0_q1.id: {'value': [44.0]}, + } + post_data = self._format_submission_data(page_0, answer_data, {'csrf_token': csrf_token, 'token': answer_token, 'button_submit': 'next'}) + r = self._access_submit(survey, post_data) + self.assertResponse(r, 200) + answers.invalidate_cache() # TDE note: necessary as lots of sudo in controllers messing with cache + + # -> this should have generated answer lines + self.assertAnswer(answers, 'skip', page_0) + self.assertAnswerLines(page_0, answers, answer_data) + + # Customer is redirected on second page and begins filling it + r = self._access_page(survey, answer_token) + self.assertResponse(r, 200) + csrf_token_re = re.compile("(input.+csrf_token.+value=\")([_a-zA-Z0-9]{51})", re.MULTILINE) + csrf_token = csrf_token_re.search(r.text).groups()[1] + + # Customer submit second page answers + answer_data = { + page1_q0.id: {'value': [page1_q0.labels_ids.ids[0], page1_q0.labels_ids.ids[1]]}, + } + post_data = self._format_submission_data(page_1, answer_data, {'csrf_token': csrf_token, 'token': answer_token, 'button_submit': 'next'}) + r = self._access_submit(survey, post_data) + self.assertResponse(r, 200) + answers.invalidate_cache() # TDE note: necessary as lots of sudo in controllers messing with cache + + # -> this should have generated answer lines and closed the answer + self.assertAnswer(answers, 'done', page_1) + self.assertAnswerLines(page_1, answers, answer_data) diff --git a/addons/survey/tests/test_survey_invite.py b/addons/survey/tests/test_survey_invite.py new file mode 100644 index 00000000000..b2f464dcf98 --- /dev/null +++ b/addons/survey/tests/test_survey_invite.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.survey.tests import common +from odoo.exceptions import UserError +from odoo.tests.common import users + + +class TestSurveyInvite(common.SurveyCase): + + @users('survey_manager') + def test_survey_invite_action(self): + # Check correctly configured survey returns an invite wizard action + action = self.survey.action_send_survey() + self.assertEqual(action['res_model'], 'survey.invite') + + # Bad cases + surveys = [ + # no page + self.env['survey.survey'].create({'title': 'Test survey'}), + # no questions + self.env['survey.survey'].create({'title': 'Test survey', 'page_ids': [(0, 0, {'title': 'P0'})]}), + # closed + self.env['survey.survey'].sudo(self.survey_manager).create({ + 'title': 'S0', + 'stage_id': self.env['survey.stage'].search([('closed', '=', True)]).id, + 'page_ids': [(0, 0, {'title': 'P0', 'question_ids': [(0, 0, {'question': 'Q0', 'question_type': 'free_text'})]})] + }) + ] + for survey in surveys: + with self.assertRaises(UserError): + survey.action_send_survey()