Repository: inovex/scrumlr.io
Pull request #3936 (2024-03-06 08:38:24)

tests: fix the error & warning messages when running tests
## Description
<!-- Explain the changes you’ve made. It doesn’t need to be fancy and you don’t have to get too technical. -->

## Changelog
<!-- Please provide a detailed list of the changes you have made to the codebase. -->

## Checklist

- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] The light- and dark-theme are both supported and tested
- [ ] The design was implemented and is responsive for all devices and screen sizes
- [ ] The application was tested in the most commonly used browsers (e.g. Chrome, Firefox, Safari)

## (Optional) Visual Changes
<!-- If available, please provide a before and after screenshot of your UI related changes. -->


github-actions[bot]: The deployment to the dev cluster was successful. You can find the deployment here: https://3936.development.scrumlr.fra.ics.inovex.io
This deployment is only for testing purposes and will be deleted after 1 week.
To redeploy rerun the workflow.
<strong>DO NOT STORE IMPORTANT DATA ON THIS DEPLOYMENT</strong>

<details>
<summary>Deployed Images</summary>

- ghcr.io/inovex/scrumlr.io/scrumlr-frontend:sha-5a0e1f2

- ghcr.io/inovex/scrumlr.io/scrumlr-server:sha-5a0e1f2

</details>
<!-- thollander/actions-comment-pull-request "dev-deployment" -->
octomind-dev[bot]: 
# 🐙 Octomind

[Test Report](https://app.octomind.dev/testreports/195d2dac-60bb-4868-9e6f-7d841ba4c5cf): 0/13 successful.


| description | status | details |
|--|--|--|
|change avatar|Waiting ⏳||
|check Privacy Policy|Waiting ⏳||
|check terms & conditions|Waiting ⏳||
|close cookie banner - front page|Waiting ⏳||
|close cookie banner - sign-in|Waiting ⏳||
|create and delete board columns|Waiting ⏳||
|create_and_delete_notes_and_actions_v2|Waiting ⏳||
|create lean coffee board|Waiting ⏳||
|edit_notes_and_actions_v5|Waiting ⏳||
|get started|Waiting ⏳||
|share_session|Waiting ⏳||
|sign-in|Waiting ⏳||
|test all ways to open the setup flow|Waiting ⏳||

commit sha: [5a0e1f2](https://github.com/inovex/scrumlr.io/commit/5a0e1f219a671133a4f1c23261c9357fc8559ad1)


Patch:
diff --git a/src/components/BoardHeader/HeaderMenu/BoardSettings/BoardSettings.tsx b/src/components/BoardHeader/HeaderMenu/BoardSettings/BoardSettings.tsx
index 86e8cd60..40afcb9e 100644
--- a/src/components/BoardHeader/HeaderMenu/BoardSettings/BoardSettings.tsx
+++ b/src/components/BoardHeader/HeaderMenu/BoardSettings/BoardSettings.tsx
@@ -1,5 +1,5 @@
 import "./BoardSettings.scss";
-import {Dispatch, SetStateAction, useEffect, useState} from "react";
+import {Dispatch, SetStateAction, useState} from "react";
 import store, {useAppSelector} from "store";
 import {ApplicationState} from "types";
 import {Actions} from "store/action";
@@ -14,18 +14,12 @@ export type BoardSettingsProps = {
 
 export const BoardSettings = (props: BoardSettingsProps) => {
   const {t} = useTranslation();
-  const state = useAppSelector((applicationState: ApplicationState) => ({
-    board: applicationState.board.data!,
-  }));
+  const name = useAppSelector((state: ApplicationState) => state.board.data!.name) ?? "";
 
-  const [boardName, setBoardName] = useState(state.board.name ?? "");
-
-  useEffect(() => {
-    setBoardName(state.board.name ?? "");
-  }, [state.board.name]);
+  const [boardName, setBoardName] = useState(name);
 
   const onSubmit = () => {
-    if (props.activeEditMode && state.board!.name !== boardName) {
+    if (props.activeEditMode && name !== boardName) {
       store.dispatch(Actions.editBoard({name: boardName}));
     }
     props.setActiveEditMode(!props.activeEditMode);
diff --git a/src/components/NoteInput/NoteInput.tsx b/src/components/NoteInput/NoteInput.tsx
index a18c5c40..d0d4c84e 100644
--- a/src/components/NoteInput/NoteInput.tsx
+++ b/src/components/NoteInput/NoteInput.tsx
@@ -9,7 +9,6 @@ import {useHotkeys} from "react-hotkeys-hook";
 import {Toast} from "utils/Toast";
 import {useImageChecker} from "utils/hooks/useImageChecker";
 import {useDispatch} from "react-redux";
-import {Tooltip} from "react-tooltip";
 import TextareaAutosize from "react-autosize-textarea";
 import {hotkeyMap} from "constants/hotkeys";
 import {useEmojiAutocomplete} from "utils/hooks/useEmojiAutocomplete";
@@ -73,8 +72,10 @@ export const NoteInput = ({columnIndex, columnId, columnIsVisible, toggleColumnV
         ref={noteInputRef}
         className="note-input__input"
         placeholder={t("NoteInput.placeholder")}
-        id={`note-input-${columnId}`}
+        data-tooltip-id="scrumlr-tooltip"
         data-tooltip-content={hotkeyKey}
+        data-tooltip-delay-show={500}
+        data-tooltip-float
         {...emoji.inputBindings}
         onKeyDown={(e) => {
           // handle emoji input
@@ -95,13 +96,6 @@ export const NoteInput = ({columnIndex, columnId, columnIsVisible, toggleColumnV
       <div className="note-input__emoji-suggestions">
         <EmojiSuggestions {...emoji.suggestionsProps} />
       </div>
-      <Tooltip
-        anchorSelect={`#note-input-${columnId}`}
-        place="bottom"
-        variant={document.documentElement.getAttribute("theme") === "dark" ? "dark" : "light"}
-        delayShow={500}
-        style={{zIndex: 999}}
-      />
       {isImage && (
         <div className="note-input__image-indicator" title={t("NoteInput.imageInfo")}>
           <ImageIcon className="note-input__icon--image" />
diff --git a/src/components/PassphraseDialog/PassphraseDialog.tsx b/src/components/PassphraseDialog/PassphraseDialog.tsx
index 2199f51c..31941be2 100644
--- a/src/components/PassphraseDialog/PassphraseDialog.tsx
+++ b/src/components/PassphraseDialog/PassphraseDialog.tsx
@@ -29,8 +29,8 @@ export const PassphraseDialog: FC<PassphraseDialogProps> = ({onSubmit, incorrect
   };
 
   const submitPassphraseForm = (e: FormEvent<HTMLFormElement>) => {
+    e.preventDefault();
     if (passphrase.length === 0) {
-      e.preventDefault();
       return;
     }
     onSubmit(passphrase);
diff --git a/src/components/SettingsDialog/ExportBoard/ExportHintHiddenContent/ExportHintHiddenContent.tsx b/src/components/SettingsDialog/ExportBoard/ExportHintHiddenContent/ExportHintHiddenContent.tsx
index 61950c89..83d4abd7 100644
--- a/src/components/SettingsDialog/ExportBoard/ExportHintHiddenContent/ExportHintHiddenContent.tsx
+++ b/src/components/SettingsDialog/ExportBoard/ExportHintHiddenContent/ExportHintHiddenContent.tsx
@@ -2,11 +2,12 @@ import {useTranslation} from "react-i18next";
 import {ReactComponent as InfoIcon} from "assets/icon-warning.svg";
 import {useAppSelector} from "store";
 import "./ExportHintHiddenContent.scss";
+import {isEqual} from "underscore";
 
 const ExportHintHiddenContent = () => {
   const {t} = useTranslation();
 
-  const hiddenColumns = useAppSelector((state) => state.columns.filter((col) => !col.visible));
+  const hiddenColumns = useAppSelector((state) => state.columns.filter((col) => !col.visible), isEqual);
   const showNotesOfOtherParticipants = useAppSelector((state) => state.board.data?.showNotesOfOtherUsers);
 
   if (hiddenColumns.length || !showNotesOfOtherParticipants) {
diff --git a/src/components/Tooltip/Tooltip.scss b/src/components/Tooltip/Tooltip.scss
index ade0e056..84de148a 100644
--- a/src/components/Tooltip/Tooltip.scss
+++ b/src/components/Tooltip/Tooltip.scss
@@ -1,9 +1,5 @@
 @import "constants/style.scss";
 
-.tooltip-container {
-  position: absolute;
-}
-
 .tooltip-container .tooltip {
   background-color: $hover-background-light;
   border-radius: $rounded--full;
@@ -11,6 +7,7 @@
   font-size: $text--sm;
   font-weight: 600;
   padding: $spacing--xs $spacing--base;
+  z-index: 9999;
 }
 
 [theme="dark"] .tooltip-container .tooltip {
diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx
index 9707c829..330e768d 100644
--- a/src/components/Tooltip/Tooltip.tsx
+++ b/src/components/Tooltip/Tooltip.tsx
@@ -19,6 +19,7 @@ export const Tooltip = (props: TooltipProps) => (
       className={classNames("tooltip", props.className)}
       content={props.content}
       role="tooltip"
+      positionStrategy="fixed"
     />
   </div>
 );
diff --git a/src/index.tsx b/src/index.tsx
index 868bf77f..d69fb67e 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -11,6 +11,7 @@ import i18n from "i18n";
 import {LoadingScreen} from "components/LoadingScreen";
 import {Actions} from "store/action";
 import {Html} from "components/Html";
+import {Tooltip} from "components/Tooltip";
 import {APP_VERSION_STORAGE_KEY} from "constants/storage";
 import {saveToStorage} from "utils/storage";
 import {register} from "serviceWorkerRegistration";
@@ -54,6 +55,7 @@ root.render(
       <Provider store={store}>
         <Html />
         <Suspense fallback={<LoadingScreen />}>
+          <Tooltip id="scrumlr-tooltip" />
           <ToastContainer limit={2} />
           <Router />
           {SHOW_LEGAL_DOCUMENTS && <CookieNotice />}


Test patch:
diff --git a/src/components/Board/__tests__/Board.test.tsx b/src/components/Board/__tests__/Board.test.tsx
index 2394a16c..8066be1d 100644
--- a/src/components/Board/__tests__/Board.test.tsx
+++ b/src/components/Board/__tests__/Board.test.tsx
@@ -7,6 +7,10 @@ import {render} from "testUtils";
 import getTestStore from "utils/test/getTestStore";
 import {CustomDndContext} from "components/DragAndDrop/CustomDndContext";
 
+jest.mock("utils/hooks/useImageChecker.ts", () => ({
+  useImageChecker: () => false,
+}));
+
 const createBoardWithColumns = (...colors: Color[]) => {
   return (
     <Provider store={getTestStore()}>
diff --git a/src/components/BoardHeader/HeaderMenu/__tests__/HeaderMenu.test.tsx b/src/components/BoardHeader/HeaderMenu/__tests__/HeaderMenu.test.tsx
index 0c228d0f..c6d42a26 100644
--- a/src/components/BoardHeader/HeaderMenu/__tests__/HeaderMenu.test.tsx
+++ b/src/components/BoardHeader/HeaderMenu/__tests__/HeaderMenu.test.tsx
@@ -3,7 +3,6 @@ import {screen} from "@testing-library/dom";
 import {HeaderMenu} from "components/BoardHeader/HeaderMenu";
 import {render} from "testUtils";
 import getTestStore from "utils/test/getTestStore";
-import {CustomDndContext} from "components/DragAndDrop/CustomDndContext";
 
 Object.assign(navigator, {
   clipboard: {
diff --git a/src/components/BoardUsers/__tests__/BoardUsers.test.tsx b/src/components/BoardUsers/__tests__/BoardUsers.test.tsx
index 1150f1f5..2aae495b 100644
--- a/src/components/BoardUsers/__tests__/BoardUsers.test.tsx
+++ b/src/components/BoardUsers/__tests__/BoardUsers.test.tsx
@@ -17,7 +17,16 @@ describe("users", () => {
   test("correct number of online users & count of rest users", () => {
     const boardUsers = render(
       createBoardUsers({
-        participants: {self: getTestParticipant(), others: [getTestParticipant({connected: false}), getTestParticipant(), getTestParticipant(), getTestParticipant()]},
+        participants: {
+          self: getTestParticipant(),
+          others: [
+            getTestParticipant({user: {id: "other-1", name: "other-1"}, connected: false}),
+            getTestParticipant({user: {id: "other-2", name: "other-2"}}),
+            getTestParticipant({user: {id: "other-3", name: "other-3"}}),
+            getTestParticipant({user: {id: "other-4", name: "other-4"}}),
+          ],
+          focusInitiator: null,
+        },
       })
     );
     expect(boardUsers.container.querySelectorAll(".user-avatar")).toHaveLength(4);
@@ -27,7 +36,13 @@ describe("users", () => {
       createBoardUsers({
         participants: {
           self: getTestParticipant(),
-          others: [getTestParticipant({connected: false}), getTestParticipant(), getTestParticipant(), getTestParticipant(), getTestParticipant()],
+          others: [
+            getTestParticipant({user: {id: "other-1", name: "other-1"}}),
+            getTestParticipant({user: {id: "other-2", name: "other-2"}}),
+            getTestParticipant({user: {id: "other-3", name: "other-3"}}),
+            getTestParticipant({user: {id: "other-4", name: "other-4"}}),
+          ],
+          focusInitiator: null,
         },
       })
     );
@@ -38,7 +53,14 @@ describe("users", () => {
       createBoardUsers({
         participants: {
           self: getTestParticipant(),
-          others: [getTestParticipant({connected: false}), getTestParticipant(), getTestParticipant(), getTestParticipant(), getTestParticipant(), getTestParticipant()],
+          others: [
+            getTestParticipant({user: {id: "other-1", name: "other-1"}}),
+            getTestParticipant({user: {id: "other-2", name: "other-2"}}),
+            getTestParticipant({user: {id: "other-3", name: "other-3"}}),
+            getTestParticipant({user: {id: "other-4", name: "other-4"}}),
+            getTestParticipant({user: {id: "other-5", name: "other-5"}}),
+          ],
+          focusInitiator: null,
         },
       })
     );
diff --git a/src/components/Column/__tests__/Column.test.tsx b/src/components/Column/__tests__/Column.test.tsx
index 7e9a7e88..007272be 100644
--- a/src/components/Column/__tests__/Column.test.tsx
+++ b/src/components/Column/__tests__/Column.test.tsx
@@ -5,6 +5,10 @@ import getTestStore from "utils/test/getTestStore";
 import {ApplicationState} from "types";
 import {CustomDndContext} from "components/DragAndDrop/CustomDndContext";
 
+jest.mock("utils/hooks/useImageChecker.ts", () => ({
+  useImageChecker: () => false,
+}));
+
 const createColumn = (overwrite?: Partial<ApplicationState>) => {
   return (
     <Provider store={getTestStore(overwrite)}>
diff --git a/src/components/Column/__tests__/__snapshots__/Column.test.tsx.snap b/src/components/Column/__tests__/__snapshots__/Column.test.tsx.snap
index a673ba22..859f9573 100644
--- a/src/components/Column/__tests__/__snapshots__/Column.test.tsx.snap
+++ b/src/components/Column/__tests__/__snapshots__/Column.test.tsx.snap
@@ -52,7 +52,9 @@ exports[`Column should have correct style show column with correct style 1`] = `
         <textarea
           class="note-input__input"
           data-tooltip-content="OPTION/ALT + 1"
-          id="note-input-test-columns-id-1"
+          data-tooltip-delay-show="500"
+          data-tooltip-float="true"
+          data-tooltip-id="scrumlr-tooltip"
           maxlength="1024"
           placeholder="Add your note..."
           rows="1"
diff --git a/src/components/NoteInput/__tests__/NoteInput.test.tsx b/src/components/NoteInput/__tests__/NoteInput.test.tsx
index 4728aa1d..3e0a6e5e 100644
--- a/src/components/NoteInput/__tests__/NoteInput.test.tsx
+++ b/src/components/NoteInput/__tests__/NoteInput.test.tsx
@@ -5,6 +5,10 @@ import i18nTest from "i18nTest";
 import {Provider} from "react-redux";
 import getTestStore from "utils/test/getTestStore";
 
+jest.mock("utils/hooks/useImageChecker.ts", () => ({
+  useImageChecker: () => false,
+}));
+
 const createNoteInput = (columnId: string, maxNoteLength: number) => (
   <I18nextProvider i18n={i18nTest}>
     <Provider store={getTestStore()}>
diff --git a/src/components/NoteInput/__tests__/__snapshots__/NoteInput.test.tsx.snap b/src/components/NoteInput/__tests__/__snapshots__/NoteInput.test.tsx.snap
index b825df00..29a6eb5d 100644
--- a/src/components/NoteInput/__tests__/__snapshots__/NoteInput.test.tsx.snap
+++ b/src/components/NoteInput/__tests__/__snapshots__/NoteInput.test.tsx.snap
@@ -6,7 +6,9 @@ exports[`Note Input should render correctly 1`] = `
 >
   <textarea
     class="note-input__input"
-    id="note-input-TestID"
+    data-tooltip-delay-show="500"
+    data-tooltip-float="true"
+    data-tooltip-id="scrumlr-tooltip"
     maxlength="1024"
     placeholder="Add your note..."
     rows="1"
diff --git a/src/components/PassphraseDialog/__tests__/PassphraseDialog.test.tsx b/src/components/PassphraseDialog/__tests__/PassphraseDialog.test.tsx
index 0802a225..e734a1ba 100644
--- a/src/components/PassphraseDialog/__tests__/PassphraseDialog.test.tsx
+++ b/src/components/PassphraseDialog/__tests__/PassphraseDialog.test.tsx
@@ -5,19 +5,19 @@ import {t} from "i18next";
 
 describe("<PassphraseDialog />", () => {
   test("snapshot test", () => {
-    const {container} = render(<PassphraseDialog onSubmit={jest.fn()} />);
+    const {container} = render(<PassphraseDialog onSubmit={jest.fn()} incorrectPassphrase={false} />);
     expect(container.firstChild).toMatchSnapshot();
   });
 
   test("should disable submit button if no passphrase has been entered", () => {
-    const {getByLabelText} = render(<PassphraseDialog onSubmit={jest.fn()} />);
+    const {getByLabelText} = render(<PassphraseDialog onSubmit={jest.fn()} incorrectPassphrase={false} />);
     const submitButton = getByLabelText(t("PassphraseDialog.submit"));
     expect(submitButton).toHaveAttribute("aria-disabled", "true");
   });
 
   test("should not submit if no passphrase has been entered", () => {
     const onSubmit = jest.fn();
-    const {getByLabelText} = render(<PassphraseDialog onSubmit={onSubmit} />);
+    const {getByLabelText} = render(<PassphraseDialog onSubmit={onSubmit} incorrectPassphrase={false} />);
     const submitButton = getByLabelText(t("PassphraseDialog.submit"));
     fireEvent.click(submitButton);
     expect(onSubmit).not.toHaveBeenCalled();
@@ -25,7 +25,7 @@ describe("<PassphraseDialog />", () => {
 
   test("should submit with correct passphrase if passphrase has been entered", () => {
     const onSubmit = jest.fn();
-    const {getByLabelText, getByPlaceholderText} = render(<PassphraseDialog onSubmit={onSubmit} />);
+    const {getByLabelText, getByPlaceholderText} = render(<PassphraseDialog onSubmit={onSubmit} incorrectPassphrase={false} />);
     const submitButton = getByLabelText(t("PassphraseDialog.submit"));
     const passwordInput = getByPlaceholderText(t("PassphraseDialog.inputPlaceholder"));
     fireEvent.change(passwordInput, {target: {value: "Passphrase"}});
@@ -35,7 +35,7 @@ describe("<PassphraseDialog />", () => {
   });
 
   test("should toggle the password visibility", () => {
-    const {getByDisplayValue, getByLabelText, getByPlaceholderText} = render(<PassphraseDialog onSubmit={jest.fn()} />);
+    const {getByDisplayValue, getByLabelText, getByPlaceholderText} = render(<PassphraseDialog onSubmit={jest.fn()} incorrectPassphrase={false} />);
     const passwordInput = getByPlaceholderText(t("PassphraseDialog.inputPlaceholder"));
     fireEvent.change(passwordInput, {target: {value: "Passphrase"}});
     expect(getByDisplayValue("Passphrase")).toHaveProperty("type", "password");
diff --git a/src/routes/StackView/__tests__/StackView.test.tsx b/src/routes/StackView/__tests__/StackView.test.tsx
index 0e58ad9f..64f5afe3 100644
--- a/src/routes/StackView/__tests__/StackView.test.tsx
+++ b/src/routes/StackView/__tests__/StackView.test.tsx
@@ -10,6 +10,10 @@ import {StackView} from "../StackView";
 import {ApplicationState} from "types";
 import getTestStore from "utils/test/getTestStore";
 
+jest.mock("utils/hooks/useImageChecker.ts", () => ({
+  useImageChecker: () => false,
+}));
+
 const BOARD_ID = "test-board-id";
 const NOTE_ID = "test-notes-id-1";
 
diff --git a/src/utils/__tests__/toast.test.tsx b/src/utils/__tests__/toast.test.tsx
index 00932e62..7135d6d5 100644
--- a/src/utils/__tests__/toast.test.tsx
+++ b/src/utils/__tests__/toast.test.tsx
@@ -1,4 +1,4 @@
-import {render, screen} from "@testing-library/react";
+import {act, render, screen} from "@testing-library/react";
 import {ToastContainer} from "react-toastify";
 import {Toast, Options} from "utils/Toast";
 
@@ -18,7 +18,10 @@ describe("Toast", () => {
     // arrange
     render(<ToastContainer />);
     // act
-    const toastId = Toast.success(testOptions);
+    let toastId;
+    act(() => {
+      toastId = Toast.success(testOptions);
+    });
     // assert
     expect(toastId).toBe("1");
     const toastElement = await screen.findByText(testOptions.title);