Add new test for non-sharing, fix non-sharing use case

This commit is contained in:
Max Berger
2026-03-26 21:20:02 +01:00
parent 78cf5f7fe3
commit bde188ff55
10 changed files with 100 additions and 31 deletions

View File

@@ -30,21 +30,43 @@ from typing import Any, Generator, Optional
from playwright.sync_api import BrowserContext, Page from playwright.sync_api import BrowserContext, Page
from enum import Enum
class AuthType(Enum):
HTPASSWD = "htpasswd"
XREMOTE = "http_x_remote_user"
class SharingType(Enum):
SHARING = "sharing"
NOSHARING = "nosharing"
@dataclass(frozen=True) @dataclass(frozen=True)
class Config: class Config:
name: str name: str
auth_type: str auth_type: AuthType
sharing_type: SharingType
extra_config: str = "" extra_config: str = ""
SHARING_HTPASSWD = Config( SHARING_HTPASSWD = Config(
name="sharing_htpasswd", name="sharing_htpasswd",
auth_type="htpasswd", auth_type=AuthType.HTPASSWD,
sharing_type=SharingType.SHARING,
) )
SHARING_XREMOTE = Config( SHARING_XREMOTE = Config(
name="sharing_xremote", name="sharing_xremote",
auth_type="http_x_remote_user", auth_type=AuthType.XREMOTE,
sharing_type=SharingType.SHARING,
)
NOSHARE_HTPASSWD = Config(
name="noshare_htpasswd",
auth_type=AuthType.HTPASSWD,
sharing_type=SharingType.NOSHARING,
) )
@@ -71,10 +93,10 @@ hosts = 127.0.0.1:{port}
[storage] [storage]
filesystem_folder = {storage_path} filesystem_folder = {storage_path}
[auth] [auth]
type = {config.auth_type} type = {config.auth_type.value}
""" """
) )
if config.auth_type == "htpasswd": if config.auth_type == AuthType.HTPASSWD:
f.write(f"htpasswd_filename = {user_path}\n") f.write(f"htpasswd_filename = {user_path}\n")
f.write("htpasswd_encryption = plain\n") f.write("htpasswd_encryption = plain\n")
@@ -83,7 +105,11 @@ type = {config.auth_type}
type = internal type = internal
[headers] [headers]
Content-Security-Policy = default-src 'self'; object-src 'none' Content-Security-Policy = default-src 'self'; object-src 'none'
[sharing] """
)
if config.sharing_type == SharingType.SHARING:
f.write(
f"""[sharing]
type = csv type = csv
collection_by_map = true collection_by_map = true
collection_by_token = true collection_by_token = true
@@ -93,12 +119,12 @@ permit_properties_overlay = true
collection_by_bday = true collection_by_bday = true
permit_create_bday = true permit_create_bday = true
database_path = {sharing_path} database_path = {sharing_path}
{config.extra_config}
""" """
) )
if config.auth_type == "htpasswd": f.write(f"\n{config.extra_config}\n")
if config.auth_type == AuthType.HTPASSWD:
with open(user_path, "w") as f: with open(user_path, "w") as f:
f.write( f.write(
"""admin:adminpassword """admin:adminpassword
@@ -155,14 +181,14 @@ def login(
config: Config = SHARING_HTPASSWD, config: Config = SHARING_HTPASSWD,
context: Optional[BrowserContext] = None, context: Optional[BrowserContext] = None,
) -> None: ) -> None:
if config.auth_type == "http_x_remote_user": if config.auth_type == AuthType.XREMOTE:
if context is None: if context is None:
raise ValueError("context is required for http_x_remote_user login") raise ValueError("context is required for http_x_remote_user login")
context.set_extra_http_headers({"X-Remote-User": "admin"}) context.set_extra_http_headers({"X-Remote-User": "admin"})
page.goto(radicale_server) page.goto(radicale_server)
if config.auth_type == "htpasswd": if config.auth_type == AuthType.HTPASSWD:
page.fill('#loginscene input[data-name="user"]', "admin") page.fill('#loginscene input[data-name="user"]', "admin")
page.fill('#loginscene input[data-name="password"]', "adminpassword") page.fill('#loginscene input[data-name="password"]', "adminpassword")
page.click('button:has-text("Next")') page.click('button:has-text("Next")')

View File

@@ -25,6 +25,7 @@ import pytest
from playwright.sync_api import BrowserContext, Page, expect from playwright.sync_api import BrowserContext, Page, expect
from integ_tests.common import ( from integ_tests.common import (
NOSHARE_HTPASSWD,
SHARING_HTPASSWD, SHARING_HTPASSWD,
SHARING_XREMOTE, SHARING_XREMOTE,
Config, Config,
@@ -40,7 +41,7 @@ def radicale_server(
yield from start_radicale_server(tmp_path, config) yield from start_radicale_server(tmp_path, config)
@pytest.mark.parametrize("config", [SHARING_HTPASSWD, SHARING_XREMOTE]) @pytest.mark.parametrize("config", [SHARING_HTPASSWD, SHARING_XREMOTE, NOSHARE_HTPASSWD])
def test_index_html_loads(page: Page, radicale_server: str, config: Config) -> None: def test_index_html_loads(page: Page, radicale_server: str, config: Config) -> None:
"""Test that the index.html loads from the server.""" """Test that the index.html loads from the server."""
console_msgs: list[str] = [] console_msgs: list[str] = []
@@ -52,7 +53,7 @@ def test_index_html_loads(page: Page, radicale_server: str, config: Config) -> N
assert len(errors) == 0 assert len(errors) == 0
@pytest.mark.parametrize("config", [SHARING_HTPASSWD, SHARING_XREMOTE]) @pytest.mark.parametrize("config", [SHARING_HTPASSWD, SHARING_XREMOTE, NOSHARE_HTPASSWD])
def test_user_login_works( def test_user_login_works(
context: BrowserContext, page: Page, radicale_server: str, config: Config context: BrowserContext, page: Page, radicale_server: str, config: Config
) -> None: ) -> None:

View File

@@ -25,6 +25,7 @@ import pytest
from playwright.sync_api import BrowserContext, Page, expect from playwright.sync_api import BrowserContext, Page, expect
from integ_tests.common import ( from integ_tests.common import (
NOSHARE_HTPASSWD,
SHARING_HTPASSWD, SHARING_HTPASSWD,
SHARING_XREMOTE, SHARING_XREMOTE,
Config, Config,
@@ -41,7 +42,7 @@ def radicale_server(
yield from start_radicale_server(tmp_path, config) yield from start_radicale_server(tmp_path, config)
@pytest.mark.parametrize("config", [SHARING_HTPASSWD, SHARING_XREMOTE]) @pytest.mark.parametrize("config", [SHARING_HTPASSWD, SHARING_XREMOTE, NOSHARE_HTPASSWD])
def test_delete_wrong_confirmation( def test_delete_wrong_confirmation(
context: BrowserContext, page: Page, radicale_server: str, config: Config context: BrowserContext, page: Page, radicale_server: str, config: Config
) -> None: ) -> None:
@@ -67,7 +68,7 @@ def test_delete_wrong_confirmation(
expect(page.locator("#deleteconfirmationscene")).to_be_visible() expect(page.locator("#deleteconfirmationscene")).to_be_visible()
@pytest.mark.parametrize("config", [SHARING_HTPASSWD, SHARING_XREMOTE]) @pytest.mark.parametrize("config", [SHARING_HTPASSWD, SHARING_XREMOTE, NOSHARE_HTPASSWD])
def test_delete_correct_confirmation( def test_delete_correct_confirmation(
context: BrowserContext, page: Page, radicale_server: str, config: Config context: BrowserContext, page: Page, radicale_server: str, config: Config
) -> None: ) -> None:

View File

@@ -24,6 +24,7 @@ import pytest
from playwright.sync_api import BrowserContext, Page from playwright.sync_api import BrowserContext, Page
from integ_tests.common import ( from integ_tests.common import (
NOSHARE_HTPASSWD,
SHARING_HTPASSWD, SHARING_HTPASSWD,
SHARING_XREMOTE, SHARING_XREMOTE,
Config, Config,
@@ -32,7 +33,9 @@ from integ_tests.common import (
) )
@pytest.fixture(params=[SHARING_HTPASSWD, SHARING_XREMOTE], ids=lambda c: c.name) @pytest.fixture(
params=[SHARING_HTPASSWD, SHARING_XREMOTE, NOSHARE_HTPASSWD], ids=lambda c: c.name
)
def config(request: pytest.FixtureRequest) -> Config: def config(request: pytest.FixtureRequest) -> Config:
return request.param return request.param

View File

@@ -25,6 +25,7 @@ import pytest
from playwright.sync_api import BrowserContext, Page, expect from playwright.sync_api import BrowserContext, Page, expect
from integ_tests.common import ( from integ_tests.common import (
NOSHARE_HTPASSWD,
SHARING_HTPASSWD, SHARING_HTPASSWD,
SHARING_XREMOTE, SHARING_XREMOTE,
Config, Config,
@@ -34,7 +35,9 @@ from integ_tests.common import (
) )
@pytest.fixture(params=[SHARING_HTPASSWD, SHARING_XREMOTE], ids=lambda c: c.name) @pytest.fixture(
params=[SHARING_HTPASSWD, SHARING_XREMOTE, NOSHARE_HTPASSWD], ids=lambda c: c.name
)
def config(request: pytest.FixtureRequest) -> Config: def config(request: pytest.FixtureRequest) -> Config:
return request.param return request.param

View File

@@ -25,6 +25,7 @@ import pytest
from playwright.sync_api import BrowserContext, Page, expect from playwright.sync_api import BrowserContext, Page, expect
from integ_tests.common import ( from integ_tests.common import (
NOSHARE_HTPASSWD,
SHARING_HTPASSWD, SHARING_HTPASSWD,
SHARING_XREMOTE, SHARING_XREMOTE,
Config, Config,
@@ -34,7 +35,9 @@ from integ_tests.common import (
) )
@pytest.fixture(params=[SHARING_HTPASSWD, SHARING_XREMOTE], ids=lambda c: c.name) @pytest.fixture(
params=[SHARING_HTPASSWD, SHARING_XREMOTE, NOSHARE_HTPASSWD], ids=lambda c: c.name
)
def config(request: pytest.FixtureRequest) -> Config: def config(request: pytest.FixtureRequest) -> Config:
return request.param return request.param

View File

@@ -24,17 +24,30 @@ from typing import Any, Generator
import pytest import pytest
from playwright.sync_api import Page, expect from playwright.sync_api import Page, expect
from integ_tests.common import SHARING_HTPASSWD, login, start_radicale_server from integ_tests.common import (
NOSHARE_HTPASSWD,
SHARING_HTPASSWD,
Config,
login,
start_radicale_server,
)
@pytest.fixture(params=[SHARING_HTPASSWD, NOSHARE_HTPASSWD], ids=lambda c: c.name)
def config(request: pytest.FixtureRequest) -> Config:
return request.param
@pytest.fixture @pytest.fixture
def radicale_server(tmp_path: pathlib.Path) -> Generator[str, Any, None]: def radicale_server(
yield from start_radicale_server(tmp_path, SHARING_HTPASSWD) tmp_path: pathlib.Path, config: Config
) -> Generator[str, Any, None]:
yield from start_radicale_server(tmp_path, config)
def test_login_logout_login(page: Page, radicale_server: str) -> None: def test_login_logout_login(page: Page, radicale_server: str, config: Config) -> None:
# 1. First login # 1. First login
login(page, radicale_server, SHARING_HTPASSWD) login(page, radicale_server, config)
expect(page.locator("#collectionsscene")).to_be_visible() expect(page.locator("#collectionsscene")).to_be_visible()
# 2. Logout # 2. Logout
@@ -43,5 +56,5 @@ def test_login_logout_login(page: Page, radicale_server: str) -> None:
expect(page.locator("#collectionsscene")).to_be_hidden() expect(page.locator("#collectionsscene")).to_be_hidden()
# 3. Second login # 3. Second login
login(page, radicale_server, SHARING_HTPASSWD) login(page, radicale_server, config)
expect(page.locator("#collectionsscene")).to_be_visible() expect(page.locator("#collectionsscene")).to_be_visible()

View File

@@ -26,6 +26,7 @@ import pytest
from playwright.sync_api import BrowserContext, Page, expect from playwright.sync_api import BrowserContext, Page, expect
from integ_tests.common import ( from integ_tests.common import (
NOSHARE_HTPASSWD,
SHARING_HTPASSWD, SHARING_HTPASSWD,
SHARING_XREMOTE, SHARING_XREMOTE,
Config, Config,
@@ -34,7 +35,9 @@ from integ_tests.common import (
) )
@pytest.fixture(params=[SHARING_HTPASSWD, SHARING_XREMOTE], ids=lambda c: c.name) @pytest.fixture(
params=[SHARING_HTPASSWD, SHARING_XREMOTE, NOSHARE_HTPASSWD], ids=lambda c: c.name
)
def config(request: pytest.FixtureRequest) -> Config: def config(request: pytest.FixtureRequest) -> Config:
return request.param return request.param

View File

@@ -182,7 +182,10 @@ export function reload_sharing_list(user, password, collection, callback) {
let shares = (parsed["Content"] || []).map((/** @type {ShareData} */ data) => new Share(data)); let shares = (parsed["Content"] || []).map((/** @type {ShareData} */ data) => new Share(data));
callback(shares, null); callback(shares, null);
}, },
null, // on_not_found function () {
// sharing is disabled on the server
callback([], null);
},
function (error) { function (error) {
callback([], error); callback([], error);
}, },

View File

@@ -270,11 +270,24 @@ export function maybe_enable_sharing_options(features) {
let map_is_enabled = features.sharing.FeatureEnabledCollectionByMap || false; let map_is_enabled = features.sharing.FeatureEnabledCollectionByMap || false;
let token_is_enabled = features.sharing.FeatureEnabledCollectionByToken || false; let token_is_enabled = features.sharing.FeatureEnabledCollectionByToken || false;
let bday_is_enabled = features.sharing.FeatureEnabledCollectionByBday || false; let bday_is_enabled = features.sharing.FeatureEnabledCollectionByBday || false;
if (map_is_enabled || token_is_enabled || bday_is_enabled) { let any_sharing_enabled = map_is_enabled || token_is_enabled || bday_is_enabled;
let share_options = document.querySelectorAll("[data-name=shareoption]");
for (let i = 0; i < share_options.length; i++) { let share_options = document.querySelectorAll("[data-name=shareoption]");
let share_option = share_options[i]; for (let i = 0; i < share_options.length; i++) {
let share_option = share_options[i];
if (any_sharing_enabled) {
share_option.classList.remove("hidden"); share_option.classList.remove("hidden");
} else {
share_option.classList.add("hidden");
}
}
let incomingshares_btn = document.querySelector("#collectionsscene [data-name=incomingshares]");
if (incomingshares_btn) {
if (any_sharing_enabled) {
incomingshares_btn.classList.remove("hidden");
} else {
incomingshares_btn.classList.add("hidden");
} }
} }
} }