|
1 | 1 | import datetime |
| 2 | +import re |
2 | 3 |
|
3 | 4 | from django.template.loader import render_to_string |
4 | 5 | from django.test import TestCase |
| 6 | +from django.utils import timezone |
5 | 7 |
|
6 | | -from apps.events.models import Event, Calendar |
| 8 | +from apps.events.models import Calendar, Event |
7 | 9 |
|
8 | 10 |
|
9 | 11 | class TimeTagTemplateTests(TestCase): |
10 | 12 | def test_single_day_event_year_rendering(self): |
11 | 13 | """ |
12 | 14 | Verify that a single-day event does not render the year twice (Issue #2626). |
13 | 15 | """ |
14 | | - # Create a single day event in the future to trigger the year rendering condition |
15 | | - future_year = datetime.date.today().year + 1 |
16 | | - |
| 16 | + future_year = timezone.now().date().year + 1 |
| 17 | + |
17 | 18 | calendar = Calendar.objects.create( |
18 | 19 | name="Test Calendar", |
19 | 20 | slug="test-calendar-time-tag-single-day-event", |
20 | 21 | ) |
21 | | - |
22 | 22 | event = Event.objects.create( |
23 | 23 | title="Single Day Future Event", |
24 | 24 | description="Test event", |
25 | 25 | calendar=calendar, |
26 | 26 | ) |
27 | | - |
28 | | - # Manually create the next_time context object |
| 27 | + |
| 28 | + # Use timezone-aware datetimes to match the project's USE_TZ = True setting. |
29 | 29 | class MockTime: |
30 | 30 | def __init__(self): |
31 | | - self.dt_start = datetime.datetime(future_year, 5, 25, 12, 0) |
32 | | - self.dt_end = datetime.datetime(future_year, 5, 25, 14, 0) |
| 31 | + self.dt_start = datetime.datetime( |
| 32 | + future_year, 5, 25, 12, 0, tzinfo=datetime.UTC |
| 33 | + ) |
| 34 | + self.dt_end = datetime.datetime( |
| 35 | + future_year, 5, 25, 14, 0, tzinfo=datetime.UTC |
| 36 | + ) |
33 | 37 | self.single_day = True |
34 | 38 | self.all_day = False |
35 | 39 | self.valid_dt_end = True |
36 | 40 |
|
37 | 41 | context = { |
38 | 42 | "next_time": MockTime(), |
39 | | - "scheduled_start_this_year": False, # Event is in the future year |
| 43 | + "scheduled_start_this_year": False, |
40 | 44 | "scheduled_end_this_year": False, |
41 | 45 | "object": event, |
42 | 46 | } |
43 | 47 |
|
44 | 48 | rendered = render_to_string("events/includes/time_tag.html", context) |
45 | | - |
46 | | - # The year should only appear visibly once in the output (not counting the datetime ISO tag). |
| 49 | + |
| 50 | + # Count visible year occurrences inside <span> tags using a regex. |
| 51 | + # This avoids brittle exact-whitespace matching and ignores the |
| 52 | + # year inside the <time datetime="..."> ISO attribute. |
47 | 53 | year_str = str(future_year) |
48 | | - # Using string splitting to exclude the `datetime="2027...` occurrence by checking how many times |
49 | | - # it appears wrapped with whitespace or inside a span. |
50 | | - visible_occurrences = rendered.split(">\n " + year_str + "\n </span>") |
| 54 | + year_in_span = re.findall( |
| 55 | + r"<span[^>]*>\s*" + re.escape(year_str) + r"\s*</span>", rendered |
| 56 | + ) |
51 | 57 | self.assertEqual( |
52 | | - len(visible_occurrences) - 1, |
| 58 | + len(year_in_span), |
53 | 59 | 1, |
54 | | - f"Expected the visible span containing {year_str} to appear exactly once, but it was duplicated: {rendered}" |
| 60 | + f"Expected the year {year_str} to appear in exactly one <span>, " |
| 61 | + f"but found {len(year_in_span)}: {rendered}", |
55 | 62 | ) |
0 commit comments