@cccsaurora/howler-ui 2.19.0-dev.959 → 2.19.0-dev.971

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.
Files changed (43) hide show
  1. package/components/elements/addons/buttons/CustomButton.test.d.ts +1 -0
  2. package/components/elements/addons/buttons/CustomButton.test.js +106 -0
  3. package/components/elements/addons/layout/FlexOne.test.d.ts +1 -0
  4. package/components/elements/addons/layout/FlexOne.test.js +30 -0
  5. package/components/elements/addons/search/SearchPagination.test.d.ts +1 -0
  6. package/components/elements/addons/search/SearchPagination.test.js +80 -0
  7. package/components/elements/addons/search/SearchTotal.test.d.ts +1 -0
  8. package/components/elements/addons/search/SearchTotal.test.js +45 -0
  9. package/components/elements/display/DocumentationButton.test.d.ts +1 -0
  10. package/components/elements/display/DocumentationButton.test.js +84 -0
  11. package/components/elements/display/DynamicTabs.test.d.ts +1 -0
  12. package/components/elements/display/DynamicTabs.test.js +93 -0
  13. package/components/elements/display/HowlerCard.test.d.ts +1 -0
  14. package/components/elements/display/HowlerCard.test.js +55 -0
  15. package/components/elements/display/Image.test.d.ts +1 -0
  16. package/components/elements/display/Image.test.js +79 -0
  17. package/components/elements/display/QueryResultText.test.d.ts +1 -0
  18. package/components/elements/display/QueryResultText.test.js +48 -0
  19. package/components/elements/display/TextDivider.test.d.ts +1 -0
  20. package/components/elements/display/TextDivider.test.js +43 -0
  21. package/components/elements/display/TypingIndicator.test.d.ts +1 -0
  22. package/components/elements/display/TypingIndicator.test.js +33 -0
  23. package/components/routes/403.test.d.ts +1 -0
  24. package/components/routes/403.test.js +27 -0
  25. package/components/routes/404.test.d.ts +1 -0
  26. package/components/routes/404.test.js +26 -0
  27. package/components/routes/ErrorBoundary.test.d.ts +1 -0
  28. package/components/routes/ErrorBoundary.test.js +43 -0
  29. package/components/routes/ErrorOccured.test.d.ts +1 -0
  30. package/components/routes/ErrorOccured.test.js +35 -0
  31. package/components/routes/Logout.test.d.ts +1 -0
  32. package/components/routes/Logout.test.js +59 -0
  33. package/components/routes/home/AddNewCard.test.d.ts +1 -0
  34. package/components/routes/home/AddNewCard.test.js +68 -0
  35. package/components/routes/home/ViewRefresh.test.d.ts +1 -0
  36. package/components/routes/home/ViewRefresh.test.js +79 -0
  37. package/components/routes/overviews/OverviewCard.test.d.ts +1 -0
  38. package/components/routes/overviews/OverviewCard.test.js +108 -0
  39. package/components/routes/settings/SettingsSection.test.d.ts +1 -0
  40. package/components/routes/settings/SettingsSection.test.js +19 -0
  41. package/components/routes/templates/TemplateCard.test.d.ts +1 -0
  42. package/components/routes/templates/TemplateCard.test.js +115 -0
  43. package/package.json +133 -133
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render, screen } from '@testing-library/react';
4
+ import { setupReactRouterMock } from '@cccsaurora/howler-ui/tests/mocks';
5
+ import { vi } from 'vitest';
6
+ setupReactRouterMock();
7
+ import i18n from '@cccsaurora/howler-ui/i18n';
8
+ import { I18nextProvider } from 'react-i18next';
9
+ import QueryResultText from './QueryResultText';
10
+ const Wrapper = ({ children }) => (_jsx(I18nextProvider, { i18n: i18n, children: children }));
11
+ describe('QueryResultText', () => {
12
+ afterAll(() => vi.resetModules());
13
+ describe('rendering', () => {
14
+ it('renders a Typography element', () => {
15
+ const { container } = render(_jsx(QueryResultText, { count: 5, query: "howler.status:open" }), { wrapper: Wrapper });
16
+ expect(container.querySelector('.MuiTypography-root')).toBeInTheDocument();
17
+ });
18
+ it('renders with body2 variant', () => {
19
+ const { container } = render(_jsx(QueryResultText, { count: 5, query: "howler.status:open" }), { wrapper: Wrapper });
20
+ expect(container.firstChild).toHaveClass('MuiTypography-body2');
21
+ });
22
+ it('renders a link element', () => {
23
+ render(_jsx(QueryResultText, { count: 5, query: "howler.status:open" }), { wrapper: Wrapper });
24
+ expect(screen.getByRole('link')).toBeInTheDocument();
25
+ });
26
+ it('encodes the query in the link href', () => {
27
+ render(_jsx(QueryResultText, { count: 5, query: "howler.status:open AND howler.id:*" }), { wrapper: Wrapper });
28
+ const link = screen.getByRole('link');
29
+ expect(link).toHaveAttribute('href', '/hits?query=howler.status%3Aopen%20AND%20howler.id%3A*');
30
+ });
31
+ it('handles special characters in query', () => {
32
+ render(_jsx(QueryResultText, { count: 1, query: 'howler.detection:"test & value"' }), { wrapper: Wrapper });
33
+ const link = screen.getByRole('link');
34
+ expect(link.getAttribute('href')).toContain('/hits?query=');
35
+ expect(link.getAttribute('href')).toContain(encodeURIComponent('howler.detection:"test & value"'));
36
+ });
37
+ });
38
+ describe('count display', () => {
39
+ it('displays the count value', () => {
40
+ render(_jsx(QueryResultText, { count: 42, query: "test" }), { wrapper: Wrapper });
41
+ expect(screen.getByText(/42/)).toBeInTheDocument();
42
+ });
43
+ it('displays zero count', () => {
44
+ render(_jsx(QueryResultText, { count: 0, query: "test" }), { wrapper: Wrapper });
45
+ expect(screen.getByText(/0/)).toBeInTheDocument();
46
+ });
47
+ });
48
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render } from '@testing-library/react';
4
+ import i18n from '@cccsaurora/howler-ui/i18n';
5
+ import { I18nextProvider } from 'react-i18next';
6
+ import TextDivider from './TextDivider';
7
+ const Wrapper = ({ children }) => (_jsx(I18nextProvider, { i18n: i18n, children: children }));
8
+ describe('TextDivider', () => {
9
+ describe('rendering', () => {
10
+ it('renders without crashing', () => {
11
+ const { container } = render(_jsx(TextDivider, {}), { wrapper: Wrapper });
12
+ expect(container.firstChild).toBeInTheDocument();
13
+ });
14
+ it('renders with inline-block display style', () => {
15
+ const { container } = render(_jsx(TextDivider, {}), { wrapper: Wrapper });
16
+ expect(container.firstChild).toHaveStyle({ display: 'inline-block' });
17
+ });
18
+ it('renders with text-align center', () => {
19
+ const { container } = render(_jsx(TextDivider, {}), { wrapper: Wrapper });
20
+ expect(container.firstChild).toHaveStyle({ textAlign: 'center' });
21
+ });
22
+ it('renders with width 100%', () => {
23
+ const { container } = render(_jsx(TextDivider, {}), { wrapper: Wrapper });
24
+ expect(container.firstChild).toHaveStyle({ width: '100%' });
25
+ });
26
+ it('renders with position relative', () => {
27
+ const { container } = render(_jsx(TextDivider, {}), { wrapper: Wrapper });
28
+ expect(container.firstChild).toHaveStyle({ position: 'relative' });
29
+ });
30
+ it('renders a nested div element', () => {
31
+ const { container } = render(_jsx(TextDivider, {}), { wrapper: Wrapper });
32
+ const outerDiv = container.firstChild;
33
+ expect(outerDiv.querySelector('div')).toBeInTheDocument();
34
+ });
35
+ it('renders the translated divider text', () => {
36
+ const { container } = render(_jsx(TextDivider, {}), { wrapper: Wrapper });
37
+ // The divider uses i18n key 'divider', text should be present
38
+ const innerDiv = container.querySelector('div > div');
39
+ expect(innerDiv).toBeInTheDocument();
40
+ expect(innerDiv.textContent).toBeTruthy();
41
+ });
42
+ });
43
+ });
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render } from '@testing-library/react';
4
+ import TypingIndicator from './TypingIndicator';
5
+ describe('TypingIndicator', () => {
6
+ describe('rendering', () => {
7
+ it('renders exactly three circle icons', () => {
8
+ const { container } = render(_jsx(TypingIndicator, {}));
9
+ // Each Circle icon renders as an <svg> element
10
+ const svgs = container.querySelectorAll('svg');
11
+ expect(svgs).toHaveLength(3);
12
+ });
13
+ it('renders a root Stack element', () => {
14
+ const { container } = render(_jsx(TypingIndicator, {}));
15
+ expect(container.firstChild).toBeInTheDocument();
16
+ });
17
+ it('renders with flex row layout', () => {
18
+ const { container } = render(_jsx(TypingIndicator, {}));
19
+ // MUI Stack with direction="row" uses flexbox
20
+ const root = container.firstChild;
21
+ expect(root).toHaveStyle({ display: 'flex', flexDirection: 'row' });
22
+ });
23
+ });
24
+ describe('accessibility', () => {
25
+ it('renders the circles as SVG elements', () => {
26
+ const { container } = render(_jsx(TypingIndicator, {}));
27
+ const svgs = container.querySelectorAll('svg');
28
+ svgs.forEach(svg => {
29
+ expect(svg.tagName.toLowerCase()).toBe('svg');
30
+ });
31
+ });
32
+ });
33
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render, screen } from '@testing-library/react';
4
+ import i18n from '@cccsaurora/howler-ui/i18n';
5
+ import { I18nextProvider } from 'react-i18next';
6
+ import { describe, expect, it, vi } from 'vitest';
7
+ import PermissionDeniedPage from './403';
8
+ vi.mock('commons/components/pages/PageCenter', () => ({
9
+ default: ({ children }) => _jsx("div", { id: "page-center", children: children })
10
+ }));
11
+ const Wrapper = ({ children }) => (_jsx(I18nextProvider, { i18n: i18n, children: children }));
12
+ describe('PermissionDeniedPage (403)', () => {
13
+ it('should render the 403 title and description', () => {
14
+ render(_jsx(PermissionDeniedPage, {}), { wrapper: Wrapper });
15
+ expect(screen.getByText('403: Access Forbidden')).toBeInTheDocument();
16
+ expect(screen.getByText('You do not have permission to access this page.')).toBeInTheDocument();
17
+ });
18
+ it('should render inside PageCenter', () => {
19
+ render(_jsx(PermissionDeniedPage, {}), { wrapper: Wrapper });
20
+ expect(screen.getByTestId('page-center')).toBeInTheDocument();
21
+ });
22
+ it('should render the PersonOff icon', () => {
23
+ render(_jsx(PermissionDeniedPage, {}), { wrapper: Wrapper });
24
+ // MUI SVG icons have data-testid attribute
25
+ expect(document.querySelector('[data-testid="PersonOffIcon"]')).toBeInTheDocument();
26
+ });
27
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render, screen } from '@testing-library/react';
4
+ import i18n from '@cccsaurora/howler-ui/i18n';
5
+ import { I18nextProvider } from 'react-i18next';
6
+ import { describe, expect, it, vi } from 'vitest';
7
+ import NotFoundPage from './404';
8
+ vi.mock('commons/components/pages/PageCenter', () => ({
9
+ default: ({ children }) => _jsx("div", { id: "page-center", children: children })
10
+ }));
11
+ const Wrapper = ({ children }) => (_jsx(I18nextProvider, { i18n: i18n, children: children }));
12
+ describe('NotFoundPage (404)', () => {
13
+ it('should render the 404 title and description', () => {
14
+ render(_jsx(NotFoundPage, {}), { wrapper: Wrapper });
15
+ expect(screen.getByText('404: Not found')).toBeInTheDocument();
16
+ expect(screen.getByText('The page you are looking for cannot be found...')).toBeInTheDocument();
17
+ });
18
+ it('should render inside PageCenter', () => {
19
+ render(_jsx(NotFoundPage, {}), { wrapper: Wrapper });
20
+ expect(screen.getByTestId('page-center')).toBeInTheDocument();
21
+ });
22
+ it('should render the LinkOff icon', () => {
23
+ render(_jsx(NotFoundPage, {}), { wrapper: Wrapper });
24
+ expect(document.querySelector('[data-testid="LinkOffIcon"]')).toBeInTheDocument();
25
+ });
26
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render, screen } from '@testing-library/react';
4
+ import i18n from '@cccsaurora/howler-ui/i18n';
5
+ import { I18nextProvider } from 'react-i18next';
6
+ import { describe, expect, it, vi } from 'vitest';
7
+ import ErrorBoundary from './ErrorBoundary';
8
+ vi.mock('commons/components/pages/PageCenter', () => ({
9
+ default: ({ children }) => _jsx("div", { id: "page-center", children: children })
10
+ }));
11
+ vi.mock('react-pluggable', () => ({
12
+ usePluginStore: () => ({
13
+ executeFunction: vi.fn(() => null)
14
+ })
15
+ }));
16
+ vi.mock('plugins/store', () => ({
17
+ default: { plugins: [] }
18
+ }));
19
+ const Wrapper = ({ children }) => (_jsx(I18nextProvider, { i18n: i18n, children: children }));
20
+ const ThrowingComponent = ({ error }) => {
21
+ throw error;
22
+ };
23
+ describe('ErrorBoundary', () => {
24
+ it('should render children when no error occurs', () => {
25
+ render(_jsx(ErrorBoundary, { children: _jsx("div", { children: "Child content" }) }), { wrapper: Wrapper });
26
+ expect(screen.getByText('Child content')).toBeInTheDocument();
27
+ });
28
+ it('should render error UI when a child throws', () => {
29
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
30
+ const testError = new Error('Test error message');
31
+ testError.stack = 'Error: Test error message\n at TestComponent';
32
+ render(_jsx(ErrorBoundary, { children: _jsx(ThrowingComponent, { error: testError }) }), { wrapper: Wrapper });
33
+ expect(screen.getByText('Test error message')).toBeInTheDocument();
34
+ consoleSpy.mockRestore();
35
+ });
36
+ it('should display the error page title when an error occurs', () => {
37
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
38
+ const testError = new Error('Something went wrong');
39
+ render(_jsx(ErrorBoundary, { children: _jsx(ThrowingComponent, { error: testError }) }), { wrapper: Wrapper });
40
+ expect(screen.getByText('Application Stopped Working')).toBeInTheDocument();
41
+ consoleSpy.mockRestore();
42
+ });
43
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render, screen } from '@testing-library/react';
4
+ import i18n from '@cccsaurora/howler-ui/i18n';
5
+ import { I18nextProvider } from 'react-i18next';
6
+ import { describe, expect, it, vi } from 'vitest';
7
+ import ErrorOccured from './ErrorOccured';
8
+ vi.mock('commons/components/pages/PageCenter', () => ({
9
+ default: ({ children }) => _jsx("div", { id: "page-center", children: children })
10
+ }));
11
+ const mockExecuteFunction = vi.fn(() => null);
12
+ vi.mock('react-pluggable', () => ({
13
+ usePluginStore: () => ({
14
+ executeFunction: mockExecuteFunction
15
+ })
16
+ }));
17
+ vi.mock('plugins/store', () => ({
18
+ default: { plugins: ['TestPlugin'] }
19
+ }));
20
+ const Wrapper = ({ children }) => (_jsx(I18nextProvider, { i18n: i18n, children: children }));
21
+ describe('ErrorOccured', () => {
22
+ it('should render the error title and description', () => {
23
+ render(_jsx(ErrorOccured, {}), { wrapper: Wrapper });
24
+ expect(screen.getByText('Application Stopped Working')).toBeInTheDocument();
25
+ expect(screen.getByText('The application stopped working suddenly. If the problem persists please reach out on teams.')).toBeInTheDocument();
26
+ });
27
+ it('should render inside PageCenter', () => {
28
+ render(_jsx(ErrorOccured, {}), { wrapper: Wrapper });
29
+ expect(screen.getByTestId('page-center')).toBeInTheDocument();
30
+ });
31
+ it('should execute plugin support functions', () => {
32
+ render(_jsx(ErrorOccured, {}), { wrapper: Wrapper });
33
+ expect(mockExecuteFunction).toHaveBeenCalledWith('TestPlugin.support');
34
+ });
35
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render, screen } from '@testing-library/react';
4
+ import i18n from '@cccsaurora/howler-ui/i18n';
5
+ import { I18nextProvider } from 'react-i18next';
6
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
+ import Logout from './Logout';
8
+ const mockHideMenus = vi.fn();
9
+ const mockClear = vi.fn();
10
+ vi.mock('commons/components/app/hooks', () => ({
11
+ useAppBanner: () => _jsx("div", { "data-testid": "app-banner", children: "Banner" }),
12
+ useAppLayout: () => ({
13
+ hideMenus: mockHideMenus
14
+ })
15
+ }));
16
+ vi.mock('components/hooks/useMyLocalStorage', () => ({
17
+ default: () => ({
18
+ clear: mockClear
19
+ })
20
+ }));
21
+ vi.mock('commons/components/pages/PageCardCentered', () => ({
22
+ default: ({ children }) => _jsx("div", { children: children })
23
+ }));
24
+ const Wrapper = ({ children }) => (_jsx(I18nextProvider, { i18n: i18n, children: children }));
25
+ describe('Logout', () => {
26
+ const originalLocation = window.location;
27
+ beforeEach(() => {
28
+ vi.useFakeTimers();
29
+ // Mock window.location.replace
30
+ Object.defineProperty(window, 'location', {
31
+ value: { ...originalLocation, replace: vi.fn() },
32
+ writable: true
33
+ });
34
+ });
35
+ afterEach(() => {
36
+ vi.useRealTimers();
37
+ Object.defineProperty(window, 'location', { value: originalLocation, writable: true });
38
+ vi.clearAllMocks();
39
+ });
40
+ it('should render the logout message', () => {
41
+ render(_jsx(Logout, {}), { wrapper: Wrapper });
42
+ expect(screen.getByText(/Logging out current user/)).toBeInTheDocument();
43
+ });
44
+ it('should render the app banner', () => {
45
+ render(_jsx(Logout, {}), { wrapper: Wrapper });
46
+ expect(screen.getByText('Banner')).toBeInTheDocument();
47
+ });
48
+ it('should hide menus on mount', () => {
49
+ render(_jsx(Logout, {}), { wrapper: Wrapper });
50
+ expect(mockHideMenus).toHaveBeenCalled();
51
+ });
52
+ it('should clear localStorage and redirect after 2 seconds', () => {
53
+ render(_jsx(Logout, {}), { wrapper: Wrapper });
54
+ expect(mockClear).not.toHaveBeenCalled();
55
+ vi.advanceTimersByTime(2000);
56
+ expect(mockClear).toHaveBeenCalled();
57
+ expect(window.location.replace).toHaveBeenCalledWith('/');
58
+ });
59
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render, screen, waitFor } from '@testing-library/react';
4
+ import i18n from '@cccsaurora/howler-ui/i18n';
5
+ import React from 'react';
6
+ import { I18nextProvider } from 'react-i18next';
7
+ import { setupContextSelectorMock } from '@cccsaurora/howler-ui/tests/mocks';
8
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
9
+ import AddNewCard from './AddNewCard';
10
+ setupContextSelectorMock();
11
+ const mockApiSearchAnalyticPost = vi.fn();
12
+ vi.mock('api', () => ({
13
+ default: {
14
+ search: {
15
+ analytic: {
16
+ post: (...args) => mockApiSearchAnalyticPost(...args)
17
+ }
18
+ }
19
+ }
20
+ }));
21
+ vi.mock('components/elements/addons/buttons/CustomButton', () => ({
22
+ default: ({ children, disabled, onClick, ...props }) => (_jsx("button", { disabled: disabled, onClick: onClick, ...props, children: children }))
23
+ }));
24
+ const mockFetchViews = vi.hoisted(() => vi.fn().mockResolvedValue(undefined));
25
+ vi.mock('components/app/providers/ViewProvider', () => ({
26
+ ViewContext: React.createContext({
27
+ views: {
28
+ 'view-1': { view_id: 'view-1', title: 'My View', query: 'howler.status:open', sort: 'event.created desc', span: 'date.range.1.month', type: 'personal', owner: 'testuser', settings: { advance_on_triage: false } },
29
+ 'view-2': { view_id: 'view-2', title: 'Second View', query: 'howler.status:open', sort: 'event.created desc', span: 'date.range.1.month', type: 'personal', owner: 'testuser', settings: { advance_on_triage: false } }
30
+ },
31
+ fetchViews: mockFetchViews
32
+ })
33
+ }));
34
+ const Wrapper = ({ children }) => (_jsx(I18nextProvider, { i18n: i18n, children: children }));
35
+ describe('AddNewCard', () => {
36
+ const mockAddCard = vi.fn();
37
+ beforeEach(() => {
38
+ vi.clearAllMocks();
39
+ mockApiSearchAnalyticPost.mockResolvedValue({
40
+ items: [{ analytic_id: 'analytic-1', name: 'Test Analytic', description: 'Test description' }],
41
+ total: 1
42
+ });
43
+ });
44
+ it('should render the add card title', async () => {
45
+ render(_jsx(AddNewCard, { dashboard: [], addCard: mockAddCard }), { wrapper: Wrapper });
46
+ await waitFor(() => {
47
+ expect(screen.getByText('Add New Panel')).toBeInTheDocument();
48
+ });
49
+ });
50
+ it('should render the description', async () => {
51
+ render(_jsx(AddNewCard, { dashboard: [], addCard: mockAddCard }), { wrapper: Wrapper });
52
+ await waitFor(() => {
53
+ expect(screen.getByText(/Add an additional panel/)).toBeInTheDocument();
54
+ });
55
+ });
56
+ it('should have the create button disabled initially', async () => {
57
+ render(_jsx(AddNewCard, { dashboard: [], addCard: mockAddCard }), { wrapper: Wrapper });
58
+ await waitFor(() => {
59
+ expect(screen.getByRole('button', { name: /Create/i })).toBeDisabled();
60
+ });
61
+ });
62
+ it('should fetch analytics on mount', async () => {
63
+ render(_jsx(AddNewCard, { dashboard: [], addCard: mockAddCard }), { wrapper: Wrapper });
64
+ await waitFor(() => {
65
+ expect(mockApiSearchAnalyticPost).toHaveBeenCalledWith(expect.objectContaining({ query: '*:*' }));
66
+ });
67
+ });
68
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,79 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render, screen, act } from '@testing-library/react';
4
+ import i18n from '@cccsaurora/howler-ui/i18n';
5
+ import React from 'react';
6
+ import { I18nextProvider } from 'react-i18next';
7
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
8
+ import ViewRefresh, {} from './ViewRefresh';
9
+ const Wrapper = ({ children }) => (_jsx(I18nextProvider, { i18n: i18n, children: children }));
10
+ describe('ViewRefresh', () => {
11
+ beforeEach(() => {
12
+ vi.useFakeTimers();
13
+ });
14
+ afterEach(() => {
15
+ vi.useRealTimers();
16
+ });
17
+ it('should render the refresh button', () => {
18
+ const onRefresh = vi.fn();
19
+ render(_jsx(ViewRefresh, { refreshRate: 30, viewCardCount: 2, onRefresh: onRefresh }), { wrapper: Wrapper });
20
+ expect(screen.getByRole('button')).toBeInTheDocument();
21
+ });
22
+ it('should show a progress indicator initially', () => {
23
+ const onRefresh = vi.fn();
24
+ render(_jsx(ViewRefresh, { refreshRate: 30, viewCardCount: 2, onRefresh: onRefresh }), { wrapper: Wrapper });
25
+ expect(screen.getByRole('progressbar')).toBeInTheDocument();
26
+ });
27
+ it('should trigger refresh when progress reaches 100%', async () => {
28
+ const onRefresh = vi.fn();
29
+ render(_jsx(ViewRefresh, { refreshRate: 30, viewCardCount: 2, onRefresh: onRefresh }), { wrapper: Wrapper });
30
+ // Progress increments by 1 every refreshRate*10ms = 300ms.
31
+ // Each increment triggers a re-render and a new setTimeout.
32
+ // We advance one tick at a time inside act to let React process each state update.
33
+ for (let i = 0; i < 101; i++) {
34
+ await act(async () => {
35
+ vi.advanceTimersByTime(300);
36
+ });
37
+ }
38
+ expect(onRefresh).toHaveBeenCalled();
39
+ });
40
+ it('should clear refreshing state when all cards report back via ref', async () => {
41
+ const onRefresh = vi.fn();
42
+ const ref = React.createRef();
43
+ render(_jsx(ViewRefresh, { ref: ref, refreshRate: 30, viewCardCount: 2, onRefresh: onRefresh }), { wrapper: Wrapper });
44
+ // Advance to 100% to trigger refresh
45
+ for (let i = 0; i < 101; i++) {
46
+ await act(async () => {
47
+ vi.advanceTimersByTime(300);
48
+ });
49
+ }
50
+ expect(onRefresh).toHaveBeenCalled();
51
+ // Simulate both cards completing
52
+ act(() => {
53
+ ref.current?.handleRefreshComplete();
54
+ ref.current?.handleRefreshComplete();
55
+ });
56
+ // After all cards complete, the progress should reset (button re-enabled)
57
+ expect(screen.getByRole('button')).not.toBeDisabled();
58
+ });
59
+ it('should trigger refresh via manual click', () => {
60
+ const onRefresh = vi.fn();
61
+ render(_jsx(ViewRefresh, { refreshRate: 30, viewCardCount: 2, onRefresh: onRefresh }), { wrapper: Wrapper });
62
+ // Click the refresh button directly using fireEvent (avoids userEvent timer issues)
63
+ const button = screen.getByRole('button');
64
+ act(() => {
65
+ button.click();
66
+ });
67
+ expect(onRefresh).toHaveBeenCalled();
68
+ });
69
+ it('should not call onRefresh when viewCardCount is 0 and button is clicked', () => {
70
+ const onRefresh = vi.fn();
71
+ render(_jsx(ViewRefresh, { refreshRate: 30, viewCardCount: 0, onRefresh: onRefresh }), { wrapper: Wrapper });
72
+ const button = screen.getByRole('button');
73
+ act(() => {
74
+ button.click();
75
+ });
76
+ // onRefresh should not be called because viewCardCount is 0
77
+ expect(onRefresh).not.toHaveBeenCalled();
78
+ });
79
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,108 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render, screen } from '@testing-library/react';
4
+ import userEvent, {} from '@testing-library/user-event';
5
+ import i18n from '@cccsaurora/howler-ui/i18n';
6
+ import { I18nextProvider } from 'react-i18next';
7
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
8
+ import OverviewCard from './OverviewCard';
9
+ vi.mock('components/elements/display/HowlerAvatar', () => ({
10
+ default: ({ userId }) => _jsx("div", { id: "howler-avatar", children: userId })
11
+ }));
12
+ vi.mock('components/elements/addons/layout/FlexOne', () => ({
13
+ default: () => _jsx("div", { id: "flex-one" })
14
+ }));
15
+ const Wrapper = ({ children }) => (_jsx(I18nextProvider, { i18n: i18n, children: children }));
16
+ describe('OverviewCard', () => {
17
+ let user;
18
+ beforeEach(() => {
19
+ user = userEvent.setup();
20
+ vi.clearAllMocks();
21
+ });
22
+ it('should render overview analytic and detection', () => {
23
+ const overview = {
24
+ overview_id: 'ov-1',
25
+ analytic: 'Test Analytic',
26
+ detection: 'Test Detection',
27
+ content: 'line 1\nline 2\nline 3',
28
+ owner: 'testuser'
29
+ };
30
+ render(_jsx(OverviewCard, { overview: overview }), { wrapper: Wrapper });
31
+ expect(screen.getByText(/Test Analytic/)).toBeInTheDocument();
32
+ expect(screen.getByText(/Test Detection/)).toBeInTheDocument();
33
+ });
34
+ it('should render "All" when detection is null', () => {
35
+ const overview = {
36
+ overview_id: 'ov-1',
37
+ analytic: 'Test Analytic',
38
+ detection: null,
39
+ content: 'line 1',
40
+ owner: 'testuser'
41
+ };
42
+ render(_jsx(OverviewCard, { overview: overview }), { wrapper: Wrapper });
43
+ expect(screen.getByText(/All/)).toBeInTheDocument();
44
+ });
45
+ it('should display content preview (max 3 lines)', () => {
46
+ const overview = {
47
+ overview_id: 'ov-1',
48
+ analytic: 'Test Analytic',
49
+ detection: null,
50
+ content: 'first line\nsecond line\nthird line\nfourth line\nfifth line',
51
+ owner: 'testuser'
52
+ };
53
+ render(_jsx(OverviewCard, { overview: overview }), { wrapper: Wrapper });
54
+ expect(screen.getByText(/first line/)).toBeInTheDocument();
55
+ expect(screen.getByText(/second line/)).toBeInTheDocument();
56
+ expect(screen.getByText(/third line/)).toBeInTheDocument();
57
+ });
58
+ it('should render the owner avatar', () => {
59
+ const overview = {
60
+ overview_id: 'ov-1',
61
+ analytic: 'Test Analytic',
62
+ detection: null,
63
+ content: 'content',
64
+ owner: 'testuser'
65
+ };
66
+ render(_jsx(OverviewCard, { overview: overview }), { wrapper: Wrapper });
67
+ expect(screen.getByTestId('howler-avatar')).toBeInTheDocument();
68
+ expect(screen.getByText('testuser')).toBeInTheDocument();
69
+ });
70
+ it('should not show delete button when onDelete is not provided', () => {
71
+ const overview = {
72
+ overview_id: 'ov-1',
73
+ analytic: 'Test Analytic',
74
+ detection: null,
75
+ content: 'content',
76
+ owner: 'testuser'
77
+ };
78
+ render(_jsx(OverviewCard, { overview: overview }), { wrapper: Wrapper });
79
+ expect(document.querySelector('[data-testid="DeleteIcon"]')).not.toBeInTheDocument();
80
+ });
81
+ it('should show delete button and call onDelete when clicked', async () => {
82
+ const mockOnDelete = vi.fn();
83
+ const overview = {
84
+ overview_id: 'ov-1',
85
+ analytic: 'Test Analytic',
86
+ detection: null,
87
+ content: 'content',
88
+ owner: 'testuser'
89
+ };
90
+ render(_jsx(OverviewCard, { overview: overview, onDelete: mockOnDelete }), { wrapper: Wrapper });
91
+ const deleteButton = document.querySelector('[data-testid="DeleteIcon"]').closest('button');
92
+ await user.click(deleteButton);
93
+ expect(mockOnDelete).toHaveBeenCalledWith(expect.anything(), 'ov-1');
94
+ });
95
+ it('should apply custom className', () => {
96
+ const overview = {
97
+ overview_id: 'ov-1',
98
+ analytic: 'Test Analytic',
99
+ detection: null,
100
+ content: 'content',
101
+ owner: 'testuser'
102
+ };
103
+ const { container } = render(_jsx(OverviewCard, { overview: overview, className: "custom-class" }), {
104
+ wrapper: Wrapper
105
+ });
106
+ expect(container.querySelector('.custom-class')).toBeInTheDocument();
107
+ });
108
+ });
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /// <reference types="vitest" />
3
+ import { render, screen } from '@testing-library/react';
4
+ import { describe, expect, it } from 'vitest';
5
+ import SettingsSection from './SettingsSection';
6
+ describe('SettingsSection', () => {
7
+ it('should render the title in a table header', () => {
8
+ render(_jsx(SettingsSection, { title: "Test Section", colSpan: 3, children: _jsx("tr", { children: _jsx("td", { children: "Content" }) }) }));
9
+ expect(screen.getByText('Test Section')).toBeInTheDocument();
10
+ });
11
+ it('should render children inside a table body', () => {
12
+ render(_jsx(SettingsSection, { title: "Test Section", colSpan: 2, children: _jsx("tr", { children: _jsx("td", { children: "Row Content" }) }) }));
13
+ expect(screen.getByText('Row Content')).toBeInTheDocument();
14
+ });
15
+ it('should use the title as the table aria-label', () => {
16
+ render(_jsx(SettingsSection, { title: "Accessible Section", colSpan: 3, children: _jsx("tr", { children: _jsx("td", { children: "Content" }) }) }));
17
+ expect(screen.getByRole('table', { name: 'Accessible Section' })).toBeInTheDocument();
18
+ });
19
+ });
@@ -0,0 +1 @@
1
+ export {};