Add tests (WIP)

This commit is contained in:
Maximilian Harz 2025-06-10 12:58:26 +02:00
parent 71612951d1
commit 72ddc5affe
8 changed files with 357 additions and 9 deletions

View File

@ -0,0 +1,105 @@
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { render, fireEvent, screen } from '@testing-library/react'
import { describe, it, vi, expect } from 'vitest'
import { GalleryForm } from './GalleryForm'
import type { FormState } from '#types/FormState'
import type { GalleryItem, Item } from '#types/Item'
import type { MarkerIcon } from '#types/MarkerIcon'
import type { Tag } from '#types/Tag'
const testImagePaths = ['./tests/image1.jpg', './tests/image2.jpg', './tests/image3.jpg']
const testImages = testImagePaths.map((imagePath) =>
// eslint-disable-next-line security/detect-non-literal-fs-filename
readFileSync(path.join(__dirname, '../../../../', imagePath)),
)
const setState = vi.fn()
const baseState = {
color: '',
id: '',
group_type: 'wuerdekompass',
status: 'active',
name: '',
subname: '',
text: '',
contact: '',
telephone: '',
next_appointment: '',
image: '',
marker_icon: {} as MarkerIcon,
offers: [] as Tag[],
needs: [] as Tag[],
relations: [] as Item[],
start: '',
end: '',
openCollectiveSlug: '',
gallery: [],
uploadingImages: [],
}
describe('GalleryForm', () => {
const Wrapper = ({ gallery = [] as GalleryItem[], uploadingImages = [] as File[] } = {}) => {
const state: FormState = {
...baseState,
gallery,
uploadingImages,
}
return render(<GalleryForm state={state} setState={setState} />)
}
describe('without previous images', () => {
it('renders', () => {
const wrapper = Wrapper()
expect(wrapper.container).toMatchSnapshot()
})
})
describe('with previous images', () => {
const gallery = [
{
directus_files_id: {
id: '1',
width: 800,
height: 600,
},
},
{
directus_files_id: {
id: '2',
width: 1024,
height: 768,
},
},
]
it('renders', () => {
const wrapper = Wrapper({ gallery })
expect(wrapper.container).toMatchSnapshot()
})
it('can delete an image', () => {
Wrapper({ gallery })
const deleteButton = screen.getByTestId('delete-image-1')
expect(deleteButton).toBeInTheDocument()
fireEvent.click(deleteButton)
})
})
describe('with uploading images', () => {
it('renders', () => {
const wrapper = Wrapper({
uploadingImages: testImages.map(
(image, index) =>
new File([image], `test-image-${index + 1}.jpg`, { type: 'image/jpeg' }),
),
})
expect(wrapper.container).toMatchSnapshot()
})
})
})

View File

@ -121,7 +121,7 @@ export const GalleryForm = ({ state, setState }: Props) => {
{...getRootProps()}
className='tw:flex tw:flex-col tw:items-center tw:justify-center tw:text-base-content/50 tw:w-full tw:h-full tw:cursor-pointer tw:card tw:card-body tw:border tw:border-current/50 tw:border-dashed tw:bg-base-200'
>
<input {...getInputProps()} />
<input {...getInputProps()} data-testid='gallery-upload-input' />
<div>
<BiSolidImage className='tw:h-16 tw:w-16 tw:m-auto tw:mb-2' />
<span className='tw:text-center'>Upload Image</span>

View File

@ -0,0 +1,210 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`GalleryForm > with previous images > renders 1`] = `
<div>
<div
class="tw:flex tw:flex-wrap tw:gap-4 tw:my-4"
>
<div
class="tw:relative"
>
<img
alt="Gallery image 1"
class="tw:w-60 tw:h-60 tw:object-cover tw:rounded-lg "
src="undefined1.jpg"
/>
<button
class="tw:m-2 tw:bg-red-500 tw:text-white tw:p-2 tw:rounded-full tw:absolute tw:top-0 tw:right-0 tw:hover:bg-red-600"
type="button"
>
<svg
aria-hidden="true"
class="tw:h-5 tw:w-5"
data-slot="icon"
fill="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M16.5 4.478v.227a48.816 48.816 0 0 1 3.878.512.75.75 0 1 1-.256 1.478l-.209-.035-1.005 13.07a3 3 0 0 1-2.991 2.77H8.084a3 3 0 0 1-2.991-2.77L4.087 6.66l-.209.035a.75.75 0 0 1-.256-1.478A48.567 48.567 0 0 1 7.5 4.705v-.227c0-1.564 1.213-2.9 2.816-2.951a52.662 52.662 0 0 1 3.369 0c1.603.051 2.815 1.387 2.815 2.951Zm-6.136-1.452a51.196 51.196 0 0 1 3.273 0C14.39 3.05 15 3.684 15 4.478v.113a49.488 49.488 0 0 0-6 0v-.113c0-.794.609-1.428 1.364-1.452Zm-.355 5.945a.75.75 0 1 0-1.5.058l.347 9a.75.75 0 1 0 1.499-.058l-.346-9Zm5.48.058a.75.75 0 1 0-1.498-.058l-.347 9a.75.75 0 0 0 1.5.058l.345-9Z"
fill-rule="evenodd"
/>
</svg>
</button>
</div>
<div
class="tw:relative"
>
<img
alt="Gallery image 2"
class="tw:w-60 tw:h-60 tw:object-cover tw:rounded-lg "
src="undefined2.jpg"
/>
<button
class="tw:m-2 tw:bg-red-500 tw:text-white tw:p-2 tw:rounded-full tw:absolute tw:top-0 tw:right-0 tw:hover:bg-red-600"
type="button"
>
<svg
aria-hidden="true"
class="tw:h-5 tw:w-5"
data-slot="icon"
fill="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M16.5 4.478v.227a48.816 48.816 0 0 1 3.878.512.75.75 0 1 1-.256 1.478l-.209-.035-1.005 13.07a3 3 0 0 1-2.991 2.77H8.084a3 3 0 0 1-2.991-2.77L4.087 6.66l-.209.035a.75.75 0 0 1-.256-1.478A48.567 48.567 0 0 1 7.5 4.705v-.227c0-1.564 1.213-2.9 2.816-2.951a52.662 52.662 0 0 1 3.369 0c1.603.051 2.815 1.387 2.815 2.951Zm-6.136-1.452a51.196 51.196 0 0 1 3.273 0C14.39 3.05 15 3.684 15 4.478v.113a49.488 49.488 0 0 0-6 0v-.113c0-.794.609-1.428 1.364-1.452Zm-.355 5.945a.75.75 0 1 0-1.5.058l.347 9a.75.75 0 1 0 1.499-.058l-.346-9Zm5.48.058a.75.75 0 1 0-1.498-.058l-.347 9a.75.75 0 0 0 1.5.058l.345-9Z"
fill-rule="evenodd"
/>
</svg>
</button>
</div>
<div
class="tw:flex tw:center tw:w-60 tw:h-60 tw:cursor-pointer tw:border tw:border-dashed tw:border-gray-300 tw:p-4 tw:rounded-lg"
role="presentation"
tabindex="0"
>
<input
accept="image/jpeg"
data-testid="gallery-upload-input"
multiple=""
style="border: 0px; clip: rect(0, 0, 0, 0); clip-path: inset(50%); height: 1px; margin: 0px -1px -1px 0px; overflow: hidden; padding: 0px; position: absolute; width: 1px; white-space: nowrap;"
tabindex="-1"
type="file"
/>
<svg
aria-hidden="true"
class="tw:h-8 tw:w-8 tw:m-auto"
data-slot="icon"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5m-13.5-9L12 3m0 0 4.5 4.5M12 3v13.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
</div>
</div>
`;
exports[`GalleryForm > with uploading images > renders 1`] = `
<div>
<div
class="tw:flex tw:flex-wrap tw:gap-4 tw:my-4"
>
<div
class="tw:relative"
>
<img
alt="Gallery image 1"
class="tw:w-60 tw:h-60 tw:object-cover tw:rounded-lg tw:opacity-50"
src="blob:nodedata:ce8d6df7-ec41-40a0-b77b-eebfdcd76e47"
/>
<span
class="tw:loading tw:loading-spinner tw:absolute tw:inset-0 tw:m-auto"
/>
</div>
<div
class="tw:relative"
>
<img
alt="Gallery image 2"
class="tw:w-60 tw:h-60 tw:object-cover tw:rounded-lg tw:opacity-50"
src="blob:nodedata:d65c4061-dc86-40ee-a1da-7e783a6db3ce"
/>
<span
class="tw:loading tw:loading-spinner tw:absolute tw:inset-0 tw:m-auto"
/>
</div>
<div
class="tw:relative"
>
<img
alt="Gallery image 3"
class="tw:w-60 tw:h-60 tw:object-cover tw:rounded-lg tw:opacity-50"
src="blob:nodedata:c989c354-4d76-40f2-91d5-476399d52f64"
/>
<span
class="tw:loading tw:loading-spinner tw:absolute tw:inset-0 tw:m-auto"
/>
</div>
<div
class="tw:flex tw:center tw:w-60 tw:h-60 tw:cursor-pointer tw:border tw:border-dashed tw:border-gray-300 tw:p-4 tw:rounded-lg"
role="presentation"
tabindex="0"
>
<input
accept="image/jpeg"
data-testid="gallery-upload-input"
multiple=""
style="border: 0px; clip: rect(0, 0, 0, 0); clip-path: inset(50%); height: 1px; margin: 0px -1px -1px 0px; overflow: hidden; padding: 0px; position: absolute; width: 1px; white-space: nowrap;"
tabindex="-1"
type="file"
/>
<svg
aria-hidden="true"
class="tw:h-8 tw:w-8 tw:m-auto"
data-slot="icon"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5m-13.5-9L12 3m0 0 4.5 4.5M12 3v13.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
</div>
</div>
`;
exports[`GalleryForm > without previous images > renders 1`] = `
<div>
<div
class="tw:flex tw:flex-wrap tw:gap-4 tw:my-4"
>
<div
class="tw:flex tw:center tw:w-60 tw:h-60 tw:cursor-pointer tw:border tw:border-dashed tw:border-gray-300 tw:p-4 tw:rounded-lg"
role="presentation"
tabindex="0"
>
<input
accept="image/jpeg"
data-testid="gallery-upload-input"
multiple=""
style="border: 0px; clip: rect(0, 0, 0, 0); clip-path: inset(50%); height: 1px; margin: 0px -1px -1px 0px; overflow: hidden; padding: 0px; position: absolute; width: 1px; white-space: nowrap;"
tabindex="-1"
type="file"
/>
<svg
aria-hidden="true"
class="tw:h-8 tw:w-8 tw:m-auto"
data-slot="icon"
fill="none"
stroke="currentColor"
stroke-width="1.5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5m-13.5-9L12 3m0 0 4.5 4.5M12 3v13.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,33 @@
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { describe, it, expect } from 'vitest'
import { getImageDimensions } from './getImageDimensions'
const testImagePaths = ['./tests/image1.jpg', './tests/image2.jpg', './tests/image3.jpg']
const testImages = testImagePaths.map((imagePath) =>
// eslint-disable-next-line security/detect-non-literal-fs-filename
readFileSync(path.join(__dirname, '../../', imagePath)),
)
// vi.spyOn(global, 'FileReader').mockImplementation((fileReader: FileReader) => fileReader)
describe('getImageDimensions', () => {
it.skip('returns the correct dimensions for a valid image file', async () => {
const file = new File([testImages[0]], 'image1.jpg', { type: 'image/jpeg' })
const dimensions = await getImageDimensions(file)
expect(dimensions).toEqual({ width: 800, height: 600 }) // Adjust expected values based on actual test image dimensions
})
it.skip('throws an error for an invalid file type', async () => {
const file = new File(['not an image'], 'invalid.txt', { type: 'text/plain' })
await expect(getImageDimensions(file)).rejects.toThrow('Error reading image file')
})
it.skip('throws an error if the image cannot be loaded', async () => {
const file = new File([''], 'empty.jpg', { type: 'image/jpeg' })
await expect(getImageDimensions(file)).rejects.toThrow('Error loading image')
})
})

View File

@ -10,16 +10,16 @@ export const getImageDimensions = (
const fileReader = new FileReader()
fileReader.onload = () => {
const img = new Image()
try {
const img = new Image()
img.onload = function () {
resolve({
width: img.width,
height: img.height,
})
img.onload = () => resolve({ width: img.width, height: img.height })
img.src = fileReader.result as string // is the data URL because called with readAsDataURL
} catch (error) {
reject(error)
throw new Error('Error loading image')
}
img.src = fileReader.result as string // is the data URL because called with readAsDataURL
}
fileReader.readAsDataURL(file)

BIN
tests/image1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

BIN
tests/image2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

BIN
tests/image3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB