dnscms: add more tests
This commit is contained in:
@@ -5,10 +5,35 @@ import pytest
|
|||||||
import wagtail_factories
|
import wagtail_factories
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
|
|
||||||
|
from associations.models import AssociationIndex, AssociationPage
|
||||||
from events.models import EventIndex, EventPage
|
from events.models import EventIndex, EventPage
|
||||||
|
from generic.models import GenericPage
|
||||||
|
from images.models import CustomImage
|
||||||
|
from news.models import NewsIndex, NewsPage
|
||||||
from venues.models import VenueIndex, VenuePage
|
from venues.models import VenueIndex, VenuePage
|
||||||
|
|
||||||
|
|
||||||
|
class CustomImageFactory(wagtail_factories.ImageFactory):
|
||||||
|
class Meta:
|
||||||
|
model = CustomImage
|
||||||
|
|
||||||
|
|
||||||
|
class AssociationIndexFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Associations {n}")
|
||||||
|
lead = "<p>Foreninger og utvalg.</p>"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = AssociationIndex
|
||||||
|
|
||||||
|
|
||||||
|
class AssociationPageFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Association {n}")
|
||||||
|
excerpt = "Et utdrag."
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = AssociationPage
|
||||||
|
|
||||||
|
|
||||||
class EventIndexFactory(wagtail_factories.PageFactory):
|
class EventIndexFactory(wagtail_factories.PageFactory):
|
||||||
title = factory.Sequence(lambda n: f"Events {n}")
|
title = factory.Sequence(lambda n: f"Events {n}")
|
||||||
|
|
||||||
@@ -23,6 +48,29 @@ class EventPageFactory(wagtail_factories.PageFactory):
|
|||||||
model = EventPage
|
model = EventPage
|
||||||
|
|
||||||
|
|
||||||
|
class GenericPageFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Page {n}")
|
||||||
|
lead = "<p>Ingress.</p>"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = GenericPage
|
||||||
|
|
||||||
|
|
||||||
|
class NewsIndexFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"News {n}")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = NewsIndex
|
||||||
|
|
||||||
|
|
||||||
|
class NewsPageFactory(wagtail_factories.PageFactory):
|
||||||
|
title = factory.Sequence(lambda n: f"Article {n}")
|
||||||
|
excerpt = "Et utdrag."
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = NewsPage
|
||||||
|
|
||||||
|
|
||||||
class VenueIndexFactory(wagtail_factories.PageFactory):
|
class VenueIndexFactory(wagtail_factories.PageFactory):
|
||||||
title = factory.Sequence(lambda n: f"Venues {n}")
|
title = factory.Sequence(lambda n: f"Venues {n}")
|
||||||
|
|
||||||
@@ -56,6 +104,16 @@ def event_index(home_page):
|
|||||||
return EventIndexFactory(parent=home_page)
|
return EventIndexFactory(parent=home_page)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def news_index(home_page):
|
||||||
|
return NewsIndexFactory(parent=home_page)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def association_index(home_page):
|
||||||
|
return AssociationIndexFactory(parent=home_page)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def venue(home_page):
|
def venue(home_page):
|
||||||
venue_index = VenueIndexFactory(parent=home_page)
|
venue_index = VenueIndexFactory(parent=home_page)
|
||||||
|
|||||||
+319
-18
@@ -1,16 +1,21 @@
|
|||||||
from datetime import timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from events.models import (
|
from events.models import (
|
||||||
|
EventCategory,
|
||||||
EventOccurrence,
|
EventOccurrence,
|
||||||
EventOrganizer,
|
EventOrganizer,
|
||||||
EventOrganizerLink,
|
EventOrganizerLink,
|
||||||
EventPage,
|
EventPage,
|
||||||
)
|
)
|
||||||
from tests.conftest import EventPageFactory
|
from tests.conftest import (
|
||||||
|
AssociationPageFactory,
|
||||||
|
CustomImageFactory,
|
||||||
|
EventPageFactory,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_eventpage_clean_unsets_specific_pricing_when_free():
|
def test_eventpage_clean_unsets_specific_pricing_when_free():
|
||||||
@@ -30,6 +35,58 @@ def test_eventpage_clean_unsets_specific_pricing_when_free():
|
|||||||
assert page.price_member == ""
|
assert page.price_member == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_eventpage_clean_keeps_specific_pricing_when_not_free():
|
||||||
|
page = EventPage(
|
||||||
|
title="Paid event",
|
||||||
|
slug="paid-event",
|
||||||
|
free=False,
|
||||||
|
price_regular="100",
|
||||||
|
price_student="50",
|
||||||
|
price_member="25",
|
||||||
|
)
|
||||||
|
|
||||||
|
page.clean()
|
||||||
|
|
||||||
|
assert page.price_regular == "100"
|
||||||
|
assert page.price_student == "50"
|
||||||
|
assert page.price_member == "25"
|
||||||
|
|
||||||
|
|
||||||
|
def test_eventpage_clean_dedupes_organizers_by_name(event_index):
|
||||||
|
org_a = EventOrganizer.objects.create(name="DNS", slug="dns-a")
|
||||||
|
org_b = EventOrganizer.objects.create(name="DNS", slug="dns-b")
|
||||||
|
|
||||||
|
event = EventPageFactory(parent=event_index)
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=org_a)
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=org_b)
|
||||||
|
|
||||||
|
event = EventPage.objects.get(pk=event.pk)
|
||||||
|
assert event.organizer_links.count() == 2
|
||||||
|
|
||||||
|
event.clean()
|
||||||
|
|
||||||
|
assert event.organizer_links.count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_eventpage_clean_dedupes_three_duplicates_and_keeps_distinct(event_index):
|
||||||
|
dup_1 = EventOrganizer.objects.create(name="DNS", slug="dns-1")
|
||||||
|
dup_2 = EventOrganizer.objects.create(name="DNS", slug="dns-2")
|
||||||
|
dup_3 = EventOrganizer.objects.create(name="DNS", slug="dns-3")
|
||||||
|
distinct = EventOrganizer.objects.create(name="Studentersamfundet", slug="ss")
|
||||||
|
|
||||||
|
event = EventPageFactory(parent=event_index)
|
||||||
|
for organizer in (dup_1, dup_2, dup_3, distinct):
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=organizer)
|
||||||
|
|
||||||
|
event = EventPage.objects.get(pk=event.pk)
|
||||||
|
assert event.organizer_links.count() == 4
|
||||||
|
|
||||||
|
event.clean()
|
||||||
|
|
||||||
|
names = sorted(link.organizer.name for link in event.organizer_links.all())
|
||||||
|
assert names == ["DNS", "Studentersamfundet"]
|
||||||
|
|
||||||
|
|
||||||
def test_eventoccurrence_clean_rejects_both_venue_and_venue_custom(event_index, venue):
|
def test_eventoccurrence_clean_rejects_both_venue_and_venue_custom(event_index, venue):
|
||||||
event = EventPageFactory(parent=event_index)
|
event = EventPageFactory(parent=event_index)
|
||||||
occurrence = EventOccurrence(
|
occurrence = EventOccurrence(
|
||||||
@@ -57,14 +114,10 @@ def test_eventpage_manager_future_filters_past_and_annotates(event_index):
|
|||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
|
|
||||||
past = EventPageFactory(parent=event_index, title="Past")
|
past = EventPageFactory(parent=event_index, title="Past")
|
||||||
EventOccurrence.objects.create(
|
EventOccurrence.objects.create(event=past, start=now - timedelta(days=7), venue_custom="Old")
|
||||||
event=past, start=now - timedelta(days=7), venue_custom="Old"
|
|
||||||
)
|
|
||||||
|
|
||||||
future = EventPageFactory(parent=event_index, title="Future")
|
future = EventPageFactory(parent=event_index, title="Future")
|
||||||
EventOccurrence.objects.create(
|
EventOccurrence.objects.create(event=future, start=now + timedelta(days=7), venue_custom="New")
|
||||||
event=future, start=now + timedelta(days=7), venue_custom="New"
|
|
||||||
)
|
|
||||||
|
|
||||||
results = list(EventPage.objects.live().future().order_by("next_occurrence"))
|
results = list(EventPage.objects.live().future().order_by("next_occurrence"))
|
||||||
|
|
||||||
@@ -72,17 +125,265 @@ def test_eventpage_manager_future_filters_past_and_annotates(event_index):
|
|||||||
assert results[0].next_occurrence is not None
|
assert results[0].next_occurrence is not None
|
||||||
|
|
||||||
|
|
||||||
def test_eventpage_clean_dedupes_organizers_by_name(event_index):
|
def test_future_includes_occurrence_late_today(event_index):
|
||||||
org_a = EventOrganizer.objects.create(name="DNS", slug="dns-a")
|
today_start = timezone.localtime(timezone.now()).replace(
|
||||||
org_b = EventOrganizer.objects.create(name="DNS", slug="dns-b")
|
hour=0, minute=0, second=0, microsecond=0
|
||||||
|
)
|
||||||
|
late_today = today_start + timedelta(hours=23, minutes=59)
|
||||||
|
|
||||||
event = EventPageFactory(parent=event_index)
|
event = EventPageFactory(parent=event_index, title="Late today")
|
||||||
EventOrganizerLink.objects.create(event=event, organizer=org_a)
|
EventOccurrence.objects.create(event=event, start=late_today, venue_custom="X")
|
||||||
EventOrganizerLink.objects.create(event=event, organizer=org_b)
|
|
||||||
|
|
||||||
event = EventPage.objects.get(pk=event.pk)
|
assert event.pk in EventPage.objects.future().values_list("pk", flat=True)
|
||||||
assert event.organizer_links.count() == 2
|
|
||||||
|
|
||||||
event.clean()
|
|
||||||
|
|
||||||
assert event.organizer_links.count() == 1
|
def test_future_excludes_occurrence_just_before_today(event_index):
|
||||||
|
today_start = timezone.localtime(timezone.now()).replace(
|
||||||
|
hour=0, minute=0, second=0, microsecond=0
|
||||||
|
)
|
||||||
|
just_before_today = today_start - timedelta(seconds=1)
|
||||||
|
|
||||||
|
event = EventPageFactory(parent=event_index, title="Just past")
|
||||||
|
EventOccurrence.objects.create(event=event, start=just_before_today, venue_custom="X")
|
||||||
|
|
||||||
|
assert event.pk not in EventPage.objects.future().values_list("pk", flat=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_future_next_occurrence_picks_earliest_future_ignoring_past(event_index):
|
||||||
|
now = timezone.now()
|
||||||
|
soonest_future = now + timedelta(days=3)
|
||||||
|
|
||||||
|
event = EventPageFactory(parent=event_index, title="With history")
|
||||||
|
EventOccurrence.objects.create(event=event, start=now - timedelta(days=30), venue_custom="X")
|
||||||
|
EventOccurrence.objects.create(event=event, start=soonest_future, venue_custom="X")
|
||||||
|
EventOccurrence.objects.create(event=event, start=now + timedelta(days=10), venue_custom="X")
|
||||||
|
|
||||||
|
annotated = EventPage.objects.future().filter(pk=event.pk).first()
|
||||||
|
assert annotated is not None
|
||||||
|
assert abs((annotated.next_occurrence - soonest_future).total_seconds()) < 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_event_index_future_events_query(event_index, graphql_post):
|
||||||
|
upcoming = EventPageFactory(parent=event_index, title="Upcoming gig")
|
||||||
|
EventOccurrence.objects.create(
|
||||||
|
event=upcoming,
|
||||||
|
start=timezone.now() + timedelta(days=3),
|
||||||
|
venue_custom="Storsalen",
|
||||||
|
)
|
||||||
|
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
eventIndex {
|
||||||
|
futureEvents { title }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
titles = [e["title"] for e in body["data"]["eventIndex"]["futureEvents"]]
|
||||||
|
assert "Upcoming gig" in titles
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_event_index_future_events_ordered_by_next_occurrence(event_index, graphql_post):
|
||||||
|
now = timezone.now()
|
||||||
|
|
||||||
|
later = EventPageFactory(parent=event_index, title="Later gig")
|
||||||
|
EventOccurrence.objects.create(event=later, start=now + timedelta(days=10), venue_custom="X")
|
||||||
|
|
||||||
|
sooner = EventPageFactory(parent=event_index, title="Sooner gig")
|
||||||
|
EventOccurrence.objects.create(event=sooner, start=now + timedelta(days=3), venue_custom="X")
|
||||||
|
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
eventIndex {
|
||||||
|
futureEvents { title }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
titles = [e["title"] for e in body["data"]["eventIndex"]["futureEvents"]]
|
||||||
|
assert titles.index("Sooner gig") < titles.index("Later gig")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def comprehensive_event(event_index, venue, association_index):
|
||||||
|
"""A fully-populated paid EventPage exercising every field exposed via GraphQL."""
|
||||||
|
image = CustomImageFactory(
|
||||||
|
title="Cover",
|
||||||
|
alt="Et fotografi av en gris med solbriller",
|
||||||
|
attribution="Foto: Test",
|
||||||
|
)
|
||||||
|
|
||||||
|
konsert = EventCategory.objects.create(
|
||||||
|
name="Konsert", slug="konsert", show_in_filters=True, pig="pigHeadLogo"
|
||||||
|
)
|
||||||
|
klubb = EventCategory.objects.create(name="Klubb", slug="klubb")
|
||||||
|
|
||||||
|
association = AssociationPageFactory(
|
||||||
|
parent=association_index,
|
||||||
|
title="Internal",
|
||||||
|
association_type="forening",
|
||||||
|
)
|
||||||
|
internal_org = EventOrganizer.objects.create(
|
||||||
|
name="Internal", slug="internal", association=association
|
||||||
|
)
|
||||||
|
external_org = EventOrganizer.objects.create(
|
||||||
|
name="External",
|
||||||
|
slug="external",
|
||||||
|
external_url="https://external.example.com",
|
||||||
|
)
|
||||||
|
|
||||||
|
event = EventPageFactory(
|
||||||
|
parent=event_index,
|
||||||
|
title="Et arrangement",
|
||||||
|
slug="et-arrangement",
|
||||||
|
subtitle="En undertekst",
|
||||||
|
lead="<p>Ingress.</p>",
|
||||||
|
body=[("paragraph", "<p>Body content.</p>")],
|
||||||
|
pig="automatic",
|
||||||
|
free=False,
|
||||||
|
price_regular="150",
|
||||||
|
price_student="100",
|
||||||
|
price_member="75",
|
||||||
|
ticket_url="https://example.com/tickets",
|
||||||
|
facebook_url="https://facebook.com/example",
|
||||||
|
featured_image=image,
|
||||||
|
)
|
||||||
|
event.categories.add(konsert, klubb)
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=internal_org)
|
||||||
|
EventOrganizerLink.objects.create(event=event, organizer=external_org)
|
||||||
|
|
||||||
|
now = timezone.now()
|
||||||
|
EventOccurrence.objects.create(
|
||||||
|
event=event,
|
||||||
|
start=now + timedelta(days=5),
|
||||||
|
end=now + timedelta(days=5, hours=3),
|
||||||
|
venue=venue,
|
||||||
|
)
|
||||||
|
EventOccurrence.objects.create(
|
||||||
|
event=event,
|
||||||
|
start=now + timedelta(days=12),
|
||||||
|
end=now + timedelta(days=12, hours=2),
|
||||||
|
venue_custom="Frederikkeplassen",
|
||||||
|
)
|
||||||
|
|
||||||
|
event.save()
|
||||||
|
return event
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_event_index_returns_all_fields_for_comprehensive_event(
|
||||||
|
comprehensive_event, graphql_post
|
||||||
|
):
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
eventIndex {
|
||||||
|
futureEvents {
|
||||||
|
title
|
||||||
|
slug
|
||||||
|
subtitle
|
||||||
|
lead
|
||||||
|
body {
|
||||||
|
blockType
|
||||||
|
field
|
||||||
|
... on RichTextBlock {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pig
|
||||||
|
free
|
||||||
|
priceRegular
|
||||||
|
priceStudent
|
||||||
|
priceMember
|
||||||
|
ticketUrl
|
||||||
|
facebookUrl
|
||||||
|
featuredImage {
|
||||||
|
alt
|
||||||
|
attribution
|
||||||
|
}
|
||||||
|
categories {
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
showInFilters
|
||||||
|
pig
|
||||||
|
}
|
||||||
|
organizers {
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
externalUrl
|
||||||
|
association {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
occurrences {
|
||||||
|
start
|
||||||
|
end
|
||||||
|
venueCustom
|
||||||
|
venue {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
|
||||||
|
events = body["data"]["eventIndex"]["futureEvents"]
|
||||||
|
event = next(e for e in events if e["title"] == "Et arrangement")
|
||||||
|
|
||||||
|
assert event["slug"] == "et-arrangement"
|
||||||
|
assert event["subtitle"] == "En undertekst"
|
||||||
|
assert "Ingress." in event["lead"]
|
||||||
|
assert event["pig"] == "automatic"
|
||||||
|
assert event["free"] is False
|
||||||
|
assert event["priceRegular"] == "150"
|
||||||
|
assert event["priceStudent"] == "100"
|
||||||
|
assert event["priceMember"] == "75"
|
||||||
|
assert event["ticketUrl"] == "https://example.com/tickets"
|
||||||
|
assert event["facebookUrl"] == "https://facebook.com/example"
|
||||||
|
|
||||||
|
assert event["featuredImage"]["alt"] == "Et fotografi av en gris med solbriller"
|
||||||
|
assert event["featuredImage"]["attribution"] == "Foto: Test"
|
||||||
|
|
||||||
|
assert event["body"][0]["blockType"] == "RichTextBlock"
|
||||||
|
assert "Body content." in event["body"][0]["value"]
|
||||||
|
|
||||||
|
categories_by_name = {c["name"]: c for c in event["categories"]}
|
||||||
|
assert set(categories_by_name) == {"Konsert", "Klubb"}
|
||||||
|
assert categories_by_name["Konsert"]["slug"] == "konsert"
|
||||||
|
assert categories_by_name["Konsert"]["showInFilters"] is True
|
||||||
|
assert categories_by_name["Konsert"]["pig"] == "pigHeadLogo"
|
||||||
|
assert categories_by_name["Klubb"]["showInFilters"] is False
|
||||||
|
|
||||||
|
organizers_by_name = {o["name"]: o for o in event["organizers"]}
|
||||||
|
assert set(organizers_by_name) == {"Internal", "External"}
|
||||||
|
assert organizers_by_name["Internal"]["association"]["title"] == "Internal"
|
||||||
|
assert organizers_by_name["Internal"]["externalUrl"] == ""
|
||||||
|
assert organizers_by_name["External"]["association"] is None
|
||||||
|
assert organizers_by_name["External"]["externalUrl"] == "https://external.example.com"
|
||||||
|
|
||||||
|
assert len(event["occurrences"]) == 2
|
||||||
|
venue_occ = next(o for o in event["occurrences"] if o["venue"] is not None)
|
||||||
|
custom_occ = next(o for o in event["occurrences"] if o["venueCustom"])
|
||||||
|
assert venue_occ["venueCustom"] == ""
|
||||||
|
assert venue_occ["venue"]["title"]
|
||||||
|
assert custom_occ["venue"] is None
|
||||||
|
assert custom_occ["venueCustom"] == "Frederikkeplassen"
|
||||||
|
|
||||||
|
venue_occ_db = comprehensive_event.occurrences.exclude(venue=None).get()
|
||||||
|
custom_occ_db = comprehensive_event.occurrences.exclude(venue_custom="").get()
|
||||||
|
assert datetime.fromisoformat(venue_occ["start"]) == venue_occ_db.start
|
||||||
|
assert datetime.fromisoformat(venue_occ["end"]) == venue_occ_db.end
|
||||||
|
assert datetime.fromisoformat(custom_occ["start"]) == custom_occ_db.start
|
||||||
|
assert datetime.fromisoformat(custom_occ["end"]) == custom_occ_db.end
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
from generic.models import GenericPage
|
||||||
|
from tests.conftest import GenericPageFactory
|
||||||
|
|
||||||
|
|
||||||
|
def test_generic_page_persists_via_factory(home_page):
|
||||||
|
page = GenericPageFactory(
|
||||||
|
parent=home_page,
|
||||||
|
title="Om oss",
|
||||||
|
slug="om-oss",
|
||||||
|
lead="<p>Ingress.</p>",
|
||||||
|
body=[("paragraph", "<p>Body content.</p>")],
|
||||||
|
pig="drink",
|
||||||
|
)
|
||||||
|
|
||||||
|
reloaded = GenericPage.objects.get(pk=page.pk)
|
||||||
|
assert reloaded.title == "Om oss"
|
||||||
|
assert reloaded.slug == "om-oss"
|
||||||
|
assert "Ingress." in reloaded.lead
|
||||||
|
assert reloaded.pig == "drink"
|
||||||
|
assert reloaded.body[0].block_type == "paragraph"
|
||||||
|
|
||||||
|
|
||||||
|
def test_generic_page_allows_recursive_children(home_page):
|
||||||
|
parent = GenericPageFactory(parent=home_page, title="Parent", slug="parent")
|
||||||
|
child = GenericPageFactory(parent=parent, title="Child", slug="child")
|
||||||
|
|
||||||
|
assert child.get_parent().specific == parent
|
||||||
|
assert list(parent.get_children().specific()) == [child]
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_generic_page_query(home_page, graphql_post):
|
||||||
|
GenericPageFactory(
|
||||||
|
parent=home_page,
|
||||||
|
title="Om oss",
|
||||||
|
slug="om-oss",
|
||||||
|
lead="<p>Ingress text.</p>",
|
||||||
|
body=[("paragraph", "<p>Body content.</p>")],
|
||||||
|
pig="drink",
|
||||||
|
)
|
||||||
|
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
page(slug: "om-oss", contentType: "generic.GenericPage") {
|
||||||
|
title
|
||||||
|
slug
|
||||||
|
... on GenericPage {
|
||||||
|
lead
|
||||||
|
pig
|
||||||
|
body {
|
||||||
|
blockType
|
||||||
|
field
|
||||||
|
... on RichTextBlock {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
data = body["data"]["page"]
|
||||||
|
assert data["title"] == "Om oss"
|
||||||
|
assert data["slug"] == "om-oss"
|
||||||
|
assert "Ingress text." in data["lead"]
|
||||||
|
assert data["pig"] == "drink"
|
||||||
|
assert data["body"][0]["blockType"] == "RichTextBlock"
|
||||||
|
assert "Body content." in data["body"][0]["value"]
|
||||||
@@ -1,38 +1,6 @@
|
|||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
from events.models import EventOccurrence
|
|
||||||
from tests.conftest import EventPageFactory
|
|
||||||
|
|
||||||
|
|
||||||
def test_graphql_endpoint_responds(db, graphql_post):
|
def test_graphql_endpoint_responds(db, graphql_post):
|
||||||
response, body = graphql_post("{ __schema { queryType { name } } }")
|
response, body = graphql_post("{ __schema { queryType { name } } }")
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert "errors" not in body
|
assert "errors" not in body
|
||||||
assert body["data"]["__schema"]["queryType"]["name"] == "Query"
|
assert body["data"]["__schema"]["queryType"]["name"] == "Query"
|
||||||
|
|
||||||
|
|
||||||
def test_event_index_future_events_query(event_index, graphql_post):
|
|
||||||
upcoming = EventPageFactory(parent=event_index, title="Upcoming gig")
|
|
||||||
EventOccurrence.objects.create(
|
|
||||||
event=upcoming,
|
|
||||||
start=timezone.now() + timedelta(days=3),
|
|
||||||
venue_custom="Storsalen",
|
|
||||||
)
|
|
||||||
|
|
||||||
response, body = graphql_post(
|
|
||||||
"""
|
|
||||||
query {
|
|
||||||
eventIndex {
|
|
||||||
futureEvents { title }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert "errors" not in body, body
|
|
||||||
titles = [e["title"] for e in body["data"]["eventIndex"]["futureEvents"]]
|
|
||||||
assert "Upcoming gig" in titles
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
from news.models import NewsPage
|
||||||
|
from tests.conftest import NewsPageFactory
|
||||||
|
|
||||||
|
|
||||||
|
def test_news_page_persists_via_factory(news_index):
|
||||||
|
page = NewsPageFactory(parent=news_index, title="Big news", excerpt="Short summary")
|
||||||
|
|
||||||
|
reloaded = NewsPage.objects.get(pk=page.pk)
|
||||||
|
assert reloaded.title == "Big news"
|
||||||
|
assert reloaded.excerpt == "Short summary"
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_news_index_query(news_index, graphql_post):
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
newsIndex {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
assert body["data"]["newsIndex"]["title"] == news_index.title
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import datetime
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from openinghours.models import OpeningHoursItem, OpeningHoursSet
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def opening_hours_set(db):
|
||||||
|
return OpeningHoursSet.objects.create(
|
||||||
|
name="Vanlige åpningstider",
|
||||||
|
effective_from=datetime.date(2025, 1, 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_opening_hours_set_str_with_end_date():
|
||||||
|
ohs = OpeningHoursSet(
|
||||||
|
name="Sommer",
|
||||||
|
effective_from=datetime.date(2025, 6, 1),
|
||||||
|
effective_to=datetime.date(2025, 8, 31),
|
||||||
|
)
|
||||||
|
assert str(ohs) == "Sommer (2025-06-01 - 2025-08-31)"
|
||||||
|
|
||||||
|
|
||||||
|
def test_opening_hours_set_str_uses_infinity_when_open_ended():
|
||||||
|
ohs = OpeningHoursSet(
|
||||||
|
name="Forever",
|
||||||
|
effective_from=datetime.date(2025, 1, 1),
|
||||||
|
effective_to=None,
|
||||||
|
)
|
||||||
|
assert str(ohs) == "Forever (2025-01-01 - ∞)"
|
||||||
|
|
||||||
|
|
||||||
|
def test_opening_hours_streamfield_week_roundtrip(opening_hours_set):
|
||||||
|
OpeningHoursItem.objects.create(
|
||||||
|
opening_hours_set=opening_hours_set,
|
||||||
|
function="glassbaren",
|
||||||
|
week=[
|
||||||
|
(
|
||||||
|
"week",
|
||||||
|
{
|
||||||
|
"monday": {
|
||||||
|
"time_from": datetime.time(15, 0),
|
||||||
|
"time_to": datetime.time(23, 0),
|
||||||
|
"custom": "",
|
||||||
|
},
|
||||||
|
"tuesday": {"time_from": None, "time_to": None, "custom": "Stengt"},
|
||||||
|
"wednesday": {"time_from": None, "time_to": None, "custom": ""},
|
||||||
|
"thursday": {"time_from": None, "time_to": None, "custom": ""},
|
||||||
|
"friday": {"time_from": None, "time_to": None, "custom": ""},
|
||||||
|
"saturday": {"time_from": None, "time_to": None, "custom": ""},
|
||||||
|
"sunday": {"time_from": None, "time_to": None, "custom": ""},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
reloaded = OpeningHoursSet.objects.get(pk=opening_hours_set.pk)
|
||||||
|
item = reloaded.items.get()
|
||||||
|
assert item.function == "glassbaren"
|
||||||
|
|
||||||
|
week_block = item.week[0]
|
||||||
|
assert week_block.block_type == "week"
|
||||||
|
assert week_block.value["monday"]["time_from"] == datetime.time(15, 0)
|
||||||
|
assert week_block.value["monday"]["time_to"] == datetime.time(23, 0)
|
||||||
|
assert week_block.value["tuesday"]["custom"] == "Stengt"
|
||||||
|
|
||||||
|
|
||||||
|
def test_graphql_opening_hours_sets_query(db, graphql_post):
|
||||||
|
OpeningHoursSet.objects.create(
|
||||||
|
name="Sommer 2025",
|
||||||
|
effective_from=datetime.date(2025, 6, 1),
|
||||||
|
effective_to=datetime.date(2025, 8, 31),
|
||||||
|
)
|
||||||
|
|
||||||
|
response, body = graphql_post(
|
||||||
|
"""
|
||||||
|
query {
|
||||||
|
openingHoursSets {
|
||||||
|
name
|
||||||
|
effectiveFrom
|
||||||
|
effectiveTo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "errors" not in body, body
|
||||||
|
names = [s["name"] for s in body["data"]["openingHoursSets"]]
|
||||||
|
assert "Sommer 2025" in names
|
||||||
Reference in New Issue
Block a user