Skip to content

Commit

Permalink
Income statement pdf report (#1037)
Browse files Browse the repository at this point in the history
  • Loading branch information
argaen authored Oct 1, 2024
1 parent 6035787 commit 47f1163
Show file tree
Hide file tree
Showing 26 changed files with 1,367 additions and 358 deletions.
2 changes: 1 addition & 1 deletion jest.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const config = {
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
coverageThreshold: {
global: {
lines: 94,
lines: 93.5,
branches: 87,
},
},
Expand Down
33 changes: 17 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
"@dinero.js/currencies": "^2.0.0-alpha.14",
"@fontsource/inter": "^5.0.19",
"@hookform/resolvers": "^3.9.0",
"@tanstack/react-query": "^5.51.11",
"@tanstack/react-query-devtools": "^5.45.1",
"@tanstack/react-table": "^8.19.3",
"@react-pdf/renderer": "^4.0.0",
"@tanstack/react-query": "^5.51.24",
"@tanstack/react-query-devtools": "^5.51.11",
"@tanstack/react-table": "^8.20.1",
"@testing-library/jest-dom": "^6.4.8",
"axios": "^1.7.2",
"axios": "^1.7.4",
"chart.js": "^4.4.3",
"chartjs-adapter-luxon": "^1.3.1",
"chartjs-chart-sankey": "^0.12.1",
Expand All @@ -30,32 +31,33 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"classnames": "^2.5.1",
"dayjs": "^1.11.11",
"dayjs": "^1.11.12",
"dinero.js": "^2.0.0-alpha.14",
"jsonwebtoken": "^9.0.2",
"jwks-rsa": "^3.1.0",
"lodash.debounce": "^4.0.8",
"luxon": "^3.4.4",
"luxon": "^3.5.0",
"next": "^14.2.5",
"pako": "^2.1.0",
"plaid": "^24.0.0",
"react": "^18.3.1",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.3.1",
"react-error-boundary": "^4.0.13",
"react-hook-form": "^7.52.1",
"react-hook-form": "^7.52.2",
"react-hot-toast": "^2.4.1",
"react-hotkeys-hook": "^4.5.0",
"react-icons": "^5.2.1",
"react-icons": "^5.3.0",
"react-joyride": "^2.8.1",
"react-modal": "^3.16.1",
"react-plaid-link": "^3.5.1",
"react-select": "^5.8.0",
"react-tailwindcss-datepicker": "^1.6.6",
"react-tooltip": "^5.27.1",
"react-tooltip": "^5.28.0",
"reflect-metadata": "^0.2.2",
"sharp": "^0.33.4",
"sql.js": "^1.10.3",
"sharp": "^0.33.5",
"sql.js": "^1.11.0",
"tailwindcss": "^3.4.10",
"typeorm": "^0.3.20",
"winston-react": "^1.0.0-RC.0"
},
Expand Down Expand Up @@ -115,7 +117,7 @@
"@types/jest": "^29.5.12",
"@types/lodash.debounce": "^4.0.9",
"@types/luxon": "^3.4.2",
"@types/node": "^20.14.11",
"@types/node": "^22.4.1",
"@types/pako": "^2.0.3",
"@types/react": "18.3.3",
"@types/react-dom": "^18.2.24",
Expand All @@ -124,7 +126,7 @@
"@types/react-table": "^7.7.20",
"@types/react-test-renderer": "^18.3.0",
"@types/sql.js": "^1.4.9",
"autoprefixer": "^10.4.19",
"autoprefixer": "^10.4.20",
"babel-preset-react-app": "^10.0.1",
"copy-webpack-plugin": "^12.0.2",
"eslint": "^8.57.0",
Expand All @@ -136,11 +138,10 @@
"eslint-plugin-prettier": "^5.2.1",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"postcss": "^8.4.39",
"postcss": "^8.4.41",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.5",
"prettier-plugin-tailwindcss": "^0.6.6",
"snapshot-diff": "^0.10.0",
"tailwindcss": "^3.4.6",
"typescript": "^5.5.4",
"webpack-filter-warnings-plugin": "^1.2.1"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ exports[`AccountsPage renders as expected when no data showing onboarding 1`] =
Dashboard
</span>
<div
class="ml-auto"
class="flex ml-auto gap-4"
>
<div
data-testid="ReportsDropdown"
/>
<div
data-testid="FormButton"
>
Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/app/dashboard/accounts/page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { Account } from '@/book/entities';
import AccountsPage from '@/app/dashboard/accounts/page';
import FormButton from '@/components/buttons/FormButton';
import AccountForm from '@/components/forms/account/AccountForm';
import ReportsDropdown from '@/components/buttons/ReportsDropdown';
import Onboarding from '@/components/onboarding/Onboarding';
import {
LatestTransactions,
Expand All @@ -36,6 +37,10 @@ jest.mock('@/components/forms/account/AccountForm', () => jest.fn(
() => <div data-testid="AccountForm" />,
));

jest.mock('@/components/buttons/ReportsDropdown', () => jest.fn(
() => <div data-testid="ReportsDropdown" />,
));

jest.mock('@/components/tables/AccountsTable', () => jest.fn(
() => <div data-testid="AccountsTable" />,
));
Expand Down Expand Up @@ -100,6 +105,7 @@ describe('AccountsPage', () => {
{},
{},
);
expect(ReportsDropdown).toHaveBeenLastCalledWith({}, {});

await screen.findByTestId('Onboarding');
expect(Onboarding).toHaveBeenLastCalledWith(
Expand Down
53 changes: 18 additions & 35 deletions src/__tests__/components/DateRangeInput.test.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import React from 'react';
import { render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
import Datepicker from 'react-tailwindcss-datepicker';
import { DateTime, Interval } from 'luxon';
import { DefinedUseQueryResult, QueryClientProvider } from '@tanstack/react-query';
import * as query from '@tanstack/react-query';
import type { UseQueryResult } from '@tanstack/react-query';

import DateRangeInput from '@/components/DateRangeInput';
import * as apiHook from '@/hooks/api';
import * as stateHooks from '@/hooks/state';

jest.mock('react-tailwindcss-datepicker', () => jest.fn(
() => <div data-testid="DatePicker" />,
Expand All @@ -24,31 +22,21 @@ jest.mock('@/hooks/api', () => ({
...jest.requireActual('@/hooks/api'),
}));

jest.mock('@/hooks/state', () => ({
__esModule: true,
...jest.requireActual('@/hooks/state'),
}));

const wrapper = ({ children }: React.PropsWithChildren) => (
<QueryClientProvider client={QUERY_CLIENT}>{children}</QueryClientProvider>
);

describe('DateRangeInput', () => {
beforeEach(() => {
jest.spyOn(DateTime, 'now').mockReturnValue(DateTime.fromISO('2023-01-30') as DateTime<true>);
jest.spyOn(apiHook, 'useStartDate').mockReturnValue({ data: undefined } as UseQueryResult<DateTime>);
jest.spyOn(stateHooks, 'useInterval').mockReturnValue({ data: TEST_INTERVAL } as DefinedUseQueryResult<Interval>);
});

afterEach(() => {
jest.clearAllMocks();
});

it('creates datepicker with expected params', () => {
render(<DateRangeInput />, { wrapper });
render(<DateRangeInput interval={TEST_INTERVAL} />);

expect(Datepicker).toBeCalledTimes(1);
expect(Datepicker).toBeCalledWith(
expect(Datepicker).toHaveBeenCalledTimes(1);
expect(Datepicker).toHaveBeenCalledWith(
{
containerClassName: 'relative text-sm',
displayFormat: 'DD-MM-YYYY',
Expand Down Expand Up @@ -101,9 +89,9 @@ describe('DateRangeInput', () => {
data: DateTime.fromISO('2022-01-01'),
} as UseQueryResult<DateTime>);

render(<DateRangeInput />, { wrapper });
render(<DateRangeInput interval={TEST_INTERVAL} />);

expect(Datepicker).toBeCalledWith(
expect(Datepicker).toHaveBeenCalledWith(
expect.objectContaining({
value: {
startDate: TEST_INTERVAL.start?.toJSDate(),
Expand Down Expand Up @@ -132,24 +120,19 @@ describe('DateRangeInput', () => {
* end date as 1st of the month without setting end of day, that month
* will not be included
*/
it('updates query data with selection setting end of day for end date', () => {
const mockSetQueryData = jest.fn();
jest.spyOn(query, 'useQueryClient').mockReturnValue({
setQueryData: mockSetQueryData as QueryClient['setQueryData'],
} as QueryClient);
render(<DateRangeInput />, { wrapper });

const { onChange } = (Datepicker as jest.Mock).mock.calls[0][0];
onChange({
startDate: DateTime.fromISO('2022-01-01'),
endDate: DateTime.fromISO('2023-01-01'),
});

expect(mockSetQueryData).toBeCalledWith(
['state', 'interval'],
it('calls on change with end of date for end date', () => {
const onChange = jest.fn();
render(<DateRangeInput interval={TEST_INTERVAL} onChange={onChange} />);

const { onChange: f } = (Datepicker as jest.Mock).mock.calls[0][0];
act(() => f({
startDate: '2023-01-01',
endDate: '2023-01-09',
}));
expect(onChange).toHaveBeenCalledWith(
Interval.fromDateTimes(
DateTime.fromISO('2022-01-01'),
DateTime.fromISO('2023-01-01').endOf('day'),
DateTime.fromISO('2023-01-01'),
DateTime.fromISO('2023-01-09').endOf('day'),
),
);
});
Expand Down
49 changes: 49 additions & 0 deletions src/__tests__/components/buttons/ReportsDropdown.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import {
render,
screen,
} from '@testing-library/react';

import IncomeExpenseStatementForm from '@/components/forms/reports/IncomeExpenseStatementForm';
import ReportsDropdown from '@/components/buttons/ReportsDropdown';
import FormButton from '@/components/buttons/FormButton';

jest.mock('@/components/buttons/FormButton', () => jest.fn(
(props: React.PropsWithChildren) => (
<div data-testid="FormButton">
{props.children}
</div>
),
));

jest.mock('@/components/forms/reports/IncomeExpenseStatementForm', () => jest.fn(
() => <div data-testid="IncomeExpenseStatementForm" />,
));

describe('ReportsDropdown', () => {
beforeEach(async () => {
});

afterEach(() => {
jest.clearAllMocks();
});

it('renders as expected', async () => {
const { container } = render(<ReportsDropdown />);

await screen.findAllByTestId('FormButton');
expect(FormButton).toHaveBeenCalledTimes(1);
expect(FormButton).toHaveBeenNthCalledWith(
1,
expect.objectContaining({
id: 'IE report',
modalTitle: 'Income statement',
}),
{},
);
expect(IncomeExpenseStatementForm).toHaveBeenCalledTimes(1);
expect(IncomeExpenseStatementForm).toHaveBeenNthCalledWith(1, {}, {});

expect(container).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ReportsDropdown renders as expected 1`] = `
<div>
<div
class="group relative h-full"
>
<button
class="group-hover:bg-cyan-700/80 dark:group-hover:bg-cyan-600 flex h-full w-full items-center btn btn-primary"
type="button"
>
<svg
class="mr-1"
fill="currentColor"
height="1em"
stroke="currentColor"
stroke-width="0"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 4h-3V2h-2v2h-4V2H8v2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h14c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2zM5 20V7h14V6l.002 14H5z"
/>
<path
d="M7 9h10v2H7zm0 4h5v2H7z"
/>
</svg>
Reports
</button>
<ul
class="absolute rounded-md w-40 hidden py-2 group-hover:block bg-background-800"
>
<li
class="text-sm hover:bg-background-700"
>
<div
data-testid="FormButton"
>
<div
data-testid="IncomeExpenseStatementForm"
/>
</div>
</li>
</ul>
</div>
</div>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import {
render,
screen,
} from '@testing-library/react';
import { DefinedUseQueryResult, QueryClientProvider } from '@tanstack/react-query';
import type { Interval } from 'luxon';

import IncomeExpenseStatementForm from '@/components/forms/reports/IncomeExpenseStatementForm';
import * as stateHooks from '@/hooks/state';

jest.mock('@/hooks/state', () => ({
__esModule: true,
...jest.requireActual('@/hooks/state'),
}));

jest.mock('@/components/DateRangeInput', () => jest.fn(
() => <input id="intervalInput" data-testid="DateRangeInput" />,
));

const wrapper = ({ children }: React.PropsWithChildren) => (
<QueryClientProvider client={QUERY_CLIENT}>{children}</QueryClientProvider>
);

describe('IncomeExpenseStatementForm', () => {
beforeEach(async () => {
jest.spyOn(stateHooks, 'useInterval').mockReturnValue({ data: TEST_INTERVAL } as DefinedUseQueryResult<Interval>);
});

afterEach(async () => {
jest.resetAllMocks();
});

it('renders as expected', async () => {
const { container } = render(<IncomeExpenseStatementForm />, { wrapper });

screen.getByLabelText('Select dates');
expect(container).toMatchSnapshot();
});
});
Loading

0 comments on commit 47f1163

Please sign in to comment.