380 lines
13 KiB
Python
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."
|