diff --git a/e2e/tests/entry.mobile.spec.ts b/e2e/tests/entry.mobile.spec.ts index 474d36a0..c1727a02 100644 --- a/e2e/tests/entry.mobile.spec.ts +++ b/e2e/tests/entry.mobile.spec.ts @@ -47,11 +47,8 @@ test.describe("Add entry mobile", () => { await page.getByRole("banner").getByLabel(t("controls.openMenu")).click(); await page.getByRole("button", { name: t("entryDialog.title.create") }).click(); await expect(page).toHaveURL(/.*\/create$/); - // Fill entry fields - await fillEntryFormMobile(page, t, entries[0]); - // Submit - await page.getByRole("button", { name: t("entryDialog.submit") }).click(); - await expect(page.getByText(t("notifications.addEntry.success"))).toBeAttached(); + await submitEntryMobile(page, t, entries[0]); + await expect(page).not.toHaveURL(/.*\/create$/); }); test("Should add entry from entry row", async ({ page, t }) => { @@ -61,9 +58,29 @@ test.describe("Add entry mobile", () => { .first() .click(); await expect(page).toHaveURL(/.*\/create$/); - await fillEntryFormMobile(page, t, entries[0]); - await page.getByRole("button", { name: t("entryDialog.submit") }).click(); - await expect(page.getByText(t("notifications.addEntry.success"))).toBeAttached(); + await submitEntryMobile(page, t, entries[0]); + await expect(page).not.toHaveURL(/.*\/create$/); + }); +}); + +test.describe("Add More entry mobile", () => { + test("Should add more entry from app bar", async ({ page, t }) => { + await page.getByRole("banner").getByLabel(t("controls.openMenu")).click(); + await page.getByRole("button", { name: t("entryDialog.title.create") }).click(); + await expect(page).toHaveURL(/.*\/create$/); + await addMoreEntryMobile(page, t, entries[0]); + await expect(page).toHaveURL(/.*\/create$/); + }); + + test("Should add more entry from entry row", async ({ page, t }) => { + await page + .getByRole("button") + .getByRole("button", { name: t("entryDialog.title.create") }) + .first() + .click(); + await expect(page).toHaveURL(/.*\/create$/); + await addMoreEntryMobile(page, t, entries[0]); + await expect(page).toHaveURL(/.*\/create$/); }); }); @@ -148,3 +165,15 @@ const setDefaultValuesMobile = async ( activity, ); }; + +const submitEntryMobile = async (page: Page, t: TFunction, entry: TestEntry) => { + await fillEntryFormMobile(page, t, entry); + await page.getByRole("button", { name: t("entryDialog.submit") }).click(); + await expect(page.getByText(t("notifications.addEntry.success"))).toBeAttached(); +}; + +const addMoreEntryMobile = async (page: Page, t: TFunction, entry: TestEntry) => { + await fillEntryFormMobile(page, t, entry); + await page.getByRole("button", { name: t("entryDialog.addMore") }).click(); + await expect(page.getByText(t("notifications.addEntry.success"))).toBeAttached(); +}; diff --git a/e2e/tests/entry.spec.ts b/e2e/tests/entry.spec.ts index a62ec612..c3d96b56 100644 --- a/e2e/tests/entry.spec.ts +++ b/e2e/tests/entry.spec.ts @@ -43,17 +43,13 @@ test.beforeEach(async ({ page }) => { test.describe("Add entry", () => { test("Should add entry from app bar", async ({ page, t }) => { - // Open entry dialog await page .getByRole("banner") .getByRole("button", { name: t("entryDialog.title.create") }) .click(); await expect(page).toHaveURL(/.*\/create$/); - // Fill entry fields - await fillEntryForm(page, t, entries[0]); - // Submit - await page.getByRole("button", { name: t("entryDialog.submit") }).click(); - await expect(page.getByText(t("notifications.addEntry.success"))).toBeAttached(); + await submitEntry(page, t, entries[0]); + await expect(page).not.toHaveURL(/.*\/create$/); }); test("Should add entry from entry row", async ({ page, t }) => { @@ -63,9 +59,31 @@ test.describe("Add entry", () => { .first() .click(); await expect(page).toHaveURL(/.*\/create$/); - await fillEntryForm(page, t, entries[0]); - await page.getByRole("button", { name: t("entryDialog.submit") }).click(); - await expect(page.getByText(t("notifications.addEntry.success"))).toBeAttached(); + await submitEntry(page, t, entries[0]); + await expect(page).not.toHaveURL(/.*\/create$/); + }); +}); + +test.describe("Add More entry", () => { + test("Should add more entry from app bar", async ({ page, t }) => { + await page + .getByRole("banner") + .getByRole("button", { name: t("entryDialog.title.create") }) + .click(); + await expect(page).toHaveURL(/.*\/create$/); + await addMoreEntry(page, t, entries[0]); + await expect(page).toHaveURL(/.*\/create$/); + }); + + test("Should add more entry from entry row", async ({ page, t }) => { + await page + .getByRole("button") + .getByRole("button", { name: t("entryDialog.title.create") }) + .first() + .click(); + await expect(page).toHaveURL(/.*\/create$/); + await addMoreEntry(page, t, entries[0]); + await expect(page).toHaveURL(/.*\/create$/); }); }); @@ -148,3 +166,15 @@ const setDefaultValues = async (page: Page, t: TFunction, product: string, activ activity, ); }; + +const submitEntry = async (page: Page, t: TFunction, entry: TestEntry) => { + await fillEntryForm(page, t, entry); + await page.getByRole("button", { name: t("entryDialog.submit") }).click(); + await expect(page.getByText(t("notifications.addEntry.success"))).toBeAttached(); +}; + +const addMoreEntry = async (page: Page, t: TFunction, entry: TestEntry) => { + await fillEntryForm(page, t, entry); + await page.getByRole("button", { name: t("entryDialog.addMore") }).click(); + await expect(page.getByText(t("notifications.addEntry.success"))).toBeAttached(); +}; diff --git a/web/src/components/entry-dialog/EntryForm.tsx b/web/src/components/entry-dialog/EntryForm.tsx index 643edb1e..f7fdf326 100644 --- a/web/src/components/entry-dialog/EntryForm.tsx +++ b/web/src/components/entry-dialog/EntryForm.tsx @@ -16,7 +16,7 @@ import { useTheme, } from "@mui/material"; import { Dayjs } from "dayjs"; -import { useEffect } from "react"; +import { useEffect, useRef } from "react"; import { Controller, UseFormReturn } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useLocation, useNavigate } from "react-router-dom"; @@ -46,6 +46,12 @@ type LocationState = { editEntry?: Entry; }; +enum SubmitTypes { + addMore = "addMore", + submit = "submit", +} +type SubmitType = SubmitTypes | null; + const EntryForm = () => { const { state } = useLocation(); const dayjs = useDayjs(); @@ -53,6 +59,7 @@ const EntryForm = () => { const { date: originalDate, editEntry }: LocationState = state || {}; const { form, onSubmit, loading } = useEntryForm({ editEntry, date: dayjs(originalDate) }); const navigate = useNavigate(); + const submitter = useRef(null); const { handleSubmit, @@ -63,7 +70,8 @@ const EntryForm = () => { useEffect(() => { if (isSubmitSuccessful) { reset(); - navigate(".."); + submitter.current !== SubmitTypes.addMore && navigate(".."); + submitter.current = null; } }, [isSubmitSuccessful, navigate, reset]); @@ -75,6 +83,11 @@ const EntryForm = () => { const dateWatch = dayjs(watch("date")).locale(dayjs.locale()); const { isJiraAuth } = useIsJiraAuthenticated(); + const handleAddMore = () => { + handleSubmit(onSubmit)(); + submitter.current = SubmitTypes.addMore; + }; + return (
@@ -180,6 +193,19 @@ const EntryForm = () => { {t("entryDialog.submit")} + {!editEntry && ( + + handleAddMore()} + variant="contained" + size="large" + fullWidth + > + {t("entryDialog.addMore")} + + + )} + {!editEntry && ( + handleAddMore()} + variant="contained" + size="large" + > + {t("entryDialog.addMore")} + + )} {t("entryDialog.submit")} diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index 63926c63..f08e65ab 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -46,6 +46,7 @@ const en = { duration: "Duration", reset: "Reset", submit: "Save", + addMore: "Add more", product: "Product", activity: "Function", issue: "Issue", diff --git a/web/src/i18n/fi.ts b/web/src/i18n/fi.ts index 507265bd..3f36f414 100644 --- a/web/src/i18n/fi.ts +++ b/web/src/i18n/fi.ts @@ -46,6 +46,7 @@ const fi = { duration: "Kesto", reset: "Palauta", submit: "Tallenna", + addMore: "Lisää seuraava", product: "Tuote", activity: "Toiminto", issue: "Tiketti",