fix lint issues

This commit is contained in:
juhanikat
2026-08-19 16:00:00 +03:00
parent e7b1805908
commit 43db2be4e8
37 changed files with 209 additions and 220 deletions
@@ -72,14 +72,14 @@ export const CrossSearchTable = ({ selectorFn }: { selectorFn?: (newObject: Cros
header: 'Dec lat',
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
accessorKey: 'dec_long',
header: 'Dec long',
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
accessorKey: 'altitude',
@@ -462,7 +462,7 @@ export const CrossSearchTable = ({ selectorFn }: { selectorFn?: (newObject: Cros
size: 20,
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
accessorKey: 'dc13_n',
@@ -477,7 +477,7 @@ export const CrossSearchTable = ({ selectorFn }: { selectorFn?: (newObject: Cros
size: 20,
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
accessorKey: 'dc13_min',
@@ -485,7 +485,7 @@ export const CrossSearchTable = ({ selectorFn }: { selectorFn?: (newObject: Cros
size: 20,
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
accessorKey: 'dc13_stdev',
@@ -493,7 +493,7 @@ export const CrossSearchTable = ({ selectorFn }: { selectorFn?: (newObject: Cros
size: 20,
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
accessorKey: 'do18_mean',
@@ -501,7 +501,7 @@ export const CrossSearchTable = ({ selectorFn }: { selectorFn?: (newObject: Cros
size: 20,
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
accessorKey: 'do18_n',
@@ -516,7 +516,7 @@ export const CrossSearchTable = ({ selectorFn }: { selectorFn?: (newObject: Cros
size: 20,
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
accessorKey: 'do18_min',
@@ -524,7 +524,7 @@ export const CrossSearchTable = ({ selectorFn }: { selectorFn?: (newObject: Cros
size: 20,
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
accessorKey: 'do18_stdev',
@@ -532,7 +532,7 @@ export const CrossSearchTable = ({ selectorFn }: { selectorFn?: (newObject: Cros
size: 20,
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
id: 'microwear_now_ls',
@@ -184,7 +184,7 @@ const NewReferenceDialog = ({
data: emptyReference,
mode: modeOptionToMode.new,
setMode: () => undefined,
editData: emptyReference as EditDataType<ReferenceDetailsType>,
editData: emptyReference,
textField,
dropdown,
dropdownWithSearch,
@@ -38,7 +38,7 @@ const createMockDetailContext = (reference: ReferenceDetailsType): DetailContext
data: reference,
mode: readMode,
setMode,
editData: reference as EditDataType<ReferenceDetailsType>,
editData: reference,
setEditData,
isDirty: false,
resetEditData,
@@ -92,8 +92,8 @@ export const applyDefaultSpeciesOrdering = <T extends MRT_RowData>(
return [...rows].sort((leftRow, rightRow) => {
for (const field of fields) {
const left = normalizeSortValue(getNestedValue(leftRow as MRT_RowData, field))
const right = normalizeSortValue(getNestedValue(rightRow as MRT_RowData, field))
const left = normalizeSortValue(getNestedValue(leftRow, field))
const right = normalizeSortValue(getNestedValue(rightRow, field))
const compared = left.localeCompare(right)
if (compared !== 0) {
@@ -73,7 +73,7 @@ export const UpdateTab = <T, UpdateType extends MRT_RowData & { updates: UpdateL
authorizer={row.original[`${prefix}_authorizer`] as string}
coordinator={row.original[`${prefix}_coordinator`] as string}
updates={row.original.updates}
comment={row.original[`${prefix}_comment` as keyof UpdateType] as string}
comment={row.original[`${prefix}_comment` as keyof UpdateType]}
references={row.original[refFieldName]}
/>
),
@@ -278,9 +278,9 @@ const MultiSelector = <T extends object>({
export const EditableTextField = <T extends object>(props: EditableTextFieldProps<T>) => {
const { field, disabled = false, big = false, readonly = false, handleSetEditData } = props
const type = props.type ?? 'text'
const trim = type === 'text' ? (props as EditableTextFieldTextProps<T>).trim ?? false : false
const trim = type === 'text' ? ((props as EditableTextFieldTextProps<T>).trim ?? false) : false
const round = type === 'number' ? (props as EditableTextFieldNumberProps<T>).round : undefined
const integerOnly = type === 'number' ? (props as EditableTextFieldNumberProps<T>).integerOnly ?? false : false
const integerOnly = type === 'number' ? ((props as EditableTextFieldNumberProps<T>).integerOnly ?? false) : false
const min = type === 'number' ? (props as EditableTextFieldNumberProps<T>).min : undefined
const { setEditData, editData, validator, fieldsWithErrors, setFieldsWithErrors } = useDetailContext<T>()
const errorObject = validator(editData, field)
@@ -402,7 +402,7 @@ export const EditableTextField = <T extends object>(props: EditableTextFieldProp
if (integerOnly && event.key === '.') event.preventDefault()
}}
id={`${String(field)}-textfield`}
value={type === 'number' ? numberInputValue : editData[field] ?? ''}
value={type === 'number' ? numberInputValue : (editData[field] ?? '')}
variant="outlined"
size="small"
error={!!error}
@@ -458,9 +458,7 @@ type EditableTextFieldDateProps<T extends object> = EditableTextFieldCommonProps
}
export type EditableTextFieldProps<T extends object> =
| EditableTextFieldTextProps<T>
| EditableTextFieldNumberProps<T>
| EditableTextFieldDateProps<T>
EditableTextFieldTextProps<T> | EditableTextFieldNumberProps<T> | EditableTextFieldDateProps<T>
export const FieldWithTableSelection = <T extends object, ParentType extends object>({
targetField,
@@ -41,7 +41,7 @@ export const LocalityTable = ({ selectorFn }: { selectorFn?: (newObject: Localit
if (validIds.length > 0)
setFilteredLocalities(localitiesQueryData?.filter(locality => validIds.includes(locality.lid)))
else setFilteredLocalities(localitiesQueryData as SimplifiedLocality[])
else setFilteredLocalities(localitiesQueryData)
}, [columnFilters, localitiesQueryData])
const columns = useMemo<MRT_ColumnDef<Locality>[]>(
@@ -87,14 +87,14 @@ export const LocalityTable = ({ selectorFn }: { selectorFn?: (newObject: Localit
header: 'Dec lat',
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
accessorKey: 'dec_long',
header: 'Dec long',
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
accessorKey: 'altitude',
@@ -106,7 +106,7 @@ export const LocalityTable = ({ selectorFn }: { selectorFn?: (newObject: Localit
header: 'Max age',
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
id: 'bfa_max',
@@ -130,7 +130,7 @@ export const LocalityTable = ({ selectorFn }: { selectorFn?: (newObject: Localit
header: 'Min age',
filterVariant: 'range',
enableColumnFilterModes: false,
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue() as number),
Cell: ({ cell }) => formatWithMaxThreeDecimals(cell.getValue()),
},
{
id: 'bfa_min',
@@ -93,19 +93,17 @@ export const MuseumTab = () => {
alt_int_name: normalized.alt_int_name || null,
state: normalized.state || null,
state_code: normalized.state_code || null,
} as Museum).unwrap()
}).unwrap()
const refetchResult = await refetchMuseums()
const refreshedMuseums = refetchResult.data ?? museumData ?? []
const resolvedMuseum =
refreshedMuseums.find(museum => museum.museum === museumId) ??
({
...normalized,
museum: museumId,
alt_int_name: normalized.alt_int_name || null,
state: normalized.state || null,
state_code: normalized.state_code || null,
} as Museum)
const resolvedMuseum = refreshedMuseums.find(museum => museum.museum === museumId) ?? {
...normalized,
museum: museumId,
alt_int_name: normalized.alt_int_name || null,
state: normalized.state || null,
state_code: normalized.state_code || null,
}
if (editData.now_mus.some(link => link.museum === museumId)) {
notify('Museum already linked to this locality.', 'warning')
@@ -15,7 +15,7 @@ const validateOccurrenceDetail = (
editData: EditDataType<OccurrenceDetailsType>,
fieldName: keyof EditDataType<OccurrenceDetailsType>
): ValidationObject => {
return validateOccurrence(editData as EditableOccurrenceData, fieldName as keyof EditableOccurrenceData)
return validateOccurrence(editData, fieldName as keyof EditableOccurrenceData)
}
const emptyOccurrence: OccurrenceDetailsType = {
@@ -134,10 +134,7 @@ export const ReferenceTab = () => {
}),
])
} else if (field.field_name == 'exact_date') {
nonAuthorFieldsArray.push([
field.ref_field_name,
textField(field.field_name as keyof ReferenceDetailsType, { type: 'date' }),
])
nonAuthorFieldsArray.push([field.ref_field_name, textField(field.field_name, { type: 'date' })])
} else {
nonAuthorFieldsArray.push([field.ref_field_name, fieldComponent])
}
@@ -54,7 +54,7 @@ jest.mock('@/redux/timeUnitReducer', () => {
// Handle edge case when offset exceeds totalItems
const clampedPageIndex = Math.min(derivedPageIndex, sequencesByPage.length - 1)
const pageData = clampedPageIndex >= 0 ? sequencesByPage[clampedPageIndex] ?? [] : []
const pageData = clampedPageIndex >= 0 ? (sequencesByPage[clampedPageIndex] ?? []) : []
return {
data: {
@@ -50,7 +50,7 @@ export const SpeciesDetails = ({
// this should be removed if validators are improved in the future
if (data) {
const fixedSpeciesData = fixNullValuesInTaxonomyFields(data)
setModifiedData(fixedSpeciesData as SpeciesDetailsType)
setModifiedData(fixedSpeciesData)
}
}, [data])
@@ -20,10 +20,7 @@ export const SynonymTab = () => {
const convertAndCheckNewSynonymTaxonomy = (newSynonym: EditDataType<SpeciesSynonym>) => {
const errors = []
for (const field in newSynonym) {
const errorObject = validateSynonym(
newSynonym as unknown as EditDataType<SpeciesSynonym>,
field as unknown as keyof EditDataType<SpeciesSynonym>
)
const errorObject = validateSynonym(newSynonym, field as unknown as keyof EditDataType<SpeciesSynonym>)
const { error } = errorObject
if (error) errors.push(errorObject)
}
@@ -140,7 +140,7 @@ const applyColumnFilterModeDefaults = <T extends MRT_RowData>(
columnFilterFns[columnId] = 'equals'
return {
...column,
filterFn: typeof column.filterFn === 'function' ? column.filterFn : column.filterFn ?? 'equals',
filterFn: typeof column.filterFn === 'function' ? column.filterFn : (column.filterFn ?? 'equals'),
enableColumnFilterModes: false,
columnFilterModeOptions: ['equals'],
}
@@ -150,7 +150,7 @@ const applyColumnFilterModeDefaults = <T extends MRT_RowData>(
columnFilterFns[columnId] = 'equals'
return {
...column,
filterFn: typeof column.filterFn === 'function' ? column.filterFn : column.filterFn ?? 'equals',
filterFn: typeof column.filterFn === 'function' ? column.filterFn : (column.filterFn ?? 'equals'),
enableColumnFilterModes: false,
columnFilterModeOptions: ['equals'],
}
@@ -22,49 +22,47 @@ jest.mock('@/shared/types', () => ({}))
type TestPageContext = PageContextType<unknown>
const createPageContextValue = (overrides: Partial<TestPageContext> = {}): TestPageContext =>
({
idList: [],
setIdList: () => {},
idFieldName: 'rid',
viewName: 'reference',
previousTableUrls: [],
setPreviousTableUrls: () => {},
createTitle: () => '',
createSubtitle: () => '',
editRights: {},
sqlLimit: 25,
sqlOffset: 0,
sqlColumnFilters: [],
sqlOrderBy: [],
setSqlLimit: () => {},
setSqlOffset: () => {},
setSqlColumnFilters: () => {},
setSqlOrderBy: () => {},
...overrides,
}) as TestPageContext
const createPageContextValue = (overrides: Partial<TestPageContext> = {}): TestPageContext => ({
idList: [],
setIdList: () => {},
idFieldName: 'rid',
viewName: 'reference',
previousTableUrls: [],
setPreviousTableUrls: () => {},
createTitle: () => '',
createSubtitle: () => '',
editRights: {},
sqlLimit: 25,
sqlOffset: 0,
sqlColumnFilters: [],
sqlOrderBy: [],
setSqlLimit: () => {},
setSqlOffset: () => {},
setSqlColumnFilters: () => {},
setSqlOrderBy: () => {},
...overrides,
})
type TestDetailContext = DetailContextType<unknown>
const createDetailContextValue = (overrides: Partial<TestDetailContext> = {}): TestDetailContext =>
({
data: {},
mode: modeOptionToMode.read,
setMode: () => {},
editData: {} as TestDetailContext['editData'],
setEditData: (() => {}) as TestDetailContext['setEditData'],
isDirty: false,
resetEditData: () => {},
textField: (() => <></>) as TestDetailContext['textField'],
bigTextField: (() => <></>) as TestDetailContext['bigTextField'],
dropdown: (() => <></>) as TestDetailContext['dropdown'],
dropdownWithSearch: (() => <></>) as TestDetailContext['dropdownWithSearch'],
radioSelection: (() => <></>) as TestDetailContext['radioSelection'],
validator: () => ({ name: 'field', error: null }),
fieldsWithErrors: {},
setFieldsWithErrors: () => {},
...overrides,
}) as TestDetailContext
const createDetailContextValue = (overrides: Partial<TestDetailContext> = {}): TestDetailContext => ({
data: {},
mode: modeOptionToMode.read,
setMode: () => {},
editData: {},
setEditData: () => {},
isDirty: false,
resetEditData: () => {},
textField: () => <></>,
bigTextField: () => <></>,
dropdown: () => <></>,
dropdownWithSearch: () => <></>,
radioSelection: () => <></>,
validator: () => ({ name: 'field', error: null }),
fieldsWithErrors: {},
setFieldsWithErrors: () => {},
...overrides,
})
const pageContextValue: TestPageContext = createPageContextValue()
+2 -2
View File
@@ -190,13 +190,13 @@ export const usePaginatedQuery = <
}, [rows, envelope])
const transformedRows = useMemo(() => {
if (!rows) return rows as unknown as TMapped[] | undefined
if (!rows) return rows
if (!selectorFn) return rows as unknown as TMapped[]
return rows.map((item, index) => {
const mapped = selectorFn(item)
if (index === 0 && typeof fullCount === 'number' && isObject(mapped) && !('full_count' in mapped)) {
return { ...mapped, full_count: fullCount } as TMapped
return { ...mapped, full_count: fullCount }
}
return mapped
})
@@ -117,8 +117,9 @@ const taxonomyFields = new Set([
])
const isEmptyValue = (value: unknown) => value === null || value === undefined || value === ''
const normalizeValue = (value: unknown) => (isEmptyValue(value) ? '' : String(value))
const isSameValue = (left: unknown, right: unknown) => normalizeValue(left) === normalizeValue(right)
const normalizeValue = (value: string | number | boolean | null) => (isEmptyValue(value) ? '' : String(value))
const isSameValue = (left: string | number | boolean | null, right: string | number | boolean | null) =>
normalizeValue(left) === normalizeValue(right)
const resolveMergeErrorMessage = (error: unknown): string => {
if (error && typeof error === 'object' && 'data' in error) {
+4 -4
View File
@@ -50,21 +50,21 @@ const authorCheck: (data: ReferenceAuthorType[]) => ValidationError = (data: Ref
}
}
if (errors.length > 0) {
return ('Authors gave the following errors: ' + errors.join(', ')) as ValidationError
return 'Authors gave the following errors: ' + errors.join(', ')
}
return null as ValidationError
}
const journalCheck: (journal: ReferenceJournalType) => ValidationError = (journal: ReferenceJournalType) => {
if (!journal) {
return 'You must select or create a new journal' as ValidationError
return 'You must select or create a new journal'
}
//Existing journal can't have rowState 'removed' if journal is mandatory
if (journal.rowState && journal.rowState == 'removed') {
return 'You must select or create a new journal' as ValidationError
return 'You must select or create a new journal'
}
if (typeof journal.journal_title !== 'string' || journal.journal_title?.length < 1) {
return 'Journal must have a title' as ValidationError
return 'Journal must have a title'
}
return null as ValidationError
}
@@ -74,7 +74,7 @@ const createProvider = (state: TestContextState) => (
<DetailContextProvider
contextState={{
...state,
editData: state.data as EditDataType<TestData>,
editData: state.data,
textField: () => <></>,
bigTextField: () => <></>,
dropdown: () => <></>,
@@ -62,7 +62,7 @@ const TimeUnitTabContent = () => {
onClick={() =>
setEditData({
...editData,
up_bound: { ...(editData.up_bound ?? {}), age: 99 } as TimeUnitDetailsType['up_bound'],
up_bound: { ...(editData.up_bound ?? {}), age: 99 },
})
}
>
@@ -63,7 +63,7 @@ const TimeUnitTabContent = () => {
onClick={() =>
setEditData({
...editData,
up_bound: { ...(editData.up_bound ?? {}), age: 99 } as TimeUnitDetailsType['up_bound'],
up_bound: { ...(editData.up_bound ?? {}), age: 99 },
})
}
>
@@ -29,14 +29,14 @@ const createContextValue = (): DetailContextType<ParentType> => ({
data: { species: [] },
mode: modeOptionToMode.edit,
setMode: jest.fn(),
editData: { species: [] } as EditDataType<ParentType>,
editData: { species: [] },
setEditData: jest.fn(),
textField: (() => <span />) as DetailContextType<ParentType>['textField'],
bigTextField: (() => <span />) as DetailContextType<ParentType>['bigTextField'],
dropdown: (() => <span />) as DetailContextType<ParentType>['dropdown'],
dropdownWithSearch: (() => <span />) as DetailContextType<ParentType>['dropdownWithSearch'],
radioSelection: (() => <span />) as DetailContextType<ParentType>['radioSelection'],
validator: (() => ({ name: '', error: null })) as DetailContextType<ParentType>['validator'],
textField: () => <span />,
bigTextField: () => <span />,
dropdown: () => <span />,
dropdownWithSearch: () => <span />,
radioSelection: () => <span />,
validator: () => ({ name: '', error: null }),
fieldsWithErrors: {},
setFieldsWithErrors: jest.fn(),
isDirty: false,
@@ -202,12 +202,12 @@ const createContextValue = (): DetailContextType<LocalityDetailsType> => ({
setMode: jest.fn(),
editData: { lid: 1, now_ls: [] } as unknown as EditDataType<LocalityDetailsType>,
setEditData,
textField: (() => <span />) as DetailContextType<LocalityDetailsType>['textField'],
bigTextField: (() => <span />) as DetailContextType<LocalityDetailsType>['bigTextField'],
dropdown: (() => <span />) as DetailContextType<LocalityDetailsType>['dropdown'],
dropdownWithSearch: (() => <span />) as DetailContextType<LocalityDetailsType>['dropdownWithSearch'],
radioSelection: (() => <span />) as DetailContextType<LocalityDetailsType>['radioSelection'],
validator: (() => ({ name: '', error: null })) as DetailContextType<LocalityDetailsType>['validator'],
textField: () => <span />,
bigTextField: () => <span />,
dropdown: () => <span />,
dropdownWithSearch: () => <span />,
radioSelection: () => <span />,
validator: () => ({ name: '', error: null }),
fieldsWithErrors: {},
setFieldsWithErrors: jest.fn(),
isDirty: false,
@@ -128,7 +128,7 @@ describe('ProjectDetails', () => {
mockUseUsersApi.mockReturnValue({ users: [], isLoading: false, isError: false })
mockNotify.mockReset()
mockNavigate.mockReset()
window.confirm = jest.fn(() => true) as unknown as typeof window.confirm
window.confirm = jest.fn(() => true)
})
it('deletes the project and navigates back to the list', async () => {
@@ -147,7 +147,7 @@ describe('WriteButton taxonomy handling', () => {
const onWrite = jest.fn(() => Promise.resolve())
mockCheckSpeciesTaxonomy.mockReturnValue(new Set(['The taxon already exists in the database.']))
renderButton({ ...baseSpecies, sp_comment: 'Updated comment' } as EditDataType<Species>, onWrite)
renderButton({ ...baseSpecies, sp_comment: 'Updated comment' }, onWrite)
fireEvent.click(screen.getByRole('button', { name: /save changes/i }))
@@ -162,7 +162,7 @@ describe('WriteButton taxonomy handling', () => {
const onWrite = jest.fn(() => Promise.resolve())
mockCheckSpeciesTaxonomy.mockReturnValue(new Set(['The taxon already exists in the database.']))
renderButton({ ...baseSpecies, genus_name: 'Gorilla' } as EditDataType<Species>, onWrite)
renderButton({ ...baseSpecies, genus_name: 'Gorilla' }, onWrite)
fireEvent.click(screen.getByRole('button', { name: /save changes/i }))
@@ -177,7 +177,7 @@ describe('WriteButton taxonomy handling', () => {
const onWrite = jest.fn(() => Promise.resolve())
const setEditData = jest.fn()
renderButton({ ...baseSpecies, sp_comment: '' } as EditDataType<Species>, onWrite, { setEditData })
renderButton({ ...baseSpecies, sp_comment: '' }, onWrite, { setEditData })
fireEvent.click(screen.getByRole('button', { name: /save changes/i }))
@@ -190,14 +190,14 @@ describe('WriteButton taxonomy handling', () => {
})
it('disables the button when no edits have been made', () => {
renderButton(baseSpecies as EditDataType<Species>, jest.fn(), { isDirty: false })
renderButton(baseSpecies, jest.fn(), { isDirty: false })
const button = screen.getByRole('button', { name: /save changes/i })
expect((button as HTMLButtonElement).disabled).toBe(true)
})
it('shows helper tooltip when hovering the disabled button', async () => {
renderButton(baseSpecies as EditDataType<Species>, jest.fn(), { isDirty: false })
renderButton(baseSpecies, jest.fn(), { isDirty: false })
const button = screen.getByRole('button', { name: /save changes/i })
const tooltipTrigger = button.parentElement
@@ -211,7 +211,7 @@ describe('WriteButton taxonomy handling', () => {
})
it('enables the button after edits have been made', () => {
renderButton(baseSpecies as EditDataType<Species>, jest.fn(), { isDirty: true })
renderButton(baseSpecies, jest.fn(), { isDirty: true })
const button = screen.getByRole('button', { name: /save changes/i })
expect((button as HTMLButtonElement).disabled).toBe(false)
@@ -220,7 +220,7 @@ describe('WriteButton taxonomy handling', () => {
it('disables the button in staging mode when no edits have been made', () => {
const onWrite = jest.fn(() => Promise.resolve())
renderButton(baseSpecies as EditDataType<Species>, onWrite, {
renderButton(baseSpecies, onWrite, {
isDirty: false,
mode: modeStagingEdit,
})
@@ -63,7 +63,7 @@ jest.mock('@/components/Reference/Tabs/ReferenceTab', () => ({
},
],
ref_journal: undefined,
} as unknown as EditDataType<ReferenceDetailsType>)
})
}
>
Fill reference
@@ -117,7 +117,7 @@ const renderStagingView = () => {
data: { references: [], comment: '' },
mode: modeOptionToMode['staging-new'],
setMode: jest.fn(),
editData: { references: [], comment: '' } as EditDataType<StagingData>,
editData: { references: [], comment: '' },
textField: jest.fn(() => <input aria-label="field" />),
bigTextField: jest.fn(() => <textarea aria-label="comment" />),
dropdown: jest.fn(() => <select aria-label="dropdown" />),
@@ -28,7 +28,7 @@ describe('useUnsavedChangesPrompt', () => {
proceed: undefined,
reset: undefined,
location: undefined,
} as ReturnType<typeof useBlocker>)
})
})
afterEach(() => {
+10 -1
View File
@@ -1,6 +1,15 @@
// Polyfill for React Router's data router in Jest environment
// See: https://github.com/remix-run/react-router/discussions/9851
import '@testing-library/jest-dom'
import { TextDecoder, TextEncoder } from 'util'
if (typeof global.TextEncoder === 'undefined') {
global.TextEncoder = TextEncoder
}
if (typeof global.TextDecoder === 'undefined') {
global.TextDecoder = TextDecoder
}
if (typeof global.Request === 'undefined') {
global.Request = class Request {
@@ -49,5 +58,5 @@ if (typeof global.fetch === 'undefined') {
}
if (typeof global.structuredClone === 'undefined') {
global.structuredClone = (<T>(value: T) => JSON.parse(JSON.stringify(value)) as T) as typeof structuredClone
global.structuredClone = <T>(value: T) => JSON.parse(JSON.stringify(value)) as T
}
+1 -6
View File
@@ -13,12 +13,7 @@ type ApiErrorResult = {
}
const isApiErrorShape = (error: unknown): error is ApiErrorShape => {
return (
typeof error === 'object' &&
error !== null &&
'status' in error &&
typeof (error as { status: unknown }).status === 'number'
)
return typeof error === 'object' && error !== null && 'status' in error && typeof error.status === 'number'
}
const isSerializedError = (error: unknown): error is SerializedError => {
@@ -61,7 +61,7 @@ const createContextValue = (overrides: ContextOverrides = {}): TestDetailContext
data,
mode: overrides.mode ?? modeOptionToMode.read,
setMode: jest.fn(),
editData: overrides.editData ?? (data as EditDataType<LocalityDetailsType>),
editData: overrides.editData ?? data,
setEditData: overrides.setEditData ?? jest.fn(),
textField: () => <input aria-label="text-field" readOnly />,
bigTextField: () => <textarea aria-label="big-text-field" readOnly />,
@@ -48,32 +48,30 @@ jest.mock('@/components/DetailView/Context/DetailContext', () => ({
const createDetailContextValue = (
overrides: Partial<DetailContextType<ReferenceDetailsType>> = {}
): DetailContextType<ReferenceDetailsType> =>
({
data: {} as ReferenceDetailsType,
mode: modeOptionToMode.edit,
setMode: () => {},
editData: { rid: 1, ref_authors: [] } as unknown as EditDataType<ReferenceDetailsType>,
setEditData: () => {},
textField: () => <></>,
bigTextField: () => <></>,
dropdown: () => <></>,
dropdownWithSearch: () => <></>,
radioSelection: () => <></>,
validator: () => ({ name: '', error: null }),
fieldsWithErrors: {},
setFieldsWithErrors: () => {},
isDirty: false,
resetEditData: () => {},
...overrides,
}) as DetailContextType<ReferenceDetailsType>
): DetailContextType<ReferenceDetailsType> => ({
data: {} as ReferenceDetailsType,
mode: modeOptionToMode.edit,
setMode: () => {},
editData: { rid: 1, ref_authors: [] },
setEditData: () => {},
textField: () => <></>,
bigTextField: () => <></>,
dropdown: () => <></>,
dropdownWithSearch: () => <></>,
radioSelection: () => <></>,
validator: () => ({ name: '', error: null }),
fieldsWithErrors: {},
setFieldsWithErrors: () => {},
isDirty: false,
resetEditData: () => {},
...overrides,
})
const buildAuthor = (overrides: Partial<ReferenceAuthorType> = {}): ReferenceAuthorType =>
({
author_surname: 'Cande',
author_initials: 'S.C.',
...overrides,
}) as ReferenceAuthorType
const buildAuthor = (overrides: Partial<ReferenceAuthorType> = {}): ReferenceAuthorType => ({
author_surname: 'Cande',
author_initials: 'S.C.',
...overrides,
})
describe('AuthorTab', () => {
it('filters out already-selected authors from the selector list', () => {
@@ -207,8 +207,8 @@ describe('SpeciesTable synonym filtering', () => {
expect(speciesWithSynonyms).toBeDefined()
const row = { original: speciesWithSynonyms! }
expect(genusFilter!(row as { original: Species }, 'genus_name', 'lupus')).toBe(true)
expect(genusFilter!(row as { original: Species }, 'genus_name', 'unknown')).toBe(false)
expect(genusFilter!(row, 'genus_name', 'lupus')).toBe(true)
expect(genusFilter!(row, 'genus_name', 'unknown')).toBe(false)
})
it('matches rows when the species filter value appears in a synonym', () => {
@@ -223,8 +223,8 @@ describe('SpeciesTable synonym filtering', () => {
expect(speciesWithSynonyms).toBeDefined()
const row = { original: speciesWithSynonyms! }
expect(speciesFilter!(row as { original: Species }, 'species_name', 'fami')).toBe(true)
expect(speciesFilter!(row as { original: Species }, 'species_name', 'absent')).toBe(false)
expect(speciesFilter!(row, 'species_name', 'fami')).toBe(true)
expect(speciesFilter!(row, 'species_name', 'absent')).toBe(false)
})
it('provides unique column identifiers', () => {
+1 -1
View File
@@ -177,7 +177,7 @@ describe('project mapping helpers', () => {
})
it('returns null when coordinator cannot be matched to a user', () => {
const payload = mapProjectEditDataToUpdatePayload(baseProject as EditDataType<ProjectDetailsType>, [
const payload = mapProjectEditDataToUpdatePayload(baseProject, [
{ userId: 99, label: 'Other, Person', initials: 'XX' },
])
@@ -68,7 +68,7 @@ const conflictError = Object.assign(new Error('The Reference with associated upd
})
describe('ReferenceDetails deletion', () => {
const originalConfirm = window.confirm
const originalConfirm = window.confirm.bind(window)
const mockDeleteTrigger = jest.fn()
beforeEach(() => {
@@ -40,7 +40,7 @@ const baseTimeBound: TimeBoundDetailsType = {
age: 12,
b_comment: '',
now_bau: [],
} as unknown as TimeBoundDetailsType
}
const createDetailContextValue = (overrides: Partial<DetailContextType<TimeBoundDetailsType>> = {}) =>
({
@@ -23,27 +23,26 @@ jest.mock('@/shared/types', () => ({}))
type TestPageContext = PageContextType<unknown>
const createPageContextValue = (overrides: Partial<TestPageContext> = {}): TestPageContext =>
({
idList: [],
setIdList: () => {},
idFieldName: 'rid',
viewName: 'reference',
previousTableUrls: [],
setPreviousTableUrls: () => {},
createTitle: () => '',
createSubtitle: () => '',
editRights: {},
sqlLimit: 25,
sqlOffset: 0,
sqlColumnFilters: [],
sqlOrderBy: [],
setSqlLimit: () => {},
setSqlOffset: () => {},
setSqlColumnFilters: () => {},
setSqlOrderBy: () => {},
...overrides,
}) as TestPageContext
const createPageContextValue = (overrides: Partial<TestPageContext> = {}): TestPageContext => ({
idList: [],
setIdList: () => {},
idFieldName: 'rid',
viewName: 'reference',
previousTableUrls: [],
setPreviousTableUrls: () => {},
createTitle: () => '',
createSubtitle: () => '',
editRights: {},
sqlLimit: 25,
sqlOffset: 0,
sqlColumnFilters: [],
sqlOrderBy: [],
setSqlLimit: () => {},
setSqlOffset: () => {},
setSqlColumnFilters: () => {},
setSqlOrderBy: () => {},
...overrides,
})
type TestDetailContext = DetailContextType<unknown>
@@ -52,13 +51,13 @@ const createDetailContextValue = (overrides: Partial<TestDetailContext> = {}): T
data: {},
mode: modeOptionToMode.read,
setMode: () => {},
editData: {} as TestDetailContext['editData'],
setEditData: (() => {}) as TestDetailContext['setEditData'],
textField: (() => <></>) as TestDetailContext['textField'],
bigTextField: (() => <></>) as TestDetailContext['bigTextField'],
dropdown: (() => <></>) as TestDetailContext['dropdown'],
dropdownWithSearch: (() => <></>) as TestDetailContext['dropdownWithSearch'],
radioSelection: (() => <></>) as TestDetailContext['radioSelection'],
editData: {},
setEditData: () => {},
textField: () => <></>,
bigTextField: () => <></>,
dropdown: () => <></>,
dropdownWithSearch: () => <></>,
radioSelection: () => <></>,
validator: () => ({ name: 'field', error: null }),
fieldsWithErrors: {},
setFieldsWithErrors: () => {},
+27 -28
View File
@@ -69,27 +69,26 @@ const createReference = (overrides: ReferenceOverride = {}): ReferenceMock => ({
type TestPageContext = PageContextType<unknown>
const createPageContextValue = (overrides: Partial<TestPageContext> = {}): TestPageContext =>
({
idList: [],
setIdList: () => {},
idFieldName: 'rid',
viewName: 'reference',
previousTableUrls: [],
setPreviousTableUrls: () => {},
createTitle: () => '',
createSubtitle: () => '',
editRights: {},
sqlLimit: 25,
sqlOffset: 0,
sqlColumnFilters: [],
sqlOrderBy: [],
setSqlLimit: () => {},
setSqlOffset: () => {},
setSqlColumnFilters: () => {},
setSqlOrderBy: () => {},
...overrides,
}) as TestPageContext
const createPageContextValue = (overrides: Partial<TestPageContext> = {}): TestPageContext => ({
idList: [],
setIdList: () => {},
idFieldName: 'rid',
viewName: 'reference',
previousTableUrls: [],
setPreviousTableUrls: () => {},
createTitle: () => '',
createSubtitle: () => '',
editRights: {},
sqlLimit: 25,
sqlOffset: 0,
sqlColumnFilters: [],
sqlOrderBy: [],
setSqlLimit: () => {},
setSqlOffset: () => {},
setSqlColumnFilters: () => {},
setSqlOrderBy: () => {},
...overrides,
})
type TestDetailContext = DetailContextType<unknown>
@@ -98,13 +97,13 @@ const createDetailContextValue = (overrides: Partial<TestDetailContext> = {}): T
data: {},
mode: modeOptionToMode.read,
setMode: () => {},
editData: {} as TestDetailContext['editData'],
setEditData: (() => {}) as TestDetailContext['setEditData'],
textField: (() => <></>) as TestDetailContext['textField'],
bigTextField: (() => <></>) as TestDetailContext['bigTextField'],
dropdown: (() => <></>) as TestDetailContext['dropdown'],
dropdownWithSearch: (() => <></>) as TestDetailContext['dropdownWithSearch'],
radioSelection: (() => <></>) as TestDetailContext['radioSelection'],
editData: {},
setEditData: () => {},
textField: () => <></>,
bigTextField: () => <></>,
dropdown: () => <></>,
dropdownWithSearch: () => <></>,
radioSelection: () => <></>,
validator: () => ({ name: 'field', error: null }),
fieldsWithErrors: {},
setFieldsWithErrors: () => {},