409 lines
16 KiB
Python
409 lines
16 KiB
Python
from unittest.mock import patch
|
|
|
|
from django.contrib.auth import get_user_model
|
|
from django.test import TestCase
|
|
|
|
from workflows.branding import get_company_email_domain
|
|
from workflows.models import FormConditionalRuleConfig, FormCustomFieldConfig, FormCustomSectionConfig, FormFieldConfig, FormSectionConfig, OnboardingRequest
|
|
|
|
|
|
class OnboardingFlowTests(TestCase):
|
|
def setUp(self):
|
|
self.company_domain = get_company_email_domain()
|
|
user_model = get_user_model()
|
|
self.user = user_model.objects.create_user(
|
|
username='onboard_user',
|
|
password='secret123',
|
|
email=f'requester@{self.company_domain}',
|
|
first_name='Mia',
|
|
last_name='Beispiel',
|
|
)
|
|
self.client.force_login(self.user)
|
|
|
|
@patch('workflows.views.process_onboarding_request.delay')
|
|
def test_onboarding_submit_persists_and_enqueues_task(self, mock_delay):
|
|
payload = {
|
|
'first_name': 'Max',
|
|
'last_name': 'Mustermann',
|
|
'gender': 'herr',
|
|
'job_title': 'Consultant',
|
|
'department': 'IT-Service',
|
|
'work_email': f'max.mustermann@{self.company_domain}',
|
|
'contract_start': '2026-11-01',
|
|
'employment_type': 'unbefristet',
|
|
'group_mailboxes_required_choice': 'nein',
|
|
'additional_hardware_needed_choice': 'nein',
|
|
'additional_software_needed_choice': 'nein',
|
|
'additional_access_needed_choice': 'nein',
|
|
'successor_required_choice': 'nein',
|
|
'inherit_phone_number_choice': 'nein',
|
|
'agreement_confirm': 'on',
|
|
}
|
|
|
|
response = self.client.post('/onboarding/new/', payload, HTTP_HOST='localhost')
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertIn('/onboarding/new/?saved=1&id=', response['Location'])
|
|
|
|
obj = OnboardingRequest.objects.get(work_email=f'max.mustermann@{self.company_domain}')
|
|
self.assertEqual(obj.full_name, 'Max Mustermann')
|
|
self.assertEqual(obj.onboarded_by_email, f'requester@{self.company_domain}')
|
|
self.assertEqual(obj.onboarded_by_name, 'Mia Beispiel')
|
|
self.assertEqual(obj.approval_released_by_name, '')
|
|
self.assertIsNone(obj.approval_released_at)
|
|
mock_delay.assert_called_once_with(obj.id)
|
|
|
|
@patch('workflows.views.process_onboarding_request.delay')
|
|
def test_hidden_non_locked_field_does_not_block_submission(self, mock_delay):
|
|
FormFieldConfig.objects.update_or_create(
|
|
form_type='onboarding',
|
|
field_name='department',
|
|
defaults={'is_visible': False},
|
|
)
|
|
payload = {
|
|
'first_name': 'Nora',
|
|
'last_name': 'Neutral',
|
|
'gender': 'frau',
|
|
'job_title': 'Consultant',
|
|
'work_email': f'nora.neutral@{self.company_domain}',
|
|
'contract_start': '2026-11-01',
|
|
'employment_type': 'unbefristet',
|
|
'group_mailboxes_required_choice': 'nein',
|
|
'additional_hardware_needed_choice': 'nein',
|
|
'additional_software_needed_choice': 'nein',
|
|
'additional_access_needed_choice': 'nein',
|
|
'successor_required_choice': 'nein',
|
|
'inherit_phone_number_choice': 'nein',
|
|
'agreement_confirm': 'on',
|
|
}
|
|
|
|
response = self.client.post('/onboarding/new/', payload, HTTP_HOST='localhost')
|
|
|
|
self.assertEqual(response.status_code, 302)
|
|
obj = OnboardingRequest.objects.get(work_email=f'nora.neutral@{self.company_domain}')
|
|
self.assertEqual(obj.department, '')
|
|
mock_delay.assert_called_once_with(obj.id)
|
|
|
|
@patch('workflows.views.process_onboarding_request.delay')
|
|
def test_required_override_blocks_submission_when_field_is_missing(self, mock_delay):
|
|
FormFieldConfig.objects.update_or_create(
|
|
form_type='onboarding',
|
|
field_name='job_title',
|
|
defaults={'is_required': True},
|
|
)
|
|
payload = {
|
|
'first_name': 'Lina',
|
|
'last_name': 'Leer',
|
|
'gender': 'frau',
|
|
'department': 'IT-Service',
|
|
'work_email': f'lina.leer@{self.company_domain}',
|
|
'contract_start': '2026-11-01',
|
|
'employment_type': 'unbefristet',
|
|
'group_mailboxes_required_choice': 'nein',
|
|
'additional_hardware_needed_choice': 'nein',
|
|
'additional_software_needed_choice': 'nein',
|
|
'additional_access_needed_choice': 'nein',
|
|
'successor_required_choice': 'nein',
|
|
'inherit_phone_number_choice': 'nein',
|
|
'agreement_confirm': 'on',
|
|
}
|
|
|
|
response = self.client.post('/onboarding/new/', payload, HTTP_HOST='localhost')
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, 'Dieses Feld ist zwingend erforderlich.')
|
|
self.assertFalse(OnboardingRequest.objects.filter(work_email=f'lina.leer@{self.company_domain}').exists())
|
|
mock_delay.assert_not_called()
|
|
|
|
@patch('workflows.views.process_onboarding_request.delay')
|
|
def test_hidden_itsetup_section_is_removed_from_form_and_submission(self, mock_delay):
|
|
FormSectionConfig.objects.update_or_create(
|
|
form_type='onboarding',
|
|
section_key='itsetup',
|
|
defaults={'is_visible': False},
|
|
)
|
|
|
|
response = self.client.get('/onboarding/new/', HTTP_HOST='localhost')
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertNotContains(response, '3. IT-Setup')
|
|
|
|
payload = {
|
|
'first_name': 'Nora',
|
|
'last_name': 'Neutral',
|
|
'gender': 'frau',
|
|
'job_title': 'Consultant',
|
|
'department': 'IT-Service',
|
|
'work_email': f'nora.section@{self.company_domain}',
|
|
'contract_start': '2026-11-01',
|
|
'employment_type': 'unbefristet',
|
|
'group_mailboxes_required_choice': 'nein',
|
|
'agreement_confirm': 'on',
|
|
}
|
|
|
|
submit_response = self.client.post('/onboarding/new/', payload, HTTP_HOST='localhost')
|
|
|
|
self.assertEqual(submit_response.status_code, 302)
|
|
self.assertTrue(OnboardingRequest.objects.filter(work_email=f'nora.section@{self.company_domain}').exists())
|
|
mock_delay.assert_called_once()
|
|
|
|
def test_onboarding_page_renders_conditional_rules_payload(self):
|
|
response = self.client.get('/onboarding/new/', HTTP_HOST='localhost')
|
|
html = response.content.decode('utf-8')
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn('id="onboarding-conditional-rules"', html)
|
|
self.assertIn('business-card-box', html)
|
|
self.assertIn('employment-end-box', html)
|
|
self.assertIn('data-conditional-target="business-card-box"', html)
|
|
self.assertNotIn('data-conditional-target="phone-box"', html)
|
|
|
|
def test_onboarding_page_uses_stored_conditional_rule_config(self):
|
|
FormConditionalRuleConfig.objects.update_or_create(
|
|
form_type='onboarding',
|
|
target_key='employment-end-box',
|
|
defaults={
|
|
'is_active': True,
|
|
'clauses': [{'field': 'employment_type', 'operator': 'equals', 'value': 'unbefristet'}],
|
|
},
|
|
)
|
|
|
|
response = self.client.get('/onboarding/new/', HTTP_HOST='localhost')
|
|
html = response.content.decode('utf-8')
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn('employment-end-box', html)
|
|
self.assertIn('"value": "unbefristet"', html)
|
|
|
|
def test_onboarding_custom_field_uses_combined_order(self):
|
|
FormCustomFieldConfig.objects.create(
|
|
form_type='onboarding',
|
|
field_key='office_location',
|
|
section_key='stammdaten',
|
|
sort_order=1,
|
|
field_type='text',
|
|
is_active=True,
|
|
label='Bürostandort',
|
|
)
|
|
FormFieldConfig.objects.update_or_create(
|
|
form_type='onboarding',
|
|
field_name='gender',
|
|
defaults={'sort_order': 2, 'page_key': 'stammdaten'},
|
|
)
|
|
|
|
response = self.client.get('/onboarding/new/', HTTP_HOST='localhost')
|
|
html = response.content.decode('utf-8')
|
|
|
|
self.assertLess(html.index('Bürostandort'), html.index('Anrede'))
|
|
|
|
def test_phone_direct_dial_field_is_visible_without_successor(self):
|
|
response = self.client.get('/onboarding/new/', HTTP_HOST='localhost')
|
|
html = response.content.decode('utf-8')
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn('Telefon-Direktwahl', html)
|
|
self.assertNotIn('data-conditional-target="phone-box"', html)
|
|
|
|
def test_onboarding_custom_section_is_rendered_in_navigation(self):
|
|
FormCustomSectionConfig.objects.create(
|
|
form_type='onboarding',
|
|
section_key='benefits',
|
|
sort_order=10,
|
|
title='Benefits',
|
|
is_active=True,
|
|
)
|
|
FormCustomFieldConfig.objects.create(
|
|
form_type='onboarding',
|
|
field_key='meal_allowance',
|
|
section_key='benefits',
|
|
sort_order=0,
|
|
field_type='text',
|
|
is_active=True,
|
|
label='Essenszuschuss',
|
|
)
|
|
|
|
response = self.client.get('/onboarding/new/', HTTP_HOST='localhost')
|
|
html = response.content.decode('utf-8')
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn('Benefits', html)
|
|
self.assertIn('Essenszuschuss', html)
|
|
|
|
def test_onboarding_custom_section_with_checkbox_fields_shows_section_select_all(self):
|
|
FormCustomSectionConfig.objects.create(
|
|
form_type='onboarding',
|
|
section_key='benefits',
|
|
sort_order=10,
|
|
title='Benefits',
|
|
is_active=True,
|
|
)
|
|
FormCustomFieldConfig.objects.create(
|
|
form_type='onboarding',
|
|
field_key='meal_allowance',
|
|
section_key='benefits',
|
|
sort_order=0,
|
|
field_type='checkbox',
|
|
is_active=True,
|
|
label='Essenszuschuss',
|
|
)
|
|
FormCustomFieldConfig.objects.create(
|
|
form_type='onboarding',
|
|
field_key='parking_spot',
|
|
section_key='benefits',
|
|
sort_order=1,
|
|
field_type='checkbox',
|
|
is_active=True,
|
|
label='Parkplatz',
|
|
)
|
|
|
|
response = self.client.get('/onboarding/new/', HTTP_HOST='localhost')
|
|
html = response.content.decode('utf-8')
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn('data-section-checkbox-toggle', html)
|
|
self.assertIn('Essenszuschuss', html)
|
|
self.assertIn('Parkplatz', html)
|
|
|
|
@patch('workflows.views.process_onboarding_request.delay')
|
|
def test_onboarding_custom_field_is_rendered_and_saved(self, mock_delay):
|
|
FormCustomFieldConfig.objects.create(
|
|
form_type='onboarding',
|
|
field_key='office_location',
|
|
section_key='stammdaten',
|
|
sort_order=0,
|
|
field_type='text',
|
|
is_active=True,
|
|
is_required=True,
|
|
label='Bürostandort',
|
|
)
|
|
|
|
response = self.client.get('/onboarding/new/', HTTP_HOST='localhost')
|
|
self.assertContains(response, 'Bürostandort')
|
|
|
|
payload = {
|
|
'first_name': 'Mara',
|
|
'last_name': 'Muster',
|
|
'gender': 'frau',
|
|
'job_title': 'Consultant',
|
|
'department': 'IT-Service',
|
|
'work_email': f'mara.muster@{self.company_domain}',
|
|
'contract_start': '2026-11-01',
|
|
'employment_type': 'unbefristet',
|
|
'group_mailboxes_required_choice': 'nein',
|
|
'additional_hardware_needed_choice': 'nein',
|
|
'additional_software_needed_choice': 'nein',
|
|
'additional_access_needed_choice': 'nein',
|
|
'successor_required_choice': 'nein',
|
|
'inherit_phone_number_choice': 'nein',
|
|
'custom__office_location': 'Berlin Mitte',
|
|
'agreement_confirm': 'on',
|
|
}
|
|
|
|
submit_response = self.client.post('/onboarding/new/', payload, HTTP_HOST='localhost')
|
|
|
|
self.assertEqual(submit_response.status_code, 302)
|
|
obj = OnboardingRequest.objects.get(work_email=f'mara.muster@{self.company_domain}')
|
|
self.assertEqual(obj.custom_field_values, {'office_location': 'Berlin Mitte'})
|
|
mock_delay.assert_called_once_with(obj.id)
|
|
|
|
@patch('workflows.views.process_onboarding_request.delay')
|
|
def test_hidden_required_custom_field_does_not_block_submission(self, mock_delay):
|
|
FormCustomFieldConfig.objects.create(
|
|
form_type='onboarding',
|
|
field_key='visitor_badge_name',
|
|
section_key='stammdaten',
|
|
sort_order=0,
|
|
field_type='text',
|
|
is_active=True,
|
|
is_required=True,
|
|
label='Besucherausweis',
|
|
)
|
|
FormConditionalRuleConfig.objects.update_or_create(
|
|
form_type='onboarding',
|
|
target_key='custom__visitor_badge_name',
|
|
defaults={
|
|
'is_active': True,
|
|
'clauses': [{'field': 'order_business_cards', 'operator': 'checked', 'value': True}],
|
|
},
|
|
)
|
|
|
|
response = self.client.get('/onboarding/new/', HTTP_HOST='localhost')
|
|
html = response.content.decode('utf-8')
|
|
self.assertIn('custom__visitor_badge_name', html)
|
|
self.assertIn('"custom__visitor_badge_name"', html)
|
|
|
|
payload = {
|
|
'first_name': 'Lea',
|
|
'last_name': 'Leicht',
|
|
'gender': 'frau',
|
|
'job_title': 'Consultant',
|
|
'department': 'IT-Service',
|
|
'work_email': f'lea.leicht@{self.company_domain}',
|
|
'contract_start': '2026-11-01',
|
|
'employment_type': 'unbefristet',
|
|
'group_mailboxes_required_choice': 'nein',
|
|
'additional_hardware_needed_choice': 'nein',
|
|
'additional_software_needed_choice': 'nein',
|
|
'additional_access_needed_choice': 'nein',
|
|
'successor_required_choice': 'nein',
|
|
'inherit_phone_number_choice': 'nein',
|
|
'agreement_confirm': 'on',
|
|
}
|
|
|
|
submit_response = self.client.post('/onboarding/new/', payload, HTTP_HOST='localhost')
|
|
|
|
self.assertEqual(submit_response.status_code, 302)
|
|
obj = OnboardingRequest.objects.get(work_email=f'lea.leicht@{self.company_domain}')
|
|
self.assertEqual(obj.custom_field_values, {'visitor_badge_name': ''})
|
|
mock_delay.assert_called_once_with(obj.id)
|
|
|
|
@patch('workflows.views.process_onboarding_request.delay')
|
|
def test_visible_required_custom_field_blocks_submission(self, mock_delay):
|
|
FormCustomFieldConfig.objects.create(
|
|
form_type='onboarding',
|
|
field_key='visitor_badge_name',
|
|
section_key='stammdaten',
|
|
sort_order=0,
|
|
field_type='text',
|
|
is_active=True,
|
|
is_required=True,
|
|
label='Besucherausweis',
|
|
)
|
|
FormConditionalRuleConfig.objects.update_or_create(
|
|
form_type='onboarding',
|
|
target_key='custom__visitor_badge_name',
|
|
defaults={
|
|
'is_active': True,
|
|
'clauses': [{'field': 'order_business_cards', 'operator': 'checked', 'value': True}],
|
|
},
|
|
)
|
|
|
|
payload = {
|
|
'first_name': 'Lia',
|
|
'last_name': 'Laut',
|
|
'gender': 'frau',
|
|
'job_title': 'Consultant',
|
|
'department': 'IT-Service',
|
|
'work_email': f'lia.laut@{self.company_domain}',
|
|
'contract_start': '2026-11-01',
|
|
'employment_type': 'unbefristet',
|
|
'order_business_cards': 'on',
|
|
'business_card_name': 'Lia Laut',
|
|
'business_card_title': 'Consultant',
|
|
'business_card_email': f'lia.laut@{self.company_domain}',
|
|
'business_card_phone': '030 123456',
|
|
'group_mailboxes_required_choice': 'nein',
|
|
'additional_hardware_needed_choice': 'nein',
|
|
'additional_software_needed_choice': 'nein',
|
|
'additional_access_needed_choice': 'nein',
|
|
'successor_required_choice': 'nein',
|
|
'inherit_phone_number_choice': 'nein',
|
|
'agreement_confirm': 'on',
|
|
}
|
|
|
|
response = self.client.post('/onboarding/new/', payload, HTTP_HOST='localhost')
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, 'Besucherausweis')
|
|
self.assertFalse(OnboardingRequest.objects.filter(work_email=f'lia.laut@{self.company_domain}').exists())
|
|
mock_delay.assert_not_called()
|