@conorheffron/ironoc-frontend 7.3.2 → 7.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conorheffron/ironoc-frontend",
3
- "version": "7.3.2",
3
+ "version": "7.3.3",
4
4
  "private": false,
5
5
  "dependencies": {
6
6
  "@fontsource/montserrat": "^5.1.1",
@@ -8,6 +8,7 @@
8
8
  "@testing-library/user-event": "^13.5.0",
9
9
  "axios": "^1.8.2",
10
10
  "bootstrap": "5.1",
11
+ "history": "^5.3.0",
11
12
  "react": "^18.3.1",
12
13
  "react-bootstrap": "^2.10.5",
13
14
  "react-bootstrap-carousel": "^4.1.1",
@@ -15,7 +16,8 @@
15
16
  "react-bootstrap-validation": "^0.1.11",
16
17
  "react-cookie": "^7.2.2",
17
18
  "react-dom": "^18.3.1",
18
- "react-router-dom": "^5.3.0",
19
+ "react-router": "^7.5.2",
20
+ "react-router-dom": "^7.5.2",
19
21
  "reactstrap": "^8.10.0",
20
22
  "recharts": "^3.0.0-alpha.5",
21
23
  "web-vitals": "^4.2.3"
package/src/App.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import React, { Component } from 'react';
2
- import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
2
+ import { BrowserRouter as Router, Routes, Route } from 'react-router';
3
3
  import './App.css';
4
4
  import Home from './components/Home';
5
5
  import CoffeeHome from './components/CoffeeHome';
@@ -30,11 +30,16 @@ class App extends Component {
30
30
 
31
31
  return (
32
32
  <Router forceRefresh={forceRefresh}>
33
- <Switch>
33
+ <Routes>
34
34
  {routes.map((route, index) => (
35
- <Route key={index} path={route.path} exact={route.exact} component={route.component} />
35
+ <Route
36
+ key={index}
37
+ path={route.path}
38
+ {...(route.exact ? { exact: true } : {})}
39
+ element={<route.component />}
40
+ />
36
41
  ))}
37
- </Switch>
42
+ </Routes>
38
43
  </Router>
39
44
  );
40
45
  }
package/src/App.test.js CHANGED
@@ -4,31 +4,11 @@ import '@testing-library/jest-dom';
4
4
  import Home from './components/Home';
5
5
  import App from './App';
6
6
  import { createMemoryHistory } from 'history';
7
- import { Router } from 'react-router-dom';
8
7
  import LoadingSpinner from './LoadingSpinner';
9
8
  import AppNavBar from './AppNavbar';
10
9
  import About from './components/About';
11
10
  import Footer from './Footer';
12
-
13
- describe('LoadingSpinner', () => {
14
- test('renders LoadingSpinner component correctly', () => {
15
- // Render the component
16
- const { getByRole, getByText } = render(<LoadingSpinner />);
17
-
18
- // Check if the button is present and disabled
19
- const button = getByRole('button');
20
- expect(button).toBeInTheDocument();
21
- expect(button).toBeDisabled();
22
-
23
- // Check if the spinner is present
24
- const spinner = getByRole('button');
25
- expect(spinner).toBeInTheDocument();
26
-
27
- // Check if the button contains the text "Loading..."
28
- const loadingText = getByText('Loading...');
29
- expect(loadingText).toBeInTheDocument();
30
- });
31
- });
11
+ import { Router, useLocation, MemoryRouter } from 'react-router';
32
12
 
33
13
  describe('AppNavBar', () => {
34
14
  test('renders AppNavBar component correctly', () => {
@@ -87,18 +67,6 @@ describe('AppNavBar', () => {
87
67
  });
88
68
  });
89
69
 
90
- describe('App Component Routing', () => {
91
- test("sends the user to about", () => {
92
- const history = createMemoryHistory({ initialEntries: ["/about"] });
93
- render(
94
- <Router history={history}>
95
- <App />
96
- </Router>
97
- );
98
- expect(history.location.pathname).toBe("/about");
99
- });
100
- });
101
-
102
70
  describe('Footer Component', () => {
103
71
  beforeEach(() => {
104
72
  // Mock the fetch API
@@ -3,8 +3,17 @@ import { Button, ButtonGroup, Container, InputGroup, Table } from 'reactstrap';
3
3
  import '.././App.css';
4
4
  import Form from 'react-bootstrap/Form';
5
5
  import AppNavbar from '.././AppNavbar';
6
- import { Link } from 'react-router-dom';
7
6
  import LoadingSpinner from '.././LoadingSpinner';
7
+ import { Link, useParams, useNavigate } from 'react-router';
8
+
9
+ // Helper function to inject `params` and `navigate` into class-based components
10
+ function withRouter(Component) {
11
+ return (props) => {
12
+ const params = useParams();
13
+ const navigate = useNavigate();
14
+ return <Component {...props} params={params} navigate={navigate} />;
15
+ };
16
+ }
8
17
 
9
18
  class RepoDetails extends Component {
10
19
  constructor(props) {
@@ -26,21 +35,29 @@ class RepoDetails extends Component {
26
35
  onSubmit(event) {
27
36
  event.preventDefault();
28
37
  const { value } = this.state;
29
- this.props.history.push(`/projects/${value}`, {
30
- id: value
38
+ this.props.navigate(`/projects/${value}`, {
39
+ replace: true,
40
+ state: {
41
+ id: value
42
+ }
31
43
  });
44
+ this.props.navigate(0)
32
45
  }
33
46
 
34
47
  async componentDidMount() {
35
- const { match: { params: { id } } } = this.props;
36
- const response = await fetch(`/api/get-repo-detail?username=${id}`);
37
- const body = await response.json();
38
- this.setState({ repoDetailList: body, isLoading: false });
48
+ const { id } = this.props.params;
49
+ if (id) {
50
+ const response = await fetch(`/api/get-repo-detail?username=${id}`);
51
+ const body = await response.json();
52
+ this.setState({ repoDetailList: body, isLoading: false });
53
+ } else {
54
+ this.setState({ isLoading: false });
55
+ }
39
56
  }
40
57
 
41
58
  render() {
42
59
  const { repoDetailList = [], isLoading = true, value = '' } = this.state;
43
- const { match: { params: { id: gitUser = '' } } } = this.props;
60
+ const { id: gitUser = '' } = this.props.params;
44
61
 
45
62
  if (isLoading) {
46
63
  return (
@@ -107,4 +124,4 @@ class RepoDetails extends Component {
107
124
  }
108
125
  }
109
126
 
110
- export default RepoDetails;
127
+ export default withRouter(RepoDetails);
@@ -4,6 +4,16 @@ import '.././App.css';
4
4
  import Form from 'react-bootstrap/Form';
5
5
  import AppNavbar from '.././AppNavbar';
6
6
  import LoadingSpinner from '.././LoadingSpinner';
7
+ import { useParams, useNavigate } from 'react-router';
8
+
9
+ // Helper function to inject `params` and `navigate` into class-based components
10
+ function withRouter(Component) {
11
+ return (props) => {
12
+ const params = useParams();
13
+ const navigate = useNavigate();
14
+ return <Component {...props} params={params} navigate={navigate} />;
15
+ };
16
+ }
7
17
 
8
18
  class RepoIssues extends Component {
9
19
  constructor(props) {
@@ -25,23 +35,31 @@ class RepoIssues extends Component {
25
35
  onSubmit(event) {
26
36
  event.preventDefault();
27
37
  const { value } = this.state;
28
- const { match: { params: { id } }, history } = this.props;
29
- history.push(`/issues/${id}/${value}`, {
30
- id: id,
31
- repo: value
38
+ const { id } = this.props.params;
39
+ this.props.navigate(`/issues/${id}/${value}`, {
40
+ replace: true,
41
+ state: {
42
+ id: id,
43
+ repo: value
44
+ }
32
45
  });
46
+ this.props.navigate(0)
33
47
  }
34
48
 
35
49
  async componentDidMount() {
36
- const { match: { params: { id, repo } } } = this.props;
37
- const response = await fetch(`/api/get-repo-issue/${id}/${repo}/`);
38
- const body = await response.json();
39
- this.setState({ repoIssueList: body, isLoading: false });
50
+ const { id, repo } = this.props.params;
51
+ if (id && repo) {
52
+ const response = await fetch(`/api/get-repo-issue/${id}/${repo}/`);
53
+ const body = await response.json();
54
+ this.setState({ repoIssueList: body, isLoading: false });
55
+ } else {
56
+ this.setState({ isLoading: false });
57
+ }
40
58
  }
41
59
 
42
60
  render() {
43
61
  const { repoIssueList = [], isLoading = true, value = '' } = this.state;
44
- const { match: { params: { id = '', repo = '' } } } = this.props;
62
+ const { id = '', repo = '' } = this.props.params;
45
63
 
46
64
  if (isLoading) {
47
65
  return (
@@ -103,4 +121,4 @@ class RepoIssues extends Component {
103
121
  }
104
122
  }
105
123
 
106
- export default RepoIssues;
124
+ export default withRouter(RepoIssues);
@@ -1,7 +1,6 @@
1
1
  import { render, screen, waitFor } from '@testing-library/react';
2
2
  import App from '../../App';
3
- import { createMemoryHistory } from 'history';
4
- import { Router } from 'react-router-dom';
3
+ import { Router, MemoryRouter } from 'react-router';
5
4
  import Donate from '../Donate';
6
5
  import LoadingSpinner from '../../LoadingSpinner';
7
6
 
@@ -49,12 +48,11 @@ describe('Donate', () => {
49
48
  });
50
49
 
51
50
  test('displays charity options after fetching data', async () => {
52
- const history = createMemoryHistory();
53
51
  const { container } = render(
54
- <Router history={history}>
55
- <Donate />
56
- </Router>
57
- );
52
+ <MemoryRouter>
53
+ <Donate />
54
+ </MemoryRouter>
55
+ );
58
56
 
59
57
  // Wait for the component to finish loading
60
58
  await waitFor(() => {
@@ -73,4 +71,3 @@ describe('Donate', () => {
73
71
  expect(container.querySelector('.LoadingSpinner')).not.toBeInTheDocument();
74
72
  });
75
73
  });
76
-
@@ -1,83 +1,130 @@
1
- import { render, screen, waitFor } from '@testing-library/react';
2
- import App from '../../App';
3
- import { createMemoryHistory } from 'history';
4
- import { Router } from 'react-router-dom';
1
+ import React from 'react';
2
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3
+ import { MemoryRouter } from 'react-router';
5
4
  import RepoDetails from '../RepoDetails';
6
- import LoadingSpinner from '../../LoadingSpinner';
7
-
8
- const mockRepoDetails = [
9
- {
10
- name: 'repo1',
11
- fullName: 'User/repo1',
12
- repoUrl: 'https://github.com/User/repo1',
13
- description: 'Description of repo1',
14
- appHome: 'https://repo1.com',
15
- topics: ['topic1', 'topic2']
16
- },
17
- {
18
- name: 'repo2',
19
- fullName: 'User/repo2',
20
- repoUrl: 'https://github.com/User/repo2',
21
- description: 'Description of repo2',
22
- appHome: 'https://repo2.com',
23
- topics: ['topic3', 'topic4']
24
- }
25
- ];
26
-
27
- describe('RepoDetails', () => {
28
- beforeEach(() => {
29
- jest.spyOn(global, 'fetch').mockResolvedValue({
30
- json: jest.fn().mockResolvedValue(mockRepoDetails)
5
+
6
+ jest.mock('react-router', () => ({
7
+ ...jest.requireActual('react-router'),
8
+ useParams: jest.fn(),
9
+ useNavigate: jest.fn(),
10
+ }));
11
+
12
+ describe('RepoDetails Component', () => {
13
+ const mockNavigate = jest.fn();
14
+ const mockParams = { id: 'testUser' };
15
+
16
+ beforeEach(() => {
17
+ require('react-router').useNavigate.mockReturnValue(mockNavigate);
18
+ require('react-router').useParams.mockReturnValue(mockParams);
19
+
20
+ global.fetch = jest.fn(() =>
21
+ Promise.resolve({
22
+ json: () =>
23
+ Promise.resolve([
24
+ {
25
+ name: 'testRepo',
26
+ repoUrl: 'https://github.com/testRepo',
27
+ fullName: 'testUser/testRepo',
28
+ description: 'Test repository description',
29
+ appHome: 'https://example.com',
30
+ topics: 'topic1, topic2',
31
+ },
32
+ ]),
33
+ })
34
+ );
31
35
  });
32
- });
33
-
34
- afterEach(() => {
35
- jest.restoreAllMocks();
36
- });
37
-
38
- test('renders AppNavbar component', () => {
39
- render(<App />);
40
- expect(screen.getByRole('banner')).toBeInTheDocument();
41
- });
42
-
43
- test('renders loading state initially', () => {
44
- render(<RepoDetails match={{ params: { id: 'User' } }} />);
45
- expect(screen.getByText('Loading...')).toBeInTheDocument();
46
- });
47
-
48
- test('displays repo details after fetching data', async () => {
49
- const history = createMemoryHistory();
50
- const { container } = render(
51
- <Router history={history}>
52
- <RepoDetails match={{ params: { id: 'User' } }} />
53
- </Router>
54
- );
55
-
56
- // Wait for the component to finish loading
57
- await waitFor(() => {
58
- expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
36
+
37
+ afterEach(() => {
38
+ jest.clearAllMocks();
59
39
  });
60
40
 
61
- // Check that repo details are displayed
62
- mockRepoDetails.forEach(repo => {
63
- expect(screen.getByText(repo.fullName)).toBeInTheDocument();
64
- expect(screen.queryByText(repo.repoUrl)).not.toBeInTheDocument();
65
- expect(screen.getByText(repo.description)).toBeInTheDocument();
66
- expect(screen.queryByText(repo.appHome)).not.toBeInTheDocument();
67
-
68
- repo.topics.forEach(topic => {
69
- // Select the <td> element
70
- const tdElement = screen.getByText((content, element) => {
71
- // Ensure the element is a <td>
72
- return element.tagName.toLowerCase() === 'td' && content.includes(topic);
73
- });
74
- // Check if the <td> contains the expected text
75
- expect(tdElement).toBeInTheDocument();
76
- expect(tdElement).toHaveTextContent(topic);
77
- });
41
+ test('renders loading spinner initially', () => {
42
+ render(
43
+ <MemoryRouter>
44
+ <RepoDetails />
45
+ </MemoryRouter>
46
+ );
47
+
48
+ expect(screen.getByText(/loading/i)).toBeInTheDocument();
49
+ });
50
+
51
+ test('fetches and displays repo details', async () => {
52
+ render(
53
+ <MemoryRouter>
54
+ <RepoDetails />
55
+ </MemoryRouter>
56
+ );
57
+
58
+ await waitFor(() => expect(fetch).toHaveBeenCalledTimes(1));
59
+
60
+ expect(screen.getByText('testUser/testRepo')).toBeInTheDocument();
61
+ expect(screen.getByText('Test repository description')).toBeInTheDocument();
62
+ expect(screen.getByText('topic1, topic2')).toBeInTheDocument();
63
+ });
64
+
65
+
66
+ test('handles input change', async () => {
67
+ render(
68
+ <MemoryRouter>
69
+ <RepoDetails />
70
+ </MemoryRouter>
71
+ );
72
+
73
+ // Wait for the loading spinner to disappear
74
+ await waitFor(() => expect(screen.queryByText(/loading/i)).not.toBeInTheDocument());
75
+
76
+ const input = screen.getByPlaceholderText(/Enter GitHub User ID/i);
77
+ fireEvent.change(input, { target: { value: 'newUser' } });
78
+
79
+ expect(input.value).toBe('newUser');
80
+ });
81
+
82
+ test('displays table headers correctly', async () => {
83
+ render(
84
+ <MemoryRouter>
85
+ <RepoDetails />
86
+ </MemoryRouter>
87
+ );
88
+
89
+ // Wait for the loading spinner to disappear
90
+ await waitFor(() => expect(screen.queryByText(/loading/i)).not.toBeInTheDocument());
91
+
92
+ // Assert that table headers are displayed correctly using roles
93
+ const repositoryHeader = screen.getByRole('columnheader', { name: /Repository/i });
94
+ const descriptionHeader = screen.getByRole('columnheader', { name: /Description/i });
95
+ const appUrlHeader = screen.getByRole('columnheader', { name: /App URL/i });
96
+ const topicsHeader = screen.getByRole('columnheader', { name: /Topics/i });
97
+ const actionsHeader = screen.getByRole('columnheader', { name: /Actions/i });
98
+
99
+ expect(repositoryHeader).toBeInTheDocument();
100
+ expect(descriptionHeader).toBeInTheDocument();
101
+ expect(appUrlHeader).toBeInTheDocument();
102
+ expect(topicsHeader).toBeInTheDocument();
103
+ expect(actionsHeader).toBeInTheDocument();
78
104
  });
79
105
 
80
- // Verify that the component does not show the loading spinner
81
- expect(container.querySelector('.LoadingSpinner')).not.toBeInTheDocument();
82
- });
106
+ test('navigates on form submission', async () => { // Make the function async
107
+ render(
108
+ <MemoryRouter>
109
+ <RepoDetails />
110
+ </MemoryRouter>
111
+ );
112
+
113
+ // Wait for the loading spinner to disappear
114
+ await waitFor(() => expect(screen.queryByText(/loading/i)).not.toBeInTheDocument());
115
+
116
+ // Use getByRole to target the input field and button
117
+ const input = screen.getByRole('textbox', { name: /Enter GitHub User ID/i });
118
+ const button = screen.getByRole('button', { name: /Search Projects/i });
119
+
120
+ // Simulate user input and form submission
121
+ fireEvent.change(input, { target: { value: 'newUser' } });
122
+ fireEvent.click(button);
123
+
124
+ // Assert that navigation was triggered with the correct arguments
125
+ expect(mockNavigate).toHaveBeenCalledWith('/projects/newUser', {
126
+ replace: true,
127
+ state: { id: 'newUser' },
128
+ });
129
+ });
83
130
  });
@@ -1,68 +1,121 @@
1
- import { render, screen, waitFor } from '@testing-library/react';
2
- import App from '../../App';
3
- import { createMemoryHistory } from 'history';
4
- import { Router } from 'react-router-dom';
1
+ import React from 'react';
2
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3
+ import { MemoryRouter } from 'react-router';
5
4
  import RepoIssues from '../RepoIssues';
6
- import LoadingSpinner from '../../LoadingSpinner';
7
-
8
- const mockRepoIssues = [
9
- {
10
- number: 1,
11
- title: 'Issue 1',
12
- body: 'https://github.com/User/repo/issues/1'
13
- },
14
- {
15
- number: 2,
16
- title: 'Issue 2',
17
- body: 'https://github.com/User/repo/issues/2'
18
- }
19
- ];
20
-
21
- describe('RepoIssues', () => {
22
- beforeEach(() => {
23
- jest.spyOn(global, 'fetch').mockResolvedValue({
24
- json: jest.fn().mockResolvedValue(mockRepoIssues)
5
+
6
+ jest.mock('react-router', () => ({
7
+ ...jest.requireActual('react-router'),
8
+ useParams: jest.fn(),
9
+ useNavigate: jest.fn(),
10
+ }));
11
+
12
+ describe('RepoIssues Component', () => {
13
+ const mockNavigate = jest.fn();
14
+ const mockParams = { id: 'testUser', repo: 'testRepo' };
15
+
16
+ beforeEach(() => {
17
+ require('react-router').useNavigate.mockReturnValue(mockNavigate);
18
+ require('react-router').useParams.mockReturnValue(mockParams);
19
+
20
+ global.fetch = jest.fn(() =>
21
+ Promise.resolve({
22
+ json: () =>
23
+ Promise.resolve([
24
+ {
25
+ number: 1,
26
+ title: 'Test Issue Title',
27
+ body: 'Test issue body description',
28
+ },
29
+ ]),
30
+ })
31
+ );
32
+ });
33
+
34
+ afterEach(() => {
35
+ jest.clearAllMocks();
36
+ });
37
+
38
+ test('renders loading spinner initially', () => {
39
+ render(
40
+ <MemoryRouter>
41
+ <RepoIssues />
42
+ </MemoryRouter>
43
+ );
44
+
45
+ expect(screen.getByText(/loading/i)).toBeInTheDocument();
46
+ });
47
+
48
+ test('fetches and displays repo issues', async () => {
49
+ render(
50
+ <MemoryRouter>
51
+ <RepoIssues />
52
+ </MemoryRouter>
53
+ );
54
+
55
+ await waitFor(() => expect(fetch).toHaveBeenCalledTimes(1));
56
+
57
+ expect(screen.getByText(/Test Issue Title/i)).toBeInTheDocument();
58
+ expect(screen.getByText(/Test issue body description/i)).toBeInTheDocument();
59
+ expect(screen.getByText(/1/i)).toBeInTheDocument();
25
60
  });
26
- });
27
-
28
- afterEach(() => {
29
- jest.restoreAllMocks();
30
- });
31
-
32
- test('renders AppNavbar component', () => {
33
- render(<App />);
34
- expect(screen.getByRole('banner')).toBeInTheDocument();
35
- });
36
-
37
- test('renders loading state initially', () => {
38
- render(<RepoIssues match={{ params: { id: 'User', repo: 'ironoc-test' } }} />);
39
- expect(screen.getByText('Loading...')).toBeInTheDocument();
40
- });
41
-
42
- test('displays repo issues after fetching data', async () => {
43
- const history = createMemoryHistory();
44
- const { container } = render(
45
- <Router history={history}>
46
- <RepoIssues match={{ params: { id: 'conors-id', repo: 'ironoc-testing' } }} />
47
- </Router>
48
- );
49
-
50
- // Wait for the component to finish loading
51
- await waitFor(() => {
52
- expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
61
+
62
+ test('handles input change', async () => {
63
+ render(
64
+ <MemoryRouter>
65
+ <RepoIssues />
66
+ </MemoryRouter>
67
+ );
68
+
69
+ await waitFor(() => expect(fetch).toHaveBeenCalledTimes(1));
70
+
71
+ const input = screen.getByPlaceholderText(/Enter Project Name/i);
72
+ fireEvent.change(input, { target: { value: 'newRepo' } });
73
+
74
+ expect(input.value).toBe('newRepo');
53
75
  });
54
76
 
55
- expect(screen.queryByText('conors-id')).toBeInTheDocument();
56
- expect(screen.queryByText('ironoc-testing')).toBeInTheDocument();
77
+ test('navigates on form submission', async () => {
78
+ render(
79
+ <MemoryRouter>
80
+ <RepoIssues />
81
+ </MemoryRouter>
82
+ );
83
+
84
+ await waitFor(() => expect(fetch).toHaveBeenCalledTimes(1));
57
85
 
58
- // Check that repo issues are displayed
59
- mockRepoIssues.forEach(issue => {
60
- expect(screen.getByText(issue.title)).toBeInTheDocument();
61
- expect(screen.getByText(issue.body)).toBeInTheDocument();
62
- expect(screen.getByText(issue.number)).toBeInTheDocument();
86
+ const input = screen.getByPlaceholderText(/Enter Project Name/i);
87
+ const button = screen.getByText(/Search Issues/i);
88
+
89
+ fireEvent.change(input, { target: { value: 'newRepo' } });
90
+ fireEvent.click(button);
91
+
92
+ expect(mockNavigate).toHaveBeenCalledWith('/issues/testUser/newRepo', {
93
+ replace: true,
94
+ state: {
95
+ id: 'testUser',
96
+ repo: 'newRepo',
97
+ },
98
+ });
63
99
  });
64
100
 
65
- // Verify that the component does not show the loading spinner
66
- expect(container.querySelector('.LoadingSpinner')).not.toBeInTheDocument();
67
- });
101
+ test('displays table headers correctly', async () => {
102
+ render(
103
+ <MemoryRouter>
104
+ <RepoIssues />
105
+ </MemoryRouter>
106
+ );
107
+
108
+ // Wait for the loading spinner to disappear
109
+ await waitFor(() => expect(screen.queryByText(/loading/i)).not.toBeInTheDocument());
110
+
111
+ // Use getByRole for specific headers
112
+ const issueNoHeader = screen.getByRole('columnheader', { name: /Issue No./i });
113
+ const titleHeader = screen.getByRole('columnheader', { name: /Title/i });
114
+ const descriptionHeader = screen.getByRole('columnheader', { name: /Description/i });
115
+
116
+ // Assert that headers are in the document
117
+ expect(issueNoHeader).toBeInTheDocument();
118
+ expect(titleHeader).toBeInTheDocument();
119
+ expect(descriptionHeader).toBeInTheDocument();
120
+ });
68
121
  });
package/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import React from 'react';
2
- import ReactDOM from 'react-dom/client';
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
3
  import reportWebVitals from './reportWebVitals';
4
4
  import 'bootstrap/dist/css/bootstrap.min.css';
5
5
  import './index.css';
@@ -7,9 +7,9 @@ import App from './App';
7
7
  import AppNavBar from './AppNavbar';
8
8
  import Footer from './Footer';
9
9
 
10
- const root = ReactDOM.createRoot(document.getElementById('root'));
10
+ const root = createRoot(document.getElementById('root'));
11
11
  root.render(
12
- <React.StrictMode>
12
+ <StrictMode>
13
13
  <div className="app-wrapper">
14
14
  <AppNavBar />
15
15
  <div className="content-inner">
@@ -17,7 +17,7 @@ root.render(
17
17
  </div>
18
18
  <Footer />
19
19
  </div>
20
- </React.StrictMode>
20
+ </StrictMode>
21
21
  );
22
22
 
23
23
  reportWebVitals();
package/src/setupTests.js CHANGED
@@ -1 +1,4 @@
1
1
  import '@testing-library/jest-dom';
2
+ import { TextEncoder } from 'util';
3
+
4
+ global.TextEncoder = TextEncoder;