Files
odoo_source/addons/survey/tests/test_survey.py
T
Aurélien Warnon aa66cdc596 [IMP] survey: rework survey.user_input views
PURPOSE

Globally improve the main survey.user_input form view to make it more user
friendly.
This includes fields move, new computed fields, a ribbon, ...

SPECIFICATIONS

- Make the Survey User Input model support mail.thread and mail.activity.mixin
  This lets us display the chatter on the form view and allows users to
  schedule activities.
  e.g: discuss a participation among colleagues, add activities to check some
  participation because you think the person has cheated, ...

- Add a ribbon on the form view that says "passed" or "failed" according to the
  user result

- Introduce a new "Answer" column for the questions list view that is a
  modified 'display_name' that displays the answer based on the question type ;
  This allows to see the answers at a quick glance without having to drill down
  every question to look at the "value_char_box", "value_datetime", ...

- Add the attempts count information in a stat-button, when clicked, the user
  is redirected to the list view of all survey attempts of that specific user
  for that specific survey

- Re-organize and move some fields

- Hide some advanced information into debug mode

Task-2729604

closes odoo/odoo#83781

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
2022-05-12 22:13:46 +02:00

299 lines
14 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from freezegun import freeze_time
from odoo import _, fields
from odoo.addons.survey.tests import common
from odoo.tests.common import users
class TestSurveyInternals(common.TestSurveyCommon):
def test_answer_attempts_count(self):
""" As 'attempts_number' and 'attempts_count' are computed using raw SQL queries, let us
test the results. """
test_survey = self.env['survey.survey'].create({
'title': 'Test Survey',
'is_attempts_limited': True,
'attempts_limit': 4,
})
all_attempts = self.env['survey.user_input']
for _i in range(4):
all_attempts |= self._add_answer(test_survey, self.survey_user.partner_id, state='done')
# read both fields at once to allow computing their values in batch
attempts_results = all_attempts.read(['attempts_number', 'attempts_count'])
first_attempt = attempts_results[0]
second_attempt = attempts_results[1]
third_attempt = attempts_results[2]
fourth_attempt = attempts_results[3]
self.assertEqual(first_attempt['attempts_number'], 1)
self.assertEqual(first_attempt['attempts_count'], 4)
self.assertEqual(second_attempt['attempts_number'], 2)
self.assertEqual(second_attempt['attempts_count'], 4)
self.assertEqual(third_attempt['attempts_number'], 3)
self.assertEqual(third_attempt['attempts_count'], 4)
self.assertEqual(fourth_attempt['attempts_number'], 4)
self.assertEqual(fourth_attempt['attempts_count'], 4)
@freeze_time("2020-02-15 18:00")
def test_answer_display_name(self):
""" The "display_name" field in a survey.user_input.line is a computed field that will
display the answer label for any type of question.
Let us test the various question types. """
questions = self._create_one_question_per_type()
user_input = self._add_answer(self.survey, self.survey_user.partner_id)
for question in questions:
if question.question_type == 'char_box':
question_answer = self._add_answer_line(question, user_input, 'Char box answer')
self.assertEqual(question_answer.display_name, 'Char box answer')
elif question.question_type == 'text_box':
question_answer = self._add_answer_line(question, user_input, 'Text box answer')
self.assertEqual(question_answer.display_name, 'Text box answer')
elif question.question_type == 'numerical_box':
question_answer = self._add_answer_line(question, user_input, 7)
self.assertEqual(question_answer.display_name, '7.0')
elif question.question_type == 'date':
question_answer = self._add_answer_line(question, user_input, fields.Datetime.now())
self.assertEqual(question_answer.display_name, '2020-02-15')
elif question.question_type == 'datetime':
question_answer = self._add_answer_line(question, user_input, fields.Datetime.now())
self.assertEqual(question_answer.display_name, '2020-02-15 18:00:00')
elif question.question_type == 'simple_choice':
question_answer = self._add_answer_line(question, user_input, question.suggested_answer_ids[0].id)
self.assertEqual(question_answer.display_name, 'SChoice0')
elif question.question_type == 'multiple_choice':
question_answer_1 = self._add_answer_line(question, user_input, question.suggested_answer_ids[0].id)
self.assertEqual(question_answer_1.display_name, 'MChoice0')
question_answer_2 = self._add_answer_line(question, user_input, question.suggested_answer_ids[1].id)
self.assertEqual(question_answer_2.display_name, 'MChoice1')
elif question.question_type == 'matrix':
question_answer_1 = self._add_answer_line(question, user_input,
question.suggested_answer_ids[0].id, **{'answer_value_row': question.matrix_row_ids[0].id})
self.assertEqual(question_answer_1.display_name, 'Column0: Row0')
question_answer_2 = self._add_answer_line(question, user_input,
question.suggested_answer_ids[0].id, **{'answer_value_row': question.matrix_row_ids[1].id})
self.assertEqual(question_answer_2.display_name, 'Column0: Row1')
@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 in self._create_one_question_per_type():
self.assertDictEqual(
question.validate_question(''),
{question.id: 'TestError'}
)
@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')
self.assertEqual(
question.validate_question('Is Alfred an answer ?'),
{question.id: _('This is not a date')}
)
self.assertEqual(
question.validate_question('2015-03-19'),
{question.id: 'ValidationError'}
)
self.assertEqual(
question.validate_question('2015-03-26'),
{question.id: 'ValidationError'}
)
self.assertEqual(
question.validate_question('2015-03-25'),
{}
)
@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')
self.assertEqual(
question.validate_question('Is Alfred an answer ?'),
{question.id: _('This is not a number')}
)
self.assertEqual(
question.validate_question('2.0'),
{question.id: 'ValidationError'}
)
self.assertEqual(
question.validate_question('4.0'),
{question.id: 'ValidationError'}
)
self.assertEqual(
question.validate_question('2.9'),
{}
)
@users('survey_manager')
def test_answer_validation_char_box_email(self):
question = self._add_question(self.page_0, 'Q0', 'char_box', validation_email=True)
self.assertEqual(
question.validate_question('not an email'),
{question.id: _('This answer must be an email address')}
)
self.assertEqual(
question.validate_question('email@example.com'),
{}
)
@users('survey_manager')
def test_answer_validation_char_box_length(self):
question = self._add_question(
self.page_0, 'Q0', 'char_box', validation_required=True,
validation_length_min=2, validation_length_max=8, validation_error_msg='ValidationError')
self.assertEqual(
question.validate_question('l'),
{question.id: 'ValidationError'}
)
self.assertEqual(
question.validate_question('waytoomuchlonganswer'),
{question.id: 'ValidationError'}
)
self.assertEqual(
question.validate_question('valid'),
{}
)
def test_partial_scores_simple_choice(self):
"""" Check that if partial scores are given for partially correct answers, in the case of a multiple
choice question with single choice, choosing the answer with max score gives 100% of points. """
partial_scores_survey = self.env['survey.survey'].create({
'title': 'How much do you know about words?',
'scoring_type': 'scoring_with_answers',
'scoring_success_min': 90.0,
})
[a_01, a_02, a_03] = self.env['survey.question.answer'].create([{
'value': 'A thing full of letters.',
'answer_score': 1.0
}, {
'value': 'A unit of language, [...], carrying a meaning.',
'answer_score': 4.0,
'is_correct': True
}, {
'value': '42',
'answer_score': -4.0
}])
q_01 = self.env['survey.question'].create({
'survey_id': partial_scores_survey.id,
'title': 'What is a word?',
'sequence': 1,
'question_type': 'simple_choice',
'suggested_answer_ids': [(6, 0, (a_01 | a_02 | a_03).ids)]
})
user_input = self.env['survey.user_input'].create({'survey_id': partial_scores_survey.id})
self.env['survey.user_input.line'].create({
'user_input_id': user_input.id,
'question_id': q_01.id,
'answer_type': 'suggestion',
'suggested_answer_id': a_02.id
})
# Check that scoring is correct and survey is passed
self.assertEqual(user_input.scoring_percentage, 100)
self.assertTrue(user_input.scoring_success)
@users('survey_manager')
def test_skipped_values(self):
""" Create one question per type of questions.
Make sure they are correctly registered as 'skipped' after saving an empty answer for each
of them. """
questions = self._create_one_question_per_type()
survey_user = self.survey._create_answer(user=self.survey_user)
for question in questions:
answer = '' if question.question_type in ['char_box', 'text_box'] else None
survey_user.save_lines(question, answer)
for question in questions:
self._assert_skipped_question(question, survey_user)
@users('survey_manager')
def test_copy_conditional_question_settings(self):
""" Create a survey with conditional layout, clone it and verify that the cloned survey has the same conditional
layout as the original survey.
The test also check that the cloned survey doesn't reference the original survey.
"""
def get_question_by_title(survey, title):
return survey.question_ids.filtered(lambda q: q.title == title)[0]
# Create the survey questions (! texts of the questions must be unique as they are used to query them)
q_is_vegetarian_text = 'Are you vegetarian ?'
q_is_vegetarian = self._add_question(
self.page_0, q_is_vegetarian_text, 'multiple_choice', survey_id=self.survey.id,
sequence=100, labels=[{'value': 'Yes'}, {'value': 'No'}])
q_food_vegetarian_text = 'Choose your green meal'
self._add_question(self.page_0, q_food_vegetarian_text, 'multiple_choice',
is_conditional=True, sequence=101,
triggering_question_id=q_is_vegetarian.id,
triggering_answer_id=q_is_vegetarian.suggested_answer_ids[0].id,
survey_id=self.survey.id,
labels=[{'value': 'Vegetarian pizza'}, {'value': 'Vegetarian burger'}])
q_food_not_vegetarian_text = 'Choose your meal'
self._add_question(self.page_0, q_food_not_vegetarian_text, 'multiple_choice',
is_conditional=True, sequence=102,
triggering_question_id=q_is_vegetarian.id,
triggering_answer_id=q_is_vegetarian.suggested_answer_ids[1].id,
survey_id=self.survey.id,
labels=[{'value': 'Steak with french fries'}, {'value': 'Fish'}])
# Clone the survey
survey_clone = self.survey.copy()
# Verify the conditional layout and that the cloned survey doesn't reference the original survey
q_is_vegetarian_cloned = get_question_by_title(survey_clone, q_is_vegetarian_text)
q_food_vegetarian_cloned = get_question_by_title(survey_clone, q_food_vegetarian_text)
q_food_not_vegetarian_cloned = get_question_by_title(survey_clone, q_food_not_vegetarian_text)
self.assertFalse(q_is_vegetarian_cloned.is_conditional)
# Vegetarian choice
self.assertTrue(q_food_vegetarian_cloned)
# Correct conditional layout
self.assertEqual(q_food_vegetarian_cloned.triggering_question_id.id, q_is_vegetarian_cloned.id)
self.assertEqual(q_food_vegetarian_cloned.triggering_answer_id.id,
q_is_vegetarian_cloned.suggested_answer_ids[0].id)
# Doesn't reference the original survey
self.assertNotEqual(q_food_vegetarian_cloned.triggering_question_id.id, q_is_vegetarian.id)
self.assertNotEqual(q_food_vegetarian_cloned.triggering_answer_id.id,
q_is_vegetarian.suggested_answer_ids[0].id)
# Not vegetarian choice
self.assertTrue(q_food_not_vegetarian_cloned.is_conditional)
# Correct conditional layout
self.assertEqual(q_food_not_vegetarian_cloned.triggering_question_id.id, q_is_vegetarian_cloned.id)
self.assertEqual(q_food_not_vegetarian_cloned.triggering_answer_id.id,
q_is_vegetarian_cloned.suggested_answer_ids[1].id)
# Doesn't reference the original survey
self.assertNotEqual(q_food_not_vegetarian_cloned.triggering_question_id.id, q_is_vegetarian.id)
self.assertNotEqual(q_food_not_vegetarian_cloned.triggering_answer_id.id,
q_is_vegetarian.suggested_answer_ids[1].id)