@cccsaurora/howler-ui 3.1.0-dev.1393 → 3.1.0-dev.1394
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.
|
@@ -84,7 +84,7 @@ const ViewCard = ({ viewId, limit, refreshTick, onRefreshComplete }) => {
|
|
|
84
84
|
}
|
|
85
85
|
isRefreshing.current = true;
|
|
86
86
|
try {
|
|
87
|
-
const res = await dispatchApi(api.v2.search.post(view.indexes, {
|
|
87
|
+
const res = await dispatchApi(api.v2.search.post((view.indexes ?? ['hit']), {
|
|
88
88
|
query: view.query,
|
|
89
89
|
rows: limit,
|
|
90
90
|
metadata: ['analytic']
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import i18n from '@cccsaurora/howler-ui/i18n';
|
|
4
|
+
import React, { useCallback, useState } from 'react';
|
|
5
|
+
import { I18nextProvider } from 'react-i18next';
|
|
6
|
+
import { setupContextSelectorMock } from '@cccsaurora/howler-ui/tests/mocks';
|
|
7
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
8
|
+
import ViewCard from './ViewCard';
|
|
9
|
+
setupContextSelectorMock();
|
|
10
|
+
const mockDispatchApi = vi.hoisted(() => vi.fn());
|
|
11
|
+
const mockNavigate = vi.hoisted(() => vi.fn());
|
|
12
|
+
const mockFetchViews = vi.hoisted(() => vi.fn().mockResolvedValue([]));
|
|
13
|
+
const mockBuildViewUrl = vi.hoisted(() => vi.fn(() => '/views/view-1'));
|
|
14
|
+
vi.mock('api', () => ({
|
|
15
|
+
default: {
|
|
16
|
+
v2: {
|
|
17
|
+
search: {
|
|
18
|
+
post: (...args) => mockSearchPost(...args)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}));
|
|
23
|
+
const mockSearchPost = vi.hoisted(() => vi.fn());
|
|
24
|
+
vi.mock('components/hooks/useMyApi', () => ({
|
|
25
|
+
default: () => ({ dispatchApi: mockDispatchApi })
|
|
26
|
+
}));
|
|
27
|
+
vi.mock('react-router-dom', async () => {
|
|
28
|
+
// oxlint-disable-next-line consistent-type-imports
|
|
29
|
+
const actual = await vi.importActual('react-router-dom');
|
|
30
|
+
return {
|
|
31
|
+
...actual,
|
|
32
|
+
Link: ({ children, to, ...props }) => (_jsx("a", { href: to, ...props, children: children })),
|
|
33
|
+
useNavigate: () => mockNavigate
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
vi.mock('utils/viewUtils', () => ({
|
|
37
|
+
buildViewUrl: mockBuildViewUrl
|
|
38
|
+
}));
|
|
39
|
+
const mockView = {
|
|
40
|
+
view_id: 'view-1',
|
|
41
|
+
title: 'My View',
|
|
42
|
+
query: 'howler.status:open',
|
|
43
|
+
indexes: ['hit'],
|
|
44
|
+
sort: 'event.created desc',
|
|
45
|
+
span: 'date.range.1.month',
|
|
46
|
+
type: 'personal',
|
|
47
|
+
owner: 'test-user',
|
|
48
|
+
settings: { advance_on_triage: false }
|
|
49
|
+
};
|
|
50
|
+
vi.mock('components/app/providers/ViewProvider', () => ({
|
|
51
|
+
ViewContext: React.createContext(null)
|
|
52
|
+
}));
|
|
53
|
+
vi.mock('components/app/providers/RecordProvider', () => {
|
|
54
|
+
const recordContext = React.createContext(null);
|
|
55
|
+
return {
|
|
56
|
+
RecordContext: recordContext,
|
|
57
|
+
useRecordContextSelector: (selector) => selector(React.useContext(recordContext))
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
vi.mock('components/elements/hit/HitBanner', () => ({
|
|
61
|
+
default: ({ hit }) => _jsxs("div", { children: ["Hit: ", hit.howler.id] })
|
|
62
|
+
}));
|
|
63
|
+
vi.mock('components/elements/event/EventCard', () => ({
|
|
64
|
+
default: ({ event }) => _jsxs("div", { children: ["Event: ", event.howler.id] })
|
|
65
|
+
}));
|
|
66
|
+
vi.mock('components/elements/record/RecordContextMenu', () => ({
|
|
67
|
+
default: ({ children }) => _jsx("div", { children: children })
|
|
68
|
+
}));
|
|
69
|
+
vi.mock('commons/components/display/AppListEmpty', () => ({
|
|
70
|
+
default: () => _jsx("div", { children: "No records" })
|
|
71
|
+
}));
|
|
72
|
+
import { RecordContext } from '@cccsaurora/howler-ui/components/app/providers/RecordProvider';
|
|
73
|
+
import { ViewContext } from '@cccsaurora/howler-ui/components/app/providers/ViewProvider';
|
|
74
|
+
const hit = {
|
|
75
|
+
__index: 'hit',
|
|
76
|
+
howler: {
|
|
77
|
+
id: 'hit-1',
|
|
78
|
+
status: 'open',
|
|
79
|
+
assignment: null,
|
|
80
|
+
assessment: null
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const event = {
|
|
84
|
+
__index: 'event',
|
|
85
|
+
howler: { id: 'event-1' }
|
|
86
|
+
};
|
|
87
|
+
const Wrapper = ({ children }) => {
|
|
88
|
+
const [records, setRecords] = useState({});
|
|
89
|
+
const loadRecords = useCallback((newRecords) => {
|
|
90
|
+
setRecords(current => ({
|
|
91
|
+
...current,
|
|
92
|
+
...Object.fromEntries(newRecords.map(record => [record.howler.id, record]))
|
|
93
|
+
}));
|
|
94
|
+
}, []);
|
|
95
|
+
const recordContext = {
|
|
96
|
+
records,
|
|
97
|
+
loadRecords
|
|
98
|
+
};
|
|
99
|
+
return (_jsx(I18nextProvider, { i18n: i18n, children: _jsx(ViewContext.Provider, { value: { views: { 'view-1': mockView }, fetchViews: mockFetchViews }, children: _jsx(RecordContext.Provider, { value: recordContext, children: children }) }) }));
|
|
100
|
+
};
|
|
101
|
+
describe('ViewCard', () => {
|
|
102
|
+
beforeEach(() => {
|
|
103
|
+
vi.clearAllMocks();
|
|
104
|
+
mockSearchPost.mockResolvedValue({ items: [] });
|
|
105
|
+
mockDispatchApi.mockImplementation((request) => request);
|
|
106
|
+
});
|
|
107
|
+
it('fetches the view and searches with its query and limit', async () => {
|
|
108
|
+
mockSearchPost.mockReturnValue('search-request');
|
|
109
|
+
mockDispatchApi.mockResolvedValue({ items: [] });
|
|
110
|
+
render(_jsx(ViewCard, { viewId: "view-1", limit: 3 }), { wrapper: Wrapper });
|
|
111
|
+
await waitFor(() => expect(mockDispatchApi).toHaveBeenCalled());
|
|
112
|
+
expect(mockFetchViews).toHaveBeenCalledWith(['view-1']);
|
|
113
|
+
expect(mockSearchPost).toHaveBeenCalledWith(['hit'], {
|
|
114
|
+
query: mockView.query,
|
|
115
|
+
rows: 3,
|
|
116
|
+
metadata: ['analytic']
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
it('renders the empty state when the search has no records', async () => {
|
|
120
|
+
mockDispatchApi.mockResolvedValue({ items: [] });
|
|
121
|
+
render(_jsx(ViewCard, { viewId: "view-1", limit: 3 }), { wrapper: Wrapper });
|
|
122
|
+
expect(await screen.findByText('No records')).toBeInTheDocument();
|
|
123
|
+
});
|
|
124
|
+
it('renders hits and events returned by the search', async () => {
|
|
125
|
+
mockDispatchApi.mockResolvedValue({ items: [hit, event] });
|
|
126
|
+
render(_jsx(ViewCard, { viewId: "view-1", limit: 3 }), { wrapper: Wrapper });
|
|
127
|
+
expect(await screen.findByText('Hit: hit-1')).toBeInTheDocument();
|
|
128
|
+
expect(screen.getByText('Event: event-1')).toBeInTheDocument();
|
|
129
|
+
});
|
|
130
|
+
it('renders a link to the view query', async () => {
|
|
131
|
+
mockDispatchApi.mockResolvedValue({ items: [hit] });
|
|
132
|
+
render(_jsx(ViewCard, { viewId: "view-1", limit: 3 }), { wrapper: Wrapper });
|
|
133
|
+
await screen.findByText('Hit: hit-1');
|
|
134
|
+
expect(screen.getByRole('link')).toHaveAttribute('href', '/views/view-1');
|
|
135
|
+
expect(mockNavigate).not.toHaveBeenCalled();
|
|
136
|
+
});
|
|
137
|
+
it('refreshes when refreshTick changes and reports completion', async () => {
|
|
138
|
+
const onRefreshComplete = vi.fn();
|
|
139
|
+
mockDispatchApi.mockResolvedValue({ items: [] });
|
|
140
|
+
const { rerender } = render(_jsx(ViewCard, { viewId: "view-1", limit: 3, refreshTick: Symbol('first'), onRefreshComplete: onRefreshComplete }), { wrapper: Wrapper });
|
|
141
|
+
await waitFor(() => expect(mockDispatchApi).toHaveBeenCalledTimes(1));
|
|
142
|
+
expect(onRefreshComplete).toHaveBeenCalledTimes(2);
|
|
143
|
+
rerender(_jsx(ViewCard, { viewId: "view-1", limit: 3, refreshTick: Symbol('second'), onRefreshComplete: onRefreshComplete }));
|
|
144
|
+
await waitFor(() => expect(mockDispatchApi).toHaveBeenCalledTimes(2));
|
|
145
|
+
expect(onRefreshComplete).toHaveBeenCalledTimes(3);
|
|
146
|
+
});
|
|
147
|
+
});
|