mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-01-20 15:02:31 +00:00
Merge branch 'master' into markdown
# Conflicts: # RIGS/static/scss/screen.scss # RIGS/templates/RIGS/event_detail.html # RIGS/templates/RIGS/event_form.html # RIGS/templates/RIGS/event_table.html # RIGS/templates/RIGS/item_row.html # RIGS/templates/RIGS/item_table.html # RIGS/templates/RIGS/organisation_detail.html # RIGS/templates/RIGS/organisation_form.html # RIGS/templates/RIGS/person_detail.html # RIGS/templates/RIGS/person_form.html # RIGS/templates/RIGS/rigboard.html # RIGS/templates/RIGS/venue_detail.html # RIGS/templates/RIGS/venue_form.html # RIGS/templates/event_print.xml # RIGS/templates/event_print_page.xml # RIGS/templates/item_modal.html # RIGS/tests/test_unit.py # assets/templates/asset_create.html # assets/templates/asset_update.html # assets/templates/partials/asset_detail_form.html # requirements.txt # templates/base.html
This commit is contained in:
0
RIGS/tests/__init__.py
Normal file
0
RIGS/tests/__init__.py
Normal file
108
RIGS/tests/conftest.py
Normal file
108
RIGS/tests/conftest.py
Normal file
@@ -0,0 +1,108 @@
|
||||
from RIGS import models
|
||||
import pytest
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def basic_event(db):
|
||||
event = models.Event.objects.create(name="TE E1", start_date=timezone.now())
|
||||
yield event
|
||||
event.delete()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ra(basic_event, admin_user):
|
||||
ra = models.RiskAssessment.objects.create(event=basic_event, nonstandard_equipment=False, nonstandard_use=False,
|
||||
contractors=False, other_companies=False, crew_fatigue=False,
|
||||
big_power=False, power_mic=admin_user, generators=False,
|
||||
other_companies_power=False, nonstandard_equipment_power=False,
|
||||
multiple_electrical_environments=False, noise_monitoring=False,
|
||||
known_venue=True, safe_loading=True, safe_storage=True,
|
||||
area_outside_of_control=True, barrier_required=True,
|
||||
nonstandard_emergency_procedure=True, special_structures=False,
|
||||
suspended_structures=False, outside=False)
|
||||
yield ra
|
||||
ra.delete()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def medium_ra(ra):
|
||||
ra.big_power = True
|
||||
ra.save()
|
||||
yield ra
|
||||
ra.big_power = False
|
||||
ra.save()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def venue(db):
|
||||
venue = models.Venue.objects.create(name="Venue 1")
|
||||
yield venue
|
||||
venue.delete()
|
||||
|
||||
|
||||
@pytest.fixture # TODO parameterise with Event sizes
|
||||
def checklist(basic_event, venue, admin_user, ra):
|
||||
checklist = models.EventChecklist.objects.create(event=basic_event, power_mic=admin_user, safe_parking=False,
|
||||
safe_packing=False, exits=False, trip_hazard=False, warning_signs=False,
|
||||
ear_plugs=False, hs_location="Locked away safely",
|
||||
extinguishers_location="Somewhere, I forgot", earthing=False, pat=False,
|
||||
date=timezone.now(), venue=venue)
|
||||
yield checklist
|
||||
checklist.delete()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def many_events(db, admin_user, scope="class"):
|
||||
many_events = {
|
||||
# produce 7 normal events - 5 current
|
||||
1: models.Event.objects.create(name="TE E1", start_date=date.today() + timedelta(days=6),
|
||||
description="start future no end"),
|
||||
2: models.Event.objects.create(name="TE E2", start_date=date.today(), description="start today no end"),
|
||||
3: models.Event.objects.create(name="TE E3", start_date=date.today(), end_date=date.today(),
|
||||
description="start today with end today"),
|
||||
4: models.Event.objects.create(name="TE E4", start_date='2014-03-20', description="start past no end"),
|
||||
5: models.Event.objects.create(name="TE E5", start_date='2014-03-20', end_date='2014-03-21',
|
||||
description="start past with end past"),
|
||||
6: models.Event.objects.create(name="TE E6", start_date=date.today() - timedelta(days=2),
|
||||
end_date=date.today() + timedelta(days=2),
|
||||
description="start past, end future"),
|
||||
7: models.Event.objects.create(name="TE E7", start_date=date.today() + timedelta(days=2),
|
||||
end_date=date.today() + timedelta(days=2),
|
||||
description="start + end in future"),
|
||||
|
||||
# 2 cancelled - 1 current
|
||||
8: models.Event.objects.create(name="TE E8", start_date=date.today() + timedelta(days=2),
|
||||
end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED,
|
||||
description="cancelled in future"),
|
||||
9: models.Event.objects.create(name="TE E9", start_date=date.today() - timedelta(days=1),
|
||||
end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED,
|
||||
description="cancelled and started"),
|
||||
|
||||
# 5 dry hire - 3 current
|
||||
10: models.Event.objects.create(name="TE E10", start_date=date.today(), dry_hire=True,
|
||||
description="dryhire today"),
|
||||
11: models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True,
|
||||
checked_in_by=admin_user,
|
||||
description="dryhire today, checked in"),
|
||||
12: models.Event.objects.create(name="TE E12", start_date=date.today() - timedelta(days=1), dry_hire=True,
|
||||
status=models.Event.BOOKED, description="dryhire past"),
|
||||
13: models.Event.objects.create(name="TE E13", start_date=date.today() - timedelta(days=2), dry_hire=True,
|
||||
checked_in_by=admin_user, description="dryhire past checked in"),
|
||||
14: models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True,
|
||||
status=models.Event.CANCELLED, description="dryhire today cancelled"),
|
||||
|
||||
# 4 non rig - 3 current
|
||||
15: models.Event.objects.create(name="TE E15", start_date=date.today(), is_rig=False,
|
||||
description="non rig today"),
|
||||
16: models.Event.objects.create(name="TE E16", start_date=date.today() + timedelta(days=1), is_rig=False,
|
||||
description="non rig tomorrow"),
|
||||
17: models.Event.objects.create(name="TE E17", start_date=date.today() - timedelta(days=1), is_rig=False,
|
||||
description="non rig yesterday"),
|
||||
18: models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False,
|
||||
status=models.Event.CANCELLED,
|
||||
description="non rig today cancelled"),
|
||||
}
|
||||
yield many_events
|
||||
for event in many_events:
|
||||
event.delete()
|
||||
396
RIGS/tests/pages.py
Normal file
396
RIGS/tests/pages.py
Normal file
@@ -0,0 +1,396 @@
|
||||
from django.urls import reverse
|
||||
from pypom import Region
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
|
||||
from PyRIGS.tests import regions
|
||||
from PyRIGS.tests.pages import BasePage, FormPage
|
||||
from RIGS.tests import regions as rigs_regions
|
||||
|
||||
|
||||
class Index(BasePage):
|
||||
URL_TEMPLATE = reverse('index')
|
||||
|
||||
|
||||
class Rigboard(BasePage):
|
||||
URL_TEMPLATE = reverse('rigboard')
|
||||
|
||||
_add_item_selector = (By.XPATH, "//a[contains(@class,'btn-primary') and contains(., 'New')]")
|
||||
_event_row_locator = (By.ID, 'event_row')
|
||||
|
||||
def add(self):
|
||||
self.find_element(*self._add_item_selector).click()
|
||||
|
||||
class EventListRow(Region):
|
||||
_event_number_locator = (By.ID, "event_number")
|
||||
_event_dates_locator = (By.ID, "event_dates")
|
||||
_event_details_locator = (By.ID, "event_details")
|
||||
_event_mic_locator = (By.ID, "event_mic")
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.find_element(*self._event_number_locator).text
|
||||
|
||||
@property
|
||||
def dates(self):
|
||||
return self.find_element(*self._event_dates_locator).text
|
||||
|
||||
@property
|
||||
def details(self):
|
||||
return self.find_element(*self._event_details_locator).text
|
||||
|
||||
@property
|
||||
def mic(self):
|
||||
return self.find_element(*self._event_mic_locator).text
|
||||
|
||||
@property
|
||||
def events(self):
|
||||
return [self.EventListRow(self, i) for i in self.find_elements(*self._event_row_locator)]
|
||||
|
||||
|
||||
class EventDetail(BasePage):
|
||||
URL_TEMPLATE = 'event/{event_id}'
|
||||
|
||||
# TODO Refactor into regions to match template fragmentation
|
||||
_event_name_selector = (By.XPATH, '//h2')
|
||||
_person_panel_selector = (By.XPATH, '//div[contains(text(), "Person Details")]/..')
|
||||
_name_selector = (By.XPATH, '//dt[text()="Person"]/following-sibling::dd[1]')
|
||||
_email_selector = (By.XPATH, '//dt[text()="Email"]/following-sibling::dd[1]')
|
||||
_phone_selector = (By.XPATH, '//dt[text()="Phone Number"]/following-sibling::dd[1]')
|
||||
_event_table_selector = (By.ID, 'item-table')
|
||||
|
||||
@property
|
||||
def event_name(self):
|
||||
return self.find_element(*self._event_name_selector).text
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.find_element(*self._person_panel_selector).find_element(*self._name_selector).text
|
||||
|
||||
@property
|
||||
def email(self):
|
||||
return self.find_element(*self._person_panel_selector).find_element(*self._email_selector).text
|
||||
|
||||
@property
|
||||
def phone(self):
|
||||
return self.find_element(*self._person_panel_selector).find_element(*self._phone_selector).text
|
||||
|
||||
@property
|
||||
def item_table(self):
|
||||
return self.find_element(*self._event_table_selector)
|
||||
|
||||
|
||||
class CreateEvent(FormPage):
|
||||
URL_TEMPLATE = reverse('event_create')
|
||||
|
||||
_is_rig_selector = (By.ID, 'is_rig-selector')
|
||||
_submit_locator = (By.XPATH, "//button[@type='submit' and contains(., 'Save')]")
|
||||
_person_selector_selector = (By.XPATH, '//*[@id="id_person"]/..')
|
||||
_venue_selector_selector = (By.XPATH, '//*[@id="id_venue"]/..')
|
||||
_mic_selector_selector = (By.XPATH, '//*[@id="form-hws"]/div[7]/div[1]/div/div')
|
||||
|
||||
_add_person_selector = (By.XPATH, '//a[@data-target="#id_person" and contains(@href, "add")]')
|
||||
_add_item_selector = (By.XPATH, '//button[contains(@class, "item-add")]')
|
||||
|
||||
_event_table_selector = (By.ID, 'item-table')
|
||||
_warning_selector = (By.XPATH, '/html/body/div[1]/div[1]')
|
||||
|
||||
form_items = {
|
||||
'description': (regions.TextBox, (By.ID, 'id_description')),
|
||||
|
||||
'name': (regions.TextBox, (By.ID, 'id_name')),
|
||||
'start_date': (regions.DatePicker, (By.ID, 'id_start_date')),
|
||||
'start_time': (regions.TimePicker, (By.ID, 'id_start_time')),
|
||||
'end_date': (regions.DatePicker, (By.ID, 'id_end_date')),
|
||||
'end_time': (regions.TimePicker, (By.ID, 'id_end_time')),
|
||||
'access_at': (regions.DateTimePicker, (By.ID, 'id_access_at')),
|
||||
'meet_at': (regions.DateTimePicker, (By.ID, 'id_meet_at')),
|
||||
'dry_hire': (regions.CheckBox, (By.ID, 'id_dry_hire')),
|
||||
'status': (regions.SingleSelectPicker, (By.ID, 'id_status')),
|
||||
'collected_by': (regions.TextBox, (By.ID, 'id_collector')),
|
||||
'po': (regions.TextBox, (By.ID, 'id_purchase_order')),
|
||||
|
||||
'notes': (regions.TextBox, (By.ID, 'id_notes'))
|
||||
}
|
||||
|
||||
def select_event_type(self, type_name):
|
||||
self.find_element(By.XPATH, '//button[.="{}"]'.format(type_name)).click()
|
||||
|
||||
def item_row(self, ID):
|
||||
return rigs_regions.ItemRow(self, self.find_element(By.ID, "item-" + ID))
|
||||
|
||||
@property
|
||||
def item_table(self):
|
||||
return self.find_element(*self._event_table_selector)
|
||||
|
||||
@property
|
||||
def warning(self):
|
||||
return self.find_element(*self._warning_selector).text
|
||||
|
||||
@property
|
||||
def is_expanded(self):
|
||||
return self.find_element(*self._submit_locator).is_displayed()
|
||||
|
||||
@property
|
||||
def person_selector(self):
|
||||
return regions.BootstrapSelectElement(self, self.find_element(*self._person_selector_selector))
|
||||
|
||||
@property
|
||||
def venue_selector(self):
|
||||
return regions.BootstrapSelectElement(self, self.find_element(*self._venue_selector_selector))
|
||||
|
||||
@property
|
||||
def mic_selector(self):
|
||||
return regions.BootstrapSelectElement(self, self.find_element(*self._mic_selector_selector))
|
||||
|
||||
def add_person(self):
|
||||
self.find_element(*self._add_person_selector).click()
|
||||
return regions.Modal(self, self.driver.find_element_by_id('modal'))
|
||||
|
||||
def add_event_item(self):
|
||||
self.find_element(*self._add_item_selector).click()
|
||||
element = self.driver.find_element_by_id('itemModal')
|
||||
self.wait.until(EC.visibility_of(element))
|
||||
return rigs_regions.ItemModal(self, element)
|
||||
|
||||
@property
|
||||
def success(self):
|
||||
return '/create' not in self.driver.current_url
|
||||
|
||||
|
||||
class DuplicateEvent(CreateEvent):
|
||||
URL_TEMPLATE = 'event/{event_id}/duplicate'
|
||||
|
||||
@property
|
||||
def success(self):
|
||||
return '/duplicate' not in self.driver.current_url
|
||||
|
||||
|
||||
class EditEvent(CreateEvent):
|
||||
URL_TEMPLATE = 'event/{event_id}/edit'
|
||||
|
||||
@property
|
||||
def success(self):
|
||||
return '/edit' not in self.driver.current_url
|
||||
|
||||
|
||||
class CreateRiskAssessment(FormPage):
|
||||
URL_TEMPLATE = 'event/{event_id}/ra/'
|
||||
|
||||
_submit_locator = (By.XPATH, "//button[@type='submit' and contains(., 'Save')]")
|
||||
_power_mic_selector = (By.CSS_SELECTOR, ".bootstrap-select")
|
||||
|
||||
form_items = {
|
||||
'nonstandard_equipment': (regions.RadioSelect, (By.ID, 'id_nonstandard_equipment')),
|
||||
'nonstandard_use': (regions.RadioSelect, (By.ID, 'id_nonstandard_use')),
|
||||
'contractors': (regions.RadioSelect, (By.ID, 'id_contractors')),
|
||||
'other_companies': (regions.RadioSelect, (By.ID, 'id_other_companies')),
|
||||
'crew_fatigue': (regions.RadioSelect, (By.ID, 'id_crew_fatigue')),
|
||||
'general_notes': (regions.TextBox, (By.ID, 'id_general_notes')),
|
||||
'big_power': (regions.RadioSelect, (By.ID, 'id_big_power')),
|
||||
'outside': (regions.RadioSelect, (By.ID, 'id_outside')),
|
||||
'generators': (regions.RadioSelect, (By.ID, 'id_generators')),
|
||||
'other_companies_power': (regions.RadioSelect, (By.ID, 'id_other_companies_power')),
|
||||
'nonstandard_equipment_power': (regions.RadioSelect, (By.ID, 'id_nonstandard_equipment_power')),
|
||||
'multiple_electrical_environments': (regions.RadioSelect, (By.ID, 'id_multiple_electrical_environments')),
|
||||
'power_notes': (regions.TextBox, (By.ID, 'id_power_notes')),
|
||||
'noise_monitoring': (regions.RadioSelect, (By.ID, 'id_noise_monitoring')),
|
||||
'sound_notes': (regions.TextBox, (By.ID, 'id_sound_notes')),
|
||||
'known_venue': (regions.RadioSelect, (By.ID, 'id_known_venue')),
|
||||
'safe_loading': (regions.RadioSelect, (By.ID, 'id_safe_loading')),
|
||||
'safe_storage': (regions.RadioSelect, (By.ID, 'id_safe_storage')),
|
||||
'area_outside_of_control': (regions.RadioSelect, (By.ID, 'id_area_outside_of_control')),
|
||||
'barrier_required': (regions.RadioSelect, (By.ID, 'id_barrier_required')),
|
||||
'nonstandard_emergency_procedure': (regions.RadioSelect, (By.ID, 'id_nonstandard_emergency_procedure')),
|
||||
'special_structures': (regions.RadioSelect, (By.ID, 'id_special_structures')),
|
||||
'persons_responsible_structures': (regions.TextBox, (By.ID, 'id_persons_responsible_structures')),
|
||||
'suspended_structures': (regions.RadioSelect, (By.ID, 'id_suspended_structures')),
|
||||
'supervisor_consulted': (regions.CheckBox, (By.ID, 'id_supervisor_consulted')),
|
||||
'rigging_plan': (regions.TextBox, (By.ID, 'id_rigging_plan')),
|
||||
}
|
||||
|
||||
@property
|
||||
def power_mic(self):
|
||||
return regions.BootstrapSelectElement(self, self.find_element(*self._power_mic_selector))
|
||||
|
||||
@property
|
||||
def success(self):
|
||||
return '/event/ra' in self.driver.current_url
|
||||
|
||||
|
||||
class EditRiskAssessment(CreateRiskAssessment):
|
||||
URL_TEMPLATE = 'event/ra/{pk}/edit'
|
||||
|
||||
@property
|
||||
def success(self):
|
||||
return '/edit' not in self.driver.current_url
|
||||
|
||||
|
||||
class CreateEventChecklist(FormPage):
|
||||
URL_TEMPLATE = 'event/{event_id}/checklist'
|
||||
|
||||
_submit_locator = (By.XPATH, "//button[@type='submit' and contains(., 'Save')]")
|
||||
_power_mic_selector = (By.XPATH, "//div[select[@id='id_power_mic']]")
|
||||
_add_vehicle_locator = (By.XPATH, "//button[contains(., 'Vehicle')]")
|
||||
_add_crew_locator = (By.XPATH, "//button[contains(., 'Crew')]")
|
||||
_vehicle_row_locator = ('xpath', "//tr[@id[starts-with(., 'vehicle') and not(contains(.,'new'))]]")
|
||||
_crew_row_locator = ('xpath', "//tr[@id[starts-with(., 'crew') and not(contains(.,'new'))]]")
|
||||
|
||||
form_items = {
|
||||
'safe_parking': (regions.CheckBox, (By.ID, 'id_safe_parking')),
|
||||
'safe_packing': (regions.CheckBox, (By.ID, 'id_safe_packing')),
|
||||
'exits': (regions.CheckBox, (By.ID, 'id_exits')),
|
||||
'trip_hazard': (regions.CheckBox, (By.ID, 'id_trip_hazard')),
|
||||
'warning_signs': (regions.CheckBox, (By.ID, 'id_warning_signs')),
|
||||
'ear_plugs': (regions.CheckBox, (By.ID, 'id_ear_plugs')),
|
||||
'hs_location': (regions.TextBox, (By.ID, 'id_hs_location')),
|
||||
'extinguishers_location': (regions.TextBox, (By.ID, 'id_extinguishers_location')),
|
||||
'rcds': (regions.CheckBox, (By.ID, 'id_rcds')),
|
||||
'supply_test': (regions.CheckBox, (By.ID, 'id_supply_test')),
|
||||
'earthing': (regions.CheckBox, (By.ID, 'id_earthing')),
|
||||
'pat': (regions.CheckBox, (By.ID, 'id_pat')),
|
||||
'source_rcd': (regions.CheckBox, (By.ID, 'id_source_rcd')),
|
||||
'labelling': (regions.CheckBox, (By.ID, 'id_labelling')),
|
||||
'fd_voltage_l1': (regions.TextBox, (By.ID, 'id_fd_voltage_l1')),
|
||||
'fd_voltage_l2': (regions.TextBox, (By.ID, 'id_fd_voltage_l2')),
|
||||
'fd_voltage_l3': (regions.TextBox, (By.ID, 'id_fd_voltage_l3')),
|
||||
'fd_phase_rotation': (regions.CheckBox, (By.ID, 'id_fd_phase_rotation')),
|
||||
'fd_earth_fault': (regions.TextBox, (By.ID, 'id_fd_earth_fault')),
|
||||
'fd_pssc': (regions.TextBox, (By.ID, 'id_fd_pssc')),
|
||||
'w1_description': (regions.TextBox, (By.ID, 'id_w1_description')),
|
||||
'w1_polarity': (regions.CheckBox, (By.ID, 'id_w1_polarity')),
|
||||
'w1_voltage': (regions.TextBox, (By.ID, 'id_w1_voltage')),
|
||||
'w1_earth_fault': (regions.TextBox, (By.ID, 'id_w1_earth_fault')),
|
||||
}
|
||||
|
||||
def add_vehicle(self):
|
||||
self.find_element(*self._add_vehicle_locator).click()
|
||||
|
||||
def add_crew(self):
|
||||
self.find_element(*self._add_crew_locator).click()
|
||||
|
||||
@property
|
||||
def power_mic(self):
|
||||
return regions.BootstrapSelectElement(self, self.find_element(*self._power_mic_selector))
|
||||
|
||||
@property
|
||||
def vehicles(self):
|
||||
return [self.VehicleRow(self, el) for el in self.find_elements(*self._vehicle_row_locator)]
|
||||
|
||||
class VehicleRow(Region):
|
||||
_name_locator = ('xpath', ".//input")
|
||||
_select_locator = ('xpath', ".//div[contains(@class,'bootstrap-select')]/..")
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return regions.TextBox(self, self.root.find_element(*self._name_locator))
|
||||
|
||||
@property
|
||||
def vehicle(self):
|
||||
return regions.BootstrapSelectElement(self, self.root.find_element(*self._select_locator))
|
||||
|
||||
@property
|
||||
def crew(self):
|
||||
return [self.CrewRow(self, el) for el in self.find_elements(*self._crew_row_locator)]
|
||||
|
||||
class CrewRow(Region):
|
||||
_select_locator = ('xpath', ".//div[contains(@class,'bootstrap-select')]/..")
|
||||
_start_time_locator = ('xpath', ".//input[@name[starts-with(., 'start') and not(contains(.,'new'))]]")
|
||||
_end_time_locator = ('xpath', ".//input[@name[starts-with(., 'end') and not(contains(.,'new'))]]")
|
||||
_role_locator = ('xpath', ".//input[@name[starts-with(., 'role') and not(contains(.,'new'))]]")
|
||||
|
||||
@property
|
||||
def crewmember(self):
|
||||
return regions.BootstrapSelectElement(self, self.root.find_element(*self._select_locator))
|
||||
|
||||
@property
|
||||
def start_time(self):
|
||||
return regions.DateTimePicker(self, self.root.find_element(*self._start_time_locator))
|
||||
|
||||
@property
|
||||
def end_time(self):
|
||||
return regions.DateTimePicker(self, self.root.find_element(*self._end_time_locator))
|
||||
|
||||
@property
|
||||
def role(self):
|
||||
return regions.TextBox(self, self.root.find_element(*self._role_locator))
|
||||
|
||||
@property
|
||||
def success(self):
|
||||
return '{event_id}' not in self.driver.current_url
|
||||
|
||||
|
||||
class EditEventChecklist(CreateEventChecklist):
|
||||
URL_TEMPLATE = '/event/checklist/{pk}/edit'
|
||||
|
||||
@property
|
||||
def success(self):
|
||||
return 'edit' not in self.driver.current_url
|
||||
|
||||
|
||||
class GenericList(BasePage):
|
||||
_search_selector = (By.CSS_SELECTOR, 'div.input-group:nth-child(2) > input:nth-child(1)')
|
||||
_search_go_selector = (By.ID, 'id_search')
|
||||
_add_item_selector = (By.CSS_SELECTOR, '.btn-success')
|
||||
|
||||
|
||||
class UserPage(BasePage):
|
||||
URL_TEMPLATE = 'user/'
|
||||
|
||||
_api_key_selector = (By.ID, 'api-key')
|
||||
_cal_url_selector = (By.ID, 'cal-url')
|
||||
_generation_button_selector = (By.LINK_TEXT, 'Generate API Key')
|
||||
|
||||
@property
|
||||
def api_key(self):
|
||||
return self.find_element(*self._api_key_selector).text
|
||||
|
||||
@property
|
||||
def cal_url(self):
|
||||
return self.find_element(*self._cal_url_selector).text
|
||||
|
||||
def toggle_filter(self, type_name):
|
||||
self.find_element(By.XPATH, "//input[@value='" + type_name + "']").click()
|
||||
|
||||
def generate_key(self):
|
||||
self.find_element(*self._generation_button_selector).click()
|
||||
|
||||
|
||||
class CalendarPage(BasePage):
|
||||
URL_TEMPLATE = 'rigboard/calendar'
|
||||
|
||||
_go_locator = (By.ID, 'go-to-date-button')
|
||||
|
||||
_today_selector = (By.ID, 'today-button')
|
||||
|
||||
_prev_selector = (By.ID, 'prev-button')
|
||||
_next_selector = (By.ID, 'next-button')
|
||||
|
||||
_month_selector = (By.ID, 'month-button')
|
||||
_week_selector = (By.ID, 'week-button')
|
||||
_day_selector = (By.ID, 'day-button')
|
||||
|
||||
def go(self):
|
||||
return self.find_element(*self._go_locator).click()
|
||||
|
||||
@property
|
||||
def target_date(self):
|
||||
return regions.DatePicker(self, self.find_element(By.ID, 'go-to-date-input'))
|
||||
|
||||
def today(self):
|
||||
return self.find_element(*self._today_selector).click()
|
||||
|
||||
def prev(self):
|
||||
return self.find_element(*self._prev_selector).click()
|
||||
|
||||
def next(self):
|
||||
return self.find_element(*self._next_selector).click()
|
||||
|
||||
def month(self):
|
||||
return self.find_element(*self._month_selector).click()
|
||||
|
||||
def week(self):
|
||||
return self.find_element(*self._week_selector).click()
|
||||
|
||||
def day(self):
|
||||
return self.find_element(*self._day_selector).click()
|
||||
48
RIGS/tests/regions.py
Normal file
48
RIGS/tests/regions.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from pypom import Region
|
||||
from selenium.webdriver.common.by import By
|
||||
|
||||
from PyRIGS.tests.regions import TextBox, Modal
|
||||
|
||||
|
||||
class Header(Region):
|
||||
def find_link(self, link_text):
|
||||
return self.driver.find_element_by_partial_link_text(link_text)
|
||||
|
||||
|
||||
class ItemRow(Region):
|
||||
_name_locator = (By.XPATH, '//span[@class="name"]')
|
||||
_description_locator = (By.XPATH, '//div[@class="item-description"]')
|
||||
_price_locator = (By.XPATH, '//span[@class="cost"]')
|
||||
_quantity_locator = (By.XPATH, '//td[@class="quantity"]')
|
||||
_subtotal_locator = (By.XPATH, '//span[@class="sub-total"]')
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.find_element(*self._name_locator).text
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
return self.find_element(*self._description_locator).text
|
||||
|
||||
@property
|
||||
def price(self):
|
||||
return self.find_element(*self._price_locator).text
|
||||
|
||||
@property
|
||||
def quantity(self):
|
||||
return self.find_element(*self._quantity_locator).text
|
||||
|
||||
@property
|
||||
def subtotal(self):
|
||||
return self.find_element(*self._subtotal_locator).text
|
||||
|
||||
|
||||
class ItemModal(Modal):
|
||||
_header_selector = (By.TAG_NAME, 'h4')
|
||||
|
||||
form_items = {
|
||||
'name': (TextBox, (By.ID, 'item_name')),
|
||||
'description': (TextBox, (By.ID, 'item_description')),
|
||||
'quantity': (TextBox, (By.ID, 'item_quantity')),
|
||||
'price': (TextBox, (By.ID, 'item_cost'))
|
||||
}
|
||||
157
RIGS/tests/test_functional.py
Normal file
157
RIGS/tests/test_functional.py
Normal file
@@ -0,0 +1,157 @@
|
||||
import datetime
|
||||
from datetime import date
|
||||
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
from django.core import mail, signing
|
||||
from django.http import HttpResponseBadRequest
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
import PyRIGS.tests.base
|
||||
from RIGS import models
|
||||
from pytest_django.asserts import assertContains, assertNotContains, assertFormError
|
||||
|
||||
|
||||
def setup_event():
|
||||
venue = models.Venue.objects.create(name='Authorisation Test Venue')
|
||||
client = models.Person.objects.create(name='Authorisation Test Person', email='authorisation@functional.test')
|
||||
organisation = models.Organisation.objects.create(name='Authorisation Test Organisation', union_account=True)
|
||||
return models.Event.objects.create(
|
||||
name='Authorisation Test',
|
||||
start_date=date.today(),
|
||||
venue=venue,
|
||||
person=client,
|
||||
organisation=organisation,
|
||||
)
|
||||
|
||||
|
||||
def setup_mail(event, profile):
|
||||
profile.email = "teccie@nottinghamtec.co.uk"
|
||||
profile.save()
|
||||
auth_data = {
|
||||
'name': 'Test ABC',
|
||||
'po': '1234ABCZXY',
|
||||
'account_code': 'ABC TEST 12345',
|
||||
'uni_id': 1234567890,
|
||||
'tos': True
|
||||
}
|
||||
hmac = signing.dumps({'pk': event.pk, 'email': 'authemail@function.test',
|
||||
'sent_by': profile.pk})
|
||||
url = reverse('event_authorise', kwargs={'pk': event.pk, 'hmac': hmac})
|
||||
return auth_data, hmac, url
|
||||
|
||||
|
||||
def test_create(admin_client):
|
||||
url = reverse('event_create')
|
||||
# end time before start access after start
|
||||
response = admin_client.post(url, {'start_date': datetime.date(2020, 1, 1), 'start_time': datetime.time(10, 00),
|
||||
'end_time': datetime.time(9, 00),
|
||||
'access_at': datetime.datetime(2020, 1, 5, 10)})
|
||||
assertFormError(response, 'form', 'end_time',
|
||||
"Unless you've invented time travel, the event can't finish before it has started.")
|
||||
assertFormError(response, 'form', 'access_at',
|
||||
"Regardless of what some clients might think, access time cannot be after the event has started.")
|
||||
|
||||
|
||||
def test_requires_valid_hmac(client, admin_user):
|
||||
event = setup_event()
|
||||
auth_data, hmac, url = setup_mail(event, admin_user)
|
||||
bad_hmac = hmac[:-1]
|
||||
url = reverse('event_authorise', kwargs={'pk': event.pk, 'hmac': bad_hmac})
|
||||
response = client.get(url)
|
||||
assert isinstance(response, HttpResponseBadRequest)
|
||||
# TODO: Add some form of sensible user facing error
|
||||
# self.assertIn(response.content, "new URL") # check there is some level of sane instruction
|
||||
# response = client.get(url)
|
||||
# assertContains(response, event.organisation.name)
|
||||
|
||||
|
||||
def test_validation(client, admin_user):
|
||||
event = setup_event()
|
||||
auth_data, hmac, url = setup_mail(event, admin_user)
|
||||
response = client.get(url)
|
||||
assertContains(response, "Terms of Hire")
|
||||
assertContains(response, "Account code")
|
||||
assertContains(response, "University ID")
|
||||
|
||||
response = client.post(url)
|
||||
assertContains(response, "This field is required.", 5)
|
||||
|
||||
auth_data['amount'] = event.total + 1
|
||||
|
||||
response = client.post(url, auth_data)
|
||||
assertContains(response, "The amount authorised must equal the total for the event")
|
||||
assertNotContains(response, "This field is required.")
|
||||
|
||||
auth_data['amount'] = event.total
|
||||
response = client.post(url, auth_data)
|
||||
assertContains(response, "Your event has been authorised")
|
||||
|
||||
event.refresh_from_db()
|
||||
assert event.authorised
|
||||
assert str(event.authorisation.email) == "authemail@function.test"
|
||||
|
||||
|
||||
def test_duplicate_warning(client, admin_user):
|
||||
event = setup_event()
|
||||
auth_data, hmac, url = setup_mail(event, admin_user)
|
||||
auth = models.EventAuthorisation.objects.create(event=event, name='Test ABC', email='dupe@functional.test',
|
||||
amount=event.total, sent_by=admin_user)
|
||||
response = client.get(url)
|
||||
assertContains(response, 'This event has already been authorised.')
|
||||
|
||||
auth.amount += 1
|
||||
auth.save()
|
||||
|
||||
response = client.get(url)
|
||||
assertContains(response, 'amount has changed')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_email_sent(admin_client, admin_user, mailoutbox):
|
||||
event = setup_event()
|
||||
auth_data, hmac, url = setup_mail(event, admin_user)
|
||||
|
||||
data = auth_data
|
||||
data['amount'] = event.total
|
||||
response = admin_client.post(url, data)
|
||||
assertContains(response, "Your event has been authorised.")
|
||||
assert len(mailoutbox) == 2
|
||||
assert mailoutbox[0].to == ['authemail@function.test']
|
||||
assert mailoutbox[1].to == [settings.AUTHORISATION_NOTIFICATION_ADDRESS]
|
||||
|
||||
|
||||
def test_email_check(admin_client, admin_user):
|
||||
event = setup_event()
|
||||
url = reverse('event_authorise_request', kwargs={'pk': event.pk})
|
||||
admin_user.email = 'teccie@someotherdomain.com'
|
||||
admin_user.save()
|
||||
|
||||
response = admin_client.post(url)
|
||||
|
||||
assertContains(response, 'must have an @nottinghamtec.co.uk email address')
|
||||
|
||||
|
||||
def test_request_send(admin_client, admin_user):
|
||||
event = setup_event()
|
||||
url = reverse('event_authorise_request', kwargs={'pk': event.pk})
|
||||
admin_user.email = 'teccie@nottinghamtec.co.uk'
|
||||
admin_user.save()
|
||||
response = admin_client.post(url)
|
||||
assertContains(response, 'This field is required.')
|
||||
|
||||
mail.outbox = []
|
||||
|
||||
response = admin_client.post(url, {'email': 'client@functional.test'})
|
||||
assert response.status_code == 302
|
||||
assert len(mail.outbox) == 1
|
||||
email = mail.outbox[0]
|
||||
assert 'client@functional.test' in email.to
|
||||
assert '/event/%d/' % event.pk in email.body
|
||||
|
||||
# Check sent by details are populated
|
||||
event.refresh_from_db()
|
||||
assert event.auth_request_by == admin_user
|
||||
assert event.auth_request_to == 'client@functional.test'
|
||||
assert event.auth_request_at is not None
|
||||
827
RIGS/tests/test_interaction.py
Normal file
827
RIGS/tests/test_interaction.py
Normal file
@@ -0,0 +1,827 @@
|
||||
import datetime
|
||||
from datetime import date, time, timedelta
|
||||
|
||||
from django.test.client import Client
|
||||
from django.utils import timezone
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
from PyRIGS.tests import base
|
||||
from PyRIGS.tests import regions as base_regions
|
||||
from PyRIGS.tests.base import (AutoLoginTest, screenshot_failure_cls)
|
||||
from PyRIGS.tests.pages import animation_is_finished
|
||||
from RIGS import models
|
||||
from RIGS.tests import regions
|
||||
from . import pages
|
||||
import pytest
|
||||
import time as t
|
||||
|
||||
|
||||
pytestmark = pytest.mark.django_db(transaction=True)
|
||||
|
||||
|
||||
@screenshot_failure_cls
|
||||
class BaseRigboardTest(AutoLoginTest):
|
||||
def setUp(self):
|
||||
self.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1')
|
||||
super().setUp()
|
||||
self.client = models.Person.objects.create(name='Rigboard Test Person', email='rigboard@functional.test')
|
||||
self.wait = WebDriverWait(self.driver, 10)
|
||||
|
||||
def select_event_type(self, event_type):
|
||||
self.wait.until(animation_is_finished())
|
||||
self.assertFalse(self.page.is_expanded)
|
||||
self.page.select_event_type(event_type)
|
||||
self.wait.until(animation_is_finished())
|
||||
self.assertTrue(self.page.is_expanded)
|
||||
|
||||
|
||||
@screenshot_failure_cls
|
||||
class TestRigboard(BaseRigboardTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.testEvent = models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL,
|
||||
start_date=date.today() + timedelta(days=6),
|
||||
description="start future no end",
|
||||
purchase_order='TESTPO',
|
||||
person=self.client,
|
||||
auth_request_by=self.profile,
|
||||
auth_request_at=base.create_datetime(2015, 0o6, 0o4, 10, 00),
|
||||
auth_request_to="some@email.address")
|
||||
|
||||
item1 = models.EventItem(
|
||||
event=self.testEvent,
|
||||
name="Test Item 1",
|
||||
cost="10.00",
|
||||
quantity="1",
|
||||
order=1
|
||||
).save()
|
||||
item2 = models.EventItem(
|
||||
event=self.testEvent,
|
||||
name="Test Item 2",
|
||||
description="Foo",
|
||||
cost="9.72",
|
||||
quantity="3",
|
||||
order=2,
|
||||
).save()
|
||||
self.testEvent2 = models.Event.objects.create(name="TE E2", status=models.Event.PROVISIONAL,
|
||||
start_date=date.today() + timedelta(days=8),
|
||||
description="start future no end, later",
|
||||
purchase_order='TESTPO',
|
||||
person=self.client,
|
||||
auth_request_by=self.profile,
|
||||
auth_request_at=base.create_datetime(2015, 0o6, 0o4, 10, 00),
|
||||
auth_request_to="some@email.address")
|
||||
self.page = pages.Rigboard(self.driver, self.live_server_url).open()
|
||||
|
||||
def test_buttons(self):
|
||||
header = regions.Header(self.page, self.driver.find_element(By.CSS_SELECTOR, '.navbar'))
|
||||
# TODO Switch to checking reversed links (difficult because of arguments)
|
||||
header.find_link("Rigboard").click()
|
||||
self.assertEqual(
|
||||
self.live_server_url + '/rigboard/', self.driver.current_url)
|
||||
header.find_link("Archive").click()
|
||||
self.assertEqual(
|
||||
self.live_server_url + '/event/archive/', self.driver.current_url)
|
||||
# TODO - This fails for some reason
|
||||
# header.find_link("New").click()
|
||||
# self.assertEqual(
|
||||
# self.live_server_url + '/event/create/', self.driver.current_url)
|
||||
|
||||
def test_event_order(self):
|
||||
self.assertIn(self.testEvent.start_date.strftime('%a %d/%m/%Y'), self.page.events[0].dates)
|
||||
self.assertIn(self.testEvent2.start_date.strftime('%a %d/%m/%Y'), self.page.events[1].dates)
|
||||
|
||||
def test_add_button(self):
|
||||
self.page.add()
|
||||
self.assertIn('create', self.driver.current_url)
|
||||
# Ideally get a response object to assert 200 on
|
||||
|
||||
|
||||
@screenshot_failure_cls
|
||||
class TestEventCreate(BaseRigboardTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.page = pages.CreateEvent(self.driver, self.live_server_url).open()
|
||||
|
||||
def test_rig_creation(self):
|
||||
self.select_event_type("Rig")
|
||||
|
||||
self.page.person_selector.search(self.client.name)
|
||||
self.page.person_selector.set_option(self.client.name, True)
|
||||
self.page.person_selector.toggle()
|
||||
self.assertFalse(self.page.person_selector.is_open)
|
||||
|
||||
self.page.name = "Test Rig"
|
||||
|
||||
# Both dates, no times, end before start
|
||||
self.page.start_date = datetime.date(2020, 1, 10)
|
||||
self.page.end_date = datetime.date(2020, 1, 1)
|
||||
# Expected to fail
|
||||
self.page.submit()
|
||||
self.assertFalse(self.page.success)
|
||||
self.assertIn("Unless you've invented time travel, the event can't finish before it has started.", self.page.errors["End date"])
|
||||
self.wait.until(animation_is_finished())
|
||||
|
||||
# Fix it
|
||||
self.page.end_date = datetime.date(2020, 1, 11)
|
||||
self.page.access_at = datetime.datetime(2020, 1, 1, 9)
|
||||
self.page.dry_hire = True
|
||||
self.page.status = "Booked"
|
||||
self.page.collected_by = "Fred"
|
||||
self.page.po = "1234"
|
||||
self.page.notes = "A note!"
|
||||
|
||||
self.page.submit()
|
||||
self.assertTrue(self.page.success)
|
||||
|
||||
# TODO
|
||||
def test_modals(self):
|
||||
self.select_event_type("Rig")
|
||||
# Create new person
|
||||
modal = self.page.add_person()
|
||||
# animation_is_finished doesn't work for whatever reason...
|
||||
self.wait.until(EC.visibility_of_element_located((By.ID, 'modal')))
|
||||
self.assertTrue(modal.is_open)
|
||||
self.assertIn("Create Person", modal.header)
|
||||
|
||||
# Fill person form out and submit
|
||||
person_name = "Test Person"
|
||||
modal.name = person_name
|
||||
modal.submit()
|
||||
self.wait.until(EC.invisibility_of_element_located((By.ID, 'modal')))
|
||||
self.assertFalse(modal.is_open)
|
||||
|
||||
# See new person selected
|
||||
self.page.person_selector.toggle()
|
||||
self.assertEqual(self.page.person_selector.options[0].name, person_name)
|
||||
self.page.person_selector.toggle()
|
||||
|
||||
# Change mind and add another
|
||||
self.wait.until(animation_is_finished())
|
||||
modal = self.page.add_person()
|
||||
self.wait.until(EC.visibility_of_element_located((By.ID, 'modal')))
|
||||
self.assertTrue(modal.is_open)
|
||||
self.assertIn("Create Person", modal.header)
|
||||
|
||||
person_name = "Test Person 2"
|
||||
modal.name = person_name
|
||||
modal.submit()
|
||||
self.wait.until(EC.invisibility_of_element_located((By.ID, 'modal')))
|
||||
self.assertFalse(modal.is_open)
|
||||
self.page.person_selector.toggle()
|
||||
self.assertEqual(self.page.person_selector.options[0].name, person_name)
|
||||
self.page.person_selector.toggle()
|
||||
|
||||
# TODO
|
||||
|
||||
def test_event_item_creation(self):
|
||||
self.select_event_type("Rig")
|
||||
|
||||
self.page.name = "Test Event with Items"
|
||||
|
||||
self.page.person_selector.search(self.client.name)
|
||||
self.page.person_selector.set_option(self.client.name, True)
|
||||
# TODO This should not be necessary, normally closes automatically
|
||||
self.page.person_selector.toggle()
|
||||
self.assertFalse(self.page.person_selector.is_open)
|
||||
|
||||
# Note to self, don't set dates before 2014, which is the beginning of VAT as far as the tests are concerned...
|
||||
self.page.start_date = datetime.date(2084, 1, 1)
|
||||
|
||||
modal = self.page.add_event_item()
|
||||
self.wait.until(animation_is_finished())
|
||||
# See modal has opened
|
||||
self.assertTrue(modal.is_open)
|
||||
self.assertIn("New Event", modal.header)
|
||||
|
||||
modal.name = "Test Item 1"
|
||||
modal.description = "This is an item description\nthat for reasons unknown spans two lines"
|
||||
modal.quantity = "2"
|
||||
modal.price = "23.95"
|
||||
modal.submit()
|
||||
self.wait.until(animation_is_finished())
|
||||
|
||||
# Confirm item has been saved to json field
|
||||
objectitems = self.driver.execute_script("return objectitems;")
|
||||
self.assertEqual(1, len(objectitems))
|
||||
testitem = objectitems["-1"]['fields'] # as we are deliberately creating this we know the ID
|
||||
self.assertEqual("Test Item 1", testitem['name'])
|
||||
self.assertEqual("2", testitem['quantity']) # test a couple of "worse case" fields
|
||||
|
||||
total = self.driver.find_element_by_id('total')
|
||||
ActionChains(self.driver).move_to_element(total).perform()
|
||||
|
||||
# See new item appear in table
|
||||
row = self.page.item_row("-1") # ID number is known, see above
|
||||
self.assertIn("Test Item 1", row.name)
|
||||
self.assertIn("This is an item description",
|
||||
row.description)
|
||||
self.assertEqual('23.95', row.price)
|
||||
self.assertEqual("2", row.quantity)
|
||||
self.assertEqual('47.90', row.subtotal)
|
||||
|
||||
# Check totals TODO convert to page properties
|
||||
self.assertEqual("47.90", self.driver.find_element_by_id('sumtotal').text)
|
||||
self.assertIn("(TBC)", self.driver.find_element_by_id('vat-rate').text)
|
||||
self.assertEqual("9.58", self.driver.find_element_by_id('vat').text)
|
||||
self.assertEqual("57.48", total.text)
|
||||
|
||||
self.page.submit()
|
||||
|
||||
# TODO Testing of internal rig (approval system interface)
|
||||
|
||||
def test_non_rig_creation(self):
|
||||
self.select_event_type("Non-Rig")
|
||||
|
||||
self.assertFalse(self.page.person_selector.is_open)
|
||||
|
||||
rig_name = "Test Non-Rig"
|
||||
self.page.name = rig_name
|
||||
|
||||
# Double-check we don't lose data when swapping
|
||||
self.page.select_event_type("Rig")
|
||||
self.wait.until(animation_is_finished())
|
||||
self.assertEqual(self.page.name, rig_name)
|
||||
self.wait.until(animation_is_finished())
|
||||
self.page.select_event_type("Non-Rig")
|
||||
|
||||
self.page.start_date = datetime.date(2020, 1, 1)
|
||||
self.page.status = "Confirmed"
|
||||
|
||||
self.page.submit()
|
||||
self.assertTrue(self.page.success)
|
||||
|
||||
|
||||
@screenshot_failure_cls
|
||||
class TestEventDuplicate(BaseRigboardTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.testEvent = models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL,
|
||||
start_date=date.today() + timedelta(days=6),
|
||||
description="start future no end",
|
||||
purchase_order='TESTPO',
|
||||
person=self.client,
|
||||
auth_request_by=self.profile,
|
||||
auth_request_at=base.create_datetime(2015, 0o6, 0o4, 10, 00),
|
||||
auth_request_to="some@email.address")
|
||||
|
||||
item1 = models.EventItem(
|
||||
event=self.testEvent,
|
||||
name="Test Item 1",
|
||||
cost="10.00",
|
||||
quantity="1",
|
||||
order=1
|
||||
).save()
|
||||
item2 = models.EventItem(
|
||||
event=self.testEvent,
|
||||
name="Test Item 2",
|
||||
description="Foo",
|
||||
cost="9.72",
|
||||
quantity="3",
|
||||
order=2,
|
||||
).save()
|
||||
self.page = pages.DuplicateEvent(self.driver, self.live_server_url, event_id=self.testEvent.pk).open()
|
||||
|
||||
def test_rig_duplicate(self):
|
||||
table = self.page.item_table
|
||||
self.assertIn("Test Item 1", table.text)
|
||||
self.assertIn("Test Item 2", table.text)
|
||||
|
||||
# Check the info message is visible
|
||||
self.assertIn("Event data duplicated but not yet saved", self.page.warning)
|
||||
|
||||
modal = self.page.add_event_item()
|
||||
self.wait.until(animation_is_finished())
|
||||
# See modal has opened
|
||||
self.assertTrue(modal.is_open)
|
||||
self.assertIn(self.testEvent.name, modal.header)
|
||||
|
||||
modal.name = "Test Item 3"
|
||||
modal.description = "This is an item description\nthat for reasons unknown spans two lines"
|
||||
modal.quantity = "2"
|
||||
modal.price = "23.95"
|
||||
modal.submit()
|
||||
self.wait.until(animation_is_finished())
|
||||
|
||||
# Attempt to save
|
||||
self.page.submit()
|
||||
|
||||
# TODO Rewrite when EventDetail page is implemented
|
||||
newEvent = models.Event.objects.latest('pk')
|
||||
|
||||
assert newEvent.auth_request_to == ''
|
||||
self.assertEqual(newEvent.auth_request_by, None)
|
||||
self.assertEqual(newEvent.auth_request_at, None)
|
||||
|
||||
self.assertFalse(newEvent.authorised)
|
||||
|
||||
self.assertNotIn("N%05d" % self.testEvent.pk, self.driver.find_element_by_xpath('//h2').text)
|
||||
self.assertNotIn("Event data duplicated but not yet saved", self.page.warning) # Check info message not visible
|
||||
|
||||
# Check the new items are visible
|
||||
table = self.page.item_table
|
||||
self.assertIn("Test Item 1", table.text)
|
||||
self.assertIn("Test Item 2", table.text)
|
||||
self.assertIn("Test Item 3", table.text)
|
||||
|
||||
infoPanel = self.driver.find_element_by_xpath('//div[contains(text(), "Event Info")]/..')
|
||||
self.assertIn("N%05d" % self.testEvent.pk,
|
||||
infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text)
|
||||
# Check the PO hasn't carried through
|
||||
self.assertNotIn("TESTPO", infoPanel.find_element_by_xpath('//dt[text()="PO"]/following-sibling::dd[1]').text)
|
||||
|
||||
self.assertIn("N%05d" % self.testEvent.pk,
|
||||
infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text)
|
||||
|
||||
self.driver.get(self.live_server_url + '/event/' + str(self.testEvent.pk)) # Go back to the old event
|
||||
|
||||
# Check that based-on hasn't crept into the old event
|
||||
infoPanel = self.driver.find_element_by_xpath('//div[contains(text(), "Event Info")]/..')
|
||||
self.assertNotIn("N%05d" % self.testEvent.pk,
|
||||
infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text)
|
||||
# Check the PO remains on the old event
|
||||
self.assertIn("TESTPO", infoPanel.find_element_by_xpath('//dt[text()="PO"]/following-sibling::dd[1]').text)
|
||||
|
||||
self.assertNotIn("N%05d" % self.testEvent.pk,
|
||||
infoPanel.find_element_by_xpath('//dt[text()="Based On"]/following-sibling::dd[1]').text)
|
||||
|
||||
# Check the items are as they were
|
||||
table = self.page.item_table # ID number is known, see above
|
||||
self.assertIn("Test Item 1", table.text)
|
||||
self.assertIn("Test Item 2", table.text)
|
||||
self.assertNotIn("Test Item 3", table.text)
|
||||
|
||||
|
||||
@screenshot_failure_cls
|
||||
class TestEventEdit(BaseRigboardTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.testEvent = models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL,
|
||||
start_date=date.today() + timedelta(days=6),
|
||||
description="start future no end",
|
||||
purchase_order='TESTPO',
|
||||
person=self.client,
|
||||
auth_request_by=self.profile,
|
||||
auth_request_at=base.create_datetime(2015, 0o6, 0o4, 10, 00),
|
||||
auth_request_to="some@email.address")
|
||||
|
||||
item1 = models.EventItem(
|
||||
event=self.testEvent,
|
||||
name="Test Item 1",
|
||||
cost="10.00",
|
||||
quantity="1",
|
||||
order=1
|
||||
).save()
|
||||
item2 = models.EventItem(
|
||||
event=self.testEvent,
|
||||
name="Test Item 2",
|
||||
description="Foo",
|
||||
cost="9.72",
|
||||
quantity="3",
|
||||
order=2,
|
||||
).save()
|
||||
self.page = pages.EditEvent(self.driver, self.live_server_url, event_id=self.testEvent.pk).open()
|
||||
|
||||
def test_rig_edit(self):
|
||||
self.page.name = "Edited Event"
|
||||
|
||||
modal = self.page.add_event_item()
|
||||
self.wait.until(animation_is_finished())
|
||||
# See modal has opened
|
||||
self.assertTrue(modal.is_open)
|
||||
self.assertIn(self.testEvent.name, modal.header)
|
||||
|
||||
modal.name = "Test Item 3"
|
||||
modal.description = "This is an item description\nthat for reasons unknown spans two lines"
|
||||
modal.quantity = "2"
|
||||
modal.price = "23.95"
|
||||
modal.submit()
|
||||
self.wait.until(animation_is_finished())
|
||||
|
||||
# Attempt to save
|
||||
ActionChains(self.driver).move_to_element(self.page.item_table).perform()
|
||||
self.page.submit()
|
||||
|
||||
self.assertTrue(self.page.success)
|
||||
|
||||
self.page = pages.EventDetail(self.driver, self.live_server_url, event_id=self.testEvent.pk).open()
|
||||
self.testEvent.refresh_from_db()
|
||||
self.assertIn(self.testEvent.name, self.page.event_name)
|
||||
self.assertEqual(self.page.name, self.testEvent.person.name)
|
||||
# Check the new items are visible
|
||||
table = self.page.item_table
|
||||
self.assertIn("Test Item 3", table.text)
|
||||
|
||||
|
||||
@screenshot_failure_cls
|
||||
class TestEventDetail(BaseRigboardTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.testEvent = models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL,
|
||||
start_date=date.today() + timedelta(days=6),
|
||||
description="start future no end",
|
||||
purchase_order='TESTPO',
|
||||
person=self.client,
|
||||
auth_request_by=self.profile,
|
||||
auth_request_at=base.create_datetime(2015, 0o6, 0o4, 10, 00),
|
||||
auth_request_to="some@email.address")
|
||||
|
||||
item1 = models.EventItem(
|
||||
event=self.testEvent,
|
||||
name="Test Item 1",
|
||||
cost="10.00",
|
||||
quantity="1",
|
||||
order=1
|
||||
).save()
|
||||
item2 = models.EventItem(
|
||||
event=self.testEvent,
|
||||
name="Test Item 2",
|
||||
description="Foo",
|
||||
cost="9.72",
|
||||
quantity="3",
|
||||
order=2,
|
||||
).save()
|
||||
self.page = pages.EventDetail(self.driver, self.live_server_url, event_id=self.testEvent.pk).open()
|
||||
|
||||
def test_rig_detail(self):
|
||||
self.assertIn("N%05d | %s" % (self.testEvent.pk, self.testEvent.name), self.page.event_name)
|
||||
self.assertEqual(self.client.name, self.page.name)
|
||||
self.assertEqual(self.client.email, self.page.email)
|
||||
assert self.client.phone == ''
|
||||
|
||||
|
||||
@screenshot_failure_cls
|
||||
class TestCalendar(BaseRigboardTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.all_events = set(range(1, 18))
|
||||
self.current_events = (1, 2, 3, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18)
|
||||
self.not_current_events = set(self.all_events) - set(self.current_events)
|
||||
|
||||
# produce 7 normal events - 5 current - 1 last week - 1 two years ago - 2 provisional - 2 confirmed - 3 booked
|
||||
models.Event.objects.create(name="TE E1", status=models.Event.PROVISIONAL,
|
||||
start_date=date.today() + timedelta(days=6), description="start future no end")
|
||||
models.Event.objects.create(name="TE E2", status=models.Event.PROVISIONAL, start_date=date.today(),
|
||||
description="start today no end")
|
||||
models.Event.objects.create(name="TE E3", status=models.Event.CONFIRMED, start_date=date.today(),
|
||||
end_date=date.today(), description="start today with end today")
|
||||
models.Event.objects.create(name="TE E4", status=models.Event.CONFIRMED,
|
||||
start_date=date.today() - timedelta(weeks=104),
|
||||
description="start past 2 years no end")
|
||||
models.Event.objects.create(name="TE E5", status=models.Event.BOOKED,
|
||||
start_date=date.today() - timedelta(days=7),
|
||||
end_date=date.today() - timedelta(days=1),
|
||||
description="start past 1 week with end past")
|
||||
models.Event.objects.create(name="TE E6", status=models.Event.BOOKED,
|
||||
start_date=date.today() - timedelta(days=2),
|
||||
start_time=time(8, 00),
|
||||
end_date=date.today() + timedelta(days=2),
|
||||
end_time=time(23, 00), description="start past, end future")
|
||||
models.Event.objects.create(name="TE E7", status=models.Event.BOOKED,
|
||||
start_date=date.today() + timedelta(days=2),
|
||||
end_date=date.today() + timedelta(days=2), description="start + end in future")
|
||||
|
||||
# 2 cancelled - 1 current
|
||||
models.Event.objects.create(name="TE E8", start_date=date.today() + timedelta(days=2),
|
||||
end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED,
|
||||
description="cancelled in future")
|
||||
models.Event.objects.create(name="TE E9", start_date=date.today() - timedelta(days=1),
|
||||
end_date=date.today() + timedelta(days=2), status=models.Event.CANCELLED,
|
||||
description="cancelled and started")
|
||||
|
||||
# 5 dry hire - 3 current - 1 cancelled
|
||||
models.Event.objects.create(name="TE E10", start_date=date.today(), dry_hire=True, description="dryhire today")
|
||||
models.Event.objects.create(name="TE E11", start_date=date.today(), dry_hire=True, checked_in_by=self.profile,
|
||||
description="dryhire today, checked in")
|
||||
models.Event.objects.create(name="TE E12", start_date=date.today() - timedelta(days=1), dry_hire=True,
|
||||
status=models.Event.BOOKED, description="dryhire past")
|
||||
models.Event.objects.create(name="TE E13", start_date=date.today() - timedelta(days=2), dry_hire=True,
|
||||
checked_in_by=self.profile, description="dryhire past checked in")
|
||||
models.Event.objects.create(name="TE E14", start_date=date.today(), dry_hire=True,
|
||||
status=models.Event.CANCELLED, description="dryhire today cancelled")
|
||||
|
||||
# 4 non rig - 3 current
|
||||
models.Event.objects.create(name="TE E15", start_date=date.today(), is_rig=False, description="non rig today")
|
||||
models.Event.objects.create(name="TE E16", start_date=date.today() + timedelta(days=1), is_rig=False,
|
||||
description="non rig tomorrow")
|
||||
models.Event.objects.create(name="TE E17", start_date=date.today() - timedelta(days=1), is_rig=False,
|
||||
description="non rig yesterday")
|
||||
models.Event.objects.create(name="TE E18", start_date=date.today(), is_rig=False, status=models.Event.CANCELLED,
|
||||
description="non rig today cancelled")
|
||||
|
||||
self.page = pages.UserPage(self.driver, self.live_server_url).open()
|
||||
|
||||
def test_api_key_generation(self):
|
||||
# Completes and comes back to /user/
|
||||
# Checks that no api key is displayed
|
||||
self.assertEqual("No API Key Generated", self.page.api_key)
|
||||
|
||||
# Now creates an API key, and check a URL is displayed one
|
||||
self.page.generate_key()
|
||||
self.assertIn("rigs.ics", self.page.cal_url)
|
||||
self.assertNotIn("?", self.page.cal_url)
|
||||
|
||||
# Lets change everything so it's not the default value
|
||||
self.page.toggle_filter('rig')
|
||||
self.page.toggle_filter('non-rig')
|
||||
self.page.toggle_filter('dry-hire')
|
||||
self.page.toggle_filter('cancelled')
|
||||
self.page.toggle_filter('provisional')
|
||||
self.page.toggle_filter('confirmed')
|
||||
|
||||
# and then check the url is correct
|
||||
self.assertIn(
|
||||
"rigs.ics?rig=false&non-rig=false&dry-hire=false&cancelled=true&provisional=false&confirmed=false",
|
||||
self.page.cal_url)
|
||||
|
||||
# Awesome - all seems to work
|
||||
|
||||
def test_ics_files(self):
|
||||
specialEvent = models.Event.objects.get(name="TE E6")
|
||||
|
||||
# Now creates an API key, and check a URL is displayed one
|
||||
self.page.generate_key()
|
||||
|
||||
c = Client()
|
||||
|
||||
# Default settings - should have all non-cancelled events
|
||||
# Get the ical file (can't do this in selanium because reasons)
|
||||
icalUrl = self.page.cal_url
|
||||
response = c.get(self.page.cal_url)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
# content = response.content.decode('utf-8')
|
||||
|
||||
# Check has entire file
|
||||
self.assertContains(response, "BEGIN:VCALENDAR")
|
||||
self.assertContains(response, "END:VCALENDAR")
|
||||
|
||||
expectedIn = [1, 2, 3, 5, 6, 7, 10, 11, 12, 13, 15, 16, 17]
|
||||
for test in range(1, 18):
|
||||
if test in expectedIn:
|
||||
self.assertContains(response, "TE E" + str(test) + " ")
|
||||
else:
|
||||
self.assertNotContains(response, "TE E" + str(test) + " ")
|
||||
|
||||
# Check that times have been included correctly
|
||||
self.assertContains(response,
|
||||
specialEvent.start_date.strftime('%Y%m%d') + 'T' + specialEvent.start_time.strftime(
|
||||
'%H%M%S'))
|
||||
self.assertContains(response,
|
||||
specialEvent.end_date.strftime('%Y%m%d'))
|
||||
self.assertContains(response,
|
||||
specialEvent.end_time.strftime('%H%M%S'))
|
||||
|
||||
# Only non rigs
|
||||
self.page.toggle_filter('rig')
|
||||
self.page.toggle_filter('non-rig')
|
||||
|
||||
icalUrl = self.page.cal_url
|
||||
response = c.get(icalUrl)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
expectedIn = [10, 11, 12, 13]
|
||||
for test in range(1, 18):
|
||||
if test in expectedIn:
|
||||
self.assertContains(response, "TE E" + str(test) + " ")
|
||||
else:
|
||||
self.assertNotContains(response, "TE E" + str(test) + " ")
|
||||
|
||||
# Only provisional rigs
|
||||
self.page.toggle_filter('rig')
|
||||
self.page.toggle_filter('dry-hire')
|
||||
self.page.toggle_filter('confirmed')
|
||||
|
||||
icalUrl = self.page.cal_url
|
||||
response = c.get(icalUrl)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
expectedIn = [1, 2]
|
||||
for test in range(1, 18):
|
||||
if test in expectedIn:
|
||||
self.assertContains(response, "TE E" + str(test) + " ")
|
||||
else:
|
||||
self.assertNotContains(response, "TE E" + str(test) + " ")
|
||||
|
||||
# Only cancelled non-rigs
|
||||
self.page.toggle_filter('rig')
|
||||
self.page.toggle_filter('non-rig')
|
||||
self.page.toggle_filter('provisional')
|
||||
self.page.toggle_filter('cancelled')
|
||||
|
||||
icalUrl = self.page.cal_url
|
||||
response = c.get(icalUrl)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
expectedIn = [18]
|
||||
for test in range(1, 18):
|
||||
if test in expectedIn:
|
||||
self.assertContains(response, "TE E" + str(test) + " ")
|
||||
else:
|
||||
self.assertNotContains(response, "TE E" + str(test) + " ")
|
||||
|
||||
# Nothing selected
|
||||
self.page.toggle_filter('non-rig')
|
||||
self.page.toggle_filter('cancelled')
|
||||
|
||||
icalUrl = self.page.cal_url
|
||||
response = c.get(icalUrl)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
expectedIn = []
|
||||
for test in range(1, 18):
|
||||
if test in expectedIn:
|
||||
self.assertContains(response, "TE E" + str(test) + " ")
|
||||
else:
|
||||
self.assertNotContains(response, "TE E" + str(test) + " ")
|
||||
|
||||
|
||||
def test_calendar_buttons(logged_in_browser, live_server): # If FullCalendar fails to load for whatever reason, the buttons don't work
|
||||
page = pages.CalendarPage(logged_in_browser.driver, live_server.url).open()
|
||||
assert timezone.now().strftime("%Y-%m") in logged_in_browser.url
|
||||
|
||||
target_date = datetime.date(2020, 1, 1)
|
||||
page.target_date.set_value(target_date)
|
||||
page.go()
|
||||
assert page.target_date.value.strftime("%Y-%m") in logged_in_browser.url
|
||||
|
||||
page.next()
|
||||
target_date += datetime.timedelta(days=32)
|
||||
assert target_date.strftime("%m") in logged_in_browser.url
|
||||
|
||||
|
||||
def test_ra_edit(logged_in_browser, live_server, ra):
|
||||
page = pages.EditRiskAssessment(logged_in_browser.driver, live_server.url, pk=ra.pk).open()
|
||||
page.nonstandard_equipment = nse = True
|
||||
page.general_notes = gn = "There are some notes, but I've not written them here as that would be helpful"
|
||||
page.submit()
|
||||
assert not page.success
|
||||
page.supervisor_consulted = True
|
||||
page.submit()
|
||||
assert page.success
|
||||
# Check that data is right
|
||||
ra = models.RiskAssessment.objects.get(pk=ra.pk)
|
||||
assert ra.general_notes == gn
|
||||
assert ra.nonstandard_equipment == nse
|
||||
|
||||
|
||||
def small_ec(page, admin_user):
|
||||
page.safe_parking = True
|
||||
page.safe_packing = True
|
||||
page.exits = True
|
||||
page.trip_hazard = True
|
||||
page.warning_signs = True
|
||||
page.ear_plugs = True
|
||||
page.hs_location = "The Moon"
|
||||
page.extinguishers_location = "With the rest of the fire"
|
||||
# If we do this first the search fails, for ... reasons
|
||||
page.power_mic.search(admin_user.name)
|
||||
page.power_mic.toggle()
|
||||
assert not page.power_mic.is_open
|
||||
page.earthing = True
|
||||
page.rcds = True
|
||||
page.supply_test = True
|
||||
page.pat = True
|
||||
|
||||
|
||||
def test_ec_create_small(logged_in_browser, live_server, admin_user, ra):
|
||||
page = pages.CreateEventChecklist(logged_in_browser.driver, live_server.url, event_id=ra.event.pk).open()
|
||||
small_ec(page, admin_user)
|
||||
page.submit()
|
||||
assert page.success
|
||||
|
||||
|
||||
def test_ec_create_medium(logged_in_browser, live_server, admin_user, medium_ra):
|
||||
page = pages.CreateEventChecklist(logged_in_browser.driver, live_server.url, event_id=medium_ra.event.pk).open()
|
||||
|
||||
page.safe_parking = True
|
||||
page.safe_packing = True
|
||||
page.exits = True
|
||||
page.trip_hazard = True
|
||||
page.warning_signs = True
|
||||
page.ear_plugs = True
|
||||
page.hs_location = "Death Valley"
|
||||
page.extinguishers_location = "With the rest of the fire"
|
||||
# If we do this first the search fails, for ... reasons
|
||||
page.power_mic.search(admin_user.name)
|
||||
page.power_mic.toggle()
|
||||
assert not page.power_mic.is_open
|
||||
|
||||
# Gotta scroll to make the button clickable
|
||||
logged_in_browser.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
|
||||
|
||||
page.earthing = True
|
||||
page.pat = True
|
||||
page.source_rcd = True
|
||||
page.labelling = True
|
||||
page.fd_voltage_l1 = 240
|
||||
page.fd_voltage_l2 = 235
|
||||
page.fd_voltage_l3 = 0
|
||||
page.fd_phase_rotation = True
|
||||
page.fd_earth_fault = "1.21"
|
||||
page.fd_pssc = 1984
|
||||
page.w1_description = "In the carpark, by the bins"
|
||||
page.w1_polarity = True
|
||||
page.w1_voltage = 240
|
||||
page.w1_earth_fault = "0.42"
|
||||
|
||||
page.submit()
|
||||
assert page.success
|
||||
|
||||
|
||||
def test_ec_create_vehicle(logged_in_browser, live_server, admin_user, checklist):
|
||||
page = pages.EditEventChecklist(logged_in_browser.driver, live_server.url, pk=checklist.pk).open()
|
||||
small_ec(page, admin_user)
|
||||
page.add_vehicle()
|
||||
assert len(page.vehicles) == 1
|
||||
vehicle_name = 'Brian'
|
||||
page.vehicles[0].name.set_value(vehicle_name)
|
||||
# Appears we're moving too fast for javascript...
|
||||
t.sleep(1)
|
||||
page.vehicles[0].vehicle.search(admin_user.first_name)
|
||||
t.sleep(1)
|
||||
page.submit()
|
||||
assert page.success
|
||||
# Check data is correct
|
||||
checklist.refresh_from_db()
|
||||
vehicle = models.EventChecklistVehicle.objects.get(checklist=checklist.pk)
|
||||
assert vehicle_name == vehicle.vehicle
|
||||
|
||||
|
||||
# TODO Test validation of end before start
|
||||
def test_ec_create_crew(logged_in_browser, live_server, admin_user, checklist):
|
||||
page = pages.EditEventChecklist(logged_in_browser.driver, live_server.url, pk=checklist.pk).open()
|
||||
small_ec(page, admin_user)
|
||||
page.add_crew()
|
||||
assert len(page.crew) == 1
|
||||
role = "MIC"
|
||||
start_time = timezone.make_aware(datetime.datetime(2015, 1, 1, 9, 0))
|
||||
end_time = timezone.make_aware(datetime.datetime(2015, 1, 1, 10, 30))
|
||||
crew = page.crew[0]
|
||||
t.sleep(2)
|
||||
crew.crewmember.search(admin_user.first_name)
|
||||
t.sleep(2)
|
||||
crew.role.set_value(role)
|
||||
crew.start_time.set_value(start_time)
|
||||
crew.end_time.set_value(end_time)
|
||||
page.submit()
|
||||
assert page.success
|
||||
# Check data is correct
|
||||
crew_obj = models.EventChecklistCrew.objects.get(checklist=checklist.pk)
|
||||
assert admin_user.pk == crew_obj.crewmember.pk
|
||||
assert role == crew_obj.role
|
||||
assert start_time == crew_obj.start
|
||||
assert end_time == crew_obj.end
|
||||
|
||||
|
||||
# TODO Can I loop through all the boolean fields and test them at once?
|
||||
def test_ra_creation(logged_in_browser, live_server, admin_user, basic_event):
|
||||
page = pages.CreateRiskAssessment(logged_in_browser.driver, live_server.url, event_id=basic_event.pk).open()
|
||||
|
||||
# Check there are no defaults
|
||||
assert page.nonstandard_equipment is None
|
||||
|
||||
# No database side validation, only HTML5.
|
||||
page.nonstandard_equipment = False
|
||||
page.nonstandard_use = False
|
||||
page.contractors = False
|
||||
page.other_companies = False
|
||||
page.crew_fatigue = False
|
||||
page.general_notes = "There are no notes."
|
||||
page.big_power = False
|
||||
page.outside = False
|
||||
page.power_mic.search(admin_user.first_name)
|
||||
page.generators = False
|
||||
page.other_companies_power = False
|
||||
page.nonstandard_equipment_power = False
|
||||
page.multiple_electrical_environments = False
|
||||
page.power_notes = "Remember to bring some power"
|
||||
page.noise_monitoring = False
|
||||
page.sound_notes = "Loud, but not too loud"
|
||||
page.known_venue = False
|
||||
page.safe_loading = False
|
||||
page.safe_storage = False
|
||||
page.area_outside_of_control = False
|
||||
page.barrier_required = False
|
||||
page.nonstandard_emergency_procedure = False
|
||||
page.special_structures = False
|
||||
# self.page.persons_responsible_structures = "Nobody and her cat, She"
|
||||
|
||||
page.suspended_structures = True
|
||||
# TODO Test for this proper
|
||||
page.rigging_plan = "https://nottinghamtec.sharepoint.com/test/"
|
||||
page.submit()
|
||||
assert not page.success
|
||||
|
||||
page.suspended_structures = False
|
||||
page.submit()
|
||||
assert page.success
|
||||
|
||||
|
||||
def test_ra_no_duplicates(logged_in_browser, live_server, ra):
|
||||
# Test that we can't make another one
|
||||
page = pages.CreateRiskAssessment(logged_in_browser.driver, live_server.url, event_id=ra.event.pk).open()
|
||||
assert 'edit' in logged_in_browser.url
|
||||
343
RIGS/tests/test_models.py
Normal file
343
RIGS/tests/test_models.py
Normal file
@@ -0,0 +1,343 @@
|
||||
from datetime import date, timedelta, datetime, time
|
||||
from decimal import *
|
||||
|
||||
import pytz
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from reversion import revisions as reversion
|
||||
|
||||
from RIGS import models
|
||||
|
||||
|
||||
def assert_decimal_equality(d1, d2):
|
||||
assert float(d1) == pytest.approx(float(d2))
|
||||
|
||||
|
||||
def test_str():
|
||||
profile = models.Profile(first_name='Test', last_name='Case')
|
||||
assert str(profile) == 'Test Case'
|
||||
profile.initials = 'TC'
|
||||
assert str(profile) == 'Test Case "TC"'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_find_correct(vat_rate):
|
||||
new_rate = models.VatRate.objects.create(start_at='2016-03-01', rate=0.15, comment='test2')
|
||||
r = models.VatRate.objects.find_rate('2015-03-01')
|
||||
assert_decimal_equality(r.rate, vat_rate.rate)
|
||||
r = models.VatRate.objects.find_rate('2016-03-01')
|
||||
assert_decimal_equality(r.rate, new_rate.rate)
|
||||
|
||||
|
||||
def test_percent_correct(vat_rate):
|
||||
assert vat_rate.as_percent == 20
|
||||
|
||||
|
||||
def test_related_vatrate(basic_event, vat_rate):
|
||||
assert_decimal_equality(vat_rate.rate, basic_event.vat_rate.rate)
|
||||
|
||||
|
||||
class EventTest():
|
||||
def test_count(many_events):
|
||||
# Sanity check we have the expected events created
|
||||
assert models.Event.objects.count() == 18
|
||||
|
||||
def test_rig_count(many_events):
|
||||
# Changed to not include unreturned dry hires in rig count
|
||||
assert models.Event.objects.rig_count() == 7
|
||||
|
||||
def test_current_events(many_events):
|
||||
all_events = set(range(1, 18))
|
||||
current_events = (1, 2, 3, 6, 7, 8, 10, 11, 12, 14, 15, 16, 18)
|
||||
not_current_events = set(cls.all_events) - set(cls.current_events)
|
||||
current_events = models.Event.objects.current_events()
|
||||
assert len(current_events) == len(self.current_events)
|
||||
for eid in current_events:
|
||||
assert models.Event.objects.get(name="TE E%d" % eid) in current_events
|
||||
|
||||
for eid in not_current_events:
|
||||
assert models.Event.objects.get(name="TE E%d" % eid) not in current_events
|
||||
|
||||
def test_related(many_events):
|
||||
v1 = models.Venue.objects.create(name="TE V1")
|
||||
v2 = models.Venue.objects.create(name="TE V2")
|
||||
|
||||
e1 = []
|
||||
e2 = []
|
||||
for (key, event) in self.events.items():
|
||||
if event.pk % 2:
|
||||
event.venue = v1
|
||||
e1.append(event)
|
||||
else:
|
||||
event.venue = v2
|
||||
e2.append(event)
|
||||
event.save()
|
||||
|
||||
assert set(e1) == set(v1.latest_events)
|
||||
assert set(e2) == set(v2.latest_events)
|
||||
# Cleanup
|
||||
v1.delete()
|
||||
v2.delete()
|
||||
|
||||
def test_related_person(many_events):
|
||||
p1 = models.Person.objects.create(name="TE P1")
|
||||
p2 = models.Person.objects.create(name="TE P2")
|
||||
|
||||
e1 = []
|
||||
e2 = []
|
||||
for (key, event) in self.events.items():
|
||||
if event.pk % 2:
|
||||
event.person = p1
|
||||
e1.append(event)
|
||||
else:
|
||||
event.person = p2
|
||||
e2.append(event)
|
||||
event.save()
|
||||
|
||||
assert set(e1) == set(p1.latest_events)
|
||||
assert set(e2) == set(p2.latest_events)
|
||||
|
||||
p1.delete()
|
||||
p2.delete()
|
||||
|
||||
def test_related_organisation(many_events):
|
||||
o1 = models.Organisation.objects.create(name="TE O1")
|
||||
o2 = models.Organisation.objects.create(name="TE O2")
|
||||
|
||||
e1 = []
|
||||
e2 = []
|
||||
for (key, event) in self.events.items():
|
||||
if event.pk % 2:
|
||||
event.organisation = o1
|
||||
e1.append(event)
|
||||
else:
|
||||
event.organisation = o2
|
||||
e2.append(event)
|
||||
event.save()
|
||||
|
||||
assert set(e1) == set(o1.latest_events)
|
||||
assert set(e1) == set(o2.latest_events)
|
||||
|
||||
for (key, event) in self.events.items():
|
||||
event.organisation = None
|
||||
|
||||
def test_organisation_person_join(many_events):
|
||||
p1 = models.Person.objects.create(name="TE P1")
|
||||
p2 = models.Person.objects.create(name="TE P2")
|
||||
o1 = models.Organisation.objects.create(name="TE O1")
|
||||
o2 = models.Organisation.objects.create(name="TE O2")
|
||||
|
||||
events = models.Event.objects.all()
|
||||
# p1 in o1 + o2, p2 in o1
|
||||
for event in events[:2]:
|
||||
event.person = p1
|
||||
event.organisation = o1
|
||||
event.save()
|
||||
for event in events[3:4]:
|
||||
event.person = p1
|
||||
event.organisation = o2
|
||||
event.save()
|
||||
for event in events[5:7]:
|
||||
event.person = p2
|
||||
event.organisation = o1
|
||||
event.save()
|
||||
|
||||
events = models.Event.objects.all()
|
||||
|
||||
# Check person's organisations
|
||||
assert (o1, 2) in p1.organisations
|
||||
assert (o2, 1) in p1.organisations
|
||||
assert (o1, 2) in p2.organisations
|
||||
assert len(p2.organisations) == 1
|
||||
|
||||
# Check organisation's persons
|
||||
assert (p1, 2) in o1.persons
|
||||
assert (p2, 2) in o1.persons
|
||||
assert (p1, 1) in o2.persons
|
||||
assert len(o2.persons) == 1
|
||||
|
||||
def test_cancelled_property(many_events):
|
||||
edit = many_events[1]
|
||||
edit.status = models.Event.CANCELLED
|
||||
edit.save()
|
||||
event = models.Event.objects.get(pk=edit.pk)
|
||||
assert event.status == models.Event.CANCELLED
|
||||
assert event.cancelled
|
||||
event.status = models.Event.PROVISIONAL
|
||||
event.save()
|
||||
|
||||
def test_confirmed_property(many_events):
|
||||
edit = many_events[1]
|
||||
edit.status = models.Event.CONFIRMED
|
||||
edit.save()
|
||||
event = models.Event.objects.get(pk=edit.pk)
|
||||
assert event.status == models.Event.CONFIRMED
|
||||
assert event.confirmed
|
||||
event.status = models.Event.PROVISIONAL
|
||||
event.save()
|
||||
|
||||
|
||||
def test_earliest_time():
|
||||
event = models.Event(name="TE ET", start_date=date(2016, 0o1, 0o1))
|
||||
|
||||
# Just a start date
|
||||
assert event.earliest_time == date(2016, 0o1, 0o1)
|
||||
|
||||
# With start time
|
||||
event.start_time = time(9, 00)
|
||||
assert event.earliest_time == create_datetime(2016, 1, 1, 9, 00)
|
||||
|
||||
# With access time
|
||||
event.access_at = create_datetime(2015, 12, 0o3, 9, 57)
|
||||
assert event.earliest_time == event.access_at
|
||||
|
||||
# With meet time
|
||||
event.meet_at = create_datetime(2015, 12, 0o3, 9, 55)
|
||||
assert event.earliest_time == event.meet_at
|
||||
|
||||
# Check order isn't important
|
||||
event.start_date = date(2015, 12, 0o3)
|
||||
assert event.earliest_time == create_datetime(2015, 12, 0o3, 9, 00)
|
||||
|
||||
|
||||
def test_latest_time():
|
||||
event = models.Event(name="TE LT", start_date=date(2016, 0o1, 0o1))
|
||||
|
||||
# Just start date
|
||||
assert event.latest_time == event.start_date
|
||||
|
||||
# Just end date
|
||||
event.end_date = date(2016, 1, 2)
|
||||
assert event.latest_time == event.end_date
|
||||
|
||||
# With end time
|
||||
event.end_time = time(23, 00)
|
||||
assert event.latest_time == create_datetime(2016, 1, 2, 23, 00)
|
||||
|
||||
|
||||
def test_in_bounds():
|
||||
manager = models.Event.objects
|
||||
events = [
|
||||
manager.create(name="TE IB0", start_date='2016-01-02'), # yes no
|
||||
manager.create(name="TE IB1", start_date='2015-12-31', end_date='2016-01-04'),
|
||||
|
||||
# basic checks
|
||||
manager.create(name='TE IB2', start_date='2016-01-02', end_date='2016-01-04'),
|
||||
manager.create(name='TE IB3', start_date='2015-12-31', end_date='2016-01-03'),
|
||||
manager.create(name='TE IB4', start_date='2016-01-04',
|
||||
access_at=create_datetime(2016, 0o1, 0o3, 00, 00)),
|
||||
manager.create(name='TE IB5', start_date='2016-01-04',
|
||||
meet_at=create_datetime(2016, 0o1, 0o2, 00, 00)),
|
||||
|
||||
# negative check
|
||||
manager.create(name='TE IB6', start_date='2015-12-31', end_date='2016-01-01'),
|
||||
]
|
||||
|
||||
in_bounds = manager.events_in_bounds(create_datetime(2016, 1, 2, 0, 0),
|
||||
create_datetime(2016, 1, 3, 0, 0))
|
||||
assert events[0] in in_bounds
|
||||
assert events[1], in_bounds
|
||||
assert events[2], in_bounds
|
||||
assert events[3], in_bounds
|
||||
assert events[4], in_bounds
|
||||
assert events[5], in_bounds
|
||||
|
||||
assert events[6] not in in_bounds
|
||||
|
||||
|
||||
def create_datetime(year, month, day, hour, minute):
|
||||
tz = pytz.timezone(settings.TIME_ZONE)
|
||||
return tz.localize(datetime(year, month, day, hour, minute))
|
||||
|
||||
|
||||
class EventItemTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.e1 = models.Event.objects.create(name="TI E1", start_date=date.today())
|
||||
self.e2 = models.Event.objects.create(name="TI E2", start_date=date.today())
|
||||
|
||||
def test_item_cost(self):
|
||||
item = models.EventItem.objects.create(event=self.e1, name="TI I1", quantity=1, cost=1.00, order=1)
|
||||
self.assertEqual(item.total_cost, 1.00)
|
||||
|
||||
item.cost = 2.50
|
||||
self.assertEqual(item.total_cost, 2.50)
|
||||
|
||||
item.quantity = 4
|
||||
self.assertEqual(item.total_cost, 10.00)
|
||||
|
||||
# need to tidy up
|
||||
item.delete()
|
||||
|
||||
def test_item_order(self):
|
||||
i1 = models.EventItem.objects.create(event=self.e1, name="TI I1", quantity=1, cost=1.00, order=1)
|
||||
i2 = models.EventItem.objects.create(event=self.e1, name="TI I2", quantity=1, cost=1.00, order=2)
|
||||
|
||||
items = self.e1.items.all()
|
||||
self.assertListEqual([i1, i2], list(items))
|
||||
|
||||
|
||||
class EventPricingTestCase(TestCase):
|
||||
def setUp(self):
|
||||
models.VatRate.objects.create(rate=0.10, comment="TP V2", start_at=date.today() - timedelta(days=1))
|
||||
self.e1 = models.Event.objects.create(name="TP E1", start_date=date.today() - timedelta(days=2))
|
||||
self.e2 = models.Event.objects.create(name="TP E2", start_date=date.today())
|
||||
|
||||
# Create some items E1, total 70.40
|
||||
# Create some items E2, total 381.20
|
||||
self.i1 = models.EventItem.objects.create(event=self.e1, name="TP I1", quantity=1, cost=50.00, order=1)
|
||||
self.i2 = models.EventItem.objects.create(event=self.e1, name="TP I2", quantity=2, cost=3.20, order=2)
|
||||
self.i3 = models.EventItem.objects.create(event=self.e1, name="TP I3", quantity=7, cost=2.00, order=3)
|
||||
self.i4 = models.EventItem.objects.create(event=self.e2, name="TP I4", quantity=2, cost=190.60, order=1)
|
||||
|
||||
# Decimal type is needed here as that is what is returned from the model.
|
||||
# Using anything else results in a failure due to floating point arritmetic
|
||||
def test_sum_totals(self):
|
||||
self.assertEqual(self.e1.sum_total, Decimal('70.40'))
|
||||
self.assertEqual(self.e2.sum_total, Decimal('381.20'))
|
||||
|
||||
def test_vat_rate(self):
|
||||
self.assertEqual(self.e1.vat_rate.rate, Decimal('0.20'))
|
||||
self.assertEqual(self.e2.vat_rate.rate, Decimal('0.10'))
|
||||
|
||||
def test_vat_ammount(self):
|
||||
self.assertEqual(self.e1.vat, Decimal('14.08'))
|
||||
self.assertEqual(self.e2.vat, Decimal('38.12'))
|
||||
|
||||
def test_grand_total(self):
|
||||
self.assertEqual(self.e1.total, Decimal('84.48'))
|
||||
self.assertEqual(self.e2.total, Decimal('419.32'))
|
||||
|
||||
|
||||
class EventAuthorisationTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.profile = models.Profile.objects.get_or_create(
|
||||
first_name='Test',
|
||||
last_name='TEC User',
|
||||
username='eventauthtest',
|
||||
email='teccie@functional.test',
|
||||
is_superuser=True # lazily grant all permissions
|
||||
)[0]
|
||||
self.person = models.Person.objects.create(name='Authorisation Test Person')
|
||||
self.organisation = models.Organisation.objects.create(name='Authorisation Test Organisation', union_account=True)
|
||||
self.event = models.Event.objects.create(name="AuthorisationTestCase", person=self.person, organisation=self.organisation,
|
||||
start_date=date.today())
|
||||
# Add some items
|
||||
models.EventItem.objects.create(event=self.event, name="Authorisation test item", quantity=2, cost=123.45,
|
||||
order=1)
|
||||
|
||||
def test_event_property(self):
|
||||
auth1 = models.EventAuthorisation.objects.create(event=self.event, email="authorisation@model.test.case",
|
||||
name="Test Auth 1", amount=self.event.total - 1,
|
||||
sent_by=self.profile)
|
||||
self.assertFalse(self.event.authorised)
|
||||
auth1.amount = self.event.total
|
||||
auth1.save()
|
||||
self.assertTrue(self.event.authorised)
|
||||
|
||||
def test_last_edited(self):
|
||||
with reversion.create_revision():
|
||||
auth = models.EventAuthorisation.objects.create(event=self.event, email="authorisation@model.test.case",
|
||||
name="Test Auth", amount=self.event.total,
|
||||
sent_by=self.profile)
|
||||
self.assertIsNotNone(auth.last_edited_at)
|
||||
580
RIGS/tests/test_unit.py
Normal file
580
RIGS/tests/test_unit.py
Normal file
@@ -0,0 +1,580 @@
|
||||
from datetime import date
|
||||
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.utils.safestring import SafeText
|
||||
from RIGS.templatetags.markdown_tags import markdown_filter
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
from pytest_django.asserts import assertRedirects, assertNotContains, assertContains
|
||||
|
||||
from PyRIGS.tests.base import assert_times_almost_equal, assert_oembed, login
|
||||
from RIGS import models
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
class TestAdminMergeObjects(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True,
|
||||
is_active=True, is_staff=True)
|
||||
cls.persons = {
|
||||
1: models.Person.objects.create(name="Person 1"),
|
||||
2: models.Person.objects.create(name="Person 2"),
|
||||
3: models.Person.objects.create(name="Person 3"),
|
||||
}
|
||||
|
||||
cls.organisations = {
|
||||
1: models.Organisation.objects.create(name="Organisation 1"),
|
||||
2: models.Organisation.objects.create(name="Organisation 2"),
|
||||
3: models.Organisation.objects.create(name="Organisation 3"),
|
||||
}
|
||||
|
||||
cls.venues = {
|
||||
1: models.Venue.objects.create(name="Venue 1"),
|
||||
2: models.Venue.objects.create(name="Venue 2"),
|
||||
3: models.Venue.objects.create(name="Venue 3"),
|
||||
}
|
||||
|
||||
cls.events = {
|
||||
1: models.Event.objects.create(name="TE E1", start_date=date.today(), person=cls.persons[1],
|
||||
organisation=cls.organisations[3], venue=cls.venues[2]),
|
||||
2: models.Event.objects.create(name="TE E2", start_date=date.today(), person=cls.persons[2],
|
||||
organisation=cls.organisations[2], venue=cls.venues[3]),
|
||||
3: models.Event.objects.create(name="TE E3", start_date=date.today(), person=cls.persons[3],
|
||||
organisation=cls.organisations[1], venue=cls.venues[1]),
|
||||
4: models.Event.objects.create(name="TE E4", start_date=date.today(), person=cls.persons[3],
|
||||
organisation=cls.organisations[3], venue=cls.venues[3]),
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
self.profile.set_password('testuser')
|
||||
self.profile.save()
|
||||
self.assertTrue(self.client.login(username=self.profile.username, password='testuser'))
|
||||
|
||||
def test_merge_confirmation(self):
|
||||
change_url = reverse('admin:RIGS_venue_changelist')
|
||||
data = {
|
||||
'action': 'merge',
|
||||
'_selected_action': [str(val.pk) for key, val in self.venues.items()]
|
||||
|
||||
}
|
||||
response = self.client.post(change_url, data, follow=True)
|
||||
|
||||
self.assertContains(response, "The following objects will be merged")
|
||||
for key, venue in self.venues.items():
|
||||
self.assertContains(response, venue.name)
|
||||
|
||||
def test_merge_no_master(self):
|
||||
change_url = reverse('admin:RIGS_venue_changelist')
|
||||
data = {'action': 'merge',
|
||||
'_selected_action': [str(val.pk) for key, val in self.venues.items()],
|
||||
'post': 'yes',
|
||||
}
|
||||
response = self.client.post(change_url, data, follow=True)
|
||||
|
||||
self.assertContains(response, "An error occured")
|
||||
|
||||
def test_venue_merge(self):
|
||||
change_url = reverse('admin:RIGS_venue_changelist')
|
||||
|
||||
data = {'action': 'merge',
|
||||
'_selected_action': [str(self.venues[1].pk), str(self.venues[2].pk)],
|
||||
'post': 'yes',
|
||||
'master': self.venues[1].pk
|
||||
}
|
||||
|
||||
response = self.client.post(change_url, data, follow=True)
|
||||
self.assertContains(response, "Objects successfully merged")
|
||||
self.assertContains(response, self.venues[1].name)
|
||||
|
||||
# Check the master copy still exists
|
||||
self.assertTrue(models.Venue.objects.get(pk=self.venues[1].pk))
|
||||
|
||||
# Check the un-needed venue has been disposed of
|
||||
self.assertRaises(ObjectDoesNotExist, models.Venue.objects.get, pk=self.venues[2].pk)
|
||||
|
||||
# Check the one we didn't delete is still there
|
||||
self.assertEqual(models.Venue.objects.get(pk=self.venues[3].pk), self.venues[3])
|
||||
|
||||
# Check the events have been moved to the master venue
|
||||
for key, event in self.events.items():
|
||||
updatedEvent = models.Event.objects.get(pk=event.pk)
|
||||
if event.venue == self.venues[3]: # The one we left in place
|
||||
continue
|
||||
self.assertEqual(updatedEvent.venue, self.venues[1])
|
||||
|
||||
def test_person_merge(self):
|
||||
change_url = reverse('admin:RIGS_person_changelist')
|
||||
|
||||
data = {'action': 'merge',
|
||||
'_selected_action': [str(self.persons[1].pk), str(self.persons[2].pk)],
|
||||
'post': 'yes',
|
||||
'master': self.persons[1].pk
|
||||
}
|
||||
|
||||
response = self.client.post(change_url, data, follow=True)
|
||||
self.assertContains(response, "Objects successfully merged")
|
||||
self.assertContains(response, self.persons[1].name)
|
||||
|
||||
# Check the master copy still exists
|
||||
self.assertTrue(models.Person.objects.get(pk=self.persons[1].pk))
|
||||
|
||||
# Check the un-needed people have been disposed of
|
||||
self.assertRaises(ObjectDoesNotExist, models.Person.objects.get, pk=self.persons[2].pk)
|
||||
|
||||
# Check the one we didn't delete is still there
|
||||
self.assertEqual(models.Person.objects.get(pk=self.persons[3].pk), self.persons[3])
|
||||
|
||||
# Check the events have been moved to the master person
|
||||
for key, event in self.events.items():
|
||||
updatedEvent = models.Event.objects.get(pk=event.pk)
|
||||
if event.person == self.persons[3]: # The one we left in place
|
||||
continue
|
||||
self.assertEqual(updatedEvent.person, self.persons[1])
|
||||
|
||||
def test_organisation_merge(self):
|
||||
change_url = reverse('admin:RIGS_organisation_changelist')
|
||||
|
||||
data = {'action': 'merge',
|
||||
'_selected_action': [str(self.organisations[1].pk), str(self.organisations[2].pk)],
|
||||
'post': 'yes',
|
||||
'master': self.organisations[1].pk
|
||||
}
|
||||
|
||||
response = self.client.post(change_url, data, follow=True)
|
||||
self.assertContains(response, "Objects successfully merged")
|
||||
self.assertContains(response, self.organisations[1].name)
|
||||
|
||||
# Check the master copy still exists
|
||||
self.assertTrue(models.Organisation.objects.get(pk=self.organisations[1].pk))
|
||||
|
||||
# Check the un-needed organisations have been disposed of
|
||||
self.assertRaises(ObjectDoesNotExist, models.Organisation.objects.get, pk=self.organisations[2].pk)
|
||||
|
||||
# Check the one we didn't delete is still there
|
||||
self.assertEqual(models.Organisation.objects.get(pk=self.organisations[3].pk), self.organisations[3])
|
||||
|
||||
# Check the events have been moved to the master organisation
|
||||
for key, event in self.events.items():
|
||||
updatedEvent = models.Event.objects.get(pk=event.pk)
|
||||
if event.organisation == self.organisations[3]: # The one we left in place
|
||||
continue
|
||||
self.assertEqual(updatedEvent.organisation, self.organisations[1])
|
||||
|
||||
|
||||
class TestInvoiceDelete(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True,
|
||||
is_active=True, is_staff=True)
|
||||
cls.vatrate = models.VatRate.objects.create(start_at='2014-03-05', rate=0.20, comment='test1')
|
||||
cls.events = {
|
||||
1: models.Event.objects.create(name="TE E1", start_date=date.today()),
|
||||
2: models.Event.objects.create(name="TE E2", start_date=date.today())
|
||||
}
|
||||
|
||||
cls.invoices = {
|
||||
1: models.Invoice.objects.create(event=cls.events[1]),
|
||||
2: models.Invoice.objects.create(event=cls.events[2])
|
||||
}
|
||||
|
||||
cls.payments = {
|
||||
1: models.Payment.objects.create(invoice=cls.invoices[1], date=date.today(), amount=12.34,
|
||||
method=models.Payment.CASH)
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
self.profile.set_password('testuser')
|
||||
self.profile.save()
|
||||
self.assertTrue(self.client.login(username=self.profile.username, password='testuser'))
|
||||
|
||||
def test_invoice_delete_allowed(self):
|
||||
request_url = reverse('invoice_delete', kwargs={'pk': self.invoices[2].pk})
|
||||
|
||||
response = self.client.get(request_url, follow=True)
|
||||
self.assertContains(response, "Are you sure")
|
||||
|
||||
# Check the invoice still exists
|
||||
self.assertTrue(models.Invoice.objects.get(pk=self.invoices[2].pk))
|
||||
|
||||
# Actually delete it
|
||||
self.client.post(request_url, follow=True)
|
||||
|
||||
# Check the invoice is deleted
|
||||
self.assertRaises(ObjectDoesNotExist, models.Invoice.objects.get, pk=self.invoices[2].pk)
|
||||
|
||||
def test_invoice_delete_not_allowed(self):
|
||||
request_url = reverse('invoice_delete', kwargs={'pk': self.invoices[1].pk})
|
||||
|
||||
response = self.client.get(request_url, follow=True)
|
||||
self.assertContains(response, "To delete an invoice, delete the payments first.")
|
||||
|
||||
# Check the invoice still exists
|
||||
self.assertTrue(models.Invoice.objects.get(pk=self.invoices[1].pk))
|
||||
|
||||
# Try to actually delete it
|
||||
self.client.post(request_url, follow=True)
|
||||
|
||||
# Check this didn't work
|
||||
self.assertTrue(models.Invoice.objects.get(pk=self.invoices[1].pk))
|
||||
|
||||
|
||||
class TestPrintPaperwork(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.profile = models.Profile.objects.create(username="testuser1", email="1@test.com", is_superuser=True,
|
||||
is_active=True, is_staff=True)
|
||||
cls.events = {
|
||||
1: models.Event.objects.create(name="TE E1", start_date=date.today(),
|
||||
description="This is an event description\nthat for a very specific reason spans two lines."),
|
||||
}
|
||||
|
||||
cls.invoices = {
|
||||
1: models.Invoice.objects.create(event=cls.events[1]),
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
self.profile.set_password('testuser')
|
||||
self.profile.save()
|
||||
self.assertTrue(self.client.login(username=self.profile.username, password='testuser'))
|
||||
|
||||
def test_print_paperwork_success(self):
|
||||
request_url = reverse('event_print', kwargs={'pk': self.events[1].pk})
|
||||
|
||||
response = self.client.get(request_url, follow=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_print_invoice_success(self):
|
||||
request_url = reverse('invoice_print', kwargs={'pk': self.invoices[1].pk})
|
||||
|
||||
response = self.client.get(request_url, follow=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
def test_login_redirect(client, django_user_model):
|
||||
request_url = reverse('event_embed', kwargs={'pk': 1})
|
||||
expected_url = "{0}?next={1}".format(reverse('login_embed'), request_url)
|
||||
|
||||
# Request the page and check it redirects
|
||||
response = client.get(request_url, follow=True)
|
||||
assertRedirects(response, expected_url, status_code=302, target_status_code=200)
|
||||
|
||||
# Now login
|
||||
login(client, django_user_model)
|
||||
|
||||
# And check that it no longer redirects
|
||||
response = client.get(request_url, follow=True)
|
||||
assert len(response.redirect_chain) == 0
|
||||
|
||||
|
||||
def test_login_cookie_warning(client):
|
||||
login_url = reverse('login_embed')
|
||||
response = client.post(login_url, follow=True)
|
||||
assertContains(response, "Cookies do not seem to be enabled")
|
||||
|
||||
|
||||
def test_xframe_headers(admin_client, basic_event):
|
||||
event_url = reverse('event_embed', kwargs={'pk': basic_event.pk})
|
||||
login_url = reverse('login_embed')
|
||||
|
||||
response = admin_client.get(event_url, follow=True)
|
||||
with pytest.raises(KeyError):
|
||||
response._headers["X-Frame-Options"]
|
||||
|
||||
response = admin_client.get(login_url, follow=True)
|
||||
with pytest.raises(KeyError):
|
||||
response._headers["X-Frame-Options"]
|
||||
|
||||
|
||||
def test_oembed(client, basic_event):
|
||||
event_url = reverse('event_detail', kwargs={'pk': basic_event.pk})
|
||||
event_embed_url = reverse('event_embed', kwargs={'pk': basic_event.pk})
|
||||
oembed_url = reverse('event_oembed', kwargs={'pk': basic_event.pk})
|
||||
|
||||
alt_oembed_url = reverse('event_oembed', kwargs={'pk': 999})
|
||||
alt_event_embed_url = reverse('event_embed', kwargs={'pk': 999})
|
||||
|
||||
assert_oembed(alt_event_embed_url, alt_oembed_url, client, event_embed_url, event_url, oembed_url)
|
||||
|
||||
|
||||
def search(client, url, found, notfound, arguments):
|
||||
for argument in arguments:
|
||||
query = getattr(found, argument)
|
||||
request_url = "%s?q=%s" % (reverse_lazy(url), query)
|
||||
response = client.get(request_url, follow=True)
|
||||
assertContains(response, getattr(found, 'name'))
|
||||
assertNotContains(response, getattr(notfound, 'name'))
|
||||
|
||||
|
||||
def test_search(admin_client):
|
||||
persons = {
|
||||
1: models.Person.objects.create(name="Right Person", phone="1234"),
|
||||
2: models.Person.objects.create(name="Wrong Person", phone="5678"),
|
||||
}
|
||||
organisations = {
|
||||
1: models.Organisation.objects.create(name="Right Organisation", email="test@example.com"),
|
||||
2: models.Organisation.objects.create(name="Wrong Organisation", email="check@fake.co.uk"),
|
||||
}
|
||||
venues = {
|
||||
1: models.Venue.objects.create(name="Right Venue", address="1 Test Street, EX1"),
|
||||
2: models.Venue.objects.create(name="Wrong Venue", address="2 Check Way, TS2"),
|
||||
}
|
||||
events = {
|
||||
1: models.Event.objects.create(name="Right Event", start_date=date.today(), venue=venues[1], person=persons[1],
|
||||
organisation=organisations[1]),
|
||||
2: models.Event.objects.create(name="Wrong Event", start_date=date.today(), venue=venues[2], person=persons[2],
|
||||
organisation=organisations[2]),
|
||||
}
|
||||
search(admin_client, 'event_archive', events[1], events[2], ['name', 'id'])
|
||||
search(admin_client, 'person_list', persons[1], persons[2], ['name', 'id', 'phone'])
|
||||
search(admin_client, 'organisation_list', organisations[1], organisations[2],
|
||||
['name', 'id', 'email'])
|
||||
search(admin_client, 'venue_list', venues[1], venues[2],
|
||||
['name', 'id', 'address'])
|
||||
|
||||
|
||||
def test_hs_list(admin_client, basic_event):
|
||||
request_url = reverse('hs_list')
|
||||
response = admin_client.get(request_url, follow=True)
|
||||
assertContains(response, basic_event.name)
|
||||
# assertContains(response, events[2].name)
|
||||
assertContains(response, 'Create')
|
||||
|
||||
|
||||
def review(client, profile, obj, request_url):
|
||||
time = timezone.now()
|
||||
response = client.get(reverse(request_url, kwargs={'pk': obj.pk}), follow=True)
|
||||
obj.refresh_from_db()
|
||||
assertContains(response, 'Reviewed by')
|
||||
assertContains(response, profile.name)
|
||||
assert_times_almost_equal(time, obj.reviewed_at)
|
||||
|
||||
|
||||
def test_ra_review(admin_client, admin_user, ra):
|
||||
review(admin_client, admin_user, ra, 'ra_review')
|
||||
|
||||
|
||||
def test_checklist_review(admin_client, admin_user, checklist):
|
||||
review(admin_client, admin_user, checklist, 'ec_review')
|
||||
|
||||
|
||||
def test_ra_redirect(admin_client, admin_user, ra):
|
||||
request_url = reverse('event_ra', kwargs={'pk': ra.event.pk})
|
||||
expected_url = reverse('ra_edit', kwargs={'pk': ra.pk})
|
||||
response = admin_client.get(request_url, follow=True)
|
||||
assertRedirects(response, expected_url, status_code=302, target_status_code=200)
|
||||
|
||||
|
||||
class TestMarkdownTemplateTags(TestCase):
|
||||
markdown = """
|
||||
An h1 header
|
||||
============
|
||||
|
||||
Paragraphs are separated by a blank line.
|
||||
|
||||
2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists
|
||||
look like:
|
||||
|
||||
* this one
|
||||
* that one
|
||||
* the other one
|
||||
|
||||
Note that --- not considering the asterisk --- the actual text
|
||||
content starts at 4-columns in.
|
||||
|
||||
> Block quotes are
|
||||
> written like so.
|
||||
>
|
||||
> They can span multiple paragraphs,
|
||||
> if you like.
|
||||
|
||||
Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., "it's all
|
||||
in chapters 12--14"). Three dots ... will be converted to an ellipsis.
|
||||
Unicode is supported.
|
||||
|
||||
|
||||
|
||||
An h2 header
|
||||
------------
|
||||
|
||||
Here's a numbered list:
|
||||
|
||||
1. first item
|
||||
2. second item
|
||||
3. third item
|
||||
|
||||
Note again how the actual text starts at 4 columns in (4 characters
|
||||
from the left side). Here's a code sample:
|
||||
|
||||
# Let me re-iterate ...
|
||||
for i in 1 .. 10 { do-something(i) }
|
||||
|
||||
As you probably guessed, indented 4 spaces. By the way, instead of
|
||||
indenting the block, you can use delimited blocks, if you like:
|
||||
|
||||
~~~
|
||||
define foobar() {
|
||||
print "Welcome to flavor country!";
|
||||
}
|
||||
~~~
|
||||
|
||||
(which makes copying & pasting easier). You can optionally mark the
|
||||
delimited block for Pandoc to syntax highlight it:
|
||||
|
||||
~~~python
|
||||
import time
|
||||
# Quick, count to ten!
|
||||
for i in range(10):
|
||||
# (but not *too* quick)
|
||||
time.sleep(0.5)
|
||||
print i
|
||||
~~~
|
||||
|
||||
|
||||
|
||||
### An h3 header ###
|
||||
|
||||
Now a nested list:
|
||||
|
||||
1. First, get these ingredients:
|
||||
|
||||
* carrots
|
||||
* celery
|
||||
* lentils
|
||||
|
||||
2. Boil some water.
|
||||
|
||||
3. Dump everything in the pot and follow
|
||||
this algorithm:
|
||||
|
||||
find wooden spoon
|
||||
uncover pot
|
||||
stir
|
||||
cover pot
|
||||
balance wooden spoon precariously on pot handle
|
||||
wait 10 minutes
|
||||
goto first step (or shut off burner when done)
|
||||
|
||||
Do not bump wooden spoon or it will fall.
|
||||
|
||||
Notice again how text always lines up on 4-space indents (including
|
||||
that last line which continues item 3 above).
|
||||
|
||||
Here's a link to [a website](http://foo.bar). Here's a footnote [^1].
|
||||
|
||||
[^1]: Footnote text goes here.
|
||||
|
||||
Tables can look like this:
|
||||
|
||||
size material color
|
||||
---- ------------ ------------
|
||||
9 leather brown
|
||||
10 hemp canvas natural
|
||||
11 glass transparent
|
||||
|
||||
Table: Shoes, their sizes, and what they're made of
|
||||
|
||||
(The above is the caption for the table.) Pandoc also supports
|
||||
multi-line tables:
|
||||
|
||||
-------- -----------------------
|
||||
keyword text
|
||||
-------- -----------------------
|
||||
red Sunsets, apples, and
|
||||
other red or reddish
|
||||
things.
|
||||
|
||||
green Leaves, grass, frogs
|
||||
and other things it's
|
||||
not easy being.
|
||||
-------- -----------------------
|
||||
|
||||
A horizontal rule follows.
|
||||
|
||||
***
|
||||
|
||||
Here's a definition list:
|
||||
|
||||
apples
|
||||
: Good for making applesauce.
|
||||
oranges
|
||||
: Citrus!
|
||||
tomatoes
|
||||
: There's no "e" in tomatoe.
|
||||
|
||||
Again, text is indented 4 spaces. (Put a blank line between each
|
||||
term/definition pair to spread things out more.)
|
||||
|
||||
Here's a "line block":
|
||||
|
||||
| Line one
|
||||
| Line too
|
||||
| Line tree
|
||||
|
||||
and images can be specified like so:
|
||||
|
||||

|
||||
|
||||
Inline math equations go in like so: $\omega = d\phi / dt$. Display
|
||||
math should get its own line and be put in in double-dollarsigns:
|
||||
|
||||
$$I = \int \rho R^{2} dV$$
|
||||
|
||||
And note that you can backslash-escape any punctuation characters
|
||||
which you wish to be displayed literally, ex.: \`foo\`, \*bar\*, etc.
|
||||
"""
|
||||
|
||||
def test_html_safe(self):
|
||||
html = markdown_filter(self.markdown)
|
||||
self.assertIsInstance(html, SafeText)
|
||||
|
||||
def test_img_strip(self):
|
||||
rml = markdown_filter(self.markdown, 'rml')
|
||||
self.assertNotIn("<img", rml)
|
||||
|
||||
def test_code(self):
|
||||
rml = markdown_filter(self.markdown, 'rml')
|
||||
self.assertIn('<font face="Courier">monospace</font>', rml)
|
||||
|
||||
def test_blockquote(self):
|
||||
rml = markdown_filter(self.markdown, 'rml')
|
||||
self.assertIn("<pre>\nBlock quotes", rml)
|
||||
|
||||
def test_lists(self):
|
||||
rml = markdown_filter(self.markdown, 'rml')
|
||||
self.assertIn("<li><para>second item</para></li>", rml) # <ol>
|
||||
self.assertIn("<li><para>that one</para></li>", rml) # <ul>
|
||||
|
||||
def test_in_print(self):
|
||||
event = models.Event.objects.create(
|
||||
name="MD Print Test",
|
||||
description=self.markdown,
|
||||
start_date='2016-01-01',
|
||||
)
|
||||
user = models.Profile.objects.create(
|
||||
username='RML test',
|
||||
is_superuser=True, # Don't care about permissions
|
||||
is_active=True,
|
||||
)
|
||||
user.set_password('rmltester')
|
||||
user.save()
|
||||
|
||||
self.assertTrue(self.client.login(username=user.username, password='rmltester'))
|
||||
response = self.client.get(reverse('event_print', kwargs={'pk': event.pk}))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# By the time we have a PDF it should be larger than the original by some margin
|
||||
# RML hard fails if something doesn't work
|
||||
self.assertGreater(len(response.content), len(self.markdown))
|
||||
|
||||
def test_nonetype(self):
|
||||
html = markdown_filter(None)
|
||||
self.assertIsNone(html)
|
||||
|
||||
def test_linebreaks(self):
|
||||
html = markdown_filter(self.markdown)
|
||||
self.assertIn("Itemized lists<br />\nlook like", html)
|
||||
Reference in New Issue
Block a user