diff --git a/__test__/features/Calendars/CalendarAPI.test.tsx b/__test__/features/Calendars/CalendarAPI.test.tsx index c17e69c..9c3a901 100644 --- a/__test__/features/Calendars/CalendarAPI.test.tsx +++ b/__test__/features/Calendars/CalendarAPI.test.tsx @@ -4,6 +4,7 @@ import { addSharedCalendar, getCalendar, getCalendars, + getSecretLink, postCalendar, proppatchCalendar, removeCalendar, @@ -114,6 +115,40 @@ describe("Calendar API", () => { }); }); + it("get secret link ", async () => { + const calLink = "/calendars/calId.json"; + (api.get as jest.Mock).mockReturnValue({ + json: jest.fn().mockResolvedValue("link"), + }); + + const noreset = await getSecretLink(calLink, false); + + expect(api.get).toHaveBeenCalledWith( + `calendar/api${calLink}/secret-link?shouldResetLink=false`, + { + headers: { + Accept: "application/json, text/plain, */*", + }, + } + ); + }); + it("get secret link ", async () => { + const calLink = "/calendars/calId.json"; + (api.get as jest.Mock).mockReturnValue({ + json: jest.fn().mockResolvedValue("link"), + }); + const reset = await getSecretLink(calLink, true); + + expect(api.get).toHaveBeenCalledWith( + `calendar/api${calLink}/secret-link?shouldResetLink=true`, + { + headers: { + Accept: "application/json, text/plain, */*", + }, + } + ); + }); + it("When adding a sharedCal with #default as a name a new name is sent to the back", async () => { const mockApiPost = jest.spyOn(api, "post"); diff --git a/__test__/features/Calendars/CalendarModal.test.tsx b/__test__/features/Calendars/CalendarModal.test.tsx index c0a6a65..dc904b0 100644 --- a/__test__/features/Calendars/CalendarModal.test.tsx +++ b/__test__/features/Calendars/CalendarModal.test.tsx @@ -3,6 +3,11 @@ import CalendarPopover from "../../../src/components/Calendar/CalendarModal"; import { renderWithProviders } from "../../utils/Renderwithproviders"; import * as eventThunks from "../../../src/features/Calendars/CalendarSlice"; import { Calendars } from "../../../src/features/Calendars/CalendarTypes"; +import { getSecretLink } from "../../../src/features/Calendars/CalendarApi"; + +jest.mock("../../../src/features/Calendars/CalendarApi", () => ({ + getSecretLink: jest.fn(), +})); describe("CalendarPopover", () => { const mockOnClose = jest.fn(); @@ -308,6 +313,9 @@ describe("CalendarPopover - Tabs Scenarios", () => { Object.assign(navigator, { clipboard: { writeText: jest.fn() }, }); + (getSecretLink as jest.Mock).mockResolvedValue({ + secretLink: "https://example.org/secret/initial", + }); renderWithProviders( { expect(input).toHaveValue("https://cal.example.org/calendars/user1/cal1"); // Click copy button (find button containing ContentCopyIcon) - const copyIcon = screen.getByTestId("ContentCopyIcon"); + const copyIcon = screen.getAllByTestId("ContentCopyIcon")[0]; const copyButton = copyIcon.closest("button"); if (copyButton) { fireEvent.click(copyButton); @@ -433,4 +441,46 @@ describe("CalendarPopover - Tabs Scenarios", () => { expect(importButton).toBeDisabled(); }); }); + + it("fetches and resets the secret link", async () => { + (window as any).CALENDAR_BASE_URL = "https://cal.example.org"; + + (getSecretLink as jest.Mock) + .mockResolvedValueOnce({ + secretLink: "https://example.org/secret/initial", + }) + .mockResolvedValueOnce({ + secretLink: "https://example.org/secret/new", + }); + + renderWithProviders( + , + { user: baseUser } + ); + + fireEvent.click(screen.getByRole("tab", { name: /Access/i })); + + await waitFor(() => + expect( + screen.getByDisplayValue("https://example.org/secret/initial") + ).toBeInTheDocument() + ); + + fireEvent.click(screen.getByRole("button", { name: /reset/i })); + + await waitFor(() => + expect( + screen.getByDisplayValue("https://example.org/secret/new") + ).toBeInTheDocument() + ); + + expect(getSecretLink).toHaveBeenCalledWith( + existingCalendar.link.replace(".json", ""), + true + ); + }); }); diff --git a/src/components/Calendar/AccessTab.tsx b/src/components/Calendar/AccessTab.tsx index ba361cc..ce9e054 100644 --- a/src/components/Calendar/AccessTab.tsx +++ b/src/components/Calendar/AccessTab.tsx @@ -1,39 +1,102 @@ import ContentCopyIcon from "@mui/icons-material/ContentCopy"; -import { Box, IconButton, TextField } from "@mui/material"; -import { useState } from "react"; +import { + Box, + IconButton, + TextField, + Button, + Typography, + InputAdornment, +} from "@mui/material"; +import { useState, useEffect } from "react"; +import { getSecretLink } from "../../features/Calendars/CalendarApi"; import { Calendars } from "../../features/Calendars/CalendarTypes"; +import { FieldWithLabel } from "../Event/components/FieldWithLabel"; import { SnackbarAlert } from "../Loading/SnackBarAlert"; import { useI18n } from "cozy-ui/transpiled/react/providers/I18n"; export function AccessTab({ calendar }: { calendar: Calendars }) { const { t } = useI18n(); const calDAVLink = `${(window as any).CALENDAR_BASE_URL}${calendar.link.replace(".json", "")}`; + + const [secretLink, setSecretLink] = useState(""); const [open, setOpen] = useState(false); - const handleCopy = () => { - navigator.clipboard.writeText(calDAVLink); + useEffect(() => { + async function fetchSecret() { + const existing = await getSecretLink( + calendar.link.replace(".json", ""), + false + ); + setSecretLink(existing.secretLink); + } + fetchSecret(); + }, [calendar.link]); + + const handleCopy = (content: string) => { + navigator.clipboard.writeText(content); setOpen(true); }; + const handleResetSecretLink = async () => { + const newSecret = await getSecretLink( + calendar.link.replace(".json", ""), + true + ); + setSecretLink(newSecret.secretLink); + }; + return ( <> - - + + - - + + handleCopy(calDAVLink)} edge="end"> + + + ), - }, - }} - /> - + }} + /> + + + + + + + handleCopy(secretLink)} edge="end"> + + + + ), + }} + /> + + + + + + {t("calendar.secretUrlDesc")} + +