Projet

Général

Profil

0002-fix-Django-3.2-related-test-failures-68025.patch

A. Berriot, 18 août 2022 09:12

Télécharger (23,8 ko)

Voir les différences:

Subject: [PATCH 2/2] fix Django 3.2 related test failures (#68025)

 chrono/agendas/models.py                      | 45 ++++++++++++-------
 .../datetimes/test_events_multiple_agendas.py | 16 ++++---
 tests/api/test_event.py                       |  4 +-
 tests/conftest.py                             | 20 +++++++++
 tests/manager/test_all.py                     | 31 +++++++------
 tests/manager/test_event.py                   | 14 +++---
 tests/manager/test_exception.py               |  6 +--
 tests/manager/test_resource.py                | 12 ++---
 8 files changed, 95 insertions(+), 53 deletions(-)
chrono/agendas/models.py
25 25
import uuid
26 26
from contextlib import contextmanager
27 27

  
28
import django
28 29
import requests
29 30
import vobject
30 31
from dateutil.relativedelta import SU, relativedelta
......
858 859
                guardian__user_external_id=guardian_external_id,
859 860
                agenda=agenda,
860 861
            )
861
            .annotate(day=Func(F('days'), function='unnest'))
862
            .annotate(day=Func(F('days'), function='unnest', output_field=models.IntegerField()))
862 863
            .annotate(week_day=(F('day') + 1) % 7 + 1)  # convert ISO day number to db lookup day number
863 864
            .values('week_day')
864 865
        )
......
1556 1557
                F('start_datetime') + datetime.timedelta(minutes=1) * F('duration'),
1557 1558
                output_field=models.DateTimeField(),
1558 1559
            ),
1559
            computed_slug=Concat('agenda__slug', Value('@'), 'slug'),
1560
            computed_slug=Concat('agenda__slug', Value('@'), 'slug', output_field=models.CharField()),
1560 1561
        )
1561 1562

  
1562 1563
        overlapping_events = qs.filter(
......
1581 1582
                output_field=models.DateTimeField(),
1582 1583
            ),
1583 1584
            end_hour=Cast('computed_end_datetime', models.TimeField()),
1584
            computed_slug=Concat('agenda__slug', Value('@'), 'slug'),
1585
            computed_slug=Concat('agenda__slug', Value('@'), 'slug', output_field=models.CharField()),
1585 1586
        )
1586 1587

  
1587 1588
        overlapping_events = qs.filter(
......
1590 1591
            recurrence_days__overlap=F('recurrence_days'),
1591 1592
        ).exclude(pk=OuterRef('pk'))
1592 1593

  
1593
        json_object = Func(
1594
            Value('slug'),
1595
            'computed_slug',
1596
            Value('days'),
1597
            'recurrence_days',
1598
            function='jsonb_build_object',
1599
            output_field=JSONField(),
1600
        )  # use django.db.models.functions.JSONObject in Django>=3.2
1594
        if django.VERSION >= (3, 2):
1595
            from django.db.models.functions import JSONObject
1596

  
1597
            json_object = JSONObject(
1598
                slug=F('computed_slug'),
1599
                days=F('recurrence_days'),
1600
            )
1601
        else:
1602
            json_object = Func(
1603
                Value('slug'),
1604
                'computed_slug',
1605
                Value('days'),
1606
                'recurrence_days',
1607
                function='jsonb_build_object',
1608
                output_field=JSONField(),
1609
            )
1601 1610

  
1602 1611
        return qs.annotate(
1603 1612
            overlaps=ArraySubquery(
1604 1613
                overlapping_events.values(json=json_object),
1605
                output_field=ArrayField(models.CharField()),
1614
                output_field=ArrayField(JSONField()),
1606 1615
            )
1607 1616
        )
1608 1617

  
......
3175 3184

  
3176 3185
    def is_complete(self):
3177 3186
        day_counts = self.rules.aggregate(
3178
            all_week=Coalesce(SumCardinality('days', filter=Q(weeks='')), 0),
3179
            even_week=Coalesce(SumCardinality('days', filter=Q(weeks='even')), 0),
3180
            odd_week=Coalesce(SumCardinality('days', filter=Q(weeks='odd')), 0),
3187
            all_week=Coalesce(
3188
                SumCardinality('days', filter=Q(weeks='')), 0, output_field=models.IntegerField()
3189
            ),
3190
            even_week=Coalesce(
3191
                SumCardinality('days', filter=Q(weeks='even')), 0, output_field=models.IntegerField()
3192
            ),
3193
            odd_week=Coalesce(
3194
                SumCardinality('days', filter=Q(weeks='odd')), 0, output_field=models.IntegerField()
3195
            ),
3181 3196
        )
3182 3197
        even_week_day_count = day_counts['all_week'] + day_counts['even_week']
3183 3198
        odd_week_day_count = day_counts['all_week'] + day_counts['odd_week']
tests/api/datetimes/test_events_multiple_agendas.py
1596 1596
    )
1597 1597

  
1598 1598
    resp = app.get('/api/agendas/datetimes/', params={'agendas': 'foo-bar,foo-bar-2', 'check_overlaps': True})
1599
    assert [(x['id'], x['overlaps']) for x in resp.json['data']] == [
1599

  
1600
    expected = [
1600 1601
        (
1601 1602
            'foo-bar@event-containing-all-events',
1602
            ['foo-bar@event-12-14', 'foo-bar-2@event-13-15', 'foo-bar-2@event-14-16'],
1603
            {'foo-bar@event-12-14', 'foo-bar-2@event-13-15', 'foo-bar-2@event-14-16'},
1603 1604
        ),
1604
        ('foo-bar@event-12-14', ['foo-bar@event-containing-all-events', 'foo-bar-2@event-13-15']),
1605
        ('foo-bar@event-12-14', {'foo-bar@event-containing-all-events', 'foo-bar-2@event-13-15'}),
1605 1606
        (
1606 1607
            'foo-bar-2@event-13-15',
1607
            ['foo-bar@event-containing-all-events', 'foo-bar@event-12-14', 'foo-bar-2@event-14-16'],
1608
            {'foo-bar@event-containing-all-events', 'foo-bar@event-12-14', 'foo-bar-2@event-14-16'},
1608 1609
        ),
1609
        ('foo-bar-2@event-no-duration', []),
1610
        ('foo-bar-2@event-14-16', ['foo-bar@event-containing-all-events', 'foo-bar-2@event-13-15']),
1611
        ('foo-bar-2@event-other-day', []),
1610
        ('foo-bar-2@event-no-duration', set()),
1611
        ('foo-bar-2@event-14-16', {'foo-bar@event-containing-all-events', 'foo-bar-2@event-13-15'}),
1612
        ('foo-bar-2@event-other-day', set()),
1612 1613
    ]
1614
    assert [(x['id'], set(x['overlaps'])) for x in resp.json['data']] == expected
1613 1615

  
1614 1616
    resp = app.get('/api/agendas/datetimes/', params={'agendas': 'foo-bar,foo-bar-2'})
1615 1617
    assert ['overlaps' not in x for x in resp.json['data']]
tests/api/test_event.py
965 965
                ]
966 966
            },
967 967
        )
968
        assert len(ctx.captured_queries) == 6
968
        # there's one less query on django 3.2, the ORM has probably
969
        # been optimized there
970
        assert len(ctx.captured_queries) in (5, 6)
969 971
    assert [(d['agenda'], d['slug']) for d in resp.json['data']] == [
970 972
        ('foo', 'recurring-event-slug--2022-07-01-1600'),
971 973
        ('bar', 'event-slug'),
tests/conftest.py
1
import django
1 2
import django_webtest
2 3
import pytest
3 4

  
......
23 24
            'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
24 25
        }
25 26
    }
27

  
28

  
29
@pytest.fixture
30
def get_proper_html_str():
31
    """
32
    There are some subtle differences in the HTML generated by django 2 and 3
33
    making it harder to write tests compatible with both versions.
34

  
35
    XXX: remove when django 2 compat isn't necessary.
36
    """
37

  
38
    def inner(s):
39
        if django.VERSION[0] == 2:
40
            return s.replace(''', ''')
41
        if django.VERSION[0] == 3:
42
            return s.replace(''', ''')
43
        return s
44

  
45
    return inner
tests/manager/test_all.py
797 797
    assert agenda.kind == 'meetings'
798 798

  
799 799

  
800
def test_agenda_day_view(app, admin_user, manager_user, api_user):
800
def test_agenda_day_view(app, admin_user, manager_user, api_user, get_proper_html_str):
801 801
    agenda = Agenda.objects.create(label='New Example', kind='meetings')
802 802
    desk = Desk.objects.create(agenda=agenda, label='New Desk')
803 803
    desk.save()
......
847 847
    assert resp.text.count('div class="booking') == 2
848 848
    assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
849 849
    assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"
850
    assert 'foo - bar's' in resp
850
    assert get_proper_html_str('foo - bar's') in resp
851 851
    assert 'hourspan-2' in resp.text  # table CSS class
852 852
    assert 'height: 50%; top: 0%;' in resp.text  # booking cells
853 853

  
......
856 856
    resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (agenda.id, date.year, date.month, date.day))
857 857
    assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
858 858
    assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Foo Bar"
859
    assert '&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar' in resp
859
    assert get_proper_html_str('&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar') in resp
860 860

  
861 861
    # create a shorter meeting type, this will change the table CSS class
862 862
    # (and visually this will give more room for events)
......
1351 1351
    app.get('/manage/agendas/%s/events/open/' % agenda.pk, status=404)
1352 1352

  
1353 1353

  
1354
def test_agenda_month_view(app, admin_user, manager_user, api_user):
1354
def test_agenda_month_view(app, admin_user, manager_user, api_user, get_proper_html_str):
1355 1355
    agenda = Agenda.objects.create(label='Passeports', kind='meetings')
1356 1356
    desk = Desk.objects.create(agenda=agenda, label='Desk A')
1357 1357
    today = datetime.date.today()
......
1406 1406
    assert resp.text.count('<div class="booking" style="left:1.0%;height:33.0%;') == 2  # booking cells
1407 1407
    assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
1408 1408
    assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo book - bar's"
1409
    assert 'foo book - bar&#39;s' in resp
1409
    assert get_proper_html_str('foo book - bar&#39;s') in resp
1410 1410
    assert len(resp.pyquery.find('span.desk')) == 0
1411 1411

  
1412 1412
    agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
......
1414 1414
    resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month))
1415 1415
    assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
1416 1416
    assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Foo Bar"
1417
    assert '&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar' in resp
1417
    assert get_proper_html_str('&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar') in resp
1418 1418

  
1419 1419
    desk = Desk.objects.create(agenda=agenda, label='Desk B')
1420 1420
    resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month))
......
1854 1854
    assert agenda.maximal_booking_delay is None
1855 1855

  
1856 1856

  
1857
def test_virtual_agenda_day_view(app, admin_user, manager_user):
1857
def test_virtual_agenda_day_view(app, admin_user, manager_user, get_proper_html_str):
1858 1858
    agenda = Agenda.objects.create(label='Virtual', kind='virtual')
1859 1859
    real_agenda_1 = Agenda.objects.create(label='Real 1', kind='meetings')
1860 1860
    real_agenda_2 = Agenda.objects.create(label='Real 2', kind='meetings')
......
1915 1915
    assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"
1916 1916
    assert resp.pyquery.find('div.booking a').not_('.cancel')[2].text.strip() == 'booked'
1917 1917
    assert resp.pyquery.find('div.booking a').not_('.cancel')[3].text.strip() == "foo - bar's"
1918
    assert 'foo - bar&#39;s' in resp
1918
    assert get_proper_html_str('foo - bar&#39;s') in resp
1919 1919
    assert 'hourspan-2' in resp.text  # table CSS class
1920 1920
    assert 'height: 50%; top: 0%;' in resp.text  # booking cells
1921 1921

  
......
1928 1928
    assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Bar Foo"
1929 1929
    assert resp.pyquery.find('div.booking a').not_('.cancel')[2].text.strip() == '<b></b> Foo Bar'
1930 1930
    assert resp.pyquery.find('div.booking a').not_('.cancel')[3].text.strip() == "<b>bar's</b> Bar Foo"
1931
    assert '&lt;b&gt;bar&#39;s&lt;/b&gt; Bar Foo' in resp
1931
    assert get_proper_html_str('&lt;b&gt;bar&#39;s&lt;/b&gt; Bar Foo') in resp
1932 1932

  
1933 1933
    # create a shorter meeting type, this will change the table CSS class
1934 1934
    # (and visually this will give more room for events)
......
1997 1997
    assert 'exceptions-hours' not in resp.text
1998 1998

  
1999 1999

  
2000
def test_virtual_agenda_month_view(app, admin_user):
2000
def test_virtual_agenda_month_view(app, admin_user, get_proper_html_str):
2001 2001
    agenda = Agenda.objects.create(label='Virtual', kind='virtual')
2002 2002
    real_agenda_1 = Agenda.objects.create(label='Real 1', kind='meetings')
2003 2003
    real_agenda_2 = Agenda.objects.create(label='Real 2', kind='meetings')
......
2067 2067
    assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"
2068 2068
    assert resp.pyquery.find('div.booking a').not_('.cancel')[2].text.strip() == 'booked'
2069 2069
    assert resp.pyquery.find('div.booking a').not_('.cancel')[3].text.strip() == "foo - bar's"
2070
    assert 'foo - bar&#39;s' in resp
2070
    assert get_proper_html_str('foo - bar&#39;s') in resp
2071 2071

  
2072 2072
    real_agenda_1.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
2073 2073
    real_agenda_1.save()
......
2078 2078
    assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Bar Foo"
2079 2079
    assert resp.pyquery.find('div.booking a').not_('.cancel')[2].text.strip() == '<b></b> Foo Bar'
2080 2080
    assert resp.pyquery.find('div.booking a').not_('.cancel')[3].text.strip() == "<b>bar's</b> Bar Foo"
2081
    assert '&lt;b&gt;bar&#39;s&lt;/b&gt; Bar Foo' in resp
2081
    assert get_proper_html_str('&lt;b&gt;bar&#39;s&lt;/b&gt; Bar Foo') in resp
2082 2082

  
2083 2083
    # cancel a booking
2084 2084
    booking = Booking.objects.first()
......
2388 2388
    assert mt.label == 'MTT'
2389 2389

  
2390 2390

  
2391
def test_cant_add_meetingtype_if_virtual_agenda(app, admin_user):
2391
def test_cant_add_meetingtype_if_virtual_agenda(app, admin_user, get_proper_html_str):
2392 2392
    agenda = Agenda.objects.create(label='My Virtual agenda', kind='virtual')
2393 2393
    meeting_agenda_1 = Agenda.objects.create(label='Meeting agenda 1', kind='meetings')
2394 2394
    MeetingType.objects.create(agenda=meeting_agenda_1, label='MT', slug='mt', duration=10)
......
2412 2412
    resp.form['duration'].value = '12'
2413 2413
    resp.form['label'].value = 'Oho'
2414 2414
    resp = resp.form.submit()
2415
    assert 'Can&#39;t add a meetingtype to an agenda that is included in a virtual agenda.' in resp.text
2415
    assert (
2416
        get_proper_html_str('Can&#39;t add a meetingtype to an agenda that is included in a virtual agenda.')
2417
        in resp.text
2418
    )
2416 2419
    assert MeetingType.objects.filter(agenda=meeting_agenda_1).count() == 1
2417 2420

  
2418 2421

  
tests/manager/test_event.py
1136 1136

  
1137 1137

  
1138 1138
@pytest.mark.freeze_time('2022-05-24')
1139
def test_event_detail(app, admin_user):
1139
def test_event_detail(app, admin_user, get_proper_html_str):
1140 1140
    agenda = Agenda.objects.create(label='Events', kind='events')
1141 1141
    event = Event.objects.create(
1142 1142
        label='xyz',
......
1151 1151
    login(app)
1152 1152
    resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
1153 1153
    assert 'Bookings (1/10)' in resp.text
1154
    assert 'User&#39;s 1, May 24, 2022, 2 a.m.' in resp.text
1154
    assert get_proper_html_str('User&#39;s 1, May 24, 2022, 2 a.m.') in resp.text
1155 1155
    assert 'Waiting List (1/2): 1 remaining place' in resp.text
1156 1156
    assert 'User 2, May 24, 2022, 2 a.m.' in resp.text
1157 1157

  
......
1159 1159
    agenda.save()
1160 1160
    resp = app.get('/manage/agendas/%d/events/%d/' % (agenda.pk, event.pk))
1161 1161
    assert 'Bookings (1/10)' in resp.text
1162
    assert '&lt;b&gt;User&#39;s 1&lt;/b&gt; Foo Bar, May 24, 2022, 2 a.m.' in resp.text
1162
    assert get_proper_html_str('&lt;b&gt;User&#39;s 1&lt;/b&gt; Foo Bar, May 24, 2022, 2 a.m.') in resp.text
1163 1163
    assert 'Waiting List (1/2): 1 remaining place' in resp.text
1164 1164
    assert '&lt;b&gt;User 2&lt;/b&gt; Foo Bar, May 24, 2022, 2 a.m.' in resp.text
1165 1165

  
......
1402 1402
    assert secondary.cancellation_datetime
1403 1403

  
1404 1404

  
1405
def test_event_check(app, admin_user):
1405
def test_event_check(app, admin_user, get_proper_html_str):
1406 1406
    agenda = Agenda.objects.create(label='Events', kind='events')
1407 1407
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
1408 1408
    agenda2 = Agenda.objects.create(label='Events', kind='events')
......
1476 1476
    resp = resp.click('Check')
1477 1477
    assert (
1478 1478
        resp.text.index('Bookings (6/10)')
1479
        < resp.text.index("User&#39;s 01")
1479
        < resp.text.index(get_proper_html_str("User&#39;s 01"))
1480 1480
        < resp.text.index('User 05')
1481 1481
        < resp.text.index('User 17')
1482 1482
        < resp.text.index('User 35')
......
1538 1538
    resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
1539 1539
    assert (
1540 1540
        resp.text.index('Bookings (6/10)')
1541
        < resp.text.index("User&#39;s 01")
1541
        < resp.text.index(get_proper_html_str("User&#39;s 01"))
1542 1542
        < resp.text.index('User 05')
1543 1543
        < resp.text.index('User 12 Cancelled')
1544 1544
        < resp.text.index('Subscription 14')
......
1559 1559
    agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
1560 1560
    agenda.save()
1561 1561
    resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
1562
    assert '&lt;b&gt;User&#39;s 01&lt;/b&gt; Foo Bar' in resp
1562
    assert get_proper_html_str('&lt;b&gt;User&#39;s 01&lt;/b&gt; Foo Bar') in resp
1563 1563
    assert '&lt;b&gt;Subscription 14&lt;/b&gt; Foo Bar' in resp
1564 1564
    assert '&lt;b&gt;User Waiting&lt;/b&gt; Foo Bar' in resp
1565 1565

  
tests/manager/test_exception.py
562 562
        assert '/manage/time-period-exceptions/%d/edit' % future_exception.pk not in resp.text
563 563

  
564 564

  
565
def test_agenda_import_time_period_exception_from_ics(app, admin_user):
565
def test_agenda_import_time_period_exception_from_ics(app, admin_user, get_proper_html_str):
566 566
    agenda = Agenda.objects.create(label='Example', kind='meetings')
567 567
    desk = Desk.objects.create(agenda=agenda, label='Test Desk')
568 568
    desk.duplicate()
......
588 588
END:VCALENDAR"""
589 589
    resp.form['ics_file'] = Upload('exceptions.ics', ics_with_no_start_date, 'text/calendar')
590 590
    resp = resp.form.submit(status=200)
591
    assert 'Event &quot;New Year&#39;s Eve&quot; has no start date.' in resp.text
591
    assert get_proper_html_str('Event &quot;New Year&#39;s Eve&quot; has no start date.') in resp.text
592 592
    assert TimePeriodExceptionSource.objects.filter(desk=desk).count() == 0
593 593
    ics_with_no_events = b"""BEGIN:VCALENDAR
594 594
VERSION:2.0
......
596 596
END:VCALENDAR"""
597 597
    resp.form['ics_file'] = Upload('exceptions.ics', ics_with_no_events, 'text/calendar')
598 598
    resp = resp.form.submit(status=200)
599
    assert "The file doesn&#39;t contain any events." in resp.text
599
    assert get_proper_html_str("The file doesn&#39;t contain any events.") in resp.text
600 600
    assert TimePeriodExceptionSource.objects.filter(desk=desk).count() == 0
601 601

  
602 602
    ics_with_exceptions = b"""BEGIN:VCALENDAR
tests/manager/test_resource.py
66 66
    app.get('/manage/resource/%s/' % resource.pk, status=403)
67 67

  
68 68

  
69
def test_resource_day_view(app, admin_user):
69
def test_resource_day_view(app, admin_user, get_proper_html_str):
70 70
    today = datetime.date.today()
71 71
    resource = Resource.objects.create(label='Foo bar')
72 72
    agenda = Agenda.objects.create(label='Agenda', kind='meetings')
......
113 113
    assert resp.text.count('div class="booking') == 2
114 114
    assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
115 115
    assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"
116
    assert 'foo - bar&#39;s' in resp
116
    assert get_proper_html_str('foo - bar&#39;s') in resp
117 117
    assert 'hourspan-2' in resp.text  # table CSS class
118 118
    assert 'height: 50%; top: 0%;' in resp.text  # booking cells
119 119
    assert 'height: 50%; top: 50%;' in resp.text  # booking cells
......
123 123
    resp = app.get('/manage/resource/%s/%d/%d/%d/' % (resource.pk, today.year, today.month, today.day))
124 124
    assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
125 125
    assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Foo Bar"
126
    assert '&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar' in resp
126
    assert get_proper_html_str('&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar') in resp
127 127

  
128 128
    # create a shorter meeting type, this will change the table CSS class
129 129
    # (and visually this will give more room for events)
......
221 221

  
222 222

  
223 223
@freezegun.freeze_time('2020-06-15')
224
def test_resource_month_view(app, admin_user):
224
def test_resource_month_view(app, admin_user, get_proper_html_str):
225 225
    resource = Resource.objects.create(label='Foo bar')
226 226
    agenda = Agenda.objects.create(label='Agenda', kind='meetings')
227 227
    agenda.resources.add(resource)
......
265 265
    assert resp.text.count('<div class="booking" style="height:33.0%;') == 2  # booking cells
266 266
    assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == 'booked'
267 267
    assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "foo - bar's"
268
    assert 'foo - bar&#39;s' in resp
268
    assert get_proper_html_str('foo - bar&#39;s') in resp
269 269

  
270 270
    agenda.booking_user_block_template = '<b>{{ booking.user_name }}</b> Foo Bar'
271 271
    agenda.save()
272 272
    resp = app.get('/manage/resource/%s/%s/%s/' % (resource.pk, today.year, today.month))
273 273
    assert resp.pyquery.find('div.booking a').not_('.cancel')[0].text.strip() == '<b></b> Foo Bar'
274 274
    assert resp.pyquery.find('div.booking a').not_('.cancel')[1].text.strip() == "<b>bar's</b> Foo Bar"
275
    assert '&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar' in resp
275
    assert get_proper_html_str('&lt;b&gt;bar&#39;s&lt;/b&gt; Foo Bar') in resp
276 276

  
277 277
    # cancel booking
278 278
    booking = Booking.objects.first()
279
-