mirror of
https://git.um-react.app/um/um-react.git
synced 2025-11-28 11:33:02 +00:00
test: working test with TypeScript + vite (using vitest)
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
@@ -7,6 +6,7 @@ import {
|
||||
Center,
|
||||
Grid,
|
||||
GridItem,
|
||||
Image,
|
||||
Link,
|
||||
Text,
|
||||
VStack,
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import { DecryptedAudioFile, ProcessState, deleteFile } from './fileListingSlice';
|
||||
import { useRef } from 'react';
|
||||
import { useAppDispatch } from '~/hooks';
|
||||
import coverFallback from '~/assets/no-cover.svg';
|
||||
|
||||
interface FileRowProps {
|
||||
id: string;
|
||||
@@ -25,6 +26,7 @@ interface FileRowProps {
|
||||
export function FileRow({ id, file }: FileRowProps) {
|
||||
const dispatch = useAppDispatch();
|
||||
const isDecrypted = file.state === ProcessState.COMPLETE;
|
||||
const metadata = file.metadata;
|
||||
|
||||
const nameWithoutExt = file.fileName.replace(/\.[a-z\d]{3,6}$/, '');
|
||||
const decryptedName = nameWithoutExt + '.' + file.ext;
|
||||
@@ -48,7 +50,7 @@ export function FileRow({ id, file }: FileRowProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Card w="full">
|
||||
<Card w="full" data-testid="file-row">
|
||||
<CardBody>
|
||||
<Grid
|
||||
templateAreas={{
|
||||
@@ -75,21 +77,33 @@ export function FileRow({ id, file }: FileRowProps) {
|
||||
>
|
||||
<GridItem area="cover">
|
||||
<Center w="160px" h="160px" m="auto">
|
||||
{file.metadata.cover && <Avatar size="sm" name="专辑封面" src={file.metadata.cover} />}
|
||||
{!file.metadata.cover && <Text>暂无封面</Text>}
|
||||
{metadata && (
|
||||
<Image
|
||||
objectFit="cover"
|
||||
src={metadata.cover}
|
||||
alt={`"${metadata.album}" 的专辑封面`}
|
||||
fallbackSrc={coverFallback}
|
||||
/>
|
||||
)}
|
||||
</Center>
|
||||
</GridItem>
|
||||
<GridItem area="title">
|
||||
<Box w="full" as="h4" fontWeight="semibold" mt="1" textAlign={{ base: 'center', md: 'left' }}>
|
||||
{file.metadata.name || nameWithoutExt}
|
||||
<span data-testid="audio-meta-song-name">{metadata?.name ?? nameWithoutExt}</span>
|
||||
</Box>
|
||||
</GridItem>
|
||||
<GridItem area="meta">
|
||||
{isDecrypted && (
|
||||
{isDecrypted && metadata && (
|
||||
<Box>
|
||||
<Text>专辑: {file.metadata.album}</Text>
|
||||
<Text>艺术家: {file.metadata.artist}</Text>
|
||||
<Text>专辑艺术家: {file.metadata.albumArtist}</Text>
|
||||
<Text>
|
||||
专辑: <span data-testid="audio-meta-album-name">{metadata.album}</span>
|
||||
</Text>
|
||||
<Text>
|
||||
艺术家: <span data-testid="audio-meta-song-artist">{metadata.artist}</span>
|
||||
</Text>
|
||||
<Text>
|
||||
专辑艺术家: <span data-testid="audio-meta-album-artist">{metadata.albumArtist}</span>
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</GridItem>
|
||||
|
||||
18
src/features/file-listing/__tests__/FileListing.test.tsx
Normal file
18
src/features/file-listing/__tests__/FileListing.test.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { FileListing } from '../FileListing';
|
||||
import { renderWithProviders, screen } from '~/test-utils/test-helper';
|
||||
import { ListingMode } from '../fileListingSlice';
|
||||
import { dummyFiles } from './__fixture__/file-list';
|
||||
|
||||
test('should be able to render a list of 3 items', () => {
|
||||
renderWithProviders(<FileListing />, {
|
||||
preloadedState: {
|
||||
fileListing: {
|
||||
displayMode: ListingMode.LIST,
|
||||
files: dummyFiles,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getAllByTestId('file-row')).toHaveLength(3);
|
||||
expect(screen.getByText('Für Alice')).toBeInTheDocument();
|
||||
});
|
||||
24
src/features/file-listing/__tests__/FileRow.test.tsx
Normal file
24
src/features/file-listing/__tests__/FileRow.test.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { renderWithProviders, screen } from '~/test-utils/test-helper';
|
||||
import { untouchedFile } from './__fixture__/file-list';
|
||||
import { FileRow } from '../FileRow';
|
||||
import { completedFile } from './__fixture__/file-list';
|
||||
|
||||
test('should render no metadata when unavailable', () => {
|
||||
renderWithProviders(<FileRow id="file://ready" file={untouchedFile} />);
|
||||
|
||||
expect(screen.getAllByTestId('file-row')).toHaveLength(1);
|
||||
expect(screen.getByTestId('audio-meta-song-name')).toHaveTextContent('ready');
|
||||
expect(screen.queryByTestId('audio-meta-album-name')).toBeFalsy();
|
||||
expect(screen.queryByTestId('audio-meta-song-artist')).toBeFalsy();
|
||||
expect(screen.queryByTestId('audio-meta-album-artist')).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should render metadata when file has been processed', () => {
|
||||
renderWithProviders(<FileRow id="file://done" file={completedFile} />);
|
||||
|
||||
expect(screen.getAllByTestId('file-row')).toHaveLength(1);
|
||||
expect(screen.getByTestId('audio-meta-song-name')).toHaveTextContent('Für Alice');
|
||||
expect(screen.getByTestId('audio-meta-album-name')).toHaveTextContent("NOW That's What I Call Cryptography 2023");
|
||||
expect(screen.getByTestId('audio-meta-song-artist')).toHaveTextContent('Jixun');
|
||||
expect(screen.getByTestId('audio-meta-album-artist')).toHaveTextContent('Cipher Lovers');
|
||||
});
|
||||
43
src/features/file-listing/__tests__/__fixture__/file-list.ts
Normal file
43
src/features/file-listing/__tests__/__fixture__/file-list.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { DecryptedAudioFile, ProcessState } from '../../fileListingSlice';
|
||||
|
||||
export const untouchedFile: DecryptedAudioFile = {
|
||||
fileName: 'ready.bin',
|
||||
raw: 'blob://localhost/file-a',
|
||||
decrypted: '',
|
||||
ext: '',
|
||||
state: ProcessState.UNTOUCHED,
|
||||
errorMessage: null,
|
||||
metadata: null,
|
||||
};
|
||||
|
||||
export const completedFile: DecryptedAudioFile = {
|
||||
fileName: 'hello-b.bin',
|
||||
raw: 'blob://localhost/file-b',
|
||||
decrypted: 'blob://localhost/file-b-decrypted',
|
||||
ext: 'flac',
|
||||
state: ProcessState.COMPLETE,
|
||||
errorMessage: null,
|
||||
metadata: {
|
||||
name: 'Für Alice',
|
||||
artist: 'Jixun',
|
||||
albumArtist: 'Cipher Lovers',
|
||||
album: "NOW That's What I Call Cryptography 2023",
|
||||
cover: '',
|
||||
},
|
||||
};
|
||||
|
||||
export const fileWithError: DecryptedAudioFile = {
|
||||
fileName: 'hello-c.bin',
|
||||
raw: 'blob://localhost/file-c',
|
||||
decrypted: 'blob://localhost/file-c-decrypted',
|
||||
ext: 'flac',
|
||||
state: ProcessState.ERROR,
|
||||
errorMessage: 'Could not decrypt blah blah',
|
||||
metadata: null,
|
||||
};
|
||||
|
||||
export const dummyFiles: Record<string, DecryptedAudioFile> = {
|
||||
'file://untouched': untouchedFile,
|
||||
'file://completed': completedFile,
|
||||
'file://error': fileWithError,
|
||||
};
|
||||
@@ -31,7 +31,7 @@ export interface DecryptedAudioFile {
|
||||
decrypted: string; // blob uri
|
||||
state: ProcessState;
|
||||
errorMessage: null | string;
|
||||
metadata: AudioMetadata;
|
||||
metadata: null | AudioMetadata;
|
||||
}
|
||||
|
||||
export interface FileListingState {
|
||||
@@ -69,13 +69,7 @@ export const fileListingSlice = createSlice({
|
||||
ext: '',
|
||||
state: ProcessState.UNTOUCHED,
|
||||
errorMessage: null,
|
||||
metadata: {
|
||||
name: '',
|
||||
artist: '',
|
||||
album: '',
|
||||
albumArtist: '',
|
||||
cover: '',
|
||||
},
|
||||
metadata: null,
|
||||
};
|
||||
},
|
||||
setDecryptedContent: (state, { payload }: PayloadAction<{ id: string; decryptedBlobURI: string }>) => {
|
||||
|
||||
Reference in New Issue
Block a user