katello 4.1.4 → 4.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of katello might be problematic. Click here for more details.

Files changed (240) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +2 -2
  3. data/app/controllers/katello/api/v2/content_credentials_controller.rb +3 -3
  4. data/app/controllers/katello/api/v2/content_uploads_controller.rb +3 -1
  5. data/app/controllers/katello/api/v2/content_view_components_controller.rb +33 -1
  6. data/app/controllers/katello/api/v2/content_views_controller.rb +12 -0
  7. data/app/controllers/katello/api/v2/host_errata_controller.rb +1 -1
  8. data/app/controllers/katello/api/v2/products_bulk_actions_controller.rb +1 -2
  9. data/app/controllers/katello/api/v2/products_controller.rb +4 -4
  10. data/app/controllers/katello/api/v2/repositories_bulk_actions_controller.rb +3 -11
  11. data/app/controllers/katello/api/v2/repositories_controller.rb +68 -47
  12. data/app/controllers/katello/api/v2/upstream_subscriptions_controller.rb +0 -28
  13. data/app/controllers/katello/concerns/api/v2/registration_commands_controller_extensions.rb +26 -5
  14. data/app/controllers/katello/concerns/api/v2/registration_controller_extensions.rb +26 -1
  15. data/app/lib/actions/candlepin/environment/destroy.rb +2 -0
  16. data/app/lib/actions/katello/agent_action.rb +2 -2
  17. data/app/lib/actions/katello/capsule_content/sync_capsule.rb +3 -2
  18. data/app/lib/actions/katello/{gpg_key → content_credential}/update.rb +1 -1
  19. data/app/lib/actions/katello/content_view/publish.rb +6 -1
  20. data/app/lib/actions/katello/content_view_version/create_repos.rb +1 -1
  21. data/app/lib/actions/katello/content_view_version/incremental_update.rb +0 -47
  22. data/app/lib/actions/katello/orphan_cleanup/remove_orphans.rb +1 -1
  23. data/app/lib/actions/katello/repository/clone_contents.rb +1 -7
  24. data/app/lib/actions/katello/repository/clone_to_environment.rb +1 -7
  25. data/app/lib/actions/katello/repository/create.rb +4 -8
  26. data/app/lib/actions/katello/repository/create_root.rb +1 -1
  27. data/app/lib/actions/katello/repository/destroy.rb +1 -3
  28. data/app/lib/actions/katello/repository/import_upload.rb +3 -2
  29. data/app/lib/actions/katello/repository/instance_update.rb +1 -1
  30. data/app/lib/actions/katello/repository/metadata_generate.rb +2 -8
  31. data/app/lib/actions/katello/repository/multi_clone_contents.rb +0 -1
  32. data/app/lib/actions/katello/repository/refresh_repository.rb +1 -4
  33. data/app/lib/actions/katello/repository/remove_content.rb +6 -4
  34. data/app/lib/actions/katello/repository/sync.rb +5 -25
  35. data/app/lib/actions/katello/repository/update.rb +1 -2
  36. data/app/lib/actions/katello/repository/update_http_proxy_details.rb +2 -5
  37. data/app/lib/actions/katello/repository/update_redhat_repository.rb +1 -1
  38. data/app/lib/actions/katello/repository/upload_files.rb +8 -3
  39. data/app/lib/actions/katello/repository/upload_package_group.rb +2 -11
  40. data/app/lib/actions/katello/repository/verify_checksum.rb +0 -1
  41. data/app/lib/actions/katello/repository_set/enable_repository.rb +1 -1
  42. data/app/lib/actions/pulp3/orchestration/repository/create.rb +2 -2
  43. data/app/lib/actions/pulp3/repository/create.rb +3 -4
  44. data/app/lib/actions/pulp3/repository/create_remote.rb +1 -6
  45. data/app/lib/actions/pulp3/repository/repair.rb +4 -0
  46. data/app/lib/katello/errors.rb +1 -0
  47. data/app/lib/katello/http_resource.rb +26 -73
  48. data/app/lib/katello/qpid/connection.rb +1 -3
  49. data/app/lib/katello/resources/candlepin/consumer.rb +1 -1
  50. data/app/lib/katello/resources/candlepin/environment.rb +2 -0
  51. data/app/lib/katello/resources/registry.rb +7 -20
  52. data/app/lib/katello/util/http_proxy.rb +0 -3
  53. data/app/lib/katello/validators/gpg_key_content_validator.rb +1 -1
  54. data/app/models/katello/authorization/{gpg_key.rb → content_credential.rb} +1 -1
  55. data/app/models/katello/authorization/product.rb +0 -4
  56. data/app/models/katello/concerns/host_managed_extensions.rb +2 -16
  57. data/app/models/katello/concerns/organization_extensions.rb +1 -1
  58. data/app/models/katello/concerns/pulp_database_unit.rb +13 -5
  59. data/app/models/katello/concerns/smart_proxy_extensions.rb +45 -41
  60. data/app/models/katello/{gpg_key.rb → content_credential.rb} +4 -4
  61. data/app/models/katello/content_view.rb +6 -1
  62. data/app/models/katello/generic_content_unit.rb +16 -0
  63. data/app/models/katello/glue/pulp/repos.rb +9 -25
  64. data/app/models/katello/kt_environment.rb +1 -1
  65. data/app/models/katello/product.rb +4 -4
  66. data/app/models/katello/repository.rb +13 -7
  67. data/app/models/katello/repository_generic_content_unit.rb +7 -0
  68. data/app/models/katello/root_repository.rb +38 -7
  69. data/app/models/setting/content.rb +5 -0
  70. data/app/services/cert/certs.rb +16 -8
  71. data/app/services/katello/applicability/applicable_content_helper.rb +1 -2
  72. data/app/services/katello/candlepin/consumer.rb +6 -0
  73. data/app/services/katello/component_view_presenter.rb +27 -0
  74. data/app/services/katello/pulp/repository.rb +1 -1
  75. data/app/services/katello/pulp/server.rb +2 -2
  76. data/app/services/katello/pulp3/api/core.rb +4 -0
  77. data/app/services/katello/pulp3/api/generic.rb +68 -0
  78. data/app/services/katello/pulp3/generic_content_unit.rb +29 -0
  79. data/app/services/katello/pulp3/pulp_content_unit.rb +5 -1
  80. data/app/services/katello/pulp3/repository/generic.rb +94 -0
  81. data/app/services/katello/pulp3/repository/yum.rb +4 -5
  82. data/app/services/katello/pulp3/repository.rb +27 -12
  83. data/app/services/katello/pulp3/repository_mirror.rb +2 -2
  84. data/app/services/katello/pulp3/smart_proxy_repository.rb +4 -4
  85. data/app/services/katello/registration_manager.rb +18 -7
  86. data/app/services/katello/repository_type.rb +59 -1
  87. data/app/services/katello/repository_type_manager.rb +116 -24
  88. data/app/views/katello/api/v2/content_views/base.json.rabl +4 -4
  89. data/app/views/katello/api/v2/repositories/show.json.rabl +1 -0
  90. data/app/views/smart_proxies/plugins/_pulpcore.html.erb +2 -5
  91. data/app/views/smart_proxies/pulp_status.html.erb +0 -7
  92. data/config/katello.yaml.example +0 -21
  93. data/config/routes/api/v2.rb +2 -1
  94. data/db/functions/deb_version_cmp_v01.sql +200 -0
  95. data/db/migrate/20171110082124_add_ssl_certs_to_products_and_repos.rb +5 -1
  96. data/db/migrate/20200402130013_add_repsoitory_docker_meta_tag_f_key.rb +3 -1
  97. data/db/migrate/20210624221630_katello_generic_content.rb +22 -0
  98. data/db/migrate/20210625095042_add_retain_package_versions_count.rb +9 -0
  99. data/db/migrate/20210628182553_add_generic_remote_options_to_root_repository.rb +5 -0
  100. data/db/migrate/20210714140440_remove_repo_export_permission.rb +5 -0
  101. data/db/migrate/20210721163730_change_gpg_keys_to_content_credentials.rb +8 -0
  102. data/db/migrate/20210728130748_create_function_deb_version_cmp.rb +12 -0
  103. data/db/seeds.d/111-upgrade_tasks.rb +1 -2
  104. data/engines/bastion/app/views/bastion/layouts/assets.html.erb +1 -1
  105. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/capsule-content/capsule-content.controller.js +7 -5
  106. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-errata-modal.html +4 -1
  107. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/details/views/content-view-details.html +1 -1
  108. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/apply-errata.controller.js +1 -1
  109. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/views/apply-errata-confirm.html +2 -1
  110. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/bastion_katello.pot +25 -33
  111. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/bulk/views/products-bulk-advanced-sync-modal.html +1 -1
  112. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/product-repositories.controller.js +1 -6
  113. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details-info.controller.js +10 -2
  114. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details-info.filter.js +9 -0
  115. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details.controller.js +0 -2
  116. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-advanced-sync-options.html +1 -25
  117. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-details.html +1 -1
  118. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +31 -13
  119. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +11 -3
  120. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/views/product-repositories.html +0 -6
  121. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/aggregate-task.factory.js +3 -3
  122. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/task.factory.js +1 -1
  123. data/lib/katello/engine.rb +2 -4
  124. data/lib/katello/permission_creator.rb +6 -12
  125. data/lib/katello/plugin.rb +76 -80
  126. data/lib/katello/repository_types/python.rb +37 -0
  127. data/lib/katello/tasks/reimport.rake +0 -9
  128. data/lib/katello/tasks/repository.rake +3 -4
  129. data/lib/katello/version.rb +1 -1
  130. data/locale/action_names.rb +28 -29
  131. data/locale/bn/katello.po +699 -221
  132. data/locale/cs/katello.po +167 -59
  133. data/locale/de/katello.po +585 -352
  134. data/locale/en/katello.po +167 -59
  135. data/locale/es/katello.po +1388 -1189
  136. data/locale/fr/katello.po +1740 -1494
  137. data/locale/gu/katello.po +896 -416
  138. data/locale/hi/katello.po +892 -415
  139. data/locale/it/katello.po +371 -170
  140. data/locale/ja/katello.po +1657 -1439
  141. data/locale/katello.pot +933 -736
  142. data/locale/kn/katello.po +894 -416
  143. data/locale/ko/katello.po +515 -317
  144. data/locale/mr/katello.po +857 -415
  145. data/locale/or/katello.po +894 -416
  146. data/locale/pa/katello.po +874 -411
  147. data/locale/pt/katello.po +347 -154
  148. data/locale/pt_BR/katello.po +1398 -1215
  149. data/locale/ru/katello.po +671 -463
  150. data/locale/ta/katello.po +697 -221
  151. data/locale/te/katello.po +891 -415
  152. data/locale/zh_CN/katello.po +2029 -1845
  153. data/locale/zh_TW/katello.po +735 -407
  154. data/package.json +3 -1
  155. data/webpack/components/EditableTextInput/EditableTextInput.js +3 -3
  156. data/webpack/components/RoutedTabs/RoutedTabs.js +7 -8
  157. data/webpack/components/Table/TableWrapper.js +19 -11
  158. data/webpack/components/Table/helpers.js +1 -1
  159. data/webpack/components/extensions/HostDetails/Tabs/ContentTab.js +42 -0
  160. data/webpack/components/extensions/HostDetails/Tabs/SubscriptionTab.js +12 -0
  161. data/webpack/components/extensions/RegistrationCommands/__tests__/__snapshots__/ActivationKeys.test.js.snap +4 -0
  162. data/webpack/components/extensions/RegistrationCommands/fields/ActivationKeys.js +1 -1
  163. data/webpack/components/extensions/RegistrationCommands/index.js +1 -2
  164. data/webpack/components/pf3Table/formatters/selectionHeaderCellFormatter.js +2 -1
  165. data/webpack/fills_index.js +4 -1
  166. data/webpack/redux/actions/RedHatRepositories/helpers.js +2 -4
  167. data/webpack/redux/reducers/RedHatRepositories/enabled.js +4 -1
  168. data/webpack/scenes/ContentViews/ContentViewsActions.js +16 -1
  169. data/webpack/scenes/ContentViews/ContentViewsConstants.js +15 -0
  170. data/webpack/scenes/ContentViews/ContentViewsPage.js +12 -22
  171. data/webpack/scenes/ContentViews/Copy/CopyContentViewForm.js +4 -3
  172. data/webpack/scenes/ContentViews/Create/CreateContentViewForm.js +25 -14
  173. data/webpack/scenes/ContentViews/Create/CreateContentViewModal.js +4 -2
  174. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewAddModal.js +153 -0
  175. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentVersion.js +21 -10
  176. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ContentViewComponents.js +157 -19
  177. data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/contentViewComponents.fixtures.json +100 -108
  178. data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/contentViewComponents.test.js +140 -16
  179. data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/publishedContentViewDetails.fixtures.json +367 -0
  180. data/webpack/scenes/ContentViews/Details/ContentViewDetailActions.js +59 -6
  181. data/webpack/scenes/ContentViews/Details/ContentViewDetailSelectors.js +43 -0
  182. data/webpack/scenes/ContentViews/Details/ContentViewDetails.js +44 -13
  183. data/webpack/scenes/ContentViews/Details/Filters/Add/CVFilterAddModal.js +161 -0
  184. data/webpack/scenes/ContentViews/Details/Filters/Add/__tests__/cvFilterAdd.test.js +54 -0
  185. data/webpack/scenes/ContentViews/Details/Filters/Add/__tests__/cvFilterCreateResult.fixtures.json +124 -0
  186. data/webpack/scenes/ContentViews/Details/Filters/CVPackageGroupFilterContent.js +8 -6
  187. data/webpack/scenes/ContentViews/Details/Filters/CVRpmFilterContent.js +7 -6
  188. data/webpack/scenes/ContentViews/Details/Filters/ContentViewFilterDetails.js +4 -3
  189. data/webpack/scenes/ContentViews/Details/Filters/ContentViewFilters.js +71 -12
  190. data/webpack/scenes/ContentViews/Details/Filters/__tests__/contentViewFilters.test.js +77 -0
  191. data/webpack/scenes/ContentViews/Details/Histories/ContentViewHistories.js +13 -12
  192. data/webpack/scenes/ContentViews/Details/Histories/__tests__/contentViewHistory.test.js +2 -2
  193. data/webpack/scenes/ContentViews/Details/Repositories/ContentViewRepositories.js +17 -14
  194. data/webpack/scenes/ContentViews/Details/Repositories/LastSync.js +3 -3
  195. data/webpack/scenes/ContentViews/Details/Repositories/__tests__/contentViewAddRemove.test.js +2 -2
  196. data/webpack/scenes/ContentViews/Details/Repositories/__tests__/contentViewDetailRepos.test.js +6 -2
  197. data/webpack/scenes/ContentViews/Details/Versions/ContentViewVersions.js +61 -20
  198. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewTaskInProgressResponse.fixtures.json +71 -0
  199. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewTaskResponse.fixtures.json +75 -0
  200. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersions.test.js +86 -1
  201. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersionsWithTask.fixtures.json +713 -0
  202. data/webpack/scenes/ContentViews/Details/__tests__/contentViewDetail.test.js +3 -0
  203. data/webpack/scenes/ContentViews/Publish/CVPublishFinish.js +184 -0
  204. data/webpack/scenes/ContentViews/Publish/CVPublishForm.js +104 -0
  205. data/webpack/scenes/ContentViews/Publish/CVPublishReview.js +71 -0
  206. data/webpack/scenes/ContentViews/Publish/ContentViewPublishSelectors.js +17 -0
  207. data/webpack/scenes/ContentViews/Publish/PublishContentViewWizard.js +145 -0
  208. data/webpack/scenes/ContentViews/Publish/__tests__/environmentPaths.fixtures.json +352 -0
  209. data/webpack/scenes/ContentViews/Publish/__tests__/publishContentView.test.js +184 -0
  210. data/webpack/scenes/ContentViews/Publish/__tests__/publishResponse.fixture.json +69 -0
  211. data/webpack/scenes/ContentViews/Publish/cvPublishForm.scss +3 -0
  212. data/webpack/scenes/ContentViews/Table/ContentViewsTable.js +75 -48
  213. data/webpack/scenes/ContentViews/Table/tableDataGenerator.js +15 -2
  214. data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +6 -10
  215. data/webpack/scenes/ContentViews/components/EnvironmentLabels.js +22 -10
  216. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathActions.js +12 -0
  217. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathConstants.js +2 -0
  218. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathSelectors.js +16 -0
  219. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.js +72 -0
  220. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.scss +8 -0
  221. data/webpack/scenes/ContentViews/components/TaskPresenter/TaskPresenter.js +85 -0
  222. data/webpack/scenes/SmartProxy/SmartProxyContentTable.js +9 -8
  223. data/webpack/scenes/Subscriptions/SubscriptionsPage.js +4 -25
  224. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +0 -3
  225. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +3 -3
  226. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableSchema.js +4 -2
  227. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/SubscriptionsTable.test.js.snap +24 -0
  228. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/components/Table.js +4 -1
  229. data/webpack/scenes/Subscriptions/index.js +1 -4
  230. metadata +74 -39
  231. data/app/lib/actions/candlepin/environment/create.rb +0 -21
  232. data/app/lib/actions/foreman/environment/destroy.rb +0 -23
  233. data/app/lib/actions/katello/content_view/environment_create.rb +0 -21
  234. data/app/lib/actions/katello/repository/export.rb +0 -85
  235. data/app/lib/actions/katello/repository/purge_empty_content.rb +0 -16
  236. data/app/lib/actions/katello/repository/upload_errata.rb +0 -38
  237. data/app/lib/katello/util/proxy_uri.rb +0 -64
  238. data/app/models/katello/rhsm_fact_importer.rb +0 -20
  239. data/app/models/katello/rhsm_fact_name.rb +0 -17
  240. data/app/models/katello/rhsm_fact_parser.rb +0 -120
@@ -1,4 +1,5 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useState, useCallback } from 'react';
2
+ import useDeepCompareEffect from 'use-deep-compare-effect';
2
3
  import { useSelector } from 'react-redux';
3
4
  import { TableVariant, TableText } from '@patternfly/react-table';
4
5
  import { Label } from '@patternfly/react-core';
@@ -26,14 +27,6 @@ const ContentViewHistories = ({ cvId }) => {
26
27
  const [metadata, setMetadata] = useState({});
27
28
  const [searchQuery, updateSearchQuery] = useState('');
28
29
 
29
- const taskTypes = {
30
- publish: 'Actions::Katello::ContentView::Publish',
31
- promotion: 'Actions::Katello::ContentView::Promote',
32
- removal: 'Actions::Katello::ContentView::Remove',
33
- incrementalUpdate: 'Actions::Katello::ContentView::IncrementalUpdates',
34
- export: 'Actions::Katello::ContentViewVersion::Export',
35
- };
36
-
37
30
  const columnHeaders = [
38
31
  __('Date'),
39
32
  __('Version'),
@@ -44,7 +37,15 @@ const ContentViewHistories = ({ cvId }) => {
44
37
  ];
45
38
 
46
39
 
47
- useEffect(() => {
40
+ useDeepCompareEffect(() => {
41
+ const taskTypes = {
42
+ publish: 'Actions::Katello::ContentView::Publish',
43
+ promotion: 'Actions::Katello::ContentView::Promote',
44
+ removal: 'Actions::Katello::ContentView::Remove',
45
+ incrementalUpdate: 'Actions::Katello::ContentView::IncrementalUpdates',
46
+ export: 'Actions::Katello::ContentViewVersion::Export',
47
+ };
48
+
48
49
  const actionText = (history) => {
49
50
  const {
50
51
  action,
@@ -102,7 +103,7 @@ const ContentViewHistories = ({ cvId }) => {
102
103
  const newRows = buildRows(results);
103
104
  setRows(newRows);
104
105
  }
105
- }, [JSON.stringify(response)], setMetadata, loading, setRows);
106
+ }, [response, setMetadata, loading, setRows, cvId]);
106
107
 
107
108
  const emptyContentTitle = __("You currently don't have any history for this content view.");
108
109
  const emptyContentBody = __('History will appear here when the content view is published or promoted.'); // needs link
@@ -126,7 +127,7 @@ const ContentViewHistories = ({ cvId }) => {
126
127
  cells={columnHeaders}
127
128
  variant={TableVariant.compact}
128
129
  autocompleteEndpoint={`/content_views/${cvId}/history/auto_complete_search`}
129
- fetchItems={params => getContentViewHistories(cvId, params)}
130
+ fetchItems={useCallback(params => getContentViewHistories(cvId, params), [cvId])}
130
131
  />);
131
132
  };
132
133
 
@@ -44,7 +44,7 @@ test('Can call API and show history on page load', async (done) => {
44
44
  // Nothing will show at first, page is loading
45
45
  expect(queryByText(firstHistory.description)).toBeNull();
46
46
  // Assert that the repo name is now showing on the screen, but wait for it to appear.
47
- await patientlyWaitFor(() => expect(getByText(firstHistory.description)).toBeTruthy());
47
+ await patientlyWaitFor(() => expect(getByText(firstHistory.description)).toBeInTheDocument());
48
48
  assertNockRequest(autocompleteScope);
49
49
  assertNockRequest(scope, done);
50
50
  });
@@ -88,7 +88,7 @@ test('Can handle no History being present', async (done) => {
88
88
  const { queryByText } = renderWithRedux(<ContentViewHistories cvId={1} />, renderOptions);
89
89
 
90
90
  expect(queryByText(firstHistory.description)).toBeNull();
91
- await patientlyWaitFor(() => expect(queryByText("You currently don't have any history for this content view.")).toBeTruthy());
91
+ await patientlyWaitFor(() => expect(queryByText("You currently don't have any history for this content view.")).toBeInTheDocument());
92
92
  assertNockRequest(autocompleteScope);
93
93
  assertNockRequest(scope, done);
94
94
  });
@@ -1,4 +1,5 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
+ import useDeepCompareEffect from 'use-deep-compare-effect';
2
3
  import { useSelector, shallowEqual, useDispatch } from 'react-redux';
3
4
  import {
4
5
  Bullseye,
@@ -74,7 +75,7 @@ const ContentViewRepositories = ({ cvId }) => {
74
75
  ];
75
76
  const loading = status === STATUS.PENDING;
76
77
 
77
- const buildRows = (results) => {
78
+ const buildRows = useCallback((results) => {
78
79
  const newRows = [];
79
80
  results.forEach((repo) => {
80
81
  const {
@@ -101,9 +102,9 @@ const ContentViewRepositories = ({ cvId }) => {
101
102
  newRows.push({ repoId: id, cells });
102
103
  });
103
104
  return newRows;
104
- };
105
+ }, [statusSelected]);
105
106
 
106
- useEffect(() => {
107
+ useDeepCompareEffect(() => {
107
108
  const { results, ...meta } = response;
108
109
  setMetadata(meta);
109
110
 
@@ -111,19 +112,19 @@ const ContentViewRepositories = ({ cvId }) => {
111
112
  const newRows = buildRows(results);
112
113
  setRows(newRows);
113
114
  }
114
- }, [JSON.stringify(response)]);
115
+ }, [response, loading, buildRows]);
115
116
 
116
117
  useEffect(() => {
117
118
  dispatch(getRepositoryTypes());
118
- }, []);
119
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
119
120
 
120
- useEffect(() => {
121
+ useDeepCompareEffect(() => {
121
122
  const rowsAreSelected = rows.some(row => row.selected);
122
123
  setBulkActionEnabled(rowsAreSelected);
123
124
  }, [rows]);
124
125
 
125
126
  // Get repo type filter selections dynamically from the API
126
- useEffect(() => {
127
+ useDeepCompareEffect(() => {
127
128
  if (repoTypesStatus === STATUS.RESOLVED && repoTypesResponse) {
128
129
  const allRepoTypes = {};
129
130
  allRepoTypes[allRepositories] = 'all';
@@ -135,7 +136,7 @@ const ContentViewRepositories = ({ cvId }) => {
135
136
  });
136
137
  setRepoTypes(allRepoTypes);
137
138
  }
138
- }, [JSON.stringify(repoTypesResponse), repoTypesStatus]);
139
+ }, [repoTypesResponse, repoTypesStatus]);
139
140
 
140
141
  const toggleBulkAction = () => {
141
142
  setBulkActionOpen(!bulkActionOpen);
@@ -154,12 +155,14 @@ const ContentViewRepositories = ({ cvId }) => {
154
155
  };
155
156
 
156
157
  const addBulk = () => {
158
+ setBulkActionOpen(false);
157
159
  const reposToAdd = [];
158
160
  rows.forEach(row => row.selected && reposToAdd.push(row.repoId));
159
161
  onAdd(reposToAdd);
160
162
  };
161
163
 
162
164
  const removeBulk = () => {
165
+ setBulkActionOpen(false);
163
166
  const reposToDelete = [];
164
167
  rows.forEach(row => row.selected && reposToDelete.push(row.repoId));
165
168
  onRemove(reposToDelete);
@@ -186,12 +189,12 @@ const ContentViewRepositories = ({ cvId }) => {
186
189
  ];
187
190
  };
188
191
 
189
- const getCVReposWithOptions = (params = {}) => {
192
+ const getCVReposWithOptions = useCallback((params = {}) => {
190
193
  const allParams = { ...params };
191
194
  if (typeSelected !== 'All repositories') allParams.content_type = repoTypes[typeSelected];
192
195
 
193
196
  return getContentViewRepositories(cvId, allParams, statusSelected);
194
- };
197
+ }, [cvId, repoTypes, statusSelected, typeSelected]);
195
198
 
196
199
  const emptyContentTitle = __("You currently don't have any repositories to add to this content view.");
197
200
  const emptyContentBody = __('Please add some repositories.'); // needs link
@@ -201,10 +204,10 @@ const ContentViewRepositories = ({ cvId }) => {
201
204
  (statusSelected && statusSelected !== ALL_STATUSES);
202
205
  const dropdownItems = [
203
206
  <DropdownItem aria-label="bulk_add" key="bulk_add" isDisabled={!bulkActionEnabled} component="button" onClick={addBulk}>
204
- Add
207
+ {__('Add')}
205
208
  </DropdownItem>,
206
209
  <DropdownItem aria-label="bulk_remove" key="bulk_remove" isDisabled={!bulkActionEnabled} component="button" onClick={removeBulk}>
207
- Remove
210
+ {__('Remove')}
208
211
  </DropdownItem>,
209
212
  ];
210
213
 
@@ -228,7 +231,7 @@ const ContentViewRepositories = ({ cvId }) => {
228
231
  cells={columnHeaders}
229
232
  variant={TableVariant.compact}
230
233
  autocompleteEndpoint="/repositories/auto_complete_search"
231
- fetchItems={params => getCVReposWithOptions(params)}
234
+ fetchItems={useCallback(params => getCVReposWithOptions(params), [getCVReposWithOptions])}
232
235
  additionalListeners={[typeSelected, statusSelected]}
233
236
  >
234
237
  <Split hasGutter>
@@ -1,5 +1,5 @@
1
1
  import React, { Fragment } from 'react';
2
- import { CheckCircleIcon, ExclamationTriangleIcon, CloseIcon, InProgressIcon } from '@patternfly/react-icons';
2
+ import { CheckCircleIcon, ExclamationTriangleIcon, ExclamationCircleIcon, InProgressIcon } from '@patternfly/react-icons';
3
3
  import { foremanUrl } from 'foremanReact/common/helpers';
4
4
  import PropTypes from 'prop-types';
5
5
  import InactiveText from '../../components/InactiveText';
@@ -17,9 +17,9 @@ const LastSync = ({ lastSyncWords, lastSync, emptyMessage }) => {
17
17
  Icon = ExclamationTriangleIcon;
18
18
  color = 'orange';
19
19
  } else if (result === 'error' || result === 'failed') {
20
- Icon = CloseIcon;
20
+ Icon = ExclamationCircleIcon;
21
21
  color = 'red';
22
- } else if (result === 'in progress') {
22
+ } else if (result === 'in progress' || result === 'pending') {
23
23
  Icon = InProgressIcon;
24
24
  color = 'blue';
25
25
  } else {
@@ -52,7 +52,7 @@ test('Can enable and disable add repositories button', async (done) => {
52
52
  renderOptions,
53
53
  );
54
54
 
55
- await patientlyWaitFor(() => expect(getByText(firstRepo.name)).toBeTruthy());
55
+ await patientlyWaitFor(() => expect(getByText(firstRepo.name)).toBeInTheDocument());
56
56
  expect(getByLabelText('Select all rows')).toBeInTheDocument();
57
57
  expect(getByLabelText('add_repositories')).toHaveAttribute('aria-disabled', 'true');
58
58
  getByLabelText('Select all rows').click();
@@ -118,7 +118,7 @@ test('Can remove repositories', async (done) => {
118
118
  renderOptions,
119
119
  );
120
120
 
121
- await patientlyWaitFor(() => expect(getByText(firstRepo.name)).toBeTruthy());
121
+ await patientlyWaitFor(() => expect(getByText(firstRepo.name)).toBeInTheDocument());
122
122
  expect(getByLabelText('Select all rows')).toBeInTheDocument();
123
123
  getByLabelText('Select all rows').click();
124
124
  await patientlyWaitFor(() => {
@@ -19,6 +19,9 @@ let searchDelayScope;
19
19
  let autoSearchScope;
20
20
 
21
21
  beforeEach(() => {
22
+ if (!nock.isActive()) {
23
+ nock.activate();
24
+ }
22
25
  const { results } = repoData;
23
26
  [firstRepo] = results;
24
27
  searchDelayScope = mockSetting(nockInstance, 'autosearch_delay', 500);
@@ -34,6 +37,7 @@ afterEach(() => {
34
37
  nock.cleanAll();
35
38
  assertNockRequest(searchDelayScope);
36
39
  assertNockRequest(autoSearchScope);
40
+ nock.restore();
37
41
  });
38
42
 
39
43
  test('Can call API and show repositories on page load', async (done) => {
@@ -52,7 +56,7 @@ test('Can call API and show repositories on page load', async (done) => {
52
56
  // Nothing will show at first, page is loading
53
57
  expect(queryByText(firstRepo.name)).toBeNull();
54
58
  // Assert that the repo name is now showing on the screen, but wait for it to appear.
55
- await patientlyWaitFor(() => expect(getByText(firstRepo.name)).toBeTruthy());
59
+ await patientlyWaitFor(() => expect(getByText(firstRepo.name)).toBeInTheDocument());
56
60
 
57
61
  assertNockRequest(autocompleteScope);
58
62
  assertNockRequest(scope, done);
@@ -83,7 +87,7 @@ test('Can filter by repository type', async (done) => {
83
87
  const selectYum = getByLabelText('select Yum Repositories');
84
88
  fireEvent.click(selectYum); // select yum repos
85
89
  await patientlyWaitForRemoval(() => getByText('Loading'));
86
- await patientlyWaitFor(() => expect(getByText(firstRepo.name)).toBeTruthy());
90
+ await patientlyWaitFor(() => expect(getByText(firstRepo.name)).toBeInTheDocument());
87
91
 
88
92
  assertNockRequest(autocompleteScope);
89
93
  assertNockRequest(allTypesScope);
@@ -1,5 +1,6 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { useSelector } from 'react-redux';
1
+ import React, { useState, useCallback } from 'react';
2
+ import useDeepCompareEffect from 'use-deep-compare-effect';
3
+ import { useDispatch, useSelector } from 'react-redux';
3
4
  import { TableVariant, TableText } from '@patternfly/react-table';
4
5
  import { translate as __ } from 'foremanReact/common/I18n';
5
6
  import { urlBuilder } from 'foremanReact/common/urlHelpers';
@@ -17,12 +18,16 @@ import {
17
18
  selectCVVersionsStatus,
18
19
  selectCVVersionsError,
19
20
  } from '../ContentViewDetailSelectors';
21
+ import TaskPresenter from '../../components/TaskPresenter/TaskPresenter';
22
+ import { startPollingTask } from '../../../Tasks/TaskActions';
20
23
 
21
24
  const ContentViewVersions = ({ cvId }) => {
22
25
  const response = useSelector(state => selectCVVersions(state, cvId));
23
26
  const status = useSelector(state => selectCVVersionsStatus(state, cvId));
24
27
  const error = useSelector(state => selectCVVersionsError(state, cvId));
28
+ const [pollingFinished, setPollingFinished] = useState(false);
25
29
  const loading = status === STATUS.PENDING;
30
+ const dispatch = useDispatch();
26
31
 
27
32
  const [rows, setRows] = useState([]);
28
33
  const [metadata, setMetadata] = useState({});
@@ -37,29 +42,64 @@ const ContentViewVersions = ({ cvId }) => {
37
42
  __('Description'),
38
43
  ];
39
44
 
45
+ const buildCells = useCallback((cvVersion) => {
46
+ const {
47
+ version,
48
+ description,
49
+ id: versionId,
50
+ environments,
51
+ rpm_count: packageCount,
52
+ errata_counts: errataCounts,
53
+ } = cvVersion;
54
+ return [
55
+ { title: <a href={urlBuilder(`content_views/${cvId}/versions/${versionId}`, '')}>{__('Version ')}{version}</a> },
56
+ { title: <ContentViewVersionEnvironments {...{ environments }} /> },
57
+ { title: <a href={urlBuilder(`content_views/${cvId}/versions/${versionId}/packages`, '')}>{`${packageCount}`}</a> },
58
+ { title: <ContentViewVersionErrata {...{ cvId, versionId, errataCounts }} /> },
59
+ { title: <ContentViewVersionContent {...{ cvId, versionId, cvVersion }} /> },
60
+ { title: description ? <TableText wrapModifier="truncate">{description}</TableText> : <InactiveText text={__('No description')} /> },
61
+ ];
62
+ }, [cvId]);
40
63
 
41
- useEffect(() => {
64
+ const buildActiveTaskCells = useCallback((cvVersion) => {
65
+ const {
66
+ version,
67
+ description,
68
+ id: versionId,
69
+ active_history: activeHistory,
70
+ } = cvVersion;
71
+ const { task } = activeHistory[0];
72
+ const { result } = task || {};
73
+ if (result !== 'error') {
74
+ dispatch(startPollingTask(task.id, task));
75
+ }
76
+
77
+ return [
78
+ { title: <a href={urlBuilder(`content_views/${cvId}/versions/${versionId}`, '')}>{__('Version ')}{version}</a> },
79
+ {
80
+ title: <TaskPresenter
81
+ activeHistory={activeHistory[0]}
82
+ setPollingFinished={setPollingFinished}
83
+ />,
84
+ },
85
+ { title: '' },
86
+ { title: '' },
87
+ { title: '' },
88
+ { title: description ? <TableText wrapModifier="truncate">{description}</TableText> : <InactiveText text={__('No description')} /> },
89
+ ];
90
+ }, [cvId, dispatch]);
91
+
92
+ useDeepCompareEffect(() => {
42
93
  const buildRows = (results) => {
43
94
  const newRows = [];
44
95
  results.forEach((cvVersion) => {
45
96
  const {
46
- version,
47
- description,
48
- id: versionId,
49
- environments,
50
- rpm_count: packageCount,
51
- errata_counts: errataCounts,
97
+ active_history: activeHistory,
52
98
  } = cvVersion;
53
99
 
54
- const cells = [
55
- { title: <a href={urlBuilder(`content_views/${cvId}/versions/${versionId}`, '')}>{`Version ${version}`}</a> },
56
- { title: <ContentViewVersionEnvironments {...{ environments }} /> },
57
- { title: <a href={urlBuilder(`content_views/${cvId}/versions/${versionId}/packages`, '')}>{`${packageCount}`}</a> },
58
- { title: <ContentViewVersionErrata {...{ cvId, versionId, errataCounts }} /> },
59
- { title: <ContentViewVersionContent {...{ cvId, versionId, cvVersion }} /> },
60
- { title: description ? <TableText wrapModifier="truncate">{description}</TableText> : <InactiveText text={__('No description')} /> },
61
- ];
62
-
100
+ const cells = activeHistory.length ?
101
+ buildActiveTaskCells(cvVersion) :
102
+ buildCells(cvVersion);
63
103
  newRows.push({ cells });
64
104
  });
65
105
  return newRows;
@@ -71,7 +111,7 @@ const ContentViewVersions = ({ cvId }) => {
71
111
  const newRows = buildRows(results);
72
112
  setRows(newRows);
73
113
  }
74
- }, [JSON.stringify(response)], setMetadata, loading, setRows);
114
+ }, [response, setMetadata, buildActiveTaskCells, buildCells, dispatch, loading, setRows]);
75
115
 
76
116
  const emptyContentTitle = __("You currently don't have any versions for this content view.");
77
117
  const emptyContentBody = __('Versions will appear here when the content view is published.'); // needs link
@@ -95,7 +135,8 @@ const ContentViewVersions = ({ cvId }) => {
95
135
  cells={columnHeaders}
96
136
  variant={TableVariant.compact}
97
137
  autocompleteEndpoint={`/content_view_versions/auto_complete_search?content_view_id=${cvId}`}
98
- fetchItems={params => getContentViewVersions(cvId, params)}
138
+ fetchItems={useCallback(params => getContentViewVersions(cvId, params), [cvId])}
139
+ additionalListeners={[pollingFinished]}
99
140
  />);
100
141
  };
101
142
 
@@ -0,0 +1,71 @@
1
+ {
2
+ "id": "6b900ff8-62bb-42ac-8c45-da86b7258520",
3
+ "label": "Actions::Katello::ContentView::Publish",
4
+ "pending": true,
5
+ "action": "Publish content view '5'; organization 'Default Organization'",
6
+ "username": "admin",
7
+ "started_at": "2021-07-27 13:51:11 -0400",
8
+ "ended_at": null,
9
+ "state": "running",
10
+ "result": "pending",
11
+ "progress": 0.5,
12
+ "input": {
13
+ "content_view": {
14
+ "id": 13,
15
+ "name": "5",
16
+ "label": "5"
17
+ },
18
+ "organization": {
19
+ "id": 1,
20
+ "name": "Default Organization",
21
+ "label": "Default_Organization"
22
+ },
23
+ "services_checked": [
24
+ "pulp3",
25
+ "candlepin",
26
+ "candlepin_auth"
27
+ ],
28
+ "history_id": 188,
29
+ "content_view_id": 13,
30
+ "auto_publish_composite_ids": [],
31
+ "content_view_version_name": "5 12.0",
32
+ "content_view_version_id": 109,
33
+ "environment_id": 1,
34
+ "user_id": 4,
35
+ "skip_promotion": null,
36
+ "locale": "en",
37
+ "current_request_id": "d9d0dcbe-ea44-4316-ade5-cd1a074e31e7",
38
+ "current_timezone": "America/New_York",
39
+ "current_organization_id": 1,
40
+ "current_location_id": 2,
41
+ "current_user_id": 4
42
+ },
43
+ "output": {},
44
+ "humanized": {
45
+ "action": "Publish",
46
+ "input": [
47
+ [
48
+ "content_view",
49
+ {
50
+ "text": "content view '5'",
51
+ "link": "/content_views/13/versions"
52
+ }
53
+ ],
54
+ [
55
+ "organization",
56
+ {
57
+ "text": "organization 'Default Organization'",
58
+ "link": "/organizations/1/edit"
59
+ }
60
+ ]
61
+ ],
62
+ "output": "",
63
+ "errors": []
64
+ },
65
+ "cli_example": null,
66
+ "start_at": "2021-07-27 13:51:11 -0400",
67
+ "available_actions": {
68
+ "cancellable": false,
69
+ "resumable": false
70
+ }
71
+ }
@@ -0,0 +1,75 @@
1
+ {
2
+ "id": "6b900ff8-62bb-42ac-8c45-da86b7258520",
3
+ "label": "Actions::Katello::ContentView::Publish",
4
+ "pending": false,
5
+ "action": "Publish content view '5'; organization 'Default Organization'",
6
+ "username": "admin",
7
+ "started_at": "2021-07-27 12:10:19 -0400",
8
+ "ended_at": "2021-07-27 12:10:56 -0400",
9
+ "state": "stopped",
10
+ "result": "success",
11
+ "progress": 1,
12
+ "input": {
13
+ "content_view": {
14
+ "id": 13,
15
+ "name": "5",
16
+ "label": "5"
17
+ },
18
+ "organization": {
19
+ "id": 1,
20
+ "name": "Default Organization",
21
+ "label": "Default_Organization"
22
+ },
23
+ "services_checked": [
24
+ "pulp3",
25
+ "candlepin",
26
+ "candlepin_auth"
27
+ ],
28
+ "history_id": 184,
29
+ "content_view_id": 13,
30
+ "auto_publish_composite_ids": [],
31
+ "content_view_version_name": "5 11.0",
32
+ "content_view_version_id": 108,
33
+ "environment_id": 1,
34
+ "user_id": 4,
35
+ "skip_promotion": null,
36
+ "locale": "en",
37
+ "current_request_id": "1d10507b-643a-4e9b-a317-c525a6293917",
38
+ "current_timezone": "America/New_York",
39
+ "current_organization_id": 1,
40
+ "current_location_id": 2,
41
+ "current_user_id": 4
42
+ },
43
+ "output": {
44
+ "content_view_id": 13,
45
+ "content_view_version_id": 108,
46
+ "skip_promotion": null
47
+ },
48
+ "humanized": {
49
+ "action": "Publish",
50
+ "input": [
51
+ [
52
+ "content_view",
53
+ {
54
+ "text": "content view '5'",
55
+ "link": "/content_views/13/versions"
56
+ }
57
+ ],
58
+ [
59
+ "organization",
60
+ {
61
+ "text": "organization 'Default Organization'",
62
+ "link": "/organizations/1/edit"
63
+ }
64
+ ]
65
+ ],
66
+ "output": "",
67
+ "errors": []
68
+ },
69
+ "cli_example": null,
70
+ "start_at": "2021-07-27 12:10:19 -0400",
71
+ "available_actions": {
72
+ "cancellable": false,
73
+ "resumable": false
74
+ }
75
+ }
@@ -7,10 +7,14 @@ import ContentViewVersions from '../ContentViewVersions';
7
7
 
8
8
  const cvVersionsData = require('./contentViewVersions.fixtures.json');
9
9
  const emptyCVVersionData = require('./emptyCVVersion.fixtures.json');
10
+ const cvVersionsTasksData = require('./contentViewVersionsWithTask.fixtures.json');
11
+ const contentViewTaskInProgressResponseData = require('./contentViewTaskInProgressResponse.fixtures.json');
12
+ const contentViewTaskResponseData = require('./contentViewTaskResponse.fixtures.json');
10
13
 
11
14
  const renderOptions = { apiNamespace: `${CONTENT_VIEWS_KEY}_1` };
12
15
  const cvVersions = api.getApiUrl('/content_view_versions/');
13
16
  const autocompleteUrl = '/content_view_versions/auto_complete_search';
17
+ const taskPollingUrl = '/foreman_tasks/api/tasks/6b900ff8-62bb-42ac-8c45-da86b7258520';
14
18
 
15
19
  let firstVersion;
16
20
  let searchDelayScope;
@@ -148,7 +152,88 @@ test('Can load for empty versions', async () => {
148
152
 
149
153
  expect(queryByText(`Version ${firstVersion.version}`)).toBeNull();
150
154
  await patientlyWaitFor(() =>
151
- expect(queryByText("You currently don't have any versions for this content view.")).toBeTruthy());
155
+ expect(queryByText("You currently don't have any versions for this content view.")).toBeInTheDocument());
152
156
  assertNockRequest(autocompleteScope);
153
157
  assertNockRequest(scope);
154
158
  });
159
+
160
+ test('Can call API and show versions with tasks on page load', async (done) => {
161
+ const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl);
162
+ const { results: withTaskResults } = cvVersionsTasksData;
163
+ [firstVersion] = withTaskResults;
164
+ const scope = nockInstance
165
+ .get(cvVersions)
166
+ .query(true)
167
+ .reply(200, cvVersionsTasksData);
168
+
169
+ const taskInProgressScope = nockInstance
170
+ .get(taskPollingUrl)
171
+ .reply(200, contentViewTaskInProgressResponseData);
172
+
173
+ const {
174
+ getByLabelText, queryByText,
175
+ } = renderWithRedux(
176
+ <ContentViewVersions cvId={5} />,
177
+ renderOptions,
178
+ );
179
+
180
+ // Nothing will show at first, page is loading
181
+ expect(queryByText(`Version ${firstVersion.version}`)).toBeNull();
182
+ // Assert that the CV version and active task is rendered on the screen.
183
+ await patientlyWaitFor(() => {
184
+ expect(queryByText(`Version ${firstVersion.version}`)).toBeInTheDocument();
185
+ expect(getByLabelText('task_presenter')).toBeInTheDocument();
186
+ expect(getByLabelText('task_presenter')).toHaveAttribute('aria-valuenow', '50');
187
+ });
188
+ assertNockRequest(autocompleteScope);
189
+ assertNockRequest(scope);
190
+ assertNockRequest(taskInProgressScope, done);
191
+ });
192
+
193
+ test('Can reload versions upon task completion', async (done) => {
194
+ const autocompleteScope = mockAutocomplete(nockInstance, autocompleteUrl);
195
+ const { results: withTaskResults } = cvVersionsTasksData;
196
+ [firstVersion] = withTaskResults;
197
+ const scope = nockInstance
198
+ .get(cvVersions)
199
+ .query(true)
200
+ .reply(200, cvVersionsTasksData);
201
+
202
+ const taskSuccessScope = nockInstance
203
+ .get(taskPollingUrl)
204
+ .reply(200, contentViewTaskResponseData);
205
+
206
+ const reloadScope = nockInstance
207
+ .get(cvVersions)
208
+ .query(true)
209
+ .reply(200, cvVersionsData);
210
+
211
+
212
+ const {
213
+ getByText, queryByLabelText, getByLabelText, queryByText,
214
+ } = renderWithRedux(
215
+ <ContentViewVersions cvId={5} />,
216
+ renderOptions,
217
+ );
218
+
219
+ // Nothing will show at first, page is loading
220
+ expect(queryByText(`Version ${firstVersion.version}`)).toBeNull();
221
+ // Assert that the CV version and active task is now showing on the screen.
222
+ await patientlyWaitFor(() => {
223
+ expect(queryByText(`Version ${firstVersion.version}`)).toBeInTheDocument();
224
+ expect(getByLabelText('task_presenter')).toBeInTheDocument();
225
+ });
226
+ const { results } = cvVersionsData;
227
+ [firstVersion] = results;
228
+
229
+ // Assert that the CV version is shown and active task is not rendered anymore on the screen.
230
+ await patientlyWaitFor(() => {
231
+ expect(getByText(`Version ${firstVersion.version}`)).toBeInTheDocument();
232
+ expect(queryByLabelText('task_presenter')).not.toBeInTheDocument();
233
+ });
234
+ assertNockRequest(autocompleteScope);
235
+ assertNockRequest(scope);
236
+ assertNockRequest(taskSuccessScope);
237
+ // Assert CV Versions API is called upon task completion
238
+ assertNockRequest(reloadScope, done);
239
+ });