• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fiduswriter / fiduswriter / 25361925990

05 May 2026 06:45AM UTC coverage: 88.214% (+1.9%) from 86.309%
25361925990

push

github

web-flow
switch to json posts for views with json data (#1385)

Switch to json posts for views that handle json data and do not use django forms in the backend nor send files to the backend.

9708 of 11005 relevant lines covered (88.21%)

5.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.47
fiduswriter/testing/selenium_helper.py
1
import re
17✔
2
import os
17✔
3
import time
17✔
4
from urllib3.exceptions import MaxRetryError, ReadTimeoutError
17✔
5
from selenium.common.exceptions import (
17✔
6
    ElementClickInterceptedException,
7
    TimeoutException,
8
    WebDriverException,
9
)
10
from selenium.webdriver.support.wait import WebDriverWait
17✔
11
from selenium.webdriver.support import expected_conditions as EC
17✔
12
from selenium.webdriver.common.by import By
17✔
13
from selenium import webdriver
17✔
14
from selenium.webdriver.chrome.service import Service as ChromiumService
17✔
15
from webdriver_manager.chrome import ChromeDriverManager
17✔
16
from webdriver_manager.core.os_manager import ChromeType
17✔
17

18
from allauth.account.models import EmailAddress
17✔
19
from django.contrib.auth.hashers import make_password
17✔
20
from django.contrib.auth import get_user_model
17✔
21
from django.conf import settings
17✔
22
from django.test import Client
17✔
23
from django.contrib.contenttypes.models import ContentType
17✔
24
import logging
17✔
25

26
logger = logging.getLogger(__name__)
17✔
27

28

29
class SeleniumHelper:
17✔
30
    """
31
    Methods for manipulating django and the browser for testing purposes.
32
    """
33

34
    login_page = "/"
17✔
35

36
    def find_urls(self, string):
17✔
37
        return re.findall(
2✔
38
            (
39
                "http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*(),]|"
40
                "(?:%[0-9a-fA-F][0-9a-fA-F]))+"
41
            ),
42
            string,
43
        )
44

45
    # create django data
46
    def create_user(
17✔
47
        self, username="User", email="test@example.com", passtext="p4ssw0rd"
48
    ):
49
        User = get_user_model()
16✔
50
        user = User.objects.create(
16✔
51
            username=username,
52
            email=email,
53
            password=make_password(passtext),
54
            is_active=True,
55
        )
56
        user.save()
16✔
57

58
        # avoid the unverified-email login trap
59
        EmailAddress.objects.create(
16✔
60
            user=user, email=email, verified=True, primary=True
61
        ).save()
62

63
        return user
16✔
64

65
    # drive browser
66
    def login_user(self, user, driver, client):
17✔
67
        client.force_login(user=user)
11✔
68
        cookie = client.cookies[settings.SESSION_COOKIE_NAME]
11✔
69
        # output the cookie to the console for debugging
70
        logger.debug("cookie: %s" % cookie.value)
11✔
71
        if driver.current_url == "data:,":
11✔
72
            # To set the cookie at the right domain we load the front page.
73
            driver.get(f"{self.live_server_url}{self.login_page}")
11✔
74
            WebDriverWait(driver, self.wait_time).until(
11✔
75
                EC.presence_of_element_located((By.ID, "id-login"))
76
            )
77
        driver.add_cookie(
11✔
78
            {
79
                "name": settings.SESSION_COOKIE_NAME,
80
                "value": cookie.value,
81
                "secure": False,
82
                "path": "/",
83
            }
84
        )
85

86
    def login_user_manually(self, user, driver, passtext="p4ssw0rd"):
17✔
87
        username = user.username
×
88
        driver.delete_cookie(settings.SESSION_COOKIE_NAME)
×
89
        driver.get("{}{}".format(self.live_server_url, "/"))
×
90
        driver.find_element(By.ID, "id-login").send_keys(username)
×
91
        driver.find_element(By.ID, "id-password").send_keys(passtext)
×
92
        driver.find_element(By.ID, "login-submit").click()
×
93
        # Wait until there is an element with the ID user-preferences
94
        # which is only present on the dashboard.
95
        WebDriverWait(driver, self.wait_time).until(
×
96
            EC.presence_of_element_located((By.ID, "user-preferences"))
97
        )
98

99
    def logout_user(self, driver, client):
17✔
100
        client.logout()
2✔
101
        driver.delete_cookie(settings.SESSION_COOKIE_NAME)
2✔
102

103
    def wait_until_file_exists(self, path, wait_time):
17✔
104
        count = 0
4✔
105
        while not os.path.exists(path):
4✔
106
            time.sleep(1)
4✔
107
            count += 1
4✔
108
            if count > wait_time:
4✔
109
                break
×
110

111
    def retry_click(self, driver, selector, retries=5):
17✔
112
        count = 0
1✔
113
        while count < retries:
1✔
114
            try:
1✔
115
                WebDriverWait(driver, self.wait_time).until(
1✔
116
                    EC.element_to_be_clickable(selector)
117
                ).click()
118
                break
1✔
119
            except ElementClickInterceptedException:
×
120
                count += 1
×
121
                time.sleep(1)
×
122

123
    def click_new_document_button(self, driver):
17✔
124
        """Click the new document button and handle the encryption choice dialog if present."""
125
        WebDriverWait(driver, self.wait_time).until(
4✔
126
            EC.element_to_be_clickable(
127
                (By.CSS_SELECTOR, ".new_document button")
128
            )
129
        ).click()
130
        try:
4✔
131
            WebDriverWait(driver, 2).until(
4✔
132
                EC.presence_of_element_located((By.CSS_SELECTOR, ".ui-dialog"))
133
            )
134
            driver.find_element(By.CSS_SELECTOR, ".ui-dialog .fw-dark").click()
×
135
        except TimeoutException:
4✔
136
            pass
4✔
137

138
    @classmethod
17✔
139
    def get_drivers(cls, number, download_dir=False, user_agent=False):
17✔
140
        # django native clients, to be used for faster login.
141
        clients = []
17✔
142
        for i in range(number):
17✔
143
            clients.append(Client())
17✔
144
        drivers = []
17✔
145
        wait_time = 0
17✔
146
        options = webdriver.ChromeOptions()
17✔
147
        options.add_argument("--kiosk-printing")
17✔
148
        options.add_argument("--safebrowsing-disable-download-protection")
17✔
149
        options.add_argument("--safebrowsing-disable-extension-blacklist")
17✔
150
        options.add_argument("--window-size=1920,1080")
17✔
151
        prefs = {
17✔
152
            "profile.password_manager_leak_detection": False,
153
        }
154
        if download_dir:
17✔
155
            prefs["download.default_directory"] = download_dir
5✔
156
            prefs["download.prompt_for_download"] = False
5✔
157
            prefs["download.directory_upgrade"] = True
5✔
158
        options.add_experimental_option("prefs", prefs)
17✔
159
        if user_agent:
17✔
160
            options.add_argument(f"user-agent={user_agent}")
1✔
161
        if os.getenv("CI"):
17✔
162
            options.binary_location = "/usr/bin/google-chrome-stable"
17✔
163
            if os.getenv("DEBUG_MODE") != "1":
17✔
164
                options.add_argument("--headless=new")
17✔
165
            options.add_argument("--disable-gpu")
17✔
166
            wait_time = 20
17✔
167
        else:
168

169
            wait_time = 10
×
170
        for i in range(number):
17✔
171
            driver_env = os.environ.copy()
17✔
172
            if os.getenv("CI") and os.getenv("DEBUG_MODE") == "1" and i < 2:
17✔
173
                driver_env["DISPLAY"] = f":{99 - i}"
×
174
            driver = webdriver.Chrome(
17✔
175
                service=ChromiumService(
176
                    ChromeDriverManager(
177
                        chrome_type=ChromeType.GOOGLE
178
                    ).install(),
179
                    env=driver_env,
180
                ),
181
                options=options,
182
            )
183
            # Set sizes of browsers so that all buttons are visible.
184
            driver.set_window_position(0, 0)
17✔
185
            driver.set_window_size(1920, 1080)
17✔
186
            drivers.append(driver)
17✔
187
        cls.drivers = drivers
17✔
188
        return {"clients": clients, "drivers": drivers, "wait_time": wait_time}
17✔
189

190
    def setUp(self):
17✔
191
        # Clear ContentType cache during testing to prevent FK constraint errors
192
        # Content types get new IDs after each test but cached values don't update
193
        ContentType.objects.clear_cache()
11✔
194
        self.addCleanup(ContentType.objects.clear_cache)
11✔
195
        return super().setUp()
11✔
196

197
    def tearDown(self):
17✔
198
        # Source: https://stackoverflow.com/a/39606065
199
        result = self._outcome.result
17✔
200
        ok = all(
17✔
201
            test != self for test, text in result.errors + result.failures
202
        )
203
        if ok:
17✔
204
            for driver in self.drivers:
17✔
205
                self.leave_site(driver)
17✔
206
        else:
207
            if not os.path.exists("screenshots"):
2✔
208
                os.makedirs("screenshots")
2✔
209
            for id, driver in enumerate(self.drivers, start=1):
2✔
210
                screenshotfile = (
2✔
211
                    f"screenshots/driver{id}-{self._testMethodName}.png"
212
                )
213
                logger.info(f"Saving {screenshotfile}")
2✔
214
                driver.save_screenshot(screenshotfile)
2✔
215
                self.leave_site(driver)
2✔
216
        return super().tearDown()
17✔
217

218
    def leave_site(self, driver):
17✔
219
        try:
17✔
220
            driver.execute_script(
17✔
221
                "if (window.theApp) {window.theApp.page = null;}"
222
                # Suppress any 'Leave site?' beforeunload dialog so that
223
                # driver.get() below cannot be blocked by it.
224
                "window.onbeforeunload = null;"
225
            )
226
            driver.get("data:,")
17✔
227
        except (MaxRetryError, ReadTimeoutError, WebDriverException):
1✔
228
            pass
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc