@cccsaurora/howler-ui 3.1.0-dev.1385 → 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.
@@ -16,7 +16,6 @@ class ErrorBoundaryComponent extends React.Component {
16
16
  this.setState({ hasError: true, error: error });
17
17
  }
18
18
  componentDidUpdate(previousProps) {
19
- console.log(previousProps, this.props);
20
19
  if (this.props.locationKey !== previousProps.locationKey && this.state.hasError) {
21
20
  this.setState({ hasError: false, error: null });
22
21
  }
@@ -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
+ });
package/package.json CHANGED
@@ -93,7 +93,7 @@
93
93
  "url": "https://github.com/CybercentreCanada/howler"
94
94
  },
95
95
  "type": "module",
96
- "version": "3.1.0-dev.1385",
96
+ "version": "3.1.0-dev.1394",
97
97
  "exports": {
98
98
  "./i18n": "./i18n.js",
99
99
  "./index.css": "./index.css",