combo/tests/test_calendar.py

380 lines
13 KiB
Python

import json
import urlparse
import datetime
import pytest
import mock
from django.contrib.auth.models import User
from django.core.cache import cache
from django.core.urlresolvers import reverse
from combo.data.models import Page
from combo.apps.calendar.models import BookingCalendar
from combo.apps.calendar.utils import (get_calendar, get_chrono_service,
get_chrono_events)
pytestmark = pytest.mark.django_db
CHRONO_EVENTS = {
"data": [
{
"disabled": False,
"text": "13 juin 2017 08:00",
"api": {
"fillslot_url": "http://example.net/api/agenda/whatever/fillslot/86/"
},
"id": 86,
"datetime": "2017-06-13 08:00:00"
},
{
"disabled": False,
"text": "13 juin 2017 08:30",
"api": {
"fillslot_url": "http://example.net/api/agenda/whatever/fillslot/87/"
},
"id": 87,
"datetime": "2017-06-13 08:30:00"
},
{
"disabled": False,
"text": "13 juin 2017 09:00",
"api": {
"fillslot_url": "http://example.net/api/agenda/whatever/fillslot/88/"
},
"id": 88,
"datetime": "2017-06-13 09:00:00"
},
{
"disabled": False,
"text": "13 juin 2017 09:30",
"api": {
"fillslot_url": "http://example.net/api/agenda/whatever/fillslot/89/"
},
"id": 89,
"datetime": "2017-06-13 09:30:00"
},
{
"disabled": False,
"text": "14 juin 2017 09:30",
"api": {
"fillslot_url": "http://example.net/api/agenda/whatever/fillslot/89/"
},
"id": 90,
"datetime": "2017-06-14 09:30:00"
},
{
"disabled": False,
"text": "14 juin 2017 10:00",
"api": {
"fillslot_url": "http://example.net/api/agenda/whatever/fillslot/89/"
},
"id": 91,
"datetime": "2017-06-14 10:00:00"
},
{
"disabled": True,
"text": "14 juin 2017 15:00",
"api": {
"fillslot_url": "http://example.net/api/agenda/whatever/fillslot/89/"
},
"id": 91,
"datetime": "2017-06-14 15:00:00"
},
{
"disabled": True,
"text": "15 juin 2017 10:00",
"api": {
"fillslot_url": "http://example.net/api/agenda/whatever/fillslot/89/"
},
"id": 92,
"datetime": "2017-06-15 10:00:00"
}
]
}
WCS_FORMDEFS = [
{
"count": 12,
"category": "common",
"functions": {
"_receiver": {
"label": "Recipient"
},
},
"authentication_required": False,
"description": "",
"title": "Demande de place en creche",
"url": "http://example.net/demande-de-place-en-creche/",
"category_slug": "common",
"redirection": False,
"keywords": [],
"slug": "demande-de-place-en-creche"
}
]
def login(app, username='admin', password='admin'):
login_page = app.get('/login/')
login_form = login_page.forms[0]
login_form['username'] = username
login_form['password'] = password
resp = login_form.submit()
assert resp.status_int == 302
return app
def str2datetime(sdt):
return datetime.datetime.strptime(sdt, '%Y-%m-%dT%H:%M:%S')
class MockedRequestResponse(mock.Mock):
status_code = 200
def json(self):
return json.loads(self.content)
def mocked_requests_send(request, **kwargs):
if 'chrono' in request.url:
return MockedRequestResponse(
content=json.dumps(CHRONO_EVENTS))
else:
return MockedRequestResponse(
content=json.dumps(WCS_FORMDEFS))
def teardown_function(function):
cache.clear()
@pytest.fixture
def admin(db):
return User.objects.create_superuser(username='admin', password='admin', email=None)
@pytest.fixture
def anonymous(app):
return app
@pytest.fixture
def connected(app, admin):
return login(app)
@pytest.fixture(params=['anonymous', 'connected'])
def client(request, anonymous, connected):
return locals().get(request.param)
@pytest.fixture
def cell(db):
page = Page.objects.create(title='whatever', slug='booking', template_name='standard')
cell = BookingCalendar(
page=page, title='Example Of Calendar',
agenda_reference='default:test',
formdef_reference='default:test',
slot_duration=datetime.timedelta(minutes=30),
minimal_booking_duration=datetime.timedelta(hours=1),
placeholder='content', order=0
)
cell.save()
return cell
@pytest.fixture
def async_url(cell):
return reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': cell.page.pk, 'cell_reference': cell.get_reference()})
def test_get_chrono_service(settings):
service = get_chrono_service()
assert service['title'] == 'test'
assert service['url'] == 'http://chrono.example.org'
assert service['secondary'] is False
@mock.patch('combo.apps.calendar.utils.requests.send', side_effect=mocked_requests_send)
def test_cell_rendering(mocked_send, client, cell, async_url):
page = client.get('/booking/')
cell_content = page.html.body.find('div', {'class': 'bookingcalendar'})
# test async cell loading
assert cell_content.text.strip() == 'Loading...'
# put data in cache
client.get(async_url)
# check that data are cached
page = client.get('/booking/')
# test without selecting slots
resp = page.form.submit().follow()
assert 'Please select slots' in resp.content
# test with slots from different day
resp.form.set('slots', True, 0)
resp.form.set('slots', True, 1)
resp.form.set('slots', True, 4)
resp = resp.form.submit().follow()
# test with non contiguous slots
assert 'Please select slots of the same day' in resp.content
resp.form.set('slots', True, 0)
resp.form.set('slots', True, 2)
resp = resp.form.submit().follow()
assert 'Please select contiguous slots' in resp.content
# test with invalid booking duration
resp.form.set('slots', True, 0)
resp = resp.form.submit().follow()
assert 'Minimal booking duration is 01:00' in resp.content
# test with valid selected slots
resp.form.set('slots', True, 0)
resp.form.set('slots', True, 1)
resp.form.set('slots', True, 2)
resp = resp.form.submit()
parsed = urlparse.urlparse(resp.url)
assert parsed.path == '/test/'
qs = urlparse.parse_qs(parsed.query)
assert qs['session_var_booking_agenda_slug'] == ['test']
assert qs['session_var_booking_start'] == ['2017-06-13T08:00:00+00:00']
assert qs['session_var_booking_end'] == ['2017-06-13T09:30:00+00:00']
def test_calendar(cell):
events = CHRONO_EVENTS['data']
cal = get_calendar(events, cell.slot_duration, 7, cell.minimal_booking_duration)
assert len(cal.days) == 3
for day in cal.get_computed_days():
assert day in [
str2datetime('2017-06-13T08:00:00').date() + datetime.timedelta(days=i)
for i in range(0, 7)]
min_slot = str2datetime('2017-06-13T08:00:00')
max_slot = str2datetime('2017-06-14T15:00:00')
for slot in cal.get_slots():
assert (min_slot.time() <= slot <= max_slot.time()) is True
assert cal.has_day(min_slot.date()) is True
assert cal.get_availability(str2datetime('2017-06-14T15:00:00')).available is False
assert cal.get_minimum_slot() == min_slot.time()
assert cal.get_maximum_slot() == max_slot.time()
assert cal.get_day(max_slot.date()).slots[-1].available is False
@mock.patch('combo.apps.calendar.utils.requests.send', side_effect=mocked_requests_send)
def test_cell_pagination(mocked_send, client, cell, async_url):
cell.days_displayed = 2
cell.save()
# put data in cache
client.get(async_url)
page = client.get('/booking/')
# first page
table = page.html.find('table')
thead_th = table.thead.findChildren('th')
assert len(thead_th) == 3
for th in thead_th:
if th.text:
assert th.text in ('06/13/2017', '06/14/2017')
links = page.html.findAll('a')
assert len(links) == 2
next_page_link = links[1]
assert next_page_link.text == 'next'
assert next_page_link.attrs['href'] == '?chunk_%d=2' % cell.pk
assert next_page_link.attrs['data-content-url'] == '/ajax/calendar/content/%d/?chunk_%d=2' % (cell.pk, cell.pk)
# second page without ajax call
page2 = client.get('/booking/?chunk_%d=2' % cell.pk)
table = page2.html.find('table')
thead_th = table.thead.findChildren('th')
assert len(thead_th) == 3
for th in thead_th:
if th.text:
assert th.text in ('06/15/2017', '06/16/2017')
links = page2.html.findAll('a')
assert len(links) == 2
previous_page_link = links[1]
assert previous_page_link.text == 'previous'
assert previous_page_link.attrs['href'] == '?chunk_%d=1' % cell.pk
assert previous_page_link.attrs['data-content-url'] == '/ajax/calendar/content/%d/?chunk_%d=1' % (cell.pk, cell.pk)
# second page through ajax call
page = client.get('/booking/?chunk_%d=1')
page2 = client.get(next_page_link.attrs['data-content-url'])
table = page2.html.find('table')
thead_th = table.thead.findChildren('th')
assert len(thead_th) == 3
for th in thead_th:
if th.text:
assert th.text in ('06/15/2017', '06/16/2017')
links = page2.html.findAll('a')
assert len(links) == 1
previous_page_link = links[0]
assert previous_page_link.text == 'previous'
assert previous_page_link.attrs['data-content-url'] == '/ajax/calendar/content/%d/?chunk_%d=1' % (cell.pk, cell.pk)
@mock.patch('combo.apps.calendar.utils.requests.send', side_effect=mocked_requests_send)
def test_cell_rendering_cal_info(mocked_send, client, cell, async_url):
page = client.get('/booking/')
# put data in cache
client.get(async_url)
page = client.get('/booking/')
title_info = page.html.h2.find('span', {'class': 'calinfo'})
assert title_info.text.strip() == '(Next available slot: June 13, 2017, 8 a.m.)'
def test_cell_rendering_cal_info_when_available_slots_next_day(client, cell, async_url):
with mock.patch('combo.utils.requests.get') as request_get:
events = CHRONO_EVENTS['data'][::]
for idx in range(2):
events[idx]['disabled'] = True
def side_effect(*args, **kwargs):
if 'chrono' in kwargs['remote_service']['url']:
return MockedRequestResponse(content=json.dumps({"data": events}))
return MockedRequestResponse(content=json.dumps(WCS_FORMDEFS))
request_get.side_effect = side_effect
# put data in cache
client.get(async_url)
page = client.get('/booking/')
title_info = page.html.h2.find('span', {'class': 'calinfo'})
assert title_info.text.strip() == '(Next available slot: June 14, 2017, 9:30 a.m.)'
def test_cell_rendering_cal_info_when_no_available_slots(client, cell, async_url):
with mock.patch('combo.utils.requests.get') as request_get:
def side_effect(*args, **kwargs):
if 'chrono' in kwargs['remote_service']['url']:
return MockedRequestResponse(content=json.dumps({"data": []}))
return MockedRequestResponse(content=json.dumps(WCS_FORMDEFS))
request_get.side_effect = side_effect
# put data in cache
client.get(async_url)
page = client.get('/booking/')
title_info = page.html.h2.find('span', {'class': 'calinfo'})
assert title_info.text.strip() == '(No available slots.)'
def test_booking_calendar_indexing(cell):
with mock.patch('combo.utils.requests.get') as request_get:
def side_effect(*args, **kwargs):
if 'chrono' in kwargs['remote_service']['url']:
return MockedRequestResponse(content=json.dumps({"data": []}))
return MockedRequestResponse(content=json.dumps(WCS_FORMDEFS))
request_get.side_effect = side_effect
search_text = cell.render_for_search()
assert 'Example Of Calendar' in search_text
def test_cell_async_rendering_failure(client, cell, async_url):
with mock.patch('combo.utils.requests.get') as request_get:
def side_effect(*args, **kwargs):
if 'chrono' in kwargs['remote_service']['url']:
return MockedRequestResponse(content=json.dumps({}), status_code=502)
return MockedRequestResponse(content=json.dumps(WCS_FORMDEFS))
request_get.side_effect = side_effect
page = client.get(async_url)
assert page.html.div.text == "An error occurred while retrieving calendar's availabilities."