Skip to content
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
26 changes: 26 additions & 0 deletions src/hooks/useEnsureListPath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';

/*
* This custom hook ensures that a user has a list path in localStorage.
* If not, it shows a user-friendly notification and redirects to the home page.
*/

export function useEnsureListPath() {
const navigate = useNavigate();
const [isChecking, setIsChecking] = useState(true);

useEffect(() => {
const listPath = localStorage.getItem('tcl-shopping-list-path');
if (!listPath) {
alert(
'It seems like you landed here without first creating a list or selecting an existing one. Please select or create a new list first. Redirecting to Home.',
);
navigate('/');
} else {
setIsChecking(false);
}
}, [navigate]);

return isChecking;
}
4 changes: 4 additions & 0 deletions src/views/List.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import { ListItem } from '../components';
import { useState } from 'react';
import { AddItems } from '../components/AddItems';
import TextInputElement from '../components/TextInputElement';
import { useEnsureListPath } from '../hooks/useEnsureListPath';

export function List({ data, listPath }) {
const [searchItem, setSearchItem] = useState('');

// Redirect to home if no list path is null
if (useEnsureListPath()) return <></>;

const handleTextChange = (event) => {
setSearchItem(event.target.value);
};
Expand Down
3 changes: 3 additions & 0 deletions src/views/ManageList.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { AddItems } from '../components/AddItems';
import { ShareList } from '../components/ShareList';
import { useEnsureListPath } from '../hooks/useEnsureListPath';

export function ManageList({ items }) {
// Redirect to home if no list path is null
if (useEnsureListPath()) return <></>;
return (
<div>
<AddItems items={items} />
Expand Down
49 changes: 40 additions & 9 deletions tests/List.test.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { List } from '../src/views/List';
import { mockShoppingListData } from '../src/mocks/__fixtures__/shoppingListData';
import { useStateWithStorage } from '../src/utils';

vi.mock('../src/utils', () => ({
useStateWithStorage: vi.fn(),
ONE_DAY_IN_MILLISECONDS: 86400000,
}));

beforeEach(() => {
useStateWithStorage.mockReturnValue(['/groceries']);
Object.defineProperty(window, 'localStorage', {
value: {
getItem: vi.fn((key) => {
if (key === 'tcl-shopping-list-path') {
return '/groceries';
}
return null;
}),
setItem: vi.fn(),
clear: vi.fn(),
},
writable: true,
});

vi.spyOn(window, 'alert').mockImplementation(() => {});
});

describe('List Component', () => {
test('renders the shopping list name, search field, and all list items from the data prop', () => {
render(<List data={mockShoppingListData} listPath={'/groceries'} />);
render(
<MemoryRouter>
<List data={mockShoppingListData} listPath={'/groceries'} />
</MemoryRouter>,
);

expect(screen.getByText('groceries')).toBeInTheDocument();
expect(screen.getByLabelText('Search Item:')).toBeInTheDocument();
Expand All @@ -25,7 +38,11 @@ describe('List Component', () => {
});

test('shows welcome message and AddItems component when no items are present', () => {
render(<List data={[]} listPath={'/groceries'} />);
render(
<MemoryRouter>
<List data={[]} listPath={'/groceries'} />
</MemoryRouter>,
);

expect(screen.getByText('Welcome to groceries!')).toBeInTheDocument();
expect(screen.getByLabelText('Item Name:')).toBeInTheDocument();
Expand All @@ -34,4 +51,18 @@ describe('List Component', () => {
expect(screen.getByLabelText('Not soon')).toBeInTheDocument();
expect(screen.getByText('Submit')).toBeInTheDocument();
});

test('triggers alert and redirects when no list path is found in localStorage', () => {
window.localStorage.getItem.mockReturnValueOnce(null);

render(
<MemoryRouter>
<List data={[]} listPath={null} />
</MemoryRouter>,
);

expect(window.alert).toHaveBeenCalledWith(
'It seems like you landed here without first creating a list or selecting an existing one. Please select or create a new list first. Redirecting to Home.',
);
});
});
45 changes: 43 additions & 2 deletions tests/ManageList.test.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
import { render, screen } from '@testing-library/react';
import { ManageList } from '../src/views/ManageList';
import { MemoryRouter } from 'react-router-dom';

beforeEach(() => {
Object.defineProperty(window, 'localStorage', {
value: {
getItem: vi.fn((key) => {
if (key === 'tcl-shopping-list-path') {
return '/groceries';
}
return null;
}),
setItem: vi.fn(),
clear: vi.fn(),
},
writable: true,
});

vi.spyOn(window, 'alert').mockImplementation(() => {});
});

describe('ManageList Component', () => {
test('renders AddItems component with submit button and radio buttons', () => {
render(<ManageList />);
render(
<MemoryRouter>
<ManageList />
</MemoryRouter>,
);

expect(screen.getByLabelText('Item Name:')).toBeInTheDocument();
expect(screen.getByLabelText('Soon')).toBeInTheDocument();
Expand All @@ -13,9 +36,27 @@ describe('ManageList Component', () => {
});

test('renders ShareList component with email input and invite button', () => {
render(<ManageList />);
render(
<MemoryRouter>
<ManageList />
</MemoryRouter>,
);

expect(screen.getByPlaceholderText('Enter email')).toBeInTheDocument();
expect(screen.getByText('Invite User')).toBeInTheDocument();
});

test('triggers alert and redirects when no list path is found in localStorage', () => {
window.localStorage.getItem.mockReturnValueOnce(null);

render(
<MemoryRouter>
<ManageList />
</MemoryRouter>,
);

expect(window.alert).toHaveBeenCalledWith(
'It seems like you landed here without first creating a list or selecting an existing one. Please select or create a new list first. Redirecting to Home.',
);
});
});
2 changes: 2 additions & 0 deletions tests/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ import { vi } from 'vitest';
vi.mock('@the-collab-lab/shopping-list-utils', () => ({
calculateEstimate: vi.fn(),
}));

window.alert = vi.fn();