Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(calendar): test occurrence chip component #36

Merged
merged 3 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@
"runtime": "automatic"
}
]
],
"plugins": ["react-hot-loader/babel"]
]
}
19,248 changes: 19,248 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 9 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,24 @@
"emotion",
"mui",
"framer-motion",
"date-fns"
"date-fns",
"supabase"
],
"scripts": {
"prepare": "([ -d ./.git ] && husky install) || echo \"Skipping husky install since package does not have .git folder\"",
"prebuild": "yarn typecheck && yarn eslint:check && yarn prettier:check",
"predeploy": "yarn build",
"deploy": "gh-pages -d dist",
"dev": "NODE_ENV=development webpack-dev-server --open --hot",
"prebuild": "npm run typecheck && npm run eslint:check && npm run prettier:check",
"build": "NODE_ENV=production webpack",
"dev": "NODE_ENV=development webpack-dev-server --open --hot",
"start": "NODE_ENV=production serve -s dist",
"typecheck": "tsc --noEmit",
"eslint:check": "eslint src",
"eslint:fix": "eslint --fix src",
"prettier:check": "prettier --check src",
"prettier:write": "prettier --write src",
"typecheck": "tsc --noEmit",
"db:migrate": "supabase migration up",
"test": "DEBUG_PRINT_LIMIT=30000 DOTENV_CONFIG_PATH=./.env.test jest --setupFiles dotenv/config --config tests/jest.config.js",
"test:watch": "yarn test --watch",
"test:coverage": "yarn test --coverage"
"test": "DEBUG_PRINT_LIMIT=30000 DOTENV_CONFIG_PATH=./.env.test jest --runInBand --setupFiles dotenv/config --config tests/jest.config.js",
"test:watch": "npm test --watch",
"test:coverage": "npm test --coverage",
"db:migrate": "supabase migration up"
},
"lint-staged": {
"**/*.{ts,tsx}": [
Expand Down Expand Up @@ -100,7 +99,6 @@
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"prettier": "3.1.1",
"react-hot-loader": "^4.13.1",
"style-loader": "^3.3.3",
"supabase": "^1.142.2",
"ts-jest": "^29.1.2",
Expand Down
8 changes: 8 additions & 0 deletions src/components/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ jest.mock('@components', () => ({
AccountPage: jest.fn(),
}));

jest.mock('@context', () => ({
SnackbarProvider: jest.fn().mockImplementation(({ children }) => children),
UserAccountProvider: jest.fn().mockImplementation(({ children }) => children),
TraitsProvider: jest.fn().mockImplementation(({ children }) => children),
HabitsProvider: jest.fn().mockImplementation(({ children }) => children),
OccurrencesProvider: jest.fn().mockImplementation(({ children }) => children),
}));

import { getWeeksInMonth } from '@internationalized/date';
import { act, render } from '@testing-library/react';
import { generateCalendarRange } from '@utils';
Expand Down
3 changes: 1 addition & 2 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { SessionContextProvider } from '@supabase/auth-helpers-react';
import { generateCalendarRange } from '@utils';
import React from 'react';
import { useLocale } from 'react-aria';
import { hot } from 'react-hot-loader/root';
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
import { useCalendarState } from 'react-stately';

Expand Down Expand Up @@ -95,4 +94,4 @@ const App = () => {
);
};

export default hot(App);
export default App;
121 changes: 121 additions & 0 deletions src/components/calendar/OccurrenceChip.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { useHabits, useOccurrences } from '@context';
import { render, waitFor } from '@testing-library/react';
import { getHabitIconUrl } from '@utils';
import React from 'react';

import OccurrenceChip, { type OccurrenceChipProps } from './OccurrenceChip';

jest.mock('@utils', () => ({
getHabitIconUrl: jest.fn(),
}));

jest.mock('@hooks', () => ({
useHabitTraitChipColor: jest.fn(),
}));

jest.mock('@context', () => ({
useHabits: jest.fn(),
useOccurrences: jest.fn(),
}));

jest.mock('@utils', () => ({
getHabitIconUrl: jest.fn(),
}));

describe(OccurrenceChip.name, () => {
const mockOnDelete = jest.fn();
const props: OccurrenceChipProps = {
occurrence: {
id: 1,
createdAt: '2021-01-01T00:00:00Z',
updatedAt: '2021-01-02T00:00:00Z',
timestamp: 1612137600000,
day: '2021-02-01',
time: null,
habitId: 2,
userId: '3',
},
onDelete: mockOnDelete,
};

it('should render img with habit icon', async () => {
(useHabits as jest.Mock).mockReturnValue({
habitsMap: {
2: {
id: 2,
name: 'Test Habit Name',
iconPath: 'path/to/test/icon',
traitId: 1,
},
},
});
(useOccurrences as jest.Mock).mockReturnValue({
occurrenceIdBeingDeleted: null,
});
(getHabitIconUrl as jest.Mock).mockReturnValue('path/to/test/icon');
const { getByAltText } = render(<OccurrenceChip {...props} />);
const img = getByAltText('Test Habit Name icon');
expect(img).toBeInTheDocument();
await waitFor(() => {
expect(img).toHaveAttribute('src', 'path/to/test/icon');
});
});

it('should call onDelete when delete button is clicked', () => {
(useHabits as jest.Mock).mockReturnValue({
habitsMap: {
2: {
id: 2,
name: 'Test Habit Name',
iconPath: 'path/to/test/icon',
traitId: 1,
},
},
});
(useOccurrences as jest.Mock).mockReturnValue({
occurrenceIdBeingDeleted: null,
});
(getHabitIconUrl as jest.Mock).mockReturnValue('path/to/test/icon');
const { getByRole } = render(<OccurrenceChip {...props} />);
const deleteButton = getByRole('habit-chip-delete-button');
deleteButton.click();
expect(mockOnDelete).toHaveBeenCalledWith(1, expect.anything());
});

it('should not render if habit not found', () => {
(useHabits as jest.Mock).mockReturnValue({
habitsMap: {
2: {
id: 2,
name: '',
iconPath: 'path/to/test/icon',
traitId: 1,
},
},
});
const { queryByRole } = render(<OccurrenceChip {...props} />);
const chip = queryByRole('habit-chip');
expect(chip).toBeNull();
});

it('should render CircularProgress when occurrence is being deleted', () => {
(useHabits as jest.Mock).mockReturnValue({
habitsMap: {
2: {
id: 2,
name: 'Test Habit Name',
iconPath: 'path/to/test/icon',
traitId: 1,
},
},
});
(useOccurrences as jest.Mock).mockReturnValue({
occurrenceIdBeingDeleted: 1,
});
const { getByRole, queryByRole } = render(<OccurrenceChip {...props} />);
const loader = getByRole('habit-chip-delete-loader');
const chipDelete = queryByRole('habit-chip-delete-button');
expect(loader).toBeInTheDocument();
expect(chipDelete).toBeNull();
});
});
14 changes: 9 additions & 5 deletions src/components/calendar/OccurrenceChip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React from 'react';

import { StyledHabitChip } from './styled';

type OccurrenceChipProps = {
export type OccurrenceChipProps = {
occurrence: Occurrence;
onDelete: (
occurrenceId: number,
Expand All @@ -27,29 +27,33 @@ const OccurrenceChip = ({ occurrence, onDelete }: OccurrenceChipProps) => {
const isBeingDeleted = occurrenceIdBeingDeleted === occurrence.id;

const endDecorator = isBeingDeleted ? (
<CircularProgress size="sm" />
<CircularProgress size="sm" role="habit-chip-delete-loader" />
) : (
<ChipDelete
variant="soft"
color={traitChipColor}
onClick={(clickEvent) => onDelete(occurrence.id, clickEvent)}
role="habit-chip-delete-button"
>
<DeleteForeverIcon fontSize="small" />
<DeleteForeverIcon fontSize="large" />
</ChipDelete>
);

if (!eventHabit.name) return null;

return (
<Tooltip title={eventHabit.name} key={occurrence.id}>
<StyledHabitChip
variant="soft"
color={traitChipColor}
key={occurrence.id}
role="habit-chip"
startDecorator={
<img
src={getHabitIconUrl(eventHabit.iconPath)}
alt={`${eventHabit.name} icon`}
width={16}
height={16}
width={20}
height={20}
/>
}
disabled={isBeingDeleted}
Expand Down
1 change: 1 addition & 0 deletions src/components/calendar/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ export const StyledCalendarDayCellButtonIconsContainer = styled(Box)(
);

export const StyledHabitChip = styled(Chip)(({ theme }) => ({
padding: theme.spacing(0.5, 1),
marginTop: theme.spacing(0.5),
marginRight: theme.spacing(0.5),
}));
2 changes: 1 addition & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const envPaths = {
require('dotenv').config({ path: envPaths[process.env.NODE_ENV] });

const config = {
entry: ['react-hot-loader/patch', './src/index.tsx'],
entry: './src/index.tsx',
mode: process.env.NODE_ENV,
devtool: 'inline-source-map',
devServer: {
Expand Down
Loading
Loading