Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions integ_tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ def start_radicale_server(tmp_path: pathlib.Path) -> Generator[str, Any, None]:
permit_create_token = true
permit_create_map = true
permit_properties_overlay = true
collection_by_bday = true
permit_create_bday = true

"""
)
Expand Down
14 changes: 7 additions & 7 deletions integ_tests/test_delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,18 @@ def test_delete_wrong_confirmation(page: Page, radicale_server: str) -> None:
page.click('article:not(.hidden) a[data-name="delete"]', force=True)

# Input wrong confirmation
page.fill('#deletecollectionscene input[data-name="confirmationtxt"]', "foo")
page.click('#deletecollectionscene button[data-name="delete"]')
page.fill('#deleteconfirmationscene input[data-name="confirmationtxt"]', "foo")
page.click('#deleteconfirmationscene button[data-name="delete"]')

# Check for error message
error_locator = page.locator('#deletecollectionscene span[data-name="error"]')
error_locator = page.locator('#deleteconfirmationscene span[data-name="error"]')
expect(error_locator).to_be_visible()
expect(error_locator).to_contain_text(
"Please type DELETE in the confirmation field"
)

# Scene should still be visible
expect(page.locator("#deletecollectionscene")).to_be_visible()
expect(page.locator("#deleteconfirmationscene")).to_be_visible()


def test_delete_correct_confirmation(page: Page, radicale_server: str) -> None:
Expand All @@ -67,11 +67,11 @@ def test_delete_correct_confirmation(page: Page, radicale_server: str) -> None:
page.click('article:not(.hidden) a[data-name="delete"]', force=True)

# Input correct confirmation
page.fill('#deletecollectionscene input[data-name="confirmationtxt"]', "DELETE")
page.click('#deletecollectionscene button[data-name="delete"]')
page.fill('#deleteconfirmationscene input[data-name="confirmationtxt"]', "DELETE")
page.click('#deleteconfirmationscene button[data-name="delete"]')

# Verify collection is gone
expect(page.locator("article:not(.hidden)")).to_have_count(0)

# Scene should be hidden
expect(page.locator("#deletecollectionscene")).to_be_hidden()
expect(page.locator("#deleteconfirmationscene")).to_be_hidden()
16 changes: 8 additions & 8 deletions integ_tests/test_scenes.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ def test_navigation_delete_collection_cancel(page: Page, radicale_server: str) -

page.hover("article:not(.hidden)")
page.click('article:not(.hidden) a[data-name="delete"]', force=True)
expect(page.locator("#deletecollectionscene")).to_be_visible()
expect(page.locator("#deleteconfirmationscene")).to_be_visible()

page.click('#deletecollectionscene button[data-name="cancel"]')
expect(page.locator("#deletecollectionscene")).to_be_hidden()
page.click('#deleteconfirmationscene button[data-name="cancel"]')
expect(page.locator("#deleteconfirmationscene")).to_be_hidden()
expect(page.locator("#collectionsscene")).to_be_visible()


Expand All @@ -82,18 +82,18 @@ def test_navigation_delete_collection_confirm(page: Page, radicale_server: str)

page.hover("article:not(.hidden)")
page.click('article:not(.hidden) a[data-name="delete"]', force=True)
expect(page.locator("#deletecollectionscene")).to_be_visible()
expect(page.locator("#deleteconfirmationscene")).to_be_visible()

# We need to fill the confirmation text
confirmation_text = page.locator(
"#deletecollectionscene [data-name='deleteconfirmationtext']"
"#deleteconfirmationscene [data-name='deleteconfirmationtext']"
).inner_text()
page.locator("#deletecollectionscene input[data-name='confirmationtxt']").fill(
page.locator("#deleteconfirmationscene input[data-name='confirmationtxt']").fill(
confirmation_text
)
page.click('#deletecollectionscene button[data-name="delete"]')
page.click('#deleteconfirmationscene button[data-name="delete"]')

expect(page.locator("#deletecollectionscene")).to_be_hidden()
expect(page.locator("#deleteconfirmationscene")).to_be_hidden()
expect(page.locator("#collectionsscene")).to_be_visible()
expect(page.locator("article:not(.hidden)")).to_have_count(0)

Expand Down
142 changes: 136 additions & 6 deletions integ_tests/test_sharing.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ def test_create_and_delete_share_by_key(page: Page, radicale_server: str) -> Non
"tr[data-name='sharetokenrowtemplate']:not(.hidden) span[data-name='ro']"
)
).to_be_visible()
page.once("dialog", lambda dialog: dialog.accept())
page.click('tr:not(.hidden) button[data-name="delete"]', strict=True)
page.click('#deleteconfirmationscene button[data-name="delete"]')
expect(
page.locator("tr[data-name='sharetokenrowtemplate']:not(.hidden)")
).to_have_count(0)
Expand All @@ -68,8 +68,8 @@ def test_create_and_delete_share_by_key(page: Page, radicale_server: str) -> Non
"tr[data-name='sharetokenrowtemplate']:not(.hidden) span[data-name='rw']"
)
).to_be_visible()
page.once("dialog", lambda dialog: dialog.accept())
page.click('tr:not(.hidden) button[data-name="delete"]', strict=True)
page.click('#deleteconfirmationscene button[data-name="delete"]')
expect(
page.locator("tr[data-name='sharetokenrowtemplate']:not(.hidden)")
).to_have_count(0)
Expand Down Expand Up @@ -97,8 +97,8 @@ def test_create_and_delete_share_by_map(page: Page, radicale_server: str) -> Non
"tr[data-name='sharemaprowtemplate']:not(.hidden) span[data-name='ro']"
)
).to_be_visible()
page.once("dialog", lambda dialog: dialog.accept())
page.click('tr:not(.hidden) button[data-name="delete"]', strict=True)
page.click('#deleteconfirmationscene button[data-name="delete"]')
expect(
page.locator("tr[data-name='sharemaprowtemplate']:not(.hidden)")
).to_have_count(0)
Expand All @@ -115,8 +115,8 @@ def test_create_and_delete_share_by_map(page: Page, radicale_server: str) -> Non
"tr[data-name='sharemaprowtemplate']:not(.hidden) span[data-name='rw']"
)
).to_be_visible()
page.once("dialog", lambda dialog: dialog.accept())
page.click('tr:not(.hidden) button[data-name="delete"]', strict=True)
page.click('#deleteconfirmationscene button[data-name="delete"]')
expect(
page.locator("tr[data-name='sharemaprowtemplate']:not(.hidden)")
).to_have_count(0)
Expand All @@ -139,15 +139,29 @@ def test_share_with_property_overrides(page: Page, radicale_server: str) -> None
page.click('article:not(.hidden) a[data-name="share"]', force=True, strict=True)
page.click('button[data-name="sharebytoken"]')

# Verify property override is closed by default
expect(
page.locator('input[data-name="displayname_override_enabled"]')
).not_to_be_visible()
page.click('details[data-name="properties_override"] summary')

# Verify defaults
expect(page.locator('input[data-name="displayname_override"]')).to_have_value(
"Test Collection"
)
expect(page.locator('input[data-name="description_override"]')).to_have_value(
"Original Description"
)
expect(page.locator('input[data-name="color_override"]')).to_have_value("#ff0000")
expect(page.locator('input[data-name="displayname_override"]')).to_be_disabled()
expect(page.locator('input[data-name="description_override"]')).to_be_disabled()
expect(page.locator('input[data-name="color_override"]')).to_be_disabled()

# Set overrides
page.click('label[for="newshare_attr_displayname_enabled"]')
page.locator('input[data-name="displayname_override"]').fill(
"Overridden Displayname"
)
page.click('label[for="newshare_attr_description_enabled"]')
page.locator('input[data-name="description_override"]').fill(
"Overridden Description"
Expand Down Expand Up @@ -182,8 +196,20 @@ def test_share_journal_no_overrides(page: Page, radicale_server: str) -> None:
page.click('article:not(.hidden) a[data-name="share"]', force=True, strict=True)
page.click('button[data-name="sharebytoken"]')

# Verify property override fieldset is hidden
expect(page.locator('fieldset[data-name="properties_override"]')).to_be_hidden()
# Verify property override visibility
expect(page.locator('details[data-name="properties_override"]')).to_be_visible()
expect(
page.locator('input[data-name="displayname_override_enabled"]')
).not_to_be_visible()
page.click('details[data-name="properties_override"] summary')

expect(
page.locator('input[data-name="displayname_override_enabled"]')
).to_be_visible()
expect(
page.locator('input[data-name="description_override_enabled"]')
).to_be_hidden()
expect(page.locator('input[data-name="color_override_enabled"]')).to_be_hidden()

# Create the share
page.click('#newshare button[data-name="submit"]')
Expand Down Expand Up @@ -423,3 +449,107 @@ def test_no_incoming_shares_message(page: Page, radicale_server: str) -> None:

page.click('#incomingsharingscene button[data-name="cancel"]')
expect(page.locator("#incomingsharingscene")).to_be_hidden()


def test_create_and_delete_share_by_bday(page: Page, radicale_server: str) -> None:
login(page, radicale_server)
# create collection of type ADDRESSBOOK for bday (bday only works with ADDRESSBOOK)
page.click('a[data-name="new"]')
page.locator('#createcollectionscene select[data-name="type"]').select_option(
"ADDRESSBOOK"
)
page.locator('#createcollectionscene input[data-name="displayname"]').fill(
"Addressbook For Bday"
)
page.click('#createcollectionscene button[data-name="submit"]')

page.hover("article:not(.hidden)")
page.click('article:not(.hidden) a[data-name="share"]', force=True, strict=True)

expect(
page.locator("tr[data-name='sharebdayrowtemplate']:not(.hidden)")
).to_have_count(0)

page.click('button[data-name="sharebybday"]')

# verify user is auto-filled with current user (admin)
expect(page.locator('input[data-name="shareuser"]')).to_have_value("admin")
page.locator('input[data-name="sharehref"]').fill("bdaymapped")

# verify that the permissions section is hidden entirely
expect(page.locator("input#newshare_attr_permissions_ro")).to_be_hidden()
expect(page.locator("input#newshare_attr_permissions_rw")).to_be_hidden()

page.click('#newshare button[data-name="submit"]')
expect(
page.locator("tr[data-name='sharebdayrowtemplate']:not(.hidden)")
).to_have_count(1)

# verify no permissions pill in the bday row
expect(
page.locator(
"tr[data-name='sharebdayrowtemplate']:not(.hidden) span[data-name='ro']"
)
).to_have_count(0)

# Close the share scene and verify the virtual bday calendar is now in the collections list
page.click('#sharecollectionscene button[data-name="cancel"]')
expect(page.locator("#sharecollectionscene")).to_be_hidden()

# The virtual calendar (bdaymapped) should appear as its own article
# after the cache was invalidated following the self-share
expect(page.locator("article:not(.hidden)")).to_have_count(2)

# Delete the bday share by re-opening the share scene
page.hover("article:not(.hidden) >> nth=0")
page.click('article:not(.hidden) >> nth=0 >> a[data-name="share"]', force=True)
page.click(
"tr[data-name='sharebdayrowtemplate']:not(.hidden) button[data-name='delete']",
strict=True,
)
page.click('#deleteconfirmationscene button[data-name="delete"]')
expect(
page.locator("tr[data-name='sharebdayrowtemplate']:not(.hidden)")
).to_have_count(0)


def test_bday_section_hidden_for_calendar(page: Page, radicale_server: str) -> None:
"""Verify the bday calendar section is hidden for CALENDAR collections."""
login(page, radicale_server)

page.click('a[data-name="new"]')
page.locator('#createcollectionscene select[data-name="type"]').select_option(
"CALENDAR"
)
page.locator('#createcollectionscene input[data-name="displayname"]').fill(
"My Calendar"
)
page.click('#createcollectionscene button[data-name="submit"]')

page.hover("article:not(.hidden)")
page.click('article:not(.hidden) a[data-name="share"]', force=True, strict=True)

expect(page.locator("#sharecollectionscene")).to_be_visible()
expect(page.locator("div[data-name='sharebybday']")).to_be_hidden()
page.click('#sharecollectionscene button[data-name="cancel"]')


def test_bday_section_visible_for_addressbook(page: Page, radicale_server: str) -> None:
"""Verify the bday calendar section is visible for ADDRESSBOOK collections."""
login(page, radicale_server)

page.click('a[data-name="new"]')
page.locator('#createcollectionscene select[data-name="type"]').select_option(
"ADDRESSBOOK"
)
page.locator('#createcollectionscene input[data-name="displayname"]').fill(
"My Addressbook"
)
page.click('#createcollectionscene button[data-name="submit"]')

page.hover("article:not(.hidden)")
page.click('article:not(.hidden) a[data-name="share"]', force=True, strict=True)

expect(page.locator("#sharecollectionscene")).to_be_visible()
expect(page.locator("div[data-name='sharebybday']")).to_be_visible()
page.click('#sharecollectionscene button[data-name="cancel"]')
5 changes: 4 additions & 1 deletion radicale/httputils.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,10 @@ def _serve_traversable(
return NOT_FOUND
content_type = MIMETYPES.get(
os.path.splitext(traversable.name)[1].lower(), FALLBACK_MIMETYPE)
headers = {"Content-Type": content_type}
headers = {
"Content-Type": content_type,
"Content-Security-Policy": "default-src 'self'; object-src 'none'"
}
if isinstance(traversable, pathlib.Path):
headers["Last-Modified"] = time.strftime(
"%a, %d %b %Y %H:%M:%S GMT",
Expand Down
6 changes: 6 additions & 0 deletions radicale/web/internal_data/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ main {
#logoutview span {
width: calc(100% - 60px);
display: inline-block;
word-wrap: break-word;
}

#logoutview a {
Expand Down Expand Up @@ -191,6 +192,7 @@ main {
font-size: 1em;
max-height: 130px;
overflow: overlay;
word-wrap: break-word;
}

#collectionsscene article:hover ul {
Expand Down Expand Up @@ -518,3 +520,7 @@ button.inline {
margin: 0 2px;
width: 1.4em;
}

.hidden {
display: none !important;
}
Loading
Loading