foreman_rh_cloud 14.1.1 → 14.1.3

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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/insights_cloud/api/machine_telemetries_controller.rb +4 -8
  3. data/app/controllers/insights_cloud/ui_requests_controller.rb +3 -7
  4. data/app/services/foreman_rh_cloud/url_remediations_retriever.rb +19 -5
  5. data/lib/foreman_inventory_upload/async/destroy_organization_hbi_hosts_job.rb +49 -0
  6. data/lib/foreman_inventory_upload/generators/fact_helpers.rb +26 -4
  7. data/lib/foreman_inventory_upload.rb +12 -1
  8. data/lib/foreman_rh_cloud/engine.rb +1 -0
  9. data/lib/foreman_rh_cloud/organization_destroy_extensions.rb +10 -0
  10. data/lib/foreman_rh_cloud/version.rb +1 -1
  11. data/lib/foreman_rh_cloud.rb +36 -9
  12. data/lib/insights_cloud/async/insights_generate_notifications.rb +10 -1
  13. data/lib/inventory_sync/async/inventory_self_host_sync.rb +12 -2
  14. data/package.json +1 -1
  15. data/test/controllers/insights_cloud/api/machine_telemetries_controller_test.rb +16 -2
  16. data/test/controllers/insights_cloud/ui_requests_controller_test.rb +16 -2
  17. data/test/jobs/destroy_organization_hbi_hosts_job_test.rb +63 -0
  18. data/test/jobs/insights_generate_notifications_test.rb +26 -0
  19. data/test/jobs/inventory_self_host_sync_test.rb +9 -0
  20. data/test/unit/foreman_rh_cloud_self_host_test.rb +50 -2
  21. data/test/unit/metadata_generator_test.rb +24 -1
  22. data/test/unit/organization_destroy_extensions_test.rb +50 -0
  23. data/test/unit/services/foreman_rh_cloud/url_remediations_retriever_test.rb +82 -5
  24. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyResults/__tests__/EmptyResults.test.js +10 -9
  25. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/__tests__/EmptyState.test.js +13 -9
  26. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ErrorState/__tests__/ErrorState.test.js +20 -9
  27. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/ListItem.test.js +31 -8
  28. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/__tests__/ListItemStatus.test.js +26 -10
  29. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountList.test.js +33 -9
  30. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListReducer.test.js +55 -35
  31. data/webpack/ForemanInventoryUpload/Components/FileDownload/__tests__/FileDownload.test.js +13 -9
  32. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/InventoryFilter.test.js +12 -15
  33. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/integration.test.js +32 -12
  34. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageTitle.test.js +14 -7
  35. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/CloudConnectorButton.test.js +47 -18
  36. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SettingsWarning/SettingsWarning.test.js +58 -15
  37. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/SyncButton.test.js +23 -9
  38. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/SyncButtonSelectors.test.js +19 -17
  39. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/integrations.test.js +25 -37
  40. data/webpack/ForemanInventoryUpload/Components/ScheduledRun/__tests__/ScheduledRun.test.js +28 -8
  41. data/webpack/ForemanInventoryUpload/Components/StatusChart/__tests__/StatusChart.test.js +25 -8
  42. data/webpack/ForemanInventoryUpload/Components/TabContainer/__tests__/TabContainer.test.js +11 -9
  43. data/webpack/ForemanInventoryUpload/Components/TabFooter/__tests__/TabFooter.test.js +11 -9
  44. data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/__tests__/InventoryAutoUpload.test.js +33 -12
  45. data/webpack/ForemanInventoryUpload/__tests__/ForemanInventoryHelpers.test.js +21 -8
  46. data/webpack/InsightsCloudSync/Components/InsightsSettings/__tests__/InsightsSettingsActions.test.js +61 -47
  47. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +48 -4
  48. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTableActions.test.js +126 -35
  49. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTableSelectors.test.js +90 -24
  50. data/webpack/InsightsCloudSync/InsightsCloudSync.test.js +79 -21
  51. data/webpack/InsightsCloudSync/__tests__/InsightsCloudSyncActions.test.js +31 -6
  52. data/webpack/InsightsHostDetailsTab/__tests__/InsightsTab.test.js +42 -9
  53. data/webpack/__tests__/ForemanRhCloudHelpers.test.js +91 -53
  54. data/webpack/common/Switcher/__tests__/HelpLabel.test.js +25 -10
  55. data/webpack/common/Switcher/__tests__/SwitcherPF4.test.js +41 -10
  56. metadata +9 -95
  57. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyResults/__tests__/__snapshots__/EmptyResults.test.js.snap +0 -18
  58. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/EmptyState/__tests__/__snapshots__/EmptyState.test.js.snap +0 -25
  59. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ErrorState/__tests__/__snapshots__/ErrorState.test.js.snap +0 -20
  60. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/__snapshots__/ListItem.test.js.snap +0 -47
  61. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/__tests__/__snapshots__/ListItemStatus.test.js.snap +0 -59
  62. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListActions.test.js +0 -34
  63. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListIntegration.test.js +0 -14
  64. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListSelectors.test.js +0 -25
  65. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountList.test.js.snap +0 -49
  66. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListActions.test.js.snap +0 -86
  67. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListReducer.test.js.snap +0 -75
  68. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListSelectors.test.js.snap +0 -46
  69. data/webpack/ForemanInventoryUpload/Components/FileDownload/__tests__/__snapshots__/FileDownload.test.js.snap +0 -26
  70. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/InventoryFilterActions.test.js +0 -14
  71. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/InventoryFilterReducer.test.js +0 -28
  72. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/InventoryFilterSelectors.test.js +0 -21
  73. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilter.test.js.snap +0 -21
  74. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilterActions.test.js.snap +0 -17
  75. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilterReducer.test.js.snap +0 -19
  76. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/InventoryFilterSelectors.test.js.snap +0 -9
  77. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/integration.test.js.snap +0 -43
  78. data/webpack/ForemanInventoryUpload/Components/InventorySettings/AdvancedSetting/__tests__/AdvancedSettingActions.test.js +0 -9
  79. data/webpack/ForemanInventoryUpload/Components/InventorySettings/AdvancedSetting/__tests__/__snapshots__/AdvancedSettingActions.test.js.snap +0 -18
  80. data/webpack/ForemanInventoryUpload/Components/InventorySettings/__tests__/InventorySettingsActions.test.js +0 -14
  81. data/webpack/ForemanInventoryUpload/Components/InventorySettings/__tests__/__snapshots__/InventorySettingsActions.test.js.snap +0 -26
  82. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageTitle.test.js.snap +0 -68
  83. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/CloudConnectorActions.test.js +0 -9
  84. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/__snapshots__/CloudConnectorActions.test.js.snap +0 -11
  85. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/__snapshots__/CloudConnectorButton.test.js.snap +0 -59
  86. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SettingsWarning/__snapshots__/SettingsWarning.test.js.snap +0 -32
  87. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/SyncButton.test.js.snap +0 -15
  88. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/SyncButtonSelectors.test.js.snap +0 -3
  89. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/integrations.test.js.snap +0 -58
  90. data/webpack/ForemanInventoryUpload/Components/ScheduledRun/__tests__/__snapshots__/ScheduledRun.test.js.snap +0 -23
  91. data/webpack/ForemanInventoryUpload/Components/StatusChart/__tests__/__snapshots__/StatusChart.test.js.snap +0 -74
  92. data/webpack/ForemanInventoryUpload/Components/TabContainer/__tests__/__snapshots__/TabContainer.test.js.snap +0 -18
  93. data/webpack/ForemanInventoryUpload/Components/TabFooter/__tests__/__snapshots__/TabFooter.test.js.snap +0 -12
  94. data/webpack/ForemanInventoryUpload/SubscriptionsPageExtension/InventoryAutoUpload/__tests__/__snapshots__/InventoryAutoUpload.test.js.snap +0 -96
  95. data/webpack/ForemanInventoryUpload/__tests__/ForemanInventoryUpload.test.js +0 -10
  96. data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryHelpers.test.js.snap +0 -5
  97. data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryUpload.test.js.snap +0 -14
  98. data/webpack/InsightsCloudSync/Components/InsightsSettings/__tests__/InsightsSettingsReducer.test.js +0 -33
  99. data/webpack/InsightsCloudSync/Components/InsightsSettings/__tests__/InsightsSettingsSelectors.test.js +0 -21
  100. data/webpack/InsightsCloudSync/Components/InsightsSettings/__tests__/__snapshots__/InsightsSettingsActions.test.js.snap +0 -65
  101. data/webpack/InsightsCloudSync/Components/InsightsSettings/__tests__/__snapshots__/InsightsSettingsReducer.test.js.snap +0 -19
  102. data/webpack/InsightsCloudSync/Components/InsightsSettings/__tests__/__snapshots__/InsightsSettingsSelectors.test.js.snap +0 -9
  103. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTableActions.test.js.snap +0 -131
  104. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTableSelectors.test.js.snap +0 -87
  105. data/webpack/InsightsCloudSync/__snapshots__/InsightsCloudSync.test.js.snap +0 -10
  106. data/webpack/InsightsCloudSync/__tests__/InsightsCloudSyncHelpers.test.js +0 -9
  107. data/webpack/InsightsCloudSync/__tests__/__snapshots__/InsightsCloudSyncActions.test.js.snap +0 -15
  108. data/webpack/InsightsCloudSync/__tests__/__snapshots__/InsightsCloudSyncHelpers.test.js.snap +0 -3
  109. data/webpack/InsightsHostDetailsTab/__tests__/InsightsTabActions.test.js +0 -19
  110. data/webpack/InsightsHostDetailsTab/__tests__/InsightsTabReducer.test.js +0 -26
  111. data/webpack/InsightsHostDetailsTab/__tests__/InsightsTabSelectors.test.js +0 -13
  112. data/webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTab.test.js.snap +0 -34
  113. data/webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabActions.test.js.snap +0 -56
  114. data/webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabReducer.test.js.snap +0 -32
  115. data/webpack/InsightsHostDetailsTab/__tests__/__snapshots__/InsightsTabSelectors.test.js.snap +0 -18
  116. data/webpack/__tests__/ForemanRhCloudSelectors.test.js +0 -22
  117. data/webpack/__tests__/ForemanRhCloudTestHelpers.test.js +0 -20
  118. data/webpack/__tests__/__snapshots__/ForemanRhCloudHelpers.test.js.snap +0 -19
  119. data/webpack/__tests__/__snapshots__/ForemanRhCloudSelectors.test.js.snap +0 -25
  120. data/webpack/__tests__/__snapshots__/ForemanRhCloudTestHelpers.test.js.snap +0 -39
  121. data/webpack/common/Switcher/__tests__/__snapshots__/HelpLabel.test.js.snap +0 -16
  122. data/webpack/common/Switcher/__tests__/__snapshots__/SwitcherPF4.test.js.snap +0 -24
@@ -1,18 +1,62 @@
1
1
  import React from 'react';
2
- import { rtlHelpers } from 'foremanReact/common/rtlTestHelpers';
2
+ import { render, screen } from '@testing-library/react';
3
3
  import InsightsTable from '../InsightsTable';
4
4
  import { tableProps } from './fixtures';
5
5
 
6
6
  jest.mock('../../../../common/Hooks/ConfigHooks');
7
+ jest.mock('foremanReact/Root/Context/ForemanContext');
8
+ jest.mock('../Pagination', () => () => null);
7
9
 
8
- const { renderWithStore } = rtlHelpers;
10
+ const buildProps = (overrides = {}) => ({
11
+ ...tableProps,
12
+ fetchInsights: jest.fn(),
13
+ onTableSort: jest.fn(),
14
+ onTableSelect: jest.fn(),
15
+ selectAll: jest.fn(),
16
+ clearAllSelection: jest.fn(),
17
+ ...overrides,
18
+ });
9
19
 
10
20
  describe('InsightsTable', () => {
11
21
  afterEach(() => {
12
22
  jest.clearAllMocks();
13
23
  });
14
24
 
15
- it('renders without crashing', () => {
16
- renderWithStore(<InsightsTable {...tableProps} />);
25
+ it('calls fetchInsights on mount', () => {
26
+ const props = buildProps();
27
+ render(<InsightsTable {...props} />);
28
+
29
+ expect(props.fetchInsights).toHaveBeenCalledTimes(1);
30
+ expect(props.fetchInsights).toHaveBeenCalledWith(
31
+ expect.objectContaining({ page: 1, perPage: 5 })
32
+ );
33
+ });
34
+
35
+ it('renders table with correct aria-label', () => {
36
+ const props = buildProps();
37
+ render(<InsightsTable {...props} />);
38
+
39
+ expect(
40
+ screen.getByRole('grid', { name: /Recommendations Table/ })
41
+ ).toBeTruthy();
42
+ });
43
+
44
+ it('re-fetches when hostname changes', () => {
45
+ const props = buildProps({ hostname: 'host1.example.com' });
46
+ const { rerender } = render(<InsightsTable {...props} />);
47
+
48
+ props.fetchInsights.mockClear();
49
+ rerender(
50
+ <InsightsTable {...props} hostname="host2.example.com" />
51
+ );
52
+
53
+ expect(props.fetchInsights).toHaveBeenCalledTimes(1);
54
+ });
55
+
56
+ it('renders with empty hits', () => {
57
+ const props = buildProps({ hits: [], status: 'RESOLVED' });
58
+ const { container } = render(<InsightsTable {...props} />);
59
+
60
+ expect(container.querySelector('.rh-cloud-recommendations-table')).toBeTruthy();
17
61
  });
18
62
  });
@@ -1,5 +1,4 @@
1
- import { testActionSnapshotWithFixtures } from '@theforeman/test';
2
- import API from 'foremanReact/redux/API';
1
+ import { push } from 'connected-react-router';
3
2
  import {
4
3
  fetchInsights,
5
4
  setSelectAllAlert,
@@ -8,41 +7,133 @@ import {
8
7
  selectAll,
9
8
  clearAllSelection,
10
9
  } from '../InsightsTableActions';
11
- import { hits } from './fixtures';
10
+ import {
11
+ INSIGHTS_SET_SELECTED_IDS,
12
+ INSIGHTS_SET_SELECT_ALL_ALERT,
13
+ INSIGHTS_SET_SELECT_ALL,
14
+ INSIGHTS_HITS_API_KEY,
15
+ INSIGHTS_HITS_PATH,
16
+ } from '../InsightsTableConstants';
12
17
 
13
- jest.mock('foremanReact/redux/API', () => jest.fn());
14
- API.get = jest.fn(({ handleSuccess, ...action }) => {
15
- handleSuccess({ data: { hits } });
16
- return { type: 'get', ...action };
17
- });
18
+ jest.mock('connected-react-router', () => ({
19
+ push: jest.fn(args => ({ type: '@@router/CALL_HISTORY_METHOD', payload: args })),
20
+ }));
18
21
 
19
- const runWithGetState = (state, action, params) => dispatch => {
20
- const getState = () => ({
21
- router: {
22
- location: {
23
- query: {
24
- page: '1',
25
- per_page: '7',
26
- search: '',
27
- sort_by: '',
28
- sort_order: '',
29
- select_all: 'true',
30
- },
22
+ const buildGetState = (queryOverrides = {}) => () => ({
23
+ router: {
24
+ location: {
25
+ query: {
26
+ page: '1',
27
+ per_page: '7',
28
+ search: '',
29
+ sort_by: '',
30
+ sort_order: '',
31
+ select_all: 'false',
32
+ ...queryOverrides,
31
33
  },
32
34
  },
35
+ },
36
+ });
37
+
38
+ describe('InsightsTable actions', () => {
39
+ let dispatch;
40
+
41
+ beforeEach(() => {
42
+ dispatch = jest.fn();
43
+ jest.clearAllMocks();
44
+ });
45
+
46
+ describe('plain action creators', () => {
47
+ it('setSelectAllAlert returns correct action', () => {
48
+ expect(setSelectAllAlert(true)).toEqual({
49
+ type: INSIGHTS_SET_SELECT_ALL_ALERT,
50
+ payload: { showSelectAllAlert: true },
51
+ });
52
+ });
53
+
54
+ it('selectByIds returns correct action', () => {
55
+ const ids = { 1: true, 5: true };
56
+ expect(selectByIds(ids)).toEqual({
57
+ type: INSIGHTS_SET_SELECTED_IDS,
58
+ payload: { selectedIds: ids },
59
+ });
60
+ });
61
+ });
62
+
63
+ describe('setSelectAll', () => {
64
+ it('dispatches select all action and url update', () => {
65
+ setSelectAll(false)(dispatch);
66
+
67
+ const dispatched = dispatch.mock.calls.map(c => c[0]);
68
+ const selectAllAction = dispatched.find(
69
+ a => a && a.type === INSIGHTS_SET_SELECT_ALL
70
+ );
71
+ expect(selectAllAction).toBeTruthy();
72
+ expect(selectAllAction.payload).toEqual({ isAllSelected: false });
73
+ });
74
+ });
75
+
76
+ describe('selectAll', () => {
77
+ it('dispatches setSelectAll with true', () => {
78
+ selectAll()(dispatch);
79
+
80
+ const dispatched = dispatch.mock.calls.map(c => c[0]);
81
+ const selectAllAction = dispatched.find(
82
+ a => a && a.type === INSIGHTS_SET_SELECT_ALL
83
+ );
84
+ expect(selectAllAction).toBeTruthy();
85
+ expect(selectAllAction.payload).toEqual({ isAllSelected: true });
86
+ });
87
+ });
88
+
89
+ describe('clearAllSelection', () => {
90
+ it('dispatches reset ids, clear alert, and deselect all', () => {
91
+ clearAllSelection()(dispatch);
92
+
93
+ expect(dispatch).toHaveBeenCalledWith(
94
+ expect.objectContaining({
95
+ type: INSIGHTS_SET_SELECTED_IDS,
96
+ payload: { selectedIds: {} },
97
+ })
98
+ );
99
+ expect(dispatch).toHaveBeenCalledWith(
100
+ expect.objectContaining({
101
+ type: INSIGHTS_SET_SELECT_ALL_ALERT,
102
+ payload: { showSelectAllAlert: false },
103
+ })
104
+ );
105
+ });
33
106
  });
34
- action(params)(dispatch, getState);
35
- };
36
-
37
- const fixtures = {
38
- 'should fetchInsights': () =>
39
- runWithGetState({}, fetchInsights, { page: 2, perPage: 7 }),
40
- 'should setSelectAllAlert true': () => setSelectAllAlert(true),
41
- 'should selectByIds': () => selectByIds({ 1: true, 5: true }),
42
- 'should setSelectAll false': () => setSelectAll(false),
43
- 'should selectAll': () => selectAll(),
44
- 'should clearAllSelection': () => clearAllSelection(),
45
- };
46
-
47
- describe('insights table actions', () =>
48
- testActionSnapshotWithFixtures(fixtures));
107
+
108
+ describe('fetchInsights', () => {
109
+ it('dispatches url push and API get with correct params', () => {
110
+ const getState = buildGetState();
111
+
112
+ fetchInsights({ page: 2, perPage: 7 })(dispatch, getState);
113
+
114
+ expect(push).toHaveBeenCalled();
115
+
116
+ const apiAction = dispatch.mock.calls.find(
117
+ call => call[0] && call[0].key === INSIGHTS_HITS_API_KEY
118
+ );
119
+ expect(apiAction).toBeTruthy();
120
+ const getArg = apiAction[0];
121
+ expect(getArg.url).toBe(INSIGHTS_HITS_PATH);
122
+ expect(getArg.params.page).toBe(2);
123
+ expect(getArg.params.per_page).toBe(7);
124
+ });
125
+
126
+ it('clears select all alert when isSelectAll is false', () => {
127
+ const getState = buildGetState({ select_all: 'false' });
128
+
129
+ fetchInsights({})(dispatch, getState);
130
+
131
+ expect(dispatch).toHaveBeenCalledWith(
132
+ expect.objectContaining({
133
+ type: INSIGHTS_SET_SELECT_ALL_ALERT,
134
+ payload: { showSelectAllAlert: false },
135
+ })
136
+ );
137
+ });
138
+ });
139
+ });
@@ -1,4 +1,3 @@
1
- import { testSelectorsSnapshotWithFixtures } from '@theforeman/test';
2
1
  import { insightsStateWrapper } from '../../../../ForemanRhCloudTestHelpers';
3
2
  import { routerState, APIState, APIErrorState } from './fixtures';
4
3
  import {
@@ -24,33 +23,100 @@ const state = {
24
23
  ...APIState,
25
24
  ...insightsStateWrapper({
26
25
  table: {
27
- selectedIds: {
28
- '51': true,
29
- },
26
+ selectedIds: { '51': true },
30
27
  showSelectAllAlert: true,
31
28
  isAllSelected: false,
32
29
  },
33
30
  }),
34
31
  };
35
32
 
36
- const fixtures = {
37
- 'should return router query': () => selectQuery(state),
38
- 'should return router search': () => selectSearch(state),
39
- 'should return router page': () => selectPage(state),
40
- 'should return router perPage': () => selectPerPage(state),
41
- 'should return router sort by': () => selectSortBy(state),
42
- 'should return router sort order': () => selectSortOrder(state),
43
- 'should return queryParams': () => selectQueryParams(state),
44
- 'should return API status': () => selectStatus(state),
45
- 'should return API error': () => selectError({ ...state, ...APIErrorState }),
46
- 'should return API hits': () => selectHits(state),
47
- 'should return API item count': () => selectItemCount(state),
48
- 'should return insights table': () => selectInsightsCloudTable(state),
49
- 'should return insights selectedIds': () => selectSelectedIds(state),
50
- 'should return insights isAllSelected': () => selectIsAllSelected(state),
51
- 'should return insights showSelectAllAlert': () =>
52
- selectShowSelectAllAlert(state),
53
- };
33
+ describe('InsightsTable selectors', () => {
34
+ describe('router query selectors', () => {
35
+ it('selectQuery returns the query object', () => {
36
+ expect(selectQuery(state)).toEqual(routerState.router.location.query);
37
+ });
38
+
39
+ it('selectSearch returns URI-decoded search string', () => {
40
+ expect(selectSearch(state)).toBe('total_risk < 3');
41
+ });
42
+
43
+ it('selectPage returns the page as a number', () => {
44
+ expect(selectPage(state)).toBe(1);
45
+ });
46
+
47
+ it('selectPerPage returns per_page as a number', () => {
48
+ expect(selectPerPage(state)).toBe(7);
49
+ });
50
+
51
+ it('selectSortBy returns the sort_by value', () => {
52
+ expect(selectSortBy(state)).toBe('total_risk');
53
+ });
54
+
55
+ it('selectSortOrder returns the sort_order value', () => {
56
+ expect(selectSortOrder(state)).toBe('asc');
57
+ });
58
+
59
+ it('selectQueryParams returns aggregated params object', () => {
60
+ const params = selectQueryParams(state);
61
+ expect(params.page).toBe(1);
62
+ expect(params.perPage).toBe(7);
63
+ expect(params.query).toBe('total_risk < 3');
64
+ expect(params.sortBy).toBe('total_risk');
65
+ expect(params.sortOrder).toBe('asc');
66
+ });
67
+ });
68
+
69
+ describe('API selectors', () => {
70
+ it('selectStatus returns API status', () => {
71
+ expect(selectStatus(state)).toBe('RESOLVED');
72
+ });
73
+
74
+ it('selectError returns error message from error state', () => {
75
+ const errorState = { ...state, ...APIErrorState };
76
+ expect(selectError(errorState)).toBeTruthy();
77
+ });
78
+
79
+ it('selectHits returns hits when status is RESOLVED', () => {
80
+ expect(selectHits(state)).toHaveLength(2);
81
+ });
82
+
83
+ it('selectHits returns empty array when status is not RESOLVED', () => {
84
+ const pendingState = {
85
+ ...state,
86
+ API: {
87
+ INSIGHTS_HITS: {
88
+ ...state.API.INSIGHTS_HITS,
89
+ status: 'PENDING',
90
+ },
91
+ },
92
+ };
93
+ expect(selectHits(pendingState)).toEqual([]);
94
+ });
95
+
96
+ it('selectItemCount returns the item count', () => {
97
+ expect(selectItemCount(state)).toBe(2);
98
+ });
99
+ });
100
+
101
+ describe('table state selectors', () => {
102
+ it('selectInsightsCloudTable returns the table sub-state', () => {
103
+ expect(selectInsightsCloudTable(state)).toEqual({
104
+ selectedIds: { '51': true },
105
+ showSelectAllAlert: true,
106
+ isAllSelected: false,
107
+ });
108
+ });
109
+
110
+ it('selectSelectedIds returns selected IDs', () => {
111
+ expect(selectSelectedIds(state)).toEqual({ '51': true });
112
+ });
113
+
114
+ it('selectIsAllSelected returns false', () => {
115
+ expect(selectIsAllSelected(state)).toBe(false);
116
+ });
54
117
 
55
- describe('InsightsTable selectors', () =>
56
- testSelectorsSnapshotWithFixtures(fixtures));
118
+ it('selectShowSelectAllAlert returns true', () => {
119
+ expect(selectShowSelectAllAlert(state)).toBe(true);
120
+ });
121
+ });
122
+ });
@@ -1,25 +1,83 @@
1
- import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
2
- import { noop } from 'foremanReact/common/helpers';
3
- import InsightsCloudSync from './InsightsCloudSync';
4
-
5
- jest.mock('foremanReact/Root/Context/ForemanContext', () => ({
6
- useForemanContext: () => ({
7
- metadata: {
8
- foreman_rh_cloud: {
9
- iop: true,
10
- },
11
- },
12
- }),
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import RecommendationsPage from './InsightsCloudSync';
4
+
5
+ let mockIsIop = false;
6
+ jest.mock('../common/Hooks/ConfigHooks', () => ({
7
+ useIopConfig: () => mockIsIop,
8
+ }));
9
+
10
+ jest.mock('../common/Hooks/PermissionsHooks', () => ({
11
+ useInsightsPermissions: () => ({}),
13
12
  }));
14
13
 
15
- const fixtures = {
16
- render: {
17
- status: 'RESOLVED',
18
- syncInsights: noop,
19
- fetchInsights: noop,
20
- query: '',
21
- },
14
+ jest.mock('./Components/InsightsTable', () => () => null);
15
+ jest.mock('./Components/RemediationModal', () => () => null);
16
+ jest.mock('./Components/InsightsTable/Pagination', () => () => null);
17
+ jest.mock('./Components/InsightsSettings', () => () => null);
18
+ jest.mock('foremanReact/routes/common/PageLayout/PageLayout', () => ({
19
+ children,
20
+ header,
21
+ toolbarButtons,
22
+ }) => (
23
+ <div data-testid="page-layout" data-header={header}>
24
+ {toolbarButtons}
25
+ {children}
26
+ </div>
27
+ ));
28
+ jest.mock('@scalprum/react-core', () => ({
29
+ ScalprumComponent: () => <div data-testid="scalprum-component" />,
30
+ ScalprumProvider: ({ children }) => <div>{children}</div>,
31
+ }));
32
+ jest.mock('../common/ScalprumModule/ScalprumContext', () => ({
33
+ createProviderOptions: () => ({ config: {} }),
34
+ }));
35
+
36
+ const defaultProps = {
37
+ syncInsights: jest.fn(),
38
+ fetchInsights: jest.fn(),
39
+ query: '',
22
40
  };
23
41
 
24
- describe('InsightsCloudSync', () =>
25
- testComponentSnapshotsWithFixtures(InsightsCloudSync, fixtures));
42
+ describe('RecommendationsPage', () => {
43
+ afterEach(() => {
44
+ mockIsIop = false;
45
+ jest.clearAllMocks();
46
+ });
47
+
48
+ describe('non-IOP mode', () => {
49
+ it('renders with rh-cloud-insights class and correct header', () => {
50
+ const { container } = render(
51
+ <RecommendationsPage {...defaultProps} />
52
+ );
53
+
54
+ expect(container.querySelector('.rh-cloud-insights')).toBeTruthy();
55
+ expect(
56
+ container.querySelector('[data-header="Red Hat Insights"]')
57
+ ).toBeTruthy();
58
+ });
59
+
60
+ it('does not render IOP advisor view', () => {
61
+ const { container } = render(
62
+ <RecommendationsPage {...defaultProps} />
63
+ );
64
+
65
+ expect(container.querySelector('.advisor')).toBeNull();
66
+ });
67
+ });
68
+
69
+ describe('IOP mode', () => {
70
+ beforeEach(() => {
71
+ mockIsIop = true;
72
+ });
73
+
74
+ it('renders advisor view instead of insights page', () => {
75
+ const { container } = render(
76
+ <RecommendationsPage {...defaultProps} />
77
+ );
78
+
79
+ expect(container.querySelector('.advisor')).toBeTruthy();
80
+ expect(container.querySelector('.rh-cloud-insights')).toBeNull();
81
+ });
82
+ });
83
+ });
@@ -1,9 +1,34 @@
1
- import { testActionSnapshotWithFixtures } from '@theforeman/test';
2
1
  import { syncInsights } from '../InsightsCloudSyncActions';
2
+ import { INSIGHTS_CLOUD_SYNC } from '../InsightsCloudSyncConstants';
3
3
 
4
- const fixtures = {
5
- 'should syncInsights': () => syncInsights(),
6
- };
7
4
 
8
- describe('Insights cloud sync actions', () =>
9
- testActionSnapshotWithFixtures(fixtures));
5
+ jest.mock('../../common/ForemanTasks');
6
+
7
+ describe('InsightsCloudSync actions', () => {
8
+ let dispatch;
9
+
10
+ beforeEach(() => {
11
+ dispatch = jest.fn();
12
+ jest.clearAllMocks();
13
+ });
14
+
15
+ it('dispatches post with correct key and url', () => {
16
+ const fetchInsights = jest.fn();
17
+ syncInsights(fetchInsights, 'test-query')(dispatch);
18
+
19
+ expect(dispatch).toHaveBeenCalledTimes(1);
20
+ const dispatched = dispatch.mock.calls[0][0];
21
+ expect(dispatched.key).toBe(INSIGHTS_CLOUD_SYNC);
22
+ expect(dispatched.url).toBe('/insights_cloud/tasks');
23
+ expect(typeof dispatched.handleSuccess).toBe('function');
24
+ expect(typeof dispatched.errorToast).toBe('function');
25
+ });
26
+
27
+ it('errorToast returns failure message with error details', () => {
28
+ syncInsights(jest.fn(), '')(dispatch);
29
+ const dispatched = dispatch.mock.calls[0][0];
30
+
31
+ const result = dispatched.errorToast('some error');
32
+ expect(result).toContain('some error');
33
+ });
34
+ });
@@ -1,13 +1,46 @@
1
- import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
2
-
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
3
  import InsightsTab from '../InsightsTab';
4
- import { props } from './InsightsTab.fixtures';
5
-
6
- const fixtures = {
7
- 'render with props': props,
8
- };
4
+ import { hits, hostID } from './InsightsTab.fixtures';
9
5
 
10
6
  describe('InsightsTab', () => {
11
- describe('rendering', () =>
12
- testComponentSnapshotsWithFixtures(InsightsTab, fixtures));
7
+ it('renders "No recommendations" message when hits is empty', () => {
8
+ render(<InsightsTab hostID={hostID} hits={[]} />);
9
+ expect(
10
+ screen.getByText('No recommendations were found for this host!')
11
+ ).toBeTruthy();
12
+ });
13
+
14
+ it('renders Recommendations heading when hits exist', () => {
15
+ render(<InsightsTab hostID={hostID} hits={hits} />);
16
+ expect(screen.getByText('Recommendations')).toBeTruthy();
17
+ });
18
+
19
+ it('displays hit titles', () => {
20
+ render(<InsightsTab hostID={hostID} hits={hits} />);
21
+ hits.forEach(hit => {
22
+ expect(screen.getByText(hit.title)).toBeTruthy();
23
+ });
24
+ });
25
+
26
+ it('calls fetchHits on mount', () => {
27
+ const fetchHits = jest.fn();
28
+ render(<InsightsTab hostID={hostID} hits={[]} fetchHits={fetchHits} />);
29
+ expect(fetchHits).toHaveBeenCalledWith(hostID);
30
+ });
31
+
32
+ it('sorts hits by total_risk descending', () => {
33
+ const multipleHits = [
34
+ { ...hits[0], title: 'Low risk', total_risk: 1 },
35
+ { ...hits[0], title: 'High risk', total_risk: 4 },
36
+ { ...hits[0], title: 'Medium risk', total_risk: 2 },
37
+ ];
38
+ render(<InsightsTab hostID={hostID} hits={multipleHits} />);
39
+ const titles = screen.getAllByText(/risk/i).map(el => el.textContent);
40
+ const highRiskIndex = titles.findIndex(t => t === 'High risk');
41
+ const mediumRiskIndex = titles.findIndex(t => t === 'Medium risk');
42
+ const lowRiskIndex = titles.findIndex(t => t === 'Low risk');
43
+ expect(highRiskIndex).toBeLessThan(mediumRiskIndex);
44
+ expect(mediumRiskIndex).toBeLessThan(lowRiskIndex);
45
+ });
13
46
  });