Skip to content

Commit 33ba5c7

Browse files
authored
fix: Web console errors that don't require package changes (#8896)
1 parent c3800de commit 33ba5c7

File tree

15 files changed

+1884
-59
lines changed

15 files changed

+1884
-59
lines changed

.gitignore

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
.*/
21
node_modules/
32
.DS_store
43
*.pyc
54
yarn-error.log
65
npm-error.log
76
vagrant/env_local.sh
87
__pycache__
8+
.ruff_cache/
9+
.build/
10+
.venv/
911

1012
# vi
1113
*.swp
@@ -40,3 +42,11 @@ coverage.xml
4042

4143
# Docs generation
4244
docs.egg-info
45+
46+
# AI config
47+
.roo
48+
.roomodes
49+
.vscode/mcp.json
50+
mcp-servers/
51+
.claude
52+
CLAUDE.md

.vscode/settings.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"chat.mcp.discovery.enabled": true,
3+
"editor.formatOnSave": true,
4+
"editor.defaultFormatter": "esbenp.prettier-vscode",
5+
"[javascript]": {
6+
"editor.defaultFormatter": "esbenp.prettier-vscode"
7+
},
8+
"eslint.useFlatConfig": true,
9+
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
10+
"eslint.options": {
11+
"overrideConfigFile": "eslint.config.mjs"
12+
}
13+
}

docker-compose.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
version: '3'
21
services:
32
backend:
43
container_name: backend

eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export default [
6262
'react/no-unused-class-component-methods': 'off',
6363
'react/prefer-exact-props': 'off',
6464
'react/prop-types': 'off',
65+
'react/require-default-props': 'off', // Allow default parameters instead of defaultProps
6566
'react/sort-comp': [0, {}],
6667

6768
'import/order': [
Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
import React from 'react';
2+
import { render, act } from '@testing-library/react';
3+
import { MemoryRouter } from 'react-router-dom';
4+
5+
// Import the component under test
6+
import InfraCompareView from '../../../ui/infra-compare/InfraCompare';
7+
import { getCounterMap } from '../../../ui/infra-compare/helpers';
8+
9+
// Mock dependencies
10+
jest.mock('../../../ui/perfherder/Validation', () => ({
11+
__esModule: true,
12+
default: () => (Component) => (props) => (
13+
<Component {...props} validated={props.validated || {}} />
14+
),
15+
}));
16+
17+
jest.mock('../../../ui/infra-compare/helpers', () => ({
18+
getCounterMap: jest.fn((jobName, originalData, newData) => {
19+
if (!originalData && !newData) {
20+
return { isEmpty: true };
21+
}
22+
23+
return {
24+
isEmpty: false,
25+
platform: 'mock-platform',
26+
suite: 'mock-suite',
27+
originalValue: 100,
28+
newValue: 110,
29+
delta: 10,
30+
deltaPercentage: 10,
31+
originalJobs: new Map(),
32+
newJobs: new Map(),
33+
};
34+
}),
35+
}));
36+
37+
jest.mock('../../../ui/infra-compare/constants', () => ({
38+
phTimeRanges: [
39+
{ value: 86400, text: 'Last day' },
40+
{ value: 86400 * 2, text: 'Last 2 days' },
41+
{ value: 604800, text: 'Last 7 days' },
42+
{ value: 1209600, text: 'Last 14 days' },
43+
{ value: 2592000, text: 'Last 30 days' },
44+
],
45+
}));
46+
47+
// Store props passed to the mock component for testing
48+
let mockTableViewProps = {};
49+
50+
jest.mock('../../../ui/infra-compare/InfraCompareTableView', () => {
51+
const MockTableView = (props) => {
52+
// Store the props for testing
53+
mockTableViewProps = props;
54+
return <div data-testid="infra-compare-table-view" />;
55+
};
56+
return MockTableView;
57+
});
58+
59+
describe('InfraCompareView', () => {
60+
// Test fixtures
61+
const defaultProps = {
62+
projects: [{ name: 'mozilla-central' }, { name: 'try' }],
63+
updateAppState: jest.fn(),
64+
};
65+
66+
const mockValidated = {
67+
originalProject: 'mozilla-central',
68+
newProject: 'try',
69+
originalRevision: 'abc123',
70+
newRevision: 'def456',
71+
originalResultSet: {
72+
push_timestamp: 1596240000,
73+
},
74+
newResultSet: {
75+
push_timestamp: 1596250000,
76+
},
77+
};
78+
79+
beforeEach(() => {
80+
jest.clearAllMocks();
81+
// Reset the mock props
82+
mockTableViewProps = {};
83+
// Mock Date.now() to return a fixed timestamp for consistent testing
84+
jest
85+
.spyOn(Date.prototype, 'getTime')
86+
.mockImplementation(() => 1596340000000);
87+
});
88+
89+
afterEach(() => {
90+
jest.restoreAllMocks();
91+
});
92+
93+
it('renders correctly', () => {
94+
const { getByTestId } = render(
95+
<MemoryRouter>
96+
<InfraCompareView {...defaultProps} validated={mockValidated} />
97+
</MemoryRouter>,
98+
);
99+
100+
expect(getByTestId('infra-compare-table-view')).toBeInTheDocument();
101+
});
102+
103+
it('passes the correct props to InfraCompareTableView', () => {
104+
render(
105+
<MemoryRouter>
106+
<InfraCompareView {...defaultProps} validated={mockValidated} />
107+
</MemoryRouter>,
108+
);
109+
110+
// Check that the correct props were passed to the mock component
111+
expect(mockTableViewProps).toHaveProperty('projects');
112+
expect(mockTableViewProps).toHaveProperty('validated');
113+
expect(mockTableViewProps).toHaveProperty('updateAppState');
114+
expect(mockTableViewProps).toHaveProperty('jobsNotDisplayed');
115+
expect(mockTableViewProps).toHaveProperty('getQueryParams');
116+
expect(mockTableViewProps).toHaveProperty('getDisplayResults');
117+
});
118+
119+
describe('getInterval function', () => {
120+
it('calculates the correct interval based on timestamps', () => {
121+
render(
122+
<MemoryRouter>
123+
<InfraCompareView {...defaultProps} validated={mockValidated} />
124+
</MemoryRouter>,
125+
);
126+
127+
// Since we can't directly access the component's methods,
128+
// we'll test the getQueryParams function which uses getInterval
129+
expect(mockTableViewProps).toHaveProperty('getQueryParams');
130+
131+
// We can test that getQueryParams returns the expected result
132+
const timeRange = { value: 86400 };
133+
const [originalParams, newParams] = mockTableViewProps.getQueryParams(
134+
timeRange,
135+
);
136+
137+
// Verify the params have the expected properties
138+
expect(originalParams).toHaveProperty('project', 'mozilla-central');
139+
expect(originalParams).toHaveProperty('interval');
140+
expect(originalParams).toHaveProperty('revision', 'abc123');
141+
142+
expect(newParams).toHaveProperty('project', 'try');
143+
expect(newParams).toHaveProperty('interval');
144+
expect(newParams).toHaveProperty('revision', 'def456');
145+
});
146+
});
147+
148+
describe('getQueryParams function', () => {
149+
it('returns correct params with originalRevision', () => {
150+
render(
151+
<MemoryRouter>
152+
<InfraCompareView {...defaultProps} validated={mockValidated} />
153+
</MemoryRouter>,
154+
);
155+
156+
const timeRange = { value: 86400 };
157+
const [originalParams, newParams] = mockTableViewProps.getQueryParams(
158+
timeRange,
159+
);
160+
161+
// Verify the params have the expected properties
162+
expect(originalParams).toHaveProperty('project', 'mozilla-central');
163+
expect(originalParams).toHaveProperty('interval');
164+
expect(originalParams).toHaveProperty('revision', 'abc123');
165+
166+
expect(newParams).toHaveProperty('project', 'try');
167+
expect(newParams).toHaveProperty('interval');
168+
expect(newParams).toHaveProperty('revision', 'def456');
169+
});
170+
171+
it('returns correct params without originalRevision', () => {
172+
const validatedWithoutOriginalRevision = {
173+
...mockValidated,
174+
originalRevision: null,
175+
};
176+
177+
render(
178+
<MemoryRouter>
179+
<InfraCompareView
180+
{...defaultProps}
181+
validated={validatedWithoutOriginalRevision}
182+
/>
183+
</MemoryRouter>,
184+
);
185+
186+
const timeRange = { value: 86400, text: 'Last day' };
187+
const [originalParams, newParams] = mockTableViewProps.getQueryParams(
188+
timeRange,
189+
);
190+
191+
// Verify the params have the expected properties
192+
expect(originalParams).toHaveProperty(
193+
'originalProject',
194+
'mozilla-central',
195+
);
196+
expect(originalParams).toHaveProperty('interval', 86400);
197+
expect(originalParams).toHaveProperty('startday');
198+
expect(originalParams).toHaveProperty('endday');
199+
200+
expect(newParams).toHaveProperty('project', 'try');
201+
expect(newParams).toHaveProperty('interval', 86400);
202+
expect(newParams).toHaveProperty('revision', 'def456');
203+
});
204+
});
205+
206+
describe('getDisplayResults function', () => {
207+
it('processes empty results correctly', () => {
208+
render(
209+
<MemoryRouter>
210+
<InfraCompareView {...defaultProps} validated={mockValidated} />
211+
</MemoryRouter>,
212+
);
213+
214+
const origResultsMap = [];
215+
const newResultsMap = [];
216+
const tableNames = [];
217+
218+
const result = mockTableViewProps.getDisplayResults(
219+
origResultsMap,
220+
newResultsMap,
221+
tableNames,
222+
);
223+
224+
expect(result).toHaveProperty('compareResults');
225+
expect(result).toHaveProperty('loading', false);
226+
expect(defaultProps.updateAppState).toHaveBeenCalledWith({
227+
compareData: expect.any(Map),
228+
});
229+
});
230+
231+
it('processes valid results correctly', () => {
232+
render(
233+
<MemoryRouter>
234+
<InfraCompareView {...defaultProps} validated={mockValidated} />
235+
</MemoryRouter>,
236+
);
237+
238+
const origResultsMap = [
239+
{
240+
job_type__name: 'platform/suite-1',
241+
duration: 100,
242+
result: 'success',
243+
},
244+
];
245+
const newResultsMap = [
246+
{
247+
job_type__name: 'platform/suite-1',
248+
duration: 110,
249+
result: 'success',
250+
},
251+
];
252+
const tableNames = ['platform/suite'];
253+
254+
const result = mockTableViewProps.getDisplayResults(
255+
origResultsMap,
256+
newResultsMap,
257+
tableNames,
258+
);
259+
260+
expect(result).toHaveProperty('compareResults');
261+
expect(result).toHaveProperty('loading', false);
262+
expect(defaultProps.updateAppState).toHaveBeenCalledWith({
263+
compareData: expect.any(Map),
264+
});
265+
});
266+
267+
it('updates jobsNotDisplayed state for empty results', async () => {
268+
// Mock getCounterMap to return isEmpty: true for the specific test case
269+
getCounterMap.mockImplementation((jobName) => {
270+
if (jobName === 'platform/suite') {
271+
return { isEmpty: true };
272+
}
273+
return {
274+
isEmpty: false,
275+
platform: 'mock-platform',
276+
suite: 'mock-suite',
277+
originalJobs: new Map(),
278+
newJobs: new Map(),
279+
};
280+
});
281+
282+
// Render the component
283+
const { rerender } = render(
284+
<MemoryRouter>
285+
<InfraCompareView {...defaultProps} validated={mockValidated} />
286+
</MemoryRouter>,
287+
);
288+
289+
// Initial jobsNotDisplayed should be an empty array
290+
expect(mockTableViewProps.jobsNotDisplayed).toEqual([]);
291+
292+
// Call getDisplayResults with a table name that will result in isEmpty: true
293+
const origResultsMap = [];
294+
const newResultsMap = [];
295+
const tableNames = ['platform/suite'];
296+
297+
// Use act to handle the state update
298+
await act(async () => {
299+
mockTableViewProps.getDisplayResults(
300+
origResultsMap,
301+
newResultsMap,
302+
tableNames,
303+
);
304+
});
305+
306+
// Force a re-render to ensure the updated state is reflected in the props
307+
rerender(
308+
<MemoryRouter>
309+
<InfraCompareView {...defaultProps} validated={mockValidated} />
310+
</MemoryRouter>,
311+
);
312+
313+
// Now jobsNotDisplayed should contain the table name
314+
expect(mockTableViewProps.jobsNotDisplayed).toContain('platform/suite');
315+
});
316+
});
317+
});

0 commit comments

Comments
 (0)