From b7c5f72aebb463ff24a6dbbfc067112c5f7ea297 Mon Sep 17 00:00:00 2001 From: Cornelius Roemer Date: Mon, 13 May 2024 20:44:37 +0200 Subject: [PATCH] feat(website): deflake group tests by disabling buttons in SSR Refactor reused isClient hook code into shared function --- website/src/components/SearchPage/SearchForm.tsx | 8 +++----- .../SeqSetCitations/SeqSetItemActions.tsx | 9 +++------ .../src/components/SeqSetCitations/SeqSetList.tsx | 9 +++------ .../src/components/Submission/DataUploadForm.tsx | 13 +++---------- website/src/components/User/GroupCreationForm.tsx | 9 ++++++++- website/src/components/User/GroupPage.tsx | 11 ++++++++++- website/src/hooks/isClient.ts | 13 +++++++++++++ website/tests/util/throwOnConsole.ts | 1 + 8 files changed, 44 insertions(+), 29 deletions(-) create mode 100644 website/src/hooks/isClient.ts diff --git a/website/src/components/SearchPage/SearchForm.tsx b/website/src/components/SearchPage/SearchForm.tsx index 4f465d921..928189b38 100644 --- a/website/src/components/SearchPage/SearchForm.tsx +++ b/website/src/components/SearchPage/SearchForm.tsx @@ -1,7 +1,7 @@ import CircularProgress from '@mui/material/CircularProgress'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { sentenceCase } from 'change-case'; -import { type FC, type FormEventHandler, useMemo, useState, useCallback, useEffect } from 'react'; +import { type FC, type FormEventHandler, useMemo, useState, useCallback } from 'react'; import { CustomizeModal } from './CustomizeModal.tsx'; import { AccessionField } from './fields/AccessionField.tsx'; @@ -12,6 +12,7 @@ import { NormalTextField } from './fields/NormalTextField'; import { PangoLineageField } from './fields/PangoLineageField'; import { getClientLogger } from '../../clientLogger.ts'; import { getLapisUrl } from '../../config.ts'; +import useClientFlag from '../../hooks/isClient.ts'; import { useOffCanvas } from '../../hooks/useOffCanvas'; import { type ClassOfSearchPageType, navigateToSearchLikePage } from '../../routes/routes.ts'; import type { AccessionFilter, GroupedMetadataFilter, MetadataFilter, MutationFilter } from '../../types/config.ts'; @@ -61,10 +62,7 @@ export const SearchForm: FC = ({ const [isLoading, setIsLoading] = useState(false); const { isOpen: isMobileOpen, close: closeOnMobile, toggle: toggleMobileOpen } = useOffCanvas(); const [isCustomizeModalOpen, setIsCustomizeModalOpen] = useState(false); - const [isClient, setIsClient] = useState(false); - useEffect(() => { - setIsClient(true); - }, []); + const isClient = useClientFlag(); const handleFieldChange = useCallback( (metadataName: string, filter: string) => { diff --git a/website/src/components/SeqSetCitations/SeqSetItemActions.tsx b/website/src/components/SeqSetCitations/SeqSetItemActions.tsx index 3a554e90a..adeefa691 100644 --- a/website/src/components/SeqSetCitations/SeqSetItemActions.tsx +++ b/website/src/components/SeqSetCitations/SeqSetItemActions.tsx @@ -1,8 +1,9 @@ -import { type FC, useState, useEffect } from 'react'; +import { type FC, useState } from 'react'; import { ExportSeqSet } from './ExportSeqSet'; import { SeqSetForm } from './SeqSetForm'; import { getClientLogger } from '../../clientLogger'; +import useClientFlag from '../../hooks/isClient.ts'; import { seqSetCitationClientHooks } from '../../services/serviceHooks'; import type { ClientConfig } from '../../types/runtimeConfig'; import type { SeqSetRecord, SeqSet } from '../../types/seqSetCitation'; @@ -32,11 +33,7 @@ const SeqSetItemActionsInner: FC = ({ const [editModalVisible, setEditModalVisible] = useState(false); const [exportModalVisible, setExportModalVisible] = useState(false); const { errorMessage, isErrorOpen, openErrorFeedback, closeErrorFeedback } = useErrorFeedbackState(); - const [isClient, setIsClient] = useState(false); - - useEffect(() => { - setIsClient(true); - }, []); + const isClient = useClientFlag(); const { mutate: deleteSeqSet } = useDeleteSeqSetAction( clientConfig, diff --git a/website/src/components/SeqSetCitations/SeqSetList.tsx b/website/src/components/SeqSetCitations/SeqSetList.tsx index a5b149ccb..cd81bb610 100644 --- a/website/src/components/SeqSetCitations/SeqSetList.tsx +++ b/website/src/components/SeqSetCitations/SeqSetList.tsx @@ -1,6 +1,7 @@ import MUIPagination from '@mui/material/Pagination'; -import { type FC, type MouseEvent, useState, useMemo, useEffect } from 'react'; +import { type FC, type MouseEvent, useState, useMemo } from 'react'; +import useClientFlag from '../../hooks/isClient'; import { routes } from '../../routes/routes'; import type { SeqSet } from '../../types/seqSetCitation'; import MdiTriangle from '~icons/mdi/triangle'; @@ -85,13 +86,9 @@ export const SeqSetList: FC = ({ seqSets, username }) => { const [order, setOrder] = useState('desc'); const [orderBy, setOrderBy] = useState('createdAt'); const [page, setPage] = useState(1); - const [isClient, setIsClient] = useState(false); + const isClient = useClientFlag(); const rowsPerPage = 5; - useEffect(() => { - setIsClient(true); - }, []); - const handleRequestSort = (_: MouseEvent, property: keyof SeqSet) => { const isAsc = orderBy === property && order === 'asc'; setOrder(isAsc ? 'desc' : 'asc'); diff --git a/website/src/components/Submission/DataUploadForm.tsx b/website/src/components/Submission/DataUploadForm.tsx index ddc9e3310..271103f7a 100644 --- a/website/src/components/Submission/DataUploadForm.tsx +++ b/website/src/components/Submission/DataUploadForm.tsx @@ -6,6 +6,7 @@ import { type FormEvent, useState, useRef, useEffect, useCallback, type ElementT import { DateChangeModal } from './DateChangeModal'; import { getClientLogger } from '../../clientLogger.ts'; import DataUseTermsSelector from '../../components/DataUseTerms/DataUseTermsSelector'; +import useClientFlag from '../../hooks/isClient.ts'; import { routes } from '../../routes/routes.ts'; import { backendApi } from '../../services/backendApi.ts'; import { backendClientHooks } from '../../services/serviceHooks.ts'; @@ -143,11 +144,7 @@ const UploadComponent = ({ }) => { const [myFile, rawSetMyFile] = useState(null); const [isDragOver, setIsDragOver] = useState(false); - const [isClient, setIsClient] = useState(false); - - useEffect(() => { - setIsClient(true); - }, []); + const isClient = useClientFlag(); const setMyFile = useCallback( (file: File | null) => { @@ -267,11 +264,7 @@ const InnerDataUploadForm = ({ const [dataUseTermsType, setDataUseTermsType] = useState(openDataUseTermsType); const [restrictedUntil, setRestrictedUntil] = useState(dateTimeInMonths(6)); - const [isClient, setIsClient] = useState(false); - - useEffect(() => { - setIsClient(true); - }, []); + const isClient = useClientFlag(); const handleLoadExampleData = async () => { const { metadataFileContent, revisedMetadataFileContent, sequenceFileContent } = getExampleData(exampleEntries); diff --git a/website/src/components/User/GroupCreationForm.tsx b/website/src/components/User/GroupCreationForm.tsx index beddb0b2a..f4823a489 100644 --- a/website/src/components/User/GroupCreationForm.tsx +++ b/website/src/components/User/GroupCreationForm.tsx @@ -1,6 +1,7 @@ import { type ComponentProps, type FC, type FormEvent, type PropsWithChildren, useState } from 'react'; import { listOfCountries } from './listOfCountries.ts'; +import useClientFlag from '../../hooks/isClient.ts'; import { useGroupCreation } from '../../hooks/useGroupOperations.ts'; import { routes } from '../../routes/routes.ts'; import { type ClientConfig } from '../../types/runtimeConfig.ts'; @@ -56,6 +57,8 @@ const InnerGroupCreationForm: FC = ({ clientConfig, accessTok } }; + const isClient = useClientFlag(); + return (

Create a new group

@@ -83,7 +86,11 @@ const InnerGroupCreationForm: FC = ({ clientConfig, accessTok
-
diff --git a/website/src/components/User/GroupPage.tsx b/website/src/components/User/GroupPage.tsx index 347d179a1..7f0b51081 100644 --- a/website/src/components/User/GroupPage.tsx +++ b/website/src/components/User/GroupPage.tsx @@ -1,5 +1,6 @@ import { type FC, type FormEvent, useState } from 'react'; +import useClientFlag from '../../hooks/isClient.ts'; import { useGroupPageHooks } from '../../hooks/useGroupOperations.ts'; import { routes } from '../../routes/routes.ts'; import type { Address, Group, GroupDetails } from '../../types/backend.ts'; @@ -31,6 +32,8 @@ const InnerGroupPage: FC = ({ const [errorMessage, setErrorMessage] = useState(undefined); + const isClient = useClientFlag(); + const { groupDetails, removeFromGroup, addUserToGroup } = useGroupPageHooks({ clientConfig, accessToken, @@ -101,6 +104,7 @@ const InnerGroupPage: FC = ({ }); }} className='object-right p-2 loculusColor text-white rounded px-4' + disabled={!isClient} > Leave group @@ -152,7 +156,11 @@ const InnerGroupPage: FC = ({ className='p-2 border border-gray-300 rounded mr-2' required /> - @@ -175,6 +183,7 @@ const InnerGroupPage: FC = ({ className='px-2 py-1 loculusColor text-white rounded' title='Remove user from group' aria-label={`Remove User ${user.name}`} + disabled={!isClient} > Remove user diff --git a/website/src/hooks/isClient.ts b/website/src/hooks/isClient.ts new file mode 100644 index 000000000..bc7747575 --- /dev/null +++ b/website/src/hooks/isClient.ts @@ -0,0 +1,13 @@ +import { useState, useEffect } from 'react'; + +function useClientFlag() { + const [isClient, setIsClient] = useState(false); + + useEffect(() => { + setIsClient(true); + }, []); + + return isClient; +} + +export default useClientFlag; diff --git a/website/tests/util/throwOnConsole.ts b/website/tests/util/throwOnConsole.ts index 1deab6fce..ce3d0af45 100644 --- a/website/tests/util/throwOnConsole.ts +++ b/website/tests/util/throwOnConsole.ts @@ -7,6 +7,7 @@ const messagesToIgnore = [ /\[vite\] ready\./, // Astro dev specific warning /Download the React DevTools for a better development experience: https:\/\/reactjs\.org\/link\/react-devtools/, // React info, not an error /\[astro-island\] Error hydrating .* TypeError: Importing a module script failed\./, // Fires in `astro dev` mode and only on webkit, related to preview apparently + /Error while running audit's match function: TypeError: Failed to fetch/, // Astro dev specific /downloadable font: kern: Too large subtable/, // firefox only, keycloak only, https://github.com/keycloak/keycloak/issues/29486 /downloadable font: Table discarded/, // firefox only, keycloak only /Target page, context or browser has been closed/, // Playwright specific warning after browser is closed