@conorheffron/ironoc-frontend 9.1.6 → 9.1.7

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": "9.1.6",
3
+ "version": "9.1.7",
4
4
  "private": false,
5
5
  "license": "GPL-3.0-or-later",
6
6
  "dependencies": {
@@ -127,6 +127,7 @@ const RepoIssues = () => {
127
127
  enableStickyHeader: true,
128
128
  initialState: {
129
129
  showColumnFilters: true,
130
+ columnFilters: [{ id: 'state', value: ['open'] }],
130
131
  columnVisibility: {
131
132
  state: false,
132
133
  body: false,
@@ -1,7 +1,9 @@
1
1
  import React from 'react';
2
2
  import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3
3
  import RepoIssues from '../RepoIssues';
4
- import { useMaterialReactTable } from 'material-react-table';
4
+
5
+ // Track useMaterialReactTable call opts for assertions (must start with 'mock' for Jest hoisting)
6
+ const mockUseMRTOpts = [];
5
7
 
6
8
  // Mock react-router
7
9
  jest.mock('react-router', () => ({
@@ -27,16 +29,31 @@ jest.mock('react-bootstrap', () => ({
27
29
  jest.mock('../../AppNavbar', () => () => <div data-testid="navbar">Navbar</div>);
28
30
  jest.mock('../../LoadingSpinner', () => () => <div data-testid="spinner">Loading...</div>);
29
31
 
30
- // Mock MaterialReactTable and useMaterialReactTable
32
+ // Mock MaterialReactTable and useMaterialReactTable.
33
+ // useMaterialReactTable is a plain function (not jest.fn) so React 19 concurrent mode
34
+ // commits re-renders correctly. Calls are tracked via mockUseMRTOpts for assertions.
35
+ // columnFilters from initialState are applied to table.data so MaterialReactTable
36
+ // only receives the filtered rows, allowing DOM-level assertions on visibility.
31
37
  jest.mock('material-react-table', () => ({
32
38
  MaterialReactTable: ({ table }) => (
33
39
  <div data-testid="mrt-table">
34
- {table && table.data && table.data.map((issue, idx) => (
40
+ {(table?.data ?? []).map((issue, idx) => (
35
41
  <div key={idx} data-testid="mrt-row">{issue.title}</div>
36
42
  ))}
37
43
  </div>
38
44
  ),
39
- useMaterialReactTable: jest.fn((opts) => opts),
45
+ useMaterialReactTable: (opts) => {
46
+ mockUseMRTOpts.push(opts);
47
+ const filters = opts?.initialState?.columnFilters ?? [];
48
+ let data = opts?.data ?? [];
49
+ filters.forEach((filter) => {
50
+ data = data.filter((row) => {
51
+ const val = row[filter.id];
52
+ return Array.isArray(filter.value) ? filter.value.includes(val) : val === filter.value;
53
+ });
54
+ });
55
+ return { ...opts, data };
56
+ },
40
57
  }));
41
58
 
42
59
  // Mock @mui/material theme functions
@@ -58,6 +75,7 @@ describe('RepoIssues', () => {
58
75
  const mockIssuesResponse = [];
59
76
 
60
77
  beforeEach(() => {
78
+ mockUseMRTOpts.length = 0;
61
79
  jest.clearAllMocks();
62
80
  useNavigate.mockReturnValue(mockNavigate);
63
81
  global.fetch = jest.fn(() =>
@@ -98,19 +116,21 @@ describe('RepoIssues', () => {
98
116
  expect(screen.queryByTestId('spinner')).not.toBeInTheDocument()
99
117
  );
100
118
  expect(screen.getByTestId('mrt-table')).toBeInTheDocument();
119
+ expect(screen.getByText('Test Issue')).toBeInTheDocument();
101
120
  });
102
121
 
103
- it('hides state and description columns by default', async () => {
122
+ it('defaults to open issues while hiding state and description columns', async () => {
104
123
  useParams.mockReturnValue({ id: 'user', repo: 'repo' });
105
124
  render(<RepoIssues />);
106
125
  await waitFor(() =>
107
126
  expect(screen.queryByTestId('spinner')).not.toBeInTheDocument()
108
127
  );
109
128
 
110
- expect(useMaterialReactTable).toHaveBeenCalledWith(
129
+ expect(mockUseMRTOpts).toContainEqual(
111
130
  expect.objectContaining({
112
131
  initialState: expect.objectContaining({
113
132
  showColumnFilters: true,
133
+ columnFilters: [{ id: 'state', value: ['open'] }],
114
134
  columnVisibility: {
115
135
  state: false,
116
136
  body: false,
@@ -120,6 +140,24 @@ describe('RepoIssues', () => {
120
140
  );
121
141
  });
122
142
 
143
+ it('renders open issues and excludes closed issues by default', async () => {
144
+ useParams.mockReturnValue({ id: 'user', repo: 'repo' });
145
+ global.fetch = jest.fn(() =>
146
+ Promise.resolve({
147
+ json: () => Promise.resolve([
148
+ { number: 1, state: 'open', labels: [], title: 'Open Issue', body: '' },
149
+ { number: 2, state: 'closed', labels: [], title: 'Closed Issue', body: '' },
150
+ ]),
151
+ })
152
+ );
153
+ window.fetch = global.fetch;
154
+ render(<RepoIssues />);
155
+ await waitFor(() =>
156
+ expect(screen.getByText('Open Issue')).toBeInTheDocument()
157
+ );
158
+ expect(screen.queryByText('Closed Issue')).not.toBeInTheDocument();
159
+ });
160
+
123
161
  it('navigates on form submit', async () => {
124
162
  useParams.mockReturnValue({ id: 'user', repo: 'repo' });
125
163
  global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve([]) }));