foreman_rh_cloud 13.0.8 → 13.0.10

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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/inventory_upload/report_actions.rb +8 -1
  3. data/app/controllers/foreman_inventory_upload/accounts_controller.rb +82 -7
  4. data/app/controllers/foreman_inventory_upload/api/tasks_controller.rb +110 -0
  5. data/app/controllers/foreman_inventory_upload/reports_controller.rb +41 -17
  6. data/app/controllers/foreman_inventory_upload/uploads_controller.rb +0 -9
  7. data/config/routes.rb +4 -2
  8. data/db/migrate/20251209163012_drop_task_output_tables.foreman_rh_cloud.rb +24 -0
  9. data/lib/foreman_inventory_upload/async/generate_all_reports_job.rb +1 -1
  10. data/lib/foreman_inventory_upload/async/host_inventory_report_job.rb +39 -0
  11. data/lib/foreman_inventory_upload/async/queue_for_upload_job.rb +1 -23
  12. data/lib/foreman_inventory_upload/async/upload_report_direct_job.rb +171 -0
  13. data/lib/foreman_inventory_upload.rb +0 -4
  14. data/lib/foreman_rh_cloud/plugin.rb +1 -0
  15. data/lib/foreman_rh_cloud/version.rb +1 -1
  16. data/lib/inventory_sync/async/inventory_hosts_sync.rb +0 -2
  17. data/lib/tasks/rh_cloud_inventory.rake +4 -2
  18. data/package.json +1 -1
  19. data/test/controllers/accounts_controller_test.rb +10 -11
  20. data/test/controllers/insights_cloud/api/cloud_request_controller_test.rb +1 -2
  21. data/test/jobs/host_inventory_report_job_test.rb +161 -97
  22. data/test/jobs/queue_for_upload_job_test.rb +1 -12
  23. data/test/jobs/single_host_report_job_test.rb +36 -54
  24. data/test/jobs/upload_report_direct_job_test.rb +399 -0
  25. data/test/unit/rh_cloud_permissions_test.rb +2 -0
  26. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.fixtures.js +6 -6
  27. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.js +49 -34
  28. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountListActions.js +2 -2
  29. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.fixtures.js +4 -5
  30. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/ListItem.js +15 -7
  31. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItem/__tests__/__snapshots__/ListItem.test.js.snap +11 -11
  32. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatus.fixtures.js +2 -2
  33. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatus.js +10 -14
  34. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/ListItemStatusHelper.js +9 -4
  35. data/webpack/ForemanInventoryUpload/Components/AccountList/Components/ListItemStatus/__tests__/__snapshots__/ListItemStatus.test.js.snap +4 -4
  36. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountList.test.js.snap +15 -9
  37. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListActions.test.js.snap +7 -7
  38. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListReducer.test.js.snap +6 -6
  39. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/__snapshots__/AccountListSelectors.test.js.snap +12 -12
  40. data/webpack/ForemanInventoryUpload/Components/Dashboard/Dashboard.js +37 -130
  41. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/Dashboard.test.js +60 -17
  42. data/webpack/ForemanInventoryUpload/Components/Dashboard/index.js +1 -34
  43. data/webpack/ForemanInventoryUpload/Components/InventoryFilter/__tests__/__snapshots__/integration.test.js.snap +0 -1
  44. data/webpack/ForemanInventoryUpload/Components/NavContainer/NavContainer.js +1 -26
  45. data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.js +24 -17
  46. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageHeader.test.js +178 -8
  47. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageTitle.test.js.snap +2 -2
  48. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/ToolbarButtons.js +3 -1
  49. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/__tests__/ToolbarButtons.test.js +69 -51
  50. data/webpack/ForemanInventoryUpload/Components/TabHeader/TabHeader.js +22 -9
  51. data/webpack/ForemanInventoryUpload/Components/TabHeader/__tests__/TabHeader.test.js +67 -4
  52. data/webpack/ForemanInventoryUpload/Components/TaskHistory/TaskHistory.js +140 -0
  53. data/webpack/ForemanInventoryUpload/Components/TaskHistory/index.js +1 -0
  54. data/webpack/ForemanInventoryUpload/Components/TaskHistory/taskHistory.scss +40 -0
  55. data/webpack/ForemanInventoryUpload/Components/TaskProgress/TaskProgress.js +340 -0
  56. data/webpack/ForemanInventoryUpload/Components/TaskProgress/index.js +1 -0
  57. data/webpack/ForemanInventoryUpload/Components/TaskProgress/taskProgress.scss +8 -0
  58. data/webpack/ForemanInventoryUpload/ForemanInventoryHelpers.js +2 -2
  59. data/webpack/ForemanInventoryUpload/ForemanInventoryUploadReducers.js +0 -2
  60. data/webpack/ForemanInventoryUpload/__tests__/__snapshots__/ForemanInventoryHelpers.test.js.snap +1 -1
  61. data/webpack/ForemanRhCloudPages.js +0 -1
  62. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/InsightsTable.test.js +11 -19
  63. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationHelpers.js +1 -2
  64. data/webpack/InsightsCloudSync/Components/RemediationModal/RemediationModal.js +2 -4
  65. data/webpack/InsightsVulnerabilityHostIndexExtensions/__tests__/CVECountCell.test.js +77 -22
  66. data/webpack/__mocks__/foremanReact/components/common/dates/RelativeDateTime.js +14 -0
  67. data/webpack/__tests__/ForemanRhCloudHelpers.test.js +5 -1
  68. metadata +13 -68
  69. data/app/models/task_output_line.rb +0 -2
  70. data/app/models/task_output_status.rb +0 -2
  71. data/lib/foreman_inventory_upload/async/generate_report_job.rb +0 -61
  72. data/lib/foreman_inventory_upload/async/progress_output.rb +0 -38
  73. data/lib/foreman_inventory_upload/async/shell_process.rb +0 -77
  74. data/lib/foreman_inventory_upload/async/upload_report_job.rb +0 -97
  75. data/lib/foreman_inventory_upload/scripts/uploader.sh.erb +0 -55
  76. data/test/controllers/reports_controller_test.rb +0 -21
  77. data/test/controllers/uploads_controller_test.rb +0 -21
  78. data/test/jobs/generate_report_job_test.rb +0 -146
  79. data/test/jobs/upload_report_job_test.rb +0 -38
  80. data/test/unit/shell_process_job_test.rb +0 -29
  81. data/webpack/ForemanInventoryUpload/Components/Dashboard/DashboardActions.js +0 -88
  82. data/webpack/ForemanInventoryUpload/Components/Dashboard/DashboardConstants.js +0 -9
  83. data/webpack/ForemanInventoryUpload/Components/Dashboard/DashboardReducer.js +0 -68
  84. data/webpack/ForemanInventoryUpload/Components/Dashboard/DashboardSelectors.js +0 -17
  85. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/DashboardActions.test.js +0 -51
  86. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/DashboardIntegration.test.js +0 -17
  87. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/DashboardReducer.test.js +0 -64
  88. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/DashboardSelectors.test.js +0 -46
  89. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/Dashboard.test.js.snap +0 -36
  90. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/DashboardActions.test.js.snap +0 -76
  91. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/DashboardReducer.test.js.snap +0 -44
  92. data/webpack/ForemanInventoryUpload/Components/Dashboard/__tests__/__snapshots__/DashboardSelectors.test.js.snap +0 -42
  93. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/FullScreenModal.fixtures.js +0 -0
  94. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/FullScreenModal.js +0 -55
  95. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/FullScreenModalHelper.js +0 -0
  96. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/__tests__/FullScreenModal.test.js +0 -13
  97. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/__tests__/__snapshots__/FullScreenModal.test.js.snap +0 -65
  98. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/fullScreenModal.scss +0 -20
  99. data/webpack/ForemanInventoryUpload/Components/FullScreenModal/index.js +0 -1
  100. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/__snapshots__/PageHeader.test.js.snap +0 -36
  101. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/ReportGenerate.fixtures.js +0 -18
  102. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/ReportGenerate.js +0 -65
  103. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/ReportGenerateHelper.js +0 -0
  104. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/__tests__/ReportGenerate.test.js +0 -14
  105. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/__tests__/__snapshots__/ReportGenerate.test.js.snap +0 -47
  106. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/index.js +0 -1
  107. data/webpack/ForemanInventoryUpload/Components/ReportGenerate/reportGenerate.scss +0 -0
  108. data/webpack/ForemanInventoryUpload/Components/ReportUpload/ReportUpload.fixtures.js +0 -18
  109. data/webpack/ForemanInventoryUpload/Components/ReportUpload/ReportUpload.js +0 -46
  110. data/webpack/ForemanInventoryUpload/Components/ReportUpload/ReportUploadHelper.js +0 -0
  111. data/webpack/ForemanInventoryUpload/Components/ReportUpload/__tests__/ReportUpload.test.js +0 -14
  112. data/webpack/ForemanInventoryUpload/Components/ReportUpload/__tests__/__snapshots__/ReportUpload.test.js.snap +0 -47
  113. data/webpack/ForemanInventoryUpload/Components/ReportUpload/index.js +0 -1
  114. data/webpack/ForemanInventoryUpload/Components/ReportUpload/reportUpload.scss +0 -0
  115. data/webpack/ForemanInventoryUpload/Components/TabBody/TabBody.fixtures.js +0 -0
  116. data/webpack/ForemanInventoryUpload/Components/TabBody/TabBody.js +0 -31
  117. data/webpack/ForemanInventoryUpload/Components/TabBody/TabBodyHelper.js +0 -0
  118. data/webpack/ForemanInventoryUpload/Components/TabBody/__tests__/TabBody.test.js +0 -13
  119. data/webpack/ForemanInventoryUpload/Components/TabBody/__tests__/__snapshots__/TabBody.test.js.snap +0 -19
  120. data/webpack/ForemanInventoryUpload/Components/TabBody/index.js +0 -1
  121. data/webpack/ForemanInventoryUpload/Components/TabBody/tabBody.scss +0 -5
  122. data/webpack/ForemanInventoryUpload/Components/Terminal/Terminal.fixtures.js +0 -10
  123. data/webpack/ForemanInventoryUpload/Components/Terminal/Terminal.js +0 -110
  124. data/webpack/ForemanInventoryUpload/Components/Terminal/TerminalHelper.js +0 -6
  125. data/webpack/ForemanInventoryUpload/Components/Terminal/__tests__/Terminal.test.js +0 -34
  126. data/webpack/ForemanInventoryUpload/Components/Terminal/__tests__/__snapshots__/Terminal.test.js.snap +0 -98
  127. data/webpack/ForemanInventoryUpload/Components/Terminal/index.js +0 -1
  128. data/webpack/ForemanInventoryUpload/Components/Terminal/terminal.scss +0 -32
  129. data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTable.test.js.snap +0 -112
  130. data/webpack/__mocks__/foremanReact/common/hooks/API/APIHooks.js +0 -3
@@ -6,26 +6,33 @@ import InventoryFilter from '../InventoryFilter';
6
6
  import ToolbarButtons from './components/ToolbarButtons';
7
7
  import SettingsWarning from './components/SettingsWarning';
8
8
  import PageTitle from './PageTitle';
9
+ import { useIopConfig } from '../../../common/Hooks/ConfigHooks';
9
10
  import './PageHeader.scss';
10
11
 
11
- const PageHeader = () => (
12
- <div className="inventory-upload-header">
13
- <SettingsWarning />
14
- <PageTitle />
15
- <div className="inventory-upload-header-description">
16
- <InventorySettings />
17
- <PageDescription />
12
+ const PageHeader = () => {
13
+ const isIop = useIopConfig();
14
+
15
+ return (
16
+ <div className="inventory-upload-header">
17
+ <SettingsWarning />
18
+ <PageTitle />
19
+ {!isIop && (
20
+ <div className="inventory-upload-header-description">
21
+ <InventorySettings />
22
+ <PageDescription />
23
+ </div>
24
+ )}
25
+ <Grid.Row>
26
+ <Grid.Col xs={4}>
27
+ <InventoryFilter />
28
+ </Grid.Col>
29
+ <Grid.Col xs={7} xsOffset={1}>
30
+ <ToolbarButtons />
31
+ </Grid.Col>
32
+ </Grid.Row>
18
33
  </div>
19
- <Grid.Row>
20
- <Grid.Col xs={4}>
21
- <InventoryFilter />
22
- </Grid.Col>
23
- <Grid.Col xs={7} xsOffset={1}>
24
- <ToolbarButtons />
25
- </Grid.Col>
26
- </Grid.Row>
27
- </div>
28
- );
34
+ );
35
+ };
29
36
 
30
37
  PageHeader.propTypes = {};
31
38
 
@@ -1,13 +1,183 @@
1
- import { testComponentSnapshotsWithFixtures } from '@theforeman/test';
2
-
1
+ import React from 'react';
2
+ import { screen } from '@testing-library/react';
3
+ import { rtlHelpers } from 'foremanReact/common/rtlTestHelpers';
3
4
  import PageHeader from '../PageHeader';
4
5
 
5
- const fixtures = {
6
- 'render without Props': {},
7
- /** fixtures, props for the component */
8
- };
6
+ // Create a variable to control IoP mode in tests
7
+ let mockIopMode = false;
8
+
9
+ // Mock ForemanContext
10
+ jest.mock('foremanReact/Root/Context/ForemanContext', () => ({
11
+ useForemanContext: () => ({
12
+ metadata: {
13
+ foreman_rh_cloud: {
14
+ iop: mockIopMode,
15
+ },
16
+ },
17
+ UI: {},
18
+ }),
19
+ }));
20
+
21
+ // Mock child components to isolate PageHeader testing
22
+ // This prevents child component complexity from affecting our tests
23
+ jest.mock('../components/SettingsWarning', () => () => (
24
+ <div data-testid="settings-warning">SettingsWarning</div>
25
+ ));
26
+ jest.mock('../PageTitle', () => () => (
27
+ <div data-testid="page-title">PageTitle</div>
28
+ ));
29
+ jest.mock('../../InventorySettings', () => () => (
30
+ <div data-testid="inventory-settings">InventorySettings</div>
31
+ ));
32
+ jest.mock('../components/PageDescription', () => () => (
33
+ <div data-testid="page-description">PageDescription</div>
34
+ ));
35
+ jest.mock('../../InventoryFilter', () => () => (
36
+ <div data-testid="inventory-filter">InventoryFilter</div>
37
+ ));
38
+ jest.mock('../components/ToolbarButtons', () => () => (
39
+ <div data-testid="toolbar-buttons">ToolbarButtons</div>
40
+ ));
41
+
42
+ const { renderWithStore } = rtlHelpers;
9
43
 
10
44
  describe('PageHeader', () => {
11
- describe('rendering', () =>
12
- testComponentSnapshotsWithFixtures(PageHeader, fixtures));
45
+ describe('component behavior', () => {
46
+ test('renders all components when not in IoP mode', () => {
47
+ mockIopMode = false; // Ensure IoP mode is disabled for this test
48
+
49
+ renderWithStore(<PageHeader />, {
50
+ API: {
51
+ INVENTORY_SETTINGS: {
52
+ response: { subscriptionConnectionEnabled: true },
53
+ },
54
+ ADVISOR_ENGINE_CONFIG: {
55
+ response: { use_iop_mode: false },
56
+ status: 'RESOLVED',
57
+ },
58
+ },
59
+ });
60
+
61
+ // All components should be present when not in IoP mode
62
+ expect(screen.getByTestId('settings-warning')).toBeTruthy();
63
+ expect(screen.getByTestId('page-title')).toBeTruthy();
64
+ expect(screen.getByTestId('inventory-settings')).toBeTruthy();
65
+ expect(screen.getByTestId('page-description')).toBeTruthy();
66
+ expect(screen.getByTestId('inventory-filter')).toBeTruthy();
67
+ expect(screen.getByTestId('toolbar-buttons')).toBeTruthy();
68
+ });
69
+
70
+ test('hides inventory settings and description when in IoP mode', () => {
71
+ mockIopMode = true; // Enable IoP mode for this test
72
+
73
+ renderWithStore(<PageHeader />, {
74
+ API: {
75
+ INVENTORY_SETTINGS: {
76
+ response: { subscriptionConnectionEnabled: true },
77
+ },
78
+ ADVISOR_ENGINE_CONFIG: {
79
+ response: { use_iop_mode: true },
80
+ status: 'RESOLVED',
81
+ },
82
+ },
83
+ });
84
+
85
+ // Core components should still be present
86
+ expect(screen.getByTestId('settings-warning')).toBeTruthy();
87
+ expect(screen.getByTestId('page-title')).toBeTruthy();
88
+ expect(screen.getByTestId('inventory-filter')).toBeTruthy();
89
+ expect(screen.getByTestId('toolbar-buttons')).toBeTruthy();
90
+
91
+ // These components should be hidden in IoP mode
92
+ expect(screen.queryByTestId('inventory-settings')).toBeNull();
93
+ expect(screen.queryByTestId('page-description')).toBeNull();
94
+ });
95
+
96
+ test('renders with correct CSS class', () => {
97
+ mockIopMode = false; // Ensure IoP mode is disabled for this test
98
+
99
+ const { container } = renderWithStore(<PageHeader />, {
100
+ API: {
101
+ INVENTORY_SETTINGS: {
102
+ response: { subscriptionConnectionEnabled: true },
103
+ },
104
+ ADVISOR_ENGINE_CONFIG: {
105
+ response: { use_iop_mode: false },
106
+ status: 'RESOLVED',
107
+ },
108
+ },
109
+ });
110
+
111
+ expect(container.querySelector('.inventory-upload-header')).toBeTruthy();
112
+ });
113
+
114
+ test('renders grid layout with correct structure', () => {
115
+ mockIopMode = false; // Ensure IoP mode is disabled for this test
116
+
117
+ const { container } = renderWithStore(<PageHeader />, {
118
+ API: {
119
+ INVENTORY_SETTINGS: {
120
+ response: { subscriptionConnectionEnabled: true },
121
+ },
122
+ ADVISOR_ENGINE_CONFIG: {
123
+ response: { use_iop_mode: false },
124
+ status: 'RESOLVED',
125
+ },
126
+ },
127
+ });
128
+
129
+ const gridRow = container.querySelector('.row');
130
+ expect(gridRow).toBeTruthy();
131
+
132
+ const filterColumn = container.querySelector('.col-xs-4');
133
+ expect(filterColumn).toBeTruthy();
134
+
135
+ const toolbarColumn = container.querySelector('.col-xs-7');
136
+ expect(toolbarColumn).toBeTruthy();
137
+ });
138
+
139
+ test('renders description section only when not in IoP mode', () => {
140
+ mockIopMode = false; // Ensure IoP mode is disabled for this test
141
+
142
+ const { container } = renderWithStore(<PageHeader />, {
143
+ API: {
144
+ INVENTORY_SETTINGS: {
145
+ response: { subscriptionConnectionEnabled: true },
146
+ },
147
+ ADVISOR_ENGINE_CONFIG: {
148
+ response: { use_iop_mode: false },
149
+ status: 'RESOLVED',
150
+ },
151
+ },
152
+ });
153
+
154
+ // Description section should be present when not in IoP mode
155
+ const descriptionSection = container.querySelector(
156
+ '.inventory-upload-header-description'
157
+ );
158
+ expect(descriptionSection).toBeTruthy();
159
+ });
160
+
161
+ test('does not render description section when in IoP mode', () => {
162
+ mockIopMode = true; // Enable IoP mode for this test
163
+
164
+ const { container } = renderWithStore(<PageHeader />, {
165
+ API: {
166
+ INVENTORY_SETTINGS: {
167
+ response: { subscriptionConnectionEnabled: true },
168
+ },
169
+ ADVISOR_ENGINE_CONFIG: {
170
+ response: { use_iop_mode: true },
171
+ status: 'RESOLVED',
172
+ },
173
+ },
174
+ });
175
+
176
+ // Description section should not be present in IoP mode
177
+ const descriptionSection = container.querySelector(
178
+ '.inventory-upload-header-description'
179
+ );
180
+ expect(descriptionSection).toBeNull();
181
+ });
182
+ });
13
183
  });
@@ -24,7 +24,7 @@ exports[`PageTitle rendering render without Props 1`] = `
24
24
  dropdownItems={
25
25
  Array [
26
26
  <DropdownItem
27
- href="/foreman_tasks/tasks?search=label+%3D+ForemanInventoryUpload%3A%3AAsync%3A%3AGenerateReportJob+or+label+%3D+ForemanInventoryUpload%3A%3AAsync%3A%3AGenerateAllReportsJob&page=1"
27
+ href="/foreman_tasks/tasks?search=label+%3D+ForemanInventoryUpload%3A%3AAsync%3A%3AHostInventoryReportJob+or+label+%3D+ForemanInventoryUpload%3A%3AAsync%3A%3AGenerateAllReportsJob&page=1"
28
28
  ouiaId="tasks-history-button"
29
29
  rel="noopener noreferrer"
30
30
  target="_blank"
@@ -32,7 +32,7 @@ exports[`PageTitle rendering render without Props 1`] = `
32
32
  Actions history
33
33
  </DropdownItem>,
34
34
  <DropdownItem
35
- href="/links/manual/?root_url=https%3A%2F%2Faccess.redhat.com%2Fdocumentation%2Fen-us%2Fred_hat_insights%2F2023%2Fhtml%2Fred_hat_insights_remediations_guide%2Fhost-communication-with-insights_red-hat-insights-remediation-guide%23uploading-satellite-host-inventory-to-insights_configuring-satellite-cloud-connector"
35
+ href="/links/manual/?root_url=https%3A%2F%2Fdocs.redhat.com%2Fen%2Fdocumentation%2Fred_hat_lightspeed%2F1-latest%2Fhtml-single%2Fred_hat_lightspeed_remediations_guide%2Findex"
36
36
  ouiaId="inventory-documentation-button"
37
37
  rel="noopener noreferrer"
38
38
  target="_blank"
@@ -4,11 +4,13 @@ import SyncButton from '../SyncButton';
4
4
  import CloudConnectorButton from '../CloudConnectorButton';
5
5
  import './toolbarButtons.scss';
6
6
  import { selectSubscriptionConnectionEnabled } from '../../../InventorySettings/InventorySettingsSelectors';
7
+ import { useIopConfig } from '../../../../../common/Hooks/ConfigHooks';
7
8
 
8
9
  const ToolbarButtons = () => {
9
10
  const subscriptionConnectionEnabled = useSelector(
10
11
  selectSubscriptionConnectionEnabled
11
12
  );
13
+ const isIop = useIopConfig();
12
14
 
13
15
  if (!subscriptionConnectionEnabled) {
14
16
  return null;
@@ -16,7 +18,7 @@ const ToolbarButtons = () => {
16
18
 
17
19
  return (
18
20
  <div className="inventory_toolbar_buttons">
19
- <CloudConnectorButton />
21
+ {!isIop && <CloudConnectorButton />}
20
22
  <SyncButton />
21
23
  </div>
22
24
  );
@@ -1,60 +1,78 @@
1
1
  import React from 'react';
2
- import { Provider } from 'react-redux';
3
- import configureMockStore from 'redux-mock-store';
4
- import thunk from 'redux-thunk';
5
- import { screen, render } from '@testing-library/react';
2
+ import { screen } from '@testing-library/react';
3
+ import { rtlHelpers } from 'foremanReact/common/rtlTestHelpers';
6
4
  import ToolbarButtons from '../ToolbarButtons';
5
+ import { useIopConfig } from '../../../../../../common/Hooks/ConfigHooks';
6
+ import { selectSubscriptionConnectionEnabled } from '../../../../InventorySettings/InventorySettingsSelectors';
7
7
 
8
- const middlewares = [thunk];
9
- const mockStore = configureMockStore(middlewares);
10
-
11
- const renderOptions = {
12
- API: {
13
- INVENTORY_SETTINGS: {
14
- response: { subscriptionConnectionEnabled: true },
15
- },
16
- },
17
- ForemanRhCloud: {
18
- inventoryUpload: {
19
- accountsList: {
20
- CloudConnectorStatus: {},
21
- },
22
- },
23
- },
24
- };
8
+ // Mock the config hook
9
+ jest.mock('../../../../../../common/Hooks/ConfigHooks', () => ({
10
+ useIopConfig: jest.fn(),
11
+ }));
12
+
13
+ // Mock the selector
14
+ jest.mock('../../../../InventorySettings/InventorySettingsSelectors', () => ({
15
+ selectSubscriptionConnectionEnabled: jest.fn(),
16
+ }));
17
+
18
+ // Mock child components to isolate ToolbarButtons testing
19
+ jest.mock(
20
+ '../../SyncButton',
21
+ () =>
22
+ function MockSyncButton() {
23
+ return <div data-testid="sync-button">Sync all inventory status</div>;
24
+ }
25
+ );
26
+ jest.mock(
27
+ '../../CloudConnectorButton',
28
+ () =>
29
+ function MockCloudConnectorButton() {
30
+ return (
31
+ <div data-testid="cloud-connector-button">
32
+ Configure cloud connector
33
+ </div>
34
+ );
35
+ }
36
+ );
37
+
38
+ const { renderWithStore } = rtlHelpers;
25
39
 
26
40
  describe('ToolbarButtons', () => {
27
- test('when subscription connection is enabled', () => {
28
- const store = mockStore(renderOptions);
29
-
30
- render(
31
- <Provider store={store}>
32
- <ToolbarButtons />
33
- </Provider>
34
- );
35
- expect(screen.queryAllByText('Configure cloud connector')).toHaveLength(1);
36
- expect(screen.queryAllByText('Sync all inventory status')).toHaveLength(1);
41
+ test('renders both buttons when subscription connection is enabled and not in IOP mode', () => {
42
+ useIopConfig.mockReturnValue(false);
43
+ selectSubscriptionConnectionEnabled.mockReturnValue(true);
44
+
45
+ renderWithStore(<ToolbarButtons />);
46
+
47
+ expect(screen.getByTestId('cloud-connector-button')).toBeTruthy();
48
+ expect(screen.getByTestId('sync-button')).toBeTruthy();
37
49
  });
38
50
 
39
- test('when subscription connection is not enabled', () => {
40
- const localSetting = {
41
- API: {
42
- INVENTORY_SETTINGS: {
43
- response: { subscriptionConnectionEnabled: false },
44
- },
45
- },
46
- };
47
- const store = mockStore({
48
- ...renderOptions,
49
- ...localSetting,
50
- });
51
-
52
- render(
53
- <Provider store={store}>
54
- <ToolbarButtons />
55
- </Provider>
56
- );
57
- expect(screen.queryAllByText('Configure cloud connector')).toHaveLength(0);
58
- expect(screen.queryAllByText('Sync all inventory status')).toHaveLength(0);
51
+ test('renders only sync button when in IOP mode', () => {
52
+ useIopConfig.mockReturnValue(true);
53
+ selectSubscriptionConnectionEnabled.mockReturnValue(true);
54
+
55
+ renderWithStore(<ToolbarButtons />);
56
+
57
+ expect(screen.queryByTestId('cloud-connector-button')).toBeNull();
58
+ expect(screen.getByTestId('sync-button')).toBeTruthy();
59
+ });
60
+
61
+ test('renders nothing when subscription connection is not enabled', () => {
62
+ useIopConfig.mockReturnValue(false);
63
+ selectSubscriptionConnectionEnabled.mockReturnValue(false);
64
+
65
+ const { container } = renderWithStore(<ToolbarButtons />);
66
+
67
+ expect(container.firstChild).toBeNull();
68
+ });
69
+
70
+ test('renders toolbar buttons container with correct className when enabled', () => {
71
+ useIopConfig.mockReturnValue(false);
72
+ selectSubscriptionConnectionEnabled.mockReturnValue(true);
73
+
74
+ const { container } = renderWithStore(<ToolbarButtons />);
75
+
76
+ expect(container.querySelector('.inventory_toolbar_buttons')).toBeTruthy();
59
77
  });
60
78
  });
@@ -2,10 +2,12 @@ import React from 'react';
2
2
  import { useSelector } from 'react-redux';
3
3
  import PropTypes from 'prop-types';
4
4
  import { Grid, Button, Icon } from 'patternfly-react';
5
+ import { Tooltip } from '@patternfly/react-core';
5
6
  import { noop } from 'foremanReact/common/helpers';
6
7
  import { sprintf, translate as __ } from 'foremanReact/common/I18n';
7
8
  import { selectSubscriptionConnectionEnabled } from '../InventorySettings/InventorySettingsSelectors';
8
9
  import { isExitCodeLoading } from '../../ForemanInventoryHelpers';
10
+ import { useIopConfig } from '../../../common/Hooks/ConfigHooks';
9
11
  import './tabHeader.scss';
10
12
 
11
13
  const TabHeader = ({
@@ -18,10 +20,23 @@ const TabHeader = ({
18
20
  const subscriptionConnectionEnabled = useSelector(
19
21
  selectSubscriptionConnectionEnabled
20
22
  );
23
+ const isIop = Boolean(useIopConfig());
21
24
  const buttonGenerateLabel = subscriptionConnectionEnabled
22
25
  ? __('Generate and upload report')
23
26
  : __('Generate report');
24
27
 
28
+ const isUploadDisabled = !isIop && !subscriptionConnectionEnabled;
29
+ const isButtonDisabled = isExitCodeLoading(exitCode) || isUploadDisabled;
30
+ const tooltipContent = __(
31
+ 'Upload is disabled because subscription connection is not enabled. Enable it in Administer > Settings > Content.'
32
+ );
33
+
34
+ const generateButton = onRestart ? (
35
+ <Button bsStyle="primary" onClick={onRestart} disabled={isButtonDisabled}>
36
+ {buttonGenerateLabel}
37
+ </Button>
38
+ ) : null;
39
+
25
40
  return (
26
41
  <Grid.Row className="tab-header">
27
42
  <Grid.Col sm={6}>
@@ -29,15 +44,13 @@ const TabHeader = ({
29
44
  </Grid.Col>
30
45
  <Grid.Col sm={6}>
31
46
  <div className="tab-action-buttons">
32
- {onRestart ? (
33
- <Button
34
- bsStyle="primary"
35
- onClick={onRestart}
36
- disabled={isExitCodeLoading(exitCode)}
37
- >
38
- {buttonGenerateLabel}
39
- </Button>
40
- ) : null}
47
+ {generateButton && isUploadDisabled ? (
48
+ <Tooltip content={tooltipContent}>
49
+ <div style={{ display: 'inline-block' }}>{generateButton}</div>
50
+ </Tooltip>
51
+ ) : (
52
+ generateButton
53
+ )}
41
54
  {onDownload ? (
42
55
  <Button onClick={onDownload} disabled={downloadButtonDisabled()}>
43
56
  {__('Download Report')} <Icon name="download" />
@@ -4,13 +4,21 @@ import configureMockStore from 'redux-mock-store';
4
4
  import thunk from 'redux-thunk';
5
5
  import { screen, render } from '@testing-library/react';
6
6
  import { noop } from 'foremanReact/common/helpers';
7
+ import * as ConfigHooks from '../../../../common/Hooks/ConfigHooks';
7
8
  import TabHeader from '../TabHeader';
8
9
 
9
10
  const middlewares = [thunk];
10
11
  const mockStore = configureMockStore(middlewares);
11
12
 
13
+ jest.mock('../../../../common/Hooks/ConfigHooks');
14
+
12
15
  describe('TabHeader', () => {
13
- test('when subscription connection is enabled', () => {
16
+ beforeEach(() => {
17
+ ConfigHooks.useIopConfig = jest.fn();
18
+ });
19
+
20
+ test('when subscription connection is enabled and not IoP', () => {
21
+ ConfigHooks.useIopConfig.mockReturnValue(false);
14
22
  const renderOptions = {
15
23
  API: {
16
24
  INVENTORY_SETTINGS: {
@@ -20,16 +28,19 @@ describe('TabHeader', () => {
20
28
  };
21
29
  const store = mockStore(renderOptions);
22
30
 
23
- render(
31
+ const { container } = render(
24
32
  <Provider store={store}>
25
33
  <TabHeader exitCode="exit 0" onRestart={noop} toggleFullScreen={noop} />
26
34
  </Provider>
27
35
  );
28
36
  expect(screen.queryAllByText('Generate and upload report')).toHaveLength(1);
29
37
  expect(screen.queryAllByText('Full Screen')).toHaveLength(1);
38
+ const button = container.querySelector('button[class*="btn-primary"]');
39
+ expect(button.hasAttribute('disabled')).toBe(false);
30
40
  });
31
41
 
32
- test('when subscription connection is not enabled', () => {
42
+ test('when subscription connection is not enabled and not IoP - button is disabled with tooltip', () => {
43
+ ConfigHooks.useIopConfig.mockReturnValue(false);
33
44
  const renderOptions = {
34
45
  API: {
35
46
  INVENTORY_SETTINGS: {
@@ -39,7 +50,7 @@ describe('TabHeader', () => {
39
50
  };
40
51
  const store = mockStore(renderOptions);
41
52
 
42
- render(
53
+ const { container } = render(
43
54
  <Provider store={store}>
44
55
  <TabHeader exitCode="exit 0" onRestart={noop} toggleFullScreen={noop} />
45
56
  </Provider>
@@ -47,5 +58,57 @@ describe('TabHeader', () => {
47
58
 
48
59
  expect(screen.queryAllByText('Generate report')).toHaveLength(1);
49
60
  expect(screen.queryAllByText('Full Screen')).toHaveLength(1);
61
+ const button = container.querySelector('button[class*="btn-primary"]');
62
+ expect(button.hasAttribute('disabled')).toBe(true);
63
+
64
+ // Verify button is wrapped in a div for tooltip (Patternfly requirement)
65
+ // The tooltip content prop is passed but not rendered in the DOM until hover
66
+ const wrapper = button.parentElement;
67
+ expect(wrapper.tagName).toBe('DIV');
68
+ expect(wrapper.style.display).toBe('inline-block');
69
+ });
70
+
71
+ test('when subscription connection is not enabled but IoP is enabled - button is enabled', () => {
72
+ ConfigHooks.useIopConfig.mockReturnValue(true);
73
+ const renderOptions = {
74
+ API: {
75
+ INVENTORY_SETTINGS: {
76
+ response: { subscriptionConnectionEnabled: false },
77
+ },
78
+ },
79
+ };
80
+ const store = mockStore(renderOptions);
81
+
82
+ const { container } = render(
83
+ <Provider store={store}>
84
+ <TabHeader exitCode="exit 0" onRestart={noop} toggleFullScreen={noop} />
85
+ </Provider>
86
+ );
87
+
88
+ expect(screen.queryAllByText('Generate report')).toHaveLength(1);
89
+ const button = container.querySelector('button[class*="btn-primary"]');
90
+ expect(button.hasAttribute('disabled')).toBe(false);
91
+ });
92
+
93
+ test('when IoP is enabled and subscription connection is enabled - button is enabled', () => {
94
+ ConfigHooks.useIopConfig.mockReturnValue(true);
95
+ const renderOptions = {
96
+ API: {
97
+ INVENTORY_SETTINGS: {
98
+ response: { subscriptionConnectionEnabled: true },
99
+ },
100
+ },
101
+ };
102
+ const store = mockStore(renderOptions);
103
+
104
+ const { container } = render(
105
+ <Provider store={store}>
106
+ <TabHeader exitCode="exit 0" onRestart={noop} toggleFullScreen={noop} />
107
+ </Provider>
108
+ );
109
+
110
+ expect(screen.queryAllByText('Generate and upload report')).toHaveLength(1);
111
+ const button = container.querySelector('button[class*="btn-primary"]');
112
+ expect(button.hasAttribute('disabled')).toBe(false);
50
113
  });
51
114
  });