aboutsummaryrefslogtreecommitdiff
path: root/tests/playwright/conftest.py
blob: 12a00471703884a5271ac47cd2b3d0433149a814 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import datetime
import socket
import threading
from typing import Optional
from wsgiref import simple_server

from pyramid.authentication import AuthTktCookieHelper
from pyramid.testing import DummyRequest

from testutils import load_gpx_asset
from fietsboek import models, util, actions
from fietsboek.models.track import Visibility, TrackType
from fietsboek.config import Config

import pytest


@pytest.fixture(scope="session")
def server_port():
    """Return a (likely) free port.

    Note that due to OS race conditions between picking the port and opening
    something on it, it might be taken again, but that is unlikely.
    """
    sock = socket.socket(socket.AF_INET)
    sock.bind(("", 0))
    port = sock.getsockname()[1]
    sock.close()
    return port


@pytest.fixture(scope="session", autouse=True)
def running_server(server_port, app):
    """Have the app running as an actual server."""
    server = simple_server.make_server("127.0.0.1", server_port, app)
    thread = threading.Thread(target=server.serve_forever)
    thread.daemon = True
    thread.start()
    yield
    server.shutdown()


@pytest.fixture
def browser_context_args(server_port) -> dict:
    return {"base_url": f"http://localhost:{server_port}"}


@pytest.fixture
def dbaccess(app):
    """Provide direct access to the database.

    This is needed for the selenium tests, because the normal "dbsession"
    fixture has a doomed transaction attached. This is nice for keeping the
    test database clean, but it does mean that the changes are not propagated
    through and the running WSGI app cannot read them.
    """
    session_factory = app.registry["dbsession_factory"]
    return session_factory()


class Helper:
    """Helper to insert objects for testing into the database and similar."""

    def __init__(self, dbaccess, app_settings, page, data_manager):
        self.dbaccess = dbaccess
        self.app_settings = app_settings
        self.page = page
        self.data_manager = data_manager
        self._johnny = None

    def john_doe(self) -> models.User:
        """Provides a test user (John Doe).

        This fixture either returns the existing John or creates a new one.
        """
        if self._johnny:
            return self._johnny

        with self.dbaccess:
            user = models.User(name="John Doe", email="john@doe.com", is_verified=True)
            user.set_password("password")
            self.dbaccess.add(user)
            self.dbaccess.commit()
            self.dbaccess.refresh(user, ["id", "email", "password", "session_secret"])
            self.dbaccess.expunge(user)
            self._johnny = user
            return user

    def login(self, user: Optional[models.User] = None):
        """Logs the given user in by setting the auth cookie."""
        if user is None:
            user = self.john_doe()
        config = Config.model_construct(session_key=self.app_settings["session_key"])
        secret = config.derive_secret("auth-cookie")
        helper = AuthTktCookieHelper(secret)
        headers = helper.remember(DummyRequest(), user.authenticated_user_id())
        for _, header_val in headers:
            cookie = header_val.split(";")[0]
            name, value = cookie.split("=", 1)
            self.page.context.add_cookies(
                [
                    {"name": name, "value": value, "domain": "localhost", "path": "/"},
                ]
            )

    def add_track(
        self, user: Optional[models.User] = None, track_name: str = "Teasi_1.gpx.gz"
    ) -> models.Track:
        """Add a track to the given user.

        If the user is None, John Doe is used.
        """
        if user is None:
            user = self.john_doe()
        with self.dbaccess:
            user = self.dbaccess.merge(user)
            track = actions.add_track(
                self.dbaccess,
                self.data_manager,
                owner=user,
                title="Another awesome track",
                visibility=Visibility.PRIVATE,
                description="Another description",
                track_type=TrackType.ORGANIC,
                date=datetime.datetime(2022, 12, 21, 17, 5, tzinfo=datetime.timezone.utc),
                tags=[],
                badges=[],
                tagged_people=[],
                transformers=[],
                gpx_data=load_gpx_asset(track_name),
            )
            self.dbaccess.commit()
            self.dbaccess.refresh(track, ["id", "link_secret"])
            self.dbaccess.expunge(track)
            return track


@pytest.fixture
def playwright_helper(dbaccess, app_settings, page, data_manager):
    return Helper(dbaccess, app_settings, page, data_manager)