aboutsummaryrefslogtreecommitdiff
path: root/tests/playwright
diff options
context:
space:
mode:
Diffstat (limited to 'tests/playwright')
-rw-r--r--tests/playwright/conftest.py27
-rw-r--r--tests/playwright/test_basic.py34
-rw-r--r--tests/playwright/test_climbs.py67
-rw-r--r--tests/playwright/test_journeys.py170
-rw-r--r--tests/playwright/test_profiles.py2
-rw-r--r--tests/playwright/test_share.py3
-rw-r--r--tests/playwright/test_tileproxy.py4
-rw-r--r--tests/playwright/test_transformers.py57
8 files changed, 330 insertions, 34 deletions
diff --git a/tests/playwright/conftest.py b/tests/playwright/conftest.py
index 4e7f5a4..8338f0a 100644
--- a/tests/playwright/conftest.py
+++ b/tests/playwright/conftest.py
@@ -7,10 +7,12 @@ from wsgiref import simple_server
from pyramid.authentication import AuthTktCookieHelper
from pyramid.testing import DummyRequest
+import fietsboek.config
from testutils import load_gpx_asset
from fietsboek import models, util, actions
from fietsboek.models.track import Visibility, TrackType
from fietsboek.config import Config
+from fietsboek.views.tileproxy import TileRequester
import pytest
@@ -55,7 +57,11 @@ def dbaccess(app):
through and the running WSGI app cannot read them.
"""
session_factory = app.registry["dbsession_factory"]
- return session_factory()
+ factory = session_factory()
+
+ yield factory
+
+ factory.close()
class Helper:
@@ -104,7 +110,10 @@ class Helper:
)
def add_track(
- self, user: Optional[models.User] = None, track_name: str = "Teasi_1.gpx.gz"
+ self,
+ user: Optional[models.User] = None,
+ track_name: str = "Teasi_1.gpx.gz",
+ title: str = "Another awesome track",
) -> models.Track:
"""Add a track to the given user.
@@ -112,13 +121,16 @@ class Helper:
"""
if user is None:
user = self.john_doe()
+ config = fietsboek.config.parse(self.app_settings)
with self.dbaccess:
user = self.dbaccess.merge(user)
track = actions.add_track(
self.dbaccess,
self.data_manager,
+ TileRequester(None),
+ config.public_tile_layers()[0],
owner=user,
- title="Another awesome track",
+ title=title,
visibility=Visibility.PRIVATE,
description="Another description",
track_type=TrackType.ORGANIC,
@@ -131,6 +143,15 @@ class Helper:
)
self.dbaccess.commit()
self.dbaccess.refresh(track, ["id", "link_secret"])
+
+ actions.extract_climbs(
+ self.dbaccess,
+ track,
+ TileRequester(None),
+ config.public_tile_layers()[0],
+ None,
+ )
+
self.dbaccess.expunge(track)
return track
diff --git a/tests/playwright/test_basic.py b/tests/playwright/test_basic.py
index 231962e..19ef8ff 100644
--- a/tests/playwright/test_basic.py
+++ b/tests/playwright/test_basic.py
@@ -4,7 +4,7 @@ import pytest
from playwright.sync_api import Page, expect
from sqlalchemy import select
-from testutils import load_gpx_asset
+from testutils import asset_path, load_gpx_asset
from fietsboek import models
from fietsboek.models.track import Visibility
@@ -60,6 +60,10 @@ def test_upload(page: Page, playwright_helper, tmp_path, dbaccess):
page.get_by_role("button", name="Add Tag").click()
page.get_by_label("Description").fill("Beschreibung der tollen Tour")
+ page.locator("#imageSelector").set_input_files(
+ [asset_path("picture01.jpg"), asset_path("picture02.jpg")],
+ )
+
page.locator(".btn", has_text="Upload").click()
# Once we have finished the upload, extract the ID of the track and check
@@ -72,6 +76,7 @@ def test_upload(page: Page, playwright_helper, tmp_path, dbaccess):
assert track.visibility == Visibility.PUBLIC
assert track.text_tags() == {"Tolle Tour"}
assert track.description == "Beschreibung der tollen Tour"
+ assert len(track.images) == 2
def test_edit(page: Page, playwright_helper, dbaccess):
@@ -101,6 +106,33 @@ def test_edit(page: Page, playwright_helper, dbaccess):
assert track.description == "Not so descriptive anymore"
+def test_edit_change_gpx(page: Page, playwright_helper, tmp_path, dbaccess):
+ playwright_helper.login()
+ track_id = playwright_helper.add_track().id
+
+ track = dbaccess.execute(select(models.Track).filter_by(id=track_id)).scalar_one()
+ old_cache = track.cache.length, track.cache.uphill, track.cache.downhill
+
+ page.goto(f"/track/{track_id}")
+ page.locator(".btn", has_text="Edit").click()
+
+ gpx_data = load_gpx_asset("Synthetic_BRouter_1.gpx.gz")
+ gpx_path = tmp_path / "NewGPX.gpx"
+ with open(gpx_path, "wb") as gpx_fobj:
+ gpx_fobj.write(gpx_data)
+
+ page.get_by_label("New file for this track").set_input_files(gpx_path)
+ page.locator(".btn", has_text="Save").click()
+
+ track = dbaccess.execute(select(models.Track).filter_by(id=track_id)).scalar_one()
+ new_cache = track.cache
+ dbaccess.refresh(new_cache)
+
+ assert old_cache[0] != new_cache.length
+ assert old_cache[1] != new_cache.uphill
+ assert old_cache[2] != new_cache.downhill
+
+
def test_browse(page: Page, playwright_helper, dbaccess):
playwright_helper.login()
track = playwright_helper.add_track()
diff --git a/tests/playwright/test_climbs.py b/tests/playwright/test_climbs.py
new file mode 100644
index 0000000..3c57345
--- /dev/null
+++ b/tests/playwright/test_climbs.py
@@ -0,0 +1,67 @@
+import datetime
+import re
+import time
+from pathlib import Path
+
+from playwright.sync_api import Page, expect
+
+from testutils import extract_and_upload
+from fietsboek import models
+from fietsboek.log import HANDLER
+
+
+def wait_for_log(
+ message: str,
+ timeout: datetime.timedelta = datetime.timedelta(seconds=10),
+):
+ """Waits for the given log message to appear in the logs."""
+ pattern = re.compile(message)
+ start = datetime.datetime.now(datetime.UTC)
+ while True:
+ if any(pattern.search(m.message) is not None for m in HANDLER.buffer):
+ return
+ time.sleep(0.5)
+ if datetime.datetime.now(datetime.UTC) - start > timeout:
+ raise RuntimeError(f"Timeout waiting for log {message!r}")
+
+
+def upload_and_wait(playwright_helper, filename: str, tmp_path: Path) -> int:
+ """Upload the track (via the web UI) and wait for the background tasks to
+ finish.
+
+ Returns the track's ID.
+ """
+ page: Page = playwright_helper.page
+
+ playwright_helper.login()
+
+ page.goto("/")
+ page.get_by_text("Upload").click()
+
+ extract_and_upload(page, filename, tmp_path)
+
+ page.locator(".btn", has_text="Upload").click()
+ new_track_id = int(page.url.rsplit("/", 1)[1])
+ expect(page.locator(".favourite-star")).to_be_visible()
+ wait_for_log(f"Climb extraction done for track {new_track_id}$")
+ return new_track_id
+
+
+def test_no_climbs(playwright_helper, tmp_path, dbaccess):
+ track_id = upload_and_wait(playwright_helper, "no_climbs.gpx.gz", tmp_path)
+ track = dbaccess.get(models.Track, track_id)
+ assert len(track.climbs) == 0
+
+
+def test_two_climbs(page: Page, playwright_helper, tmp_path, dbaccess):
+ track_id = upload_and_wait(playwright_helper, "two_climbs.gpx.gz", tmp_path)
+ track = dbaccess.get(models.Track, track_id)
+ assert len(track.climbs) == 2
+
+ page.goto(f"/track/{track_id}")
+ expect(page.locator(".climb-preview").nth(0)).to_be_visible()
+ expect(page.locator(".climb-details").nth(0)).to_contain_text("2.84 km")
+ expect(page.locator(".climb-details").nth(0)).to_contain_text("5.36 %")
+ expect(page.locator(".climb-preview").nth(1)).to_be_visible()
+ expect(page.locator(".climb-details").nth(1)).to_contain_text("1.29 km")
+ expect(page.locator(".climb-details").nth(1)).to_contain_text("5.58 %")
diff --git a/tests/playwright/test_journeys.py b/tests/playwright/test_journeys.py
new file mode 100644
index 0000000..2bfd271
--- /dev/null
+++ b/tests/playwright/test_journeys.py
@@ -0,0 +1,170 @@
+from playwright.sync_api import Page, expect
+from sqlalchemy import select
+
+from fietsboek import models
+
+
+def add_journey(playwright_helper, dbaccess, title):
+ """Adds a journey for testing purposes. Returns the journey ID."""
+ t_1 = playwright_helper.add_track(None, "Teasi_1.gpx.gz", "trayectoria uno")
+ t_2 = playwright_helper.add_track(None, "MyTourbook_1.gpx.gz", "trayectoria dos")
+
+ with dbaccess:
+ journey = models.Journey(
+ owner=playwright_helper.john_doe(),
+ title=title,
+ description="You saw sirens?",
+ visibility=models.journey.Visibility.PUBLIC,
+ )
+
+ dbaccess.add(journey)
+ dbaccess.flush()
+
+ journey.set_track_ids([t_1.id, t_2.id])
+ dbaccess.commit()
+ dbaccess.refresh(journey, ["id"])
+ dbaccess.expunge(journey)
+
+ playwright_helper.data_manager.initialize_journey(journey.id)
+
+ return journey.id
+
+
+def test_journey_list(page: Page, playwright_helper, dbaccess):
+ playwright_helper.login()
+
+ add_journey(playwright_helper, dbaccess, title="Our Journey")
+
+ page.goto("/journey/")
+ expect(page.locator("h5", has_text="Our Journey")).to_be_visible()
+ expect(page.locator("li", has_text="trayectoria uno")).to_be_visible()
+ expect(page.locator("li", has_text="trayectoria dos")).to_be_visible()
+
+
+def test_journey_new(page: Page, playwright_helper, dbaccess):
+ playwright_helper.login()
+
+ playwright_helper.add_track(None, "Teasi_1.gpx.gz", "trayectoria uno")
+ playwright_helper.add_track(None, "MyTourbook_1.gpx.gz", "trayectoria dos")
+ playwright_helper.add_track(None, "MyTourbook_1.gpx.gz", "trayectoria tres")
+
+ page.goto("/journey/")
+ page.get_by_text("New journey").click()
+
+ page.get_by_label("Title").fill("My Odyssey")
+ page.get_by_label("Description").fill("I saw sirens!")
+
+ page.locator("#trackSearch").fill("uno")
+ page.locator("#trackSearchButton").click()
+ page.locator(".track-query-response button").click()
+
+ page.locator("#trackSearch").fill("dos")
+ page.locator("#trackSearchButton").click()
+ page.locator(".track-query-response button").click()
+
+ page.locator("#trackSearch").fill("tres")
+ page.locator("#trackSearchButton").click()
+ page.locator(".track-query-response button").click()
+ page.locator(".journey-track", has_text="tres").locator(".btn").click()
+
+ page.locator(".btn", has_text="Save").click()
+
+ expect(page.locator("h1", has_text="My Odyssey")).to_be_visible()
+
+ expect(page.locator("h5", has_text="trayectoria uno")).to_be_visible()
+ expect(page.locator("h5", has_text="trayectoria dos")).to_be_visible()
+
+ journey = dbaccess.execute(select(models.Journey).filter_by(title="My Odyssey")).scalar_one()
+
+ assert journey.title == "My Odyssey"
+ assert journey.description == "I saw sirens!"
+ assert len(journey.tracks) == 2
+ assert journey.tracks[0].title == "trayectoria uno"
+ assert journey.tracks[1].title == "trayectoria dos"
+
+
+def test_journey_new_empty_title(page: Page, playwright_helper):
+ playwright_helper.login()
+
+ playwright_helper.add_track(None, "Teasi_1.gpx.gz", "trayectoria uno")
+
+ page.goto("/journey/")
+ page.get_by_text("New journey").click()
+
+ page.locator("#trackSearch").fill("uno")
+ page.locator("#trackSearchButton").click()
+ page.locator(".track-query-response button").click()
+ page.locator(".btn", has_text="Save").click()
+
+ expect(page.locator(".invalid-feedback", has_text="A title is required")).to_be_visible()
+
+
+def test_journey_new_no_tracks(page: Page, playwright_helper):
+ playwright_helper.login()
+
+ page.goto("/journey/")
+ page.get_by_text("New journey").click()
+
+ page.get_by_label("Title").fill("A title is there!")
+
+ page.locator(".btn", has_text="Save").click()
+
+ expect(page.locator(".invalid-feedback", has_text="A journey must have at least one track"))\
+ .to_be_visible()
+
+
+def test_journey_edit(page: Page, playwright_helper, dbaccess):
+ playwright_helper.login()
+
+ journey_id = add_journey(playwright_helper, dbaccess, title="Your Odyssey")
+
+ page.goto(f"/journey/{journey_id}/")
+
+ expect(page.locator("h1", has_text="Your Odyssey")).to_be_visible()
+
+ page.locator("a", has_text="Edit").click()
+
+ page.get_by_label("Title").fill("Their Odyssey")
+ page.get_by_label("Description").fill("Where is Homer?")
+
+ expect(page.locator(".track-title", has_text="trayectoria uno")).to_be_visible()
+ page.locator(".journey-track", has_text="uno").locator(".btn").click()
+ expect(page.locator(".track-title", has_text="trayectoria uno")).not_to_be_visible()
+
+ page.locator(".btn", has_text="Save").click()
+
+ expect(page.locator("h1", has_text="Their Odyssey")).to_be_visible()
+ expect(page.locator("h5", has_text="trayectoria uno")).not_to_be_visible()
+ expect(page.locator("h5", has_text="trayectoria dos")).to_be_visible()
+
+ journey = dbaccess.execute(select(models.Journey).filter_by(title="Their Odyssey")).scalar_one()
+
+ assert journey.title == "Their Odyssey"
+ assert journey.description == "Where is Homer?"
+ assert len(journey.tracks) == 1
+ assert journey.tracks[0].title == "trayectoria dos"
+
+
+def test_journey_reorder(page: Page, playwright_helper, dbaccess):
+ playwright_helper.login()
+
+ journey_id = add_journey(playwright_helper, dbaccess, title="Her Journey")
+
+ page.goto(f"/journey/{journey_id}/edit")
+
+ expect(page.locator("h1", has_text="Her Journey")).to_be_visible()
+
+ page.locator(".track-title", has_text="trayectoria uno").drag_to(
+ page.locator(".track-title", has_text="trayectoria dos"),
+ target_position={"x": 10, "y": 20},
+ )
+
+ page.locator(".btn", has_text="Save").click()
+
+ expect(page.locator("h1", has_text="Her Journey")).to_be_visible()
+
+ journey = dbaccess.execute(select(models.Journey).filter_by(id=journey_id)).scalar_one()
+
+ assert len(journey.tracks) == 2
+ assert journey.tracks[0].title == "trayectoria dos"
+ assert journey.tracks[1].title == "trayectoria uno"
diff --git a/tests/playwright/test_profiles.py b/tests/playwright/test_profiles.py
index 7e5fb3c..ffbaab0 100644
--- a/tests/playwright/test_profiles.py
+++ b/tests/playwright/test_profiles.py
@@ -5,7 +5,7 @@ def test_forbidden(page: Page, playwright_helper):
john = playwright_helper.john_doe()
with page.expect_response(lambda resp: resp.status == 403):
- page.goto(f"/user/{john.id}")
+ page.goto(f"/user/{john.id}/")
def test_profile(page: Page, playwright_helper):
diff --git a/tests/playwright/test_share.py b/tests/playwright/test_share.py
index de288a0..dcba899 100644
--- a/tests/playwright/test_share.py
+++ b/tests/playwright/test_share.py
@@ -29,7 +29,8 @@ def test_view_wrong_link(page: Page, playwright_helper, dbaccess):
with page.expect_response(lambda resp: resp.status == 403):
page.goto(f"/track/{track.id}?secret=foobar")
- assert "Forbidden" in page.content()
+ assert "No entry" in page.content()
+ assert "not allowed to access" in page.content()
def test_change_link(page: Page, playwright_helper, dbaccess):
diff --git a/tests/playwright/test_tileproxy.py b/tests/playwright/test_tileproxy.py
index d4d3389..2a2bdc0 100644
--- a/tests/playwright/test_tileproxy.py
+++ b/tests/playwright/test_tileproxy.py
@@ -14,7 +14,7 @@ def test_tileproxy(page: Page, playwright_helper, caplog):
# If we're too fast, the log entry might not be there yet, wait 2 more
# seconds
- if "Skipping tile proxy request for testing URL" not in caplog.messages:
+ if "Skipping tile request for testing URL" not in caplog.messages:
time.sleep(2)
- assert "Skipping tile proxy request for testing URL" in caplog.messages
+ assert "Skipping tile request for testing URL" in caplog.messages
diff --git a/tests/playwright/test_transformers.py b/tests/playwright/test_transformers.py
index fc89afb..cc897db 100644
--- a/tests/playwright/test_transformers.py
+++ b/tests/playwright/test_transformers.py
@@ -13,6 +13,17 @@ def disable_cache(page: Page):
page.route("**", lambda route: route.continue_())
+def set_transformers(page: Page, states: list[bool]):
+ """Enable/disable the transformers according to the given states."""
+ for i, state in enumerate(states, start=1):
+ page.locator(f"#transformer-heading-{i} .accordion-button").click()
+ page.locator(f"#transformer-{i}.collapse.show").wait_for()
+ if state:
+ page.locator(f"#transformer-enabled-{i}").check()
+ else:
+ page.locator(f"#transformer-enabled-{i}").uncheck()
+
+
def test_transformer_zero_elevation_disabled(page: Page, playwright_helper, tmp_path, dbaccess):
playwright_helper.login()
@@ -21,12 +32,14 @@ def test_transformer_zero_elevation_disabled(page: Page, playwright_helper, tmp_
extract_and_upload(page, "Synthetic_Zero_Elevation.gpx.gz", tmp_path)
+ set_transformers(page, [False, False, False, False])
+
page.locator(".btn", has_text="Upload").click()
# Expect early (here and in the other tests) to ensure that the backend has
# caught up with executing the transformer. Otherwise it might happen that
# we read the database while the request is not finished yet.
- expect(page.locator("#detailsUphill")).to_contain_text("167.7 m")
+ expect(page.locator("#detailsUphill")).to_contain_text("167.79 m")
new_track_id = int(page.url.rsplit("/", 1)[1])
track = dbaccess.execute(select(models.Track).filter_by(id=new_track_id)).scalar_one()
@@ -41,9 +54,7 @@ def test_transformer_zero_elevation_enabled(page: Page, playwright_helper, tmp_p
extract_and_upload(page, "Synthetic_Zero_Elevation.gpx.gz", tmp_path)
- page.locator("#transformer-heading-1 .accordion-button").click()
- page.locator("#transformer-1.collapse.show").wait_for()
- page.locator("#transformer-enabled-1").check()
+ set_transformers(page, [True, False, False, False])
page.locator(".btn", has_text="Upload").click()
@@ -62,14 +73,13 @@ def test_transformer_zero_elevation_edited(page: Page, playwright_helper, tmp_pa
page.get_by_text("Upload").click()
extract_and_upload(page, "Synthetic_Zero_Elevation.gpx.gz", tmp_path)
+ set_transformers(page, [False, False, False, False])
page.locator(".btn", has_text="Upload").click()
page.locator(".btn", has_text="Edit").click()
- page.locator("#transformer-heading-1 .accordion-button").click()
- page.locator("#transformer-1.collapse.show").wait_for()
- page.locator("#transformer-enabled-1").check()
+ set_transformers(page, [True, False, False, False])
page.locator(".btn", has_text="Save").click()
@@ -87,10 +97,11 @@ def test_transformer_steep_slope_disabled(page: Page, playwright_helper, tmp_pat
page.get_by_text("Upload").click()
extract_and_upload(page, "Synthetic_Steep_Slope.gpx.gz", tmp_path)
+ set_transformers(page, [False, False, False, False])
page.locator(".btn", has_text="Upload").click()
- expect(page.locator("#detailsUphill")).to_contain_text("61.54 m")
+ expect(page.locator("#detailsUphill")).to_contain_text("64.4 m")
new_track_id = int(page.url.rsplit("/", 1)[1])
track = dbaccess.execute(select(models.Track).filter_by(id=new_track_id)).scalar_one()
@@ -104,18 +115,15 @@ def test_transformer_steep_slope_enabled(page: Page, playwright_helper, tmp_path
page.get_by_text("Upload").click()
extract_and_upload(page, "Synthetic_Steep_Slope.gpx.gz", tmp_path)
-
- page.locator("#transformer-heading-1 .accordion-button").click()
- page.locator("#transformer-1.collapse.show").wait_for()
- page.locator("#transformer-enabled-1").check()
+ set_transformers(page, [True, False, False, False])
page.locator(".btn", has_text="Upload").click()
- expect(page.locator("#detailsUphill")).to_contain_text("1.2 m")
+ expect(page.locator("#detailsUphill")).to_contain_text("2.4 m")
new_track_id = int(page.url.rsplit("/", 1)[1])
track = dbaccess.execute(select(models.Track).filter_by(id=new_track_id)).scalar_one()
- assert track.cache.uphill < 2
+ assert track.cache.uphill < 3
def test_transformer_steep_slope_edited(page: Page, playwright_helper, tmp_path, dbaccess):
@@ -126,44 +134,41 @@ def test_transformer_steep_slope_edited(page: Page, playwright_helper, tmp_path,
page.get_by_text("Upload").click()
extract_and_upload(page, "Synthetic_Steep_Slope.gpx.gz", tmp_path)
+ set_transformers(page, [False, False, False, False])
page.locator(".btn", has_text="Upload").click()
page.locator(".btn", has_text="Edit").click()
- page.locator("#transformer-heading-1 .accordion-button").click()
- page.locator("#transformer-1.collapse.show").wait_for()
- page.locator("#transformer-enabled-1").check()
+ set_transformers(page, [True, False, False, False])
page.locator(".btn", has_text="Save").click()
- expect(page.locator("#detailsUphill")).to_contain_text("1.2 m")
+ expect(page.locator("#detailsUphill")).to_contain_text("2.4 m")
track_id = int(page.url.rsplit("/", 1)[1])
track = dbaccess.execute(select(models.Track).filter_by(id=track_id)).scalar_one()
- assert track.cache.uphill < 2
+ assert track.cache.uphill < 3
-def test_transformer_elevation_jump_enabled(page: Page, playwright_helper, tmp_path, data_manager):
+def test_transformer_elevation_jump_enabled(page: Page, playwright_helper, tmp_path, dbaccess):
playwright_helper.login()
page.goto("/")
page.get_by_text("Upload").click()
extract_and_upload(page, "Elevation_Jump.gpx.gz", tmp_path)
-
- page.locator("#transformer-heading-2 .accordion-button").click()
- page.locator("#transformer-2.collapse.show").wait_for()
- page.locator("#transformer-enabled-2").check()
+ set_transformers(page, [False, True, False, False])
page.locator(".btn", has_text="Upload").click()
page.locator(".alert", has_text="Upload successful").wait_for()
new_track_id = int(page.url.rsplit("/", 1)[1])
- data = data_manager.open(new_track_id)
- gpx = gpxpy.parse(data.decompress_gpx())
+ gpx = gpxpy.parse(
+ dbaccess.execute(select(models.Track).filter_by(id=new_track_id)).scalar_one().gpx_xml()
+ )
points = iter(gpx.walk(only_points=True))
next(points)
for prev_point, point in zip(gpx.walk(only_points=True), points):