From 71925de902332f298632f02aea0ce48323502be7 Mon Sep 17 00:00:00 2001 From: vedaperi <52008241+vedaperi@users.noreply.github.com> Date: Tue, 19 Jul 2022 14:17:27 -0700 Subject: [PATCH] Enhanced detail component (#12432) * Enhanced detail component to handle cases with no values, and refactored components that use detail component. * Add optional chaining operators where necessary to pass test cases * add test cases to test suites of modified files Co-authored-by: Veda Periwal --- awx/ui/src/components/DetailList/Detail.js | 5 ++ awx/ui/src/components/JobList/JobListItem.js | 23 ++--- .../components/JobList/JobListItem.test.js | 43 +++++++++ .../PromptInventorySourceDetail.js | 17 ++-- .../PromptInventorySourceDetail.test.js | 15 ++++ .../PromptDetail/PromptJobTemplateDetail.js | 59 ++++++------ .../PromptJobTemplateDetail.test.js | 88 ++++++++++++++++++ .../PromptDetail/PromptWFJobTemplateDetail.js | 11 ++- .../PromptWFJobTemplateDetail.test.js | 32 +++++++ .../ResourceAccessListItem.js | 54 ++++++----- .../ResourceAccessListItem.test.js | 36 ++++++++ .../TemplateList/TemplateListItem.js | 9 +- .../TemplateList/TemplateListItem.test.js | 64 +++++++++++++ .../CredentialDetail/CredentialDetail.js | 27 +++--- .../CredentialDetail/CredentialDetail.test.js | 19 ++++ .../src/screens/Host/HostDetail/HostDetail.js | 8 +- .../Host/HostDetail/HostDetail.test.js | 2 + .../InventoryDetail/InventoryDetail.js | 47 +++++----- .../InventoryDetail/InventoryDetail.test.js | 5 +- .../InventoryHostDetail.js | 11 ++- .../InventoryHostDetail.test.js | 2 + .../InventorySourceDetail.js | 17 ++-- .../InventorySourceDetail.test.js | 17 ++++ .../SmartInventoryDetail.js | 56 ++++++------ .../SmartInventoryDetail.test.js | 35 ++++++++ .../SmartInventoryHostDetail.js | 11 ++- .../SmartInventoryHostDetail.test.js | 15 ++++ awx/ui/src/screens/Job/JobDetail/JobDetail.js | 26 +++--- .../screens/Job/JobDetail/JobDetail.test.js | 60 +++++++++++++ .../OrganizationDetail/OrganizationDetail.js | 56 ++++++------ .../OrganizationDetail.test.js | 40 +++++++++ .../JobTemplateDetail/JobTemplateDetail.js | 65 +++++++------- .../JobTemplateDetail.test.js | 90 +++++++++++++++++++ .../WorkflowJobTemplateDetail.js | 50 +++++------ .../WorkflowJobTemplateDetail.test.js | 42 +++++++++ .../NodeTypeStep/JobTemplatesList.js | 46 +++++----- .../WorkflowApprovalDetail.js | 37 ++++---- .../WorkflowApprovalDetail.test.js | 27 ++++++ 38 files changed, 954 insertions(+), 313 deletions(-) diff --git a/awx/ui/src/components/DetailList/Detail.js b/awx/ui/src/components/DetailList/Detail.js index 6837e23ea1..8f56f4ad40 100644 --- a/awx/ui/src/components/DetailList/Detail.js +++ b/awx/ui/src/components/DetailList/Detail.js @@ -41,6 +41,7 @@ const Detail = ({ className, dataCy, alwaysVisible, + isEmpty, helpText, isEncrypted, isNotConfigured, @@ -49,6 +50,10 @@ const Detail = ({ return null; } + if (isEmpty && !alwaysVisible) { + return null; + } + const labelCy = dataCy ? `${dataCy}-label` : null; const valueCy = dataCy ? `${dataCy}-value` : null; diff --git a/awx/ui/src/components/JobList/JobListItem.js b/awx/ui/src/components/JobList/JobListItem.js index 364b4279c0..fdded4f857 100644 --- a/awx/ui/src/components/JobList/JobListItem.js +++ b/awx/ui/src/components/JobList/JobListItem.js @@ -163,16 +163,16 @@ function JobListItem({ - {job.type === 'inventory_update' && - inventorySourceLabels.length > 0 && ( - - string === job.source ? label : null - )} - /> - )} + {job.type === 'inventory_update' && ( + + string === job.source ? label : null + )} + isEmpty={inventorySourceLabels?.length === 0} + /> + )} {job.launch_type === 'scheduled' && (schedule ? ( @@ -254,7 +254,7 @@ function JobListItem({ dataCy={`execution-environment-detail-${job.id}`} /> )} - {credentials && credentials.length > 0 && ( + {credentials && ( } + isEmpty={credentials.length === 0} /> )} {labels && labels.count > 0 && ( diff --git a/awx/ui/src/components/JobList/JobListItem.test.js b/awx/ui/src/components/JobList/JobListItem.test.js index 4b8348a846..e3d7930763 100644 --- a/awx/ui/src/components/JobList/JobListItem.test.js +++ b/awx/ui/src/components/JobList/JobListItem.test.js @@ -203,6 +203,49 @@ describe('', () => { wrapper.find('Detail[label="Execution Environment"] dd').text() ).toBe('Missing resource'); }); + + test('should not load Source', () => { + wrapper = mountWithContexts( + + + + +
+ ); + const source_detail = wrapper.find(`Detail[label="Source"]`).at(0); + expect(source_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load Credentials', () => { + wrapper = mountWithContexts( + + + + +
+ ); + const credentials_detail = wrapper + .find(`Detail[label="Credentials"]`) + .at(0); + expect(credentials_detail.prop('isEmpty')).toEqual(true); + }); }); describe('', () => { diff --git a/awx/ui/src/components/PromptDetail/PromptInventorySourceDetail.js b/awx/ui/src/components/PromptDetail/PromptInventorySourceDetail.js index 7c3443895e..750270efe0 100644 --- a/awx/ui/src/components/PromptDetail/PromptInventorySourceDetail.js +++ b/awx/ui/src/components/PromptDetail/PromptInventorySourceDetail.js @@ -113,15 +113,14 @@ function PromptInventorySourceDetail({ resource }) { label={t`Cache Timeout`} value={`${update_cache_timeout} ${t`Seconds`}`} /> - {summary_fields?.credentials?.length > 0 && ( - ( - - ))} - /> - )} + ( + + ))} + isEmpty={summary_fields?.credentials?.length === 0} + /> {source_regions && ( { ); assertDetail(wrapper, 'Organization', 'Deleted'); }); + + test('should not load Credentials', () => { + wrapper = mountWithContexts( + + ); + const credentials_detail = wrapper.find(`Detail[label="Credential"]`).at(0); + expect(credentials_detail.prop('isEmpty')).toEqual(true); + }); }); diff --git a/awx/ui/src/components/PromptDetail/PromptJobTemplateDetail.js b/awx/ui/src/components/PromptDetail/PromptJobTemplateDetail.js index 1803426876..6e690337e1 100644 --- a/awx/ui/src/components/PromptDetail/PromptJobTemplateDetail.js +++ b/awx/ui/src/components/PromptDetail/PromptJobTemplateDetail.js @@ -26,7 +26,7 @@ function PromptJobTemplateDetail({ resource }) { extra_vars, forks, host_config_key, - instance_groups, + instance_groups = [], job_slice_count, job_tags, job_type, @@ -94,9 +94,11 @@ function PromptJobTemplateDetail({ resource }) { return ( <> - {summary_fields.recent_jobs?.length > 0 && ( - } label={t`Activity`} /> - )} + } + isEmpty={summary_fields.recent_jobs?.length === 0} + /> {summary_fields?.organization ? ( )} {optionsList && } - {summary_fields?.credentials?.length > 0 && ( + {summary_fields?.credentials && ( } + isEmpty={summary_fields?.credentials?.length === 0} /> )} - {summary_fields?.labels?.results?.length > 0 && ( + {summary_fields?.labels?.results && ( } + isEmpty={summary_fields?.labels?.results?.length === 0} /> )} - {instance_groups?.length > 0 && ( - - {instance_groups.map((ig) => ( - - {ig.name} - - ))} - - } - /> - )} - {job_tags?.length > 0 && ( + + {instance_groups?.map((ig) => ( + + {ig.name} + + ))} + + } + isEmpty={instance_groups?.length === 0} + /> + {job_tags && ( } + isEmpty={job_tags?.length === 0} /> )} - {skip_tags?.length > 0 && ( + {skip_tags && ( } + isEmpty={skip_tags?.length === 0} /> )} {extra_vars && ( diff --git a/awx/ui/src/components/PromptDetail/PromptJobTemplateDetail.test.js b/awx/ui/src/components/PromptDetail/PromptJobTemplateDetail.test.js index 8ebd1e004d..b0a782c1ec 100644 --- a/awx/ui/src/components/PromptDetail/PromptJobTemplateDetail.test.js +++ b/awx/ui/src/components/PromptDetail/PromptJobTemplateDetail.test.js @@ -125,4 +125,92 @@ describe('PromptJobTemplateDetail', () => { assertDetail(wrapper, 'Organization', 'Deleted'); assertDetail(wrapper, 'Project', 'Deleted'); }); + + test('should not load Activity', () => { + wrapper = mountWithContexts( + + ); + const activity_detail = wrapper.find(`Detail[label="Activity"]`).at(0); + expect(activity_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load Credentials', () => { + wrapper = mountWithContexts( + + ); + const credentials_detail = wrapper + .find(`Detail[label="Credentials"]`) + .at(0); + expect(credentials_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load Labels', () => { + wrapper = mountWithContexts( + + ); + const labels_detail = wrapper.find(`Detail[label="Labels"]`).at(0); + expect(labels_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load Instance Groups', () => { + wrapper = mountWithContexts( + + ); + const instance_groups_detail = wrapper + .find(`Detail[label="Instance Groups"]`) + .at(0); + expect(instance_groups_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load Job Tags', () => { + wrapper = mountWithContexts( + + ); + expect(wrapper.find('Detail[label="Job Tags"]').length).toBe(0); + }); + + test('should not load Skip Tags', () => { + wrapper = mountWithContexts( + + ); + expect(wrapper.find('Detail[label="Skip Tags"]').length).toBe(0); + }); }); diff --git a/awx/ui/src/components/PromptDetail/PromptWFJobTemplateDetail.js b/awx/ui/src/components/PromptDetail/PromptWFJobTemplateDetail.js index 70fe71d854..8b26fa1486 100644 --- a/awx/ui/src/components/PromptDetail/PromptWFJobTemplateDetail.js +++ b/awx/ui/src/components/PromptDetail/PromptWFJobTemplateDetail.js @@ -57,9 +57,11 @@ function PromptWFJobTemplateDetail({ resource }) { return ( <> - {summary_fields?.recent_jobs?.length > 0 && ( - } label={t`Activity`} /> - )} + } + isEmpty={summary_fields?.recent_jobs?.length === 0} + /> {summary_fields?.organization && ( )} - {summary_fields?.labels?.results?.length > 0 && ( + {summary_fields?.labels?.results && ( } + isEmpty={summary_fields?.labels?.results?.length === 0} /> )} {extra_vars && ( diff --git a/awx/ui/src/components/PromptDetail/PromptWFJobTemplateDetail.test.js b/awx/ui/src/components/PromptDetail/PromptWFJobTemplateDetail.test.js index 42550869cd..56eee8d368 100644 --- a/awx/ui/src/components/PromptDetail/PromptWFJobTemplateDetail.test.js +++ b/awx/ui/src/components/PromptDetail/PromptWFJobTemplateDetail.test.js @@ -62,4 +62,36 @@ describe('PromptWFJobTemplateDetail', () => { '---\nmock: data' ); }); + + test('should not load Activity', () => { + wrapper = mountWithContexts( + + ); + const activity_detail = wrapper.find(`Detail[label="Activity"]`).at(0); + expect(activity_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load Labels', () => { + wrapper = mountWithContexts( + + ); + const labels_detail = wrapper.find(`Detail[label="Labels"]`).at(0); + expect(labels_detail.prop('isEmpty')).toEqual(true); + }); }); diff --git a/awx/ui/src/components/ResourceAccessList/ResourceAccessListItem.js b/awx/ui/src/components/ResourceAccessList/ResourceAccessListItem.js index ccf692dd4c..916fbc17c4 100644 --- a/awx/ui/src/components/ResourceAccessList/ResourceAccessListItem.js +++ b/awx/ui/src/components/ResourceAccessList/ResourceAccessListItem.js @@ -68,34 +68,32 @@ function ResourceAccessListItem({ accessRecord, onRoleDelete }) { {accessRecord.last_name} - {userRoles.length > 0 && ( - - {userRoles.map(renderChip)} - - } - /> - )} - {teamRoles.length > 0 && ( - - {teamRoles.map(renderChip)} - - } - /> - )} + + {userRoles.map(renderChip)} + + } + isEmpty={userRoles.length === 0} + /> + + {teamRoles.map(renderChip)} + + } + isEmpty={teamRoles.length === 0} + /> diff --git a/awx/ui/src/components/ResourceAccessList/ResourceAccessListItem.test.js b/awx/ui/src/components/ResourceAccessList/ResourceAccessListItem.test.js index 2c168f0aa5..a3a6efe035 100644 --- a/awx/ui/src/components/ResourceAccessList/ResourceAccessListItem.test.js +++ b/awx/ui/src/components/ResourceAccessList/ResourceAccessListItem.test.js @@ -53,5 +53,41 @@ describe('', () => { expect(wrapper.find('Td[dataLabel="First name"]').text()).toBe('jane'); expect(wrapper.find('Td[dataLabel="Last name"]').text()).toBe('brown'); + + const user_roles_detail = wrapper.find(`Detail[label="User Roles"]`).at(0); + expect(user_roles_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load team roles', async () => { + let wrapper; + + await act(async () => { + wrapper = mountWithContexts( + + + {}} + /> + +
+ ); + }); + const team_roles_detail = wrapper.find(`Detail[label="Team Roles"]`).at(0); + expect(team_roles_detail.prop('isEmpty')).toEqual(true); }); }); diff --git a/awx/ui/src/components/TemplateList/TemplateListItem.js b/awx/ui/src/components/TemplateList/TemplateListItem.js index 27dad710bb..349f455379 100644 --- a/awx/ui/src/components/TemplateList/TemplateListItem.js +++ b/awx/ui/src/components/TemplateList/TemplateListItem.js @@ -272,11 +272,12 @@ function TemplateListItem({ value={template.description} dataCy={`template-${template.id}-description`} /> - {summaryFields.recent_jobs && summaryFields.recent_jobs.length ? ( + {summaryFields.recent_jobs ? ( } dataCy={`template-${template.id}-activity`} + isEmpty={summaryFields.recent_jobs.length === 0} /> ) : null} {summaryFields.inventory ? ( @@ -316,7 +317,7 @@ function TemplateListItem({ value={formatDateString(template.modified)} dataCy={`template-${template.id}-last-modified`} /> - {summaryFields.credentials && summaryFields.credentials.length ? ( + {summaryFields.credentials ? ( } dataCy={`template-${template.id}-credentials`} + isEmpty={summaryFields.credentials.length === 0} /> ) : null} - {summaryFields.labels && summaryFields.labels.results.length > 0 && ( + {summaryFields.labels && ( } dataCy={`template-${template.id}-labels`} + isEmpty={summaryFields.labels.results.length === 0} /> )}
diff --git a/awx/ui/src/components/TemplateList/TemplateListItem.test.js b/awx/ui/src/components/TemplateList/TemplateListItem.test.js index 9edbff49de..612f3868ff 100644 --- a/awx/ui/src/components/TemplateList/TemplateListItem.test.js +++ b/awx/ui/src/components/TemplateList/TemplateListItem.test.js @@ -465,4 +465,68 @@ describe('', () => { ).toEqual(true); expect(wrapper.find(`Detail[label="Activity"] Sparkline`)).toHaveLength(1); }); + + test('should not load Activity', async () => { + const wrapper = mountWithContexts( + + + + +
+ ); + const activity_detail = wrapper.find(`Detail[label="Activity"]`).at(0); + expect(activity_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load Credentials', async () => { + const wrapper = mountWithContexts( + + + + +
+ ); + const credentials_detail = wrapper + .find(`Detail[label="Credentials"]`) + .at(0); + expect(credentials_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load Labels', async () => { + const wrapper = mountWithContexts( + + + + +
+ ); + const labels_detail = wrapper.find(`Detail[label="Labels"]`).at(0); + expect(labels_detail.prop('isEmpty')).toEqual(true); + }); }); diff --git a/awx/ui/src/screens/Credential/CredentialDetail/CredentialDetail.js b/awx/ui/src/screens/Credential/CredentialDetail/CredentialDetail.js index 25c83ad143..a5ec427ad8 100644 --- a/awx/ui/src/screens/Credential/CredentialDetail/CredentialDetail.js +++ b/awx/ui/src/screens/Credential/CredentialDetail/CredentialDetail.js @@ -264,20 +264,19 @@ function CredentialDetail({ credential }) { date={modified} user={modified_by} /> - {enabledBooleanFields.length > 0 && ( - - {enabledBooleanFields.map(({ id, label }) => ( - - {label} - - ))} - - } - /> - )} + + {enabledBooleanFields.map(({ id, label }) => ( + + {label} + + ))} + + } + isEmpty={enabledBooleanFields.length === 0} + /> {Object.keys(inputSources).length > 0 && ( diff --git a/awx/ui/src/screens/Credential/CredentialDetail/CredentialDetail.test.js b/awx/ui/src/screens/Credential/CredentialDetail/CredentialDetail.test.js index c900dece85..911e2e7056 100644 --- a/awx/ui/src/screens/Credential/CredentialDetail/CredentialDetail.test.js +++ b/awx/ui/src/screens/Credential/CredentialDetail/CredentialDetail.test.js @@ -149,4 +149,23 @@ describe('', () => { wrapper.find('ModalBoxCloseButton').invoke('onClose')(); }); }); + + test('should not load enabled options', async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + const enabled_options_detail = wrapper + .find(`Detail[label="Enabled Options"]`) + .at(0); + expect(enabled_options_detail.prop('isEmpty')).toEqual(true); + }); }); diff --git a/awx/ui/src/screens/Host/HostDetail/HostDetail.js b/awx/ui/src/screens/Host/HostDetail/HostDetail.js index 02556819ad..61bdbdd8ee 100644 --- a/awx/ui/src/screens/Host/HostDetail/HostDetail.js +++ b/awx/ui/src/screens/Host/HostDetail/HostDetail.js @@ -67,9 +67,11 @@ function HostDetail({ host }) { - {recentJobs?.length > 0 && ( - } /> - )} + } + isEmpty={recentJobs?.length === 0} + /> ', () => { expect(wrapper.find(`Detail[label="Activity"] Sparkline`)).toHaveLength( 0 ); + const activity_detail = wrapper.find(`Detail[label="Activity"]`).at(0); + expect(activity_detail.prop('isEmpty')).toEqual(true); }); test('should hide edit button for users without edit permission', async () => { diff --git a/awx/ui/src/screens/Inventory/InventoryDetail/InventoryDetail.js b/awx/ui/src/screens/Inventory/InventoryDetail/InventoryDetail.js index 42cda6e630..d69b9a6419 100644 --- a/awx/ui/src/screens/Inventory/InventoryDetail/InventoryDetail.js +++ b/awx/ui/src/screens/Inventory/InventoryDetail/InventoryDetail.js @@ -79,17 +79,17 @@ function InventoryDetail({ inventory }) { } /> - {instanceGroups && instanceGroups.length > 0 && ( + {instanceGroups && ( - {instanceGroups.map((ig) => ( + {instanceGroups?.map((ig) => ( } + isEmpty={instanceGroups.length === 0} + /> + )} + {inventory.summary_fields.labels && ( + + {inventory.summary_fields.labels?.results?.map((l) => ( + + {l.name} + + ))} + + } + isEmpty={inventory.summary_fields.labels?.results?.length === 0} /> )} - {inventory.summary_fields.labels && - inventory.summary_fields.labels?.results?.length > 0 && ( - - {inventory.summary_fields.labels.results.map((l) => ( - - {l.name} - - ))} - - } - /> - )} ', () => { expect(InventoriesAPI.readInstanceGroups).toHaveBeenCalledWith( mockInventory.id ); - expect(wrapper.find(`Detail[label="Instance Groups"]`)).toHaveLength(0); + const instance_groups_detail = wrapper + .find(`Detail[label="Instance Groups"]`) + .at(0); + expect(instance_groups_detail.prop('isEmpty')).toEqual(true); }); }); diff --git a/awx/ui/src/screens/Inventory/InventoryHostDetail/InventoryHostDetail.js b/awx/ui/src/screens/Inventory/InventoryHostDetail/InventoryHostDetail.js index 777cb42be9..d7bfa590a2 100644 --- a/awx/ui/src/screens/Inventory/InventoryHostDetail/InventoryHostDetail.js +++ b/awx/ui/src/screens/Inventory/InventoryHostDetail/InventoryHostDetail.js @@ -72,12 +72,11 @@ function InventoryHostDetail({ host }) { - {recentPlaybookJobs?.length > 0 && ( - } - /> - )} + } + isEmpty={recentPlaybookJobs?.length === 0} + /> ', () => { expect(wrapper.find(`Detail[label="Activity"] Sparkline`)).toHaveLength( 0 ); + const activity_detail = wrapper.find(`Detail[label="Activity"]`).at(0); + expect(activity_detail.prop('isEmpty')).toEqual(true); }); test('should hide edit button for users without edit permission', async () => { diff --git a/awx/ui/src/screens/Inventory/InventorySourceDetail/InventorySourceDetail.js b/awx/ui/src/screens/Inventory/InventorySourceDetail/InventorySourceDetail.js index db5a8008f3..4106b891fd 100644 --- a/awx/ui/src/screens/Inventory/InventorySourceDetail/InventorySourceDetail.js +++ b/awx/ui/src/screens/Inventory/InventorySourceDetail/InventorySourceDetail.js @@ -268,15 +268,14 @@ function InventorySourceDetail({ inventorySource }) { helpText={helpText.enabledValue} value={enabled_value} /> - {credentials?.length > 0 && ( - ( - - ))} - /> - )} + ( + + ))} + isEmpty={credentials?.length === 0} + /> {optionsList && ( )} diff --git a/awx/ui/src/screens/Inventory/InventorySourceDetail/InventorySourceDetail.test.js b/awx/ui/src/screens/Inventory/InventorySourceDetail/InventorySourceDetail.test.js index 1bf4518861..b3ed34a5a2 100644 --- a/awx/ui/src/screens/Inventory/InventorySourceDetail/InventorySourceDetail.test.js +++ b/awx/ui/src/screens/Inventory/InventorySourceDetail/InventorySourceDetail.test.js @@ -237,4 +237,21 @@ describe('InventorySourceDetail', () => { (el) => el.length === 0 ); }); + + test('should not load Credentials', async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + const credentials_detail = wrapper.find(`Detail[label="Credential"]`).at(0); + expect(credentials_detail.prop('isEmpty')).toEqual(true); + }); }); diff --git a/awx/ui/src/screens/Inventory/SmartInventoryDetail/SmartInventoryDetail.js b/awx/ui/src/screens/Inventory/SmartInventoryDetail/SmartInventoryDetail.js index 6518f97411..e91e22b116 100644 --- a/awx/ui/src/screens/Inventory/SmartInventoryDetail/SmartInventoryDetail.js +++ b/awx/ui/src/screens/Inventory/SmartInventoryDetail/SmartInventoryDetail.js @@ -96,12 +96,11 @@ function SmartInventoryDetail({ inventory }) { - {recentJobs.length > 0 && ( - } - /> - )} + } + isEmpty={recentJobs.length === 0} + /> {host_filter}} /> - {instanceGroups.length > 0 && ( - - {instanceGroups.map((ig) => ( - - {ig.name} - - ))} - - } - /> - )} + + {instanceGroups.map((ig) => ( + + {ig.name} + + ))} + + } + isEmpty={instanceGroups.length === 0} + /> ', () => { (el) => el.length === 0 ); }); + + test('should not load Activity', async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + const activity_detail = wrapper.find(`Detail[label="Activity"]`).at(0); + expect(activity_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load Instance Groups', async () => { + InventoriesAPI.readInstanceGroups.mockResolvedValue({ + data: { + results: [], + }, + }); + + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + wrapper.update(); + const instance_groups_detail = wrapper + .find(`Detail[label="Instance groups"]`) + .at(0); + expect(instance_groups_detail.prop('isEmpty')).toEqual(true); + }); }); describe('User has read-only permissions', () => { diff --git a/awx/ui/src/screens/Inventory/SmartInventoryHostDetail/SmartInventoryHostDetail.js b/awx/ui/src/screens/Inventory/SmartInventoryHostDetail/SmartInventoryHostDetail.js index e792f29c46..27af396135 100644 --- a/awx/ui/src/screens/Inventory/SmartInventoryHostDetail/SmartInventoryHostDetail.js +++ b/awx/ui/src/screens/Inventory/SmartInventoryHostDetail/SmartInventoryHostDetail.js @@ -28,12 +28,11 @@ function SmartInventoryHostDetail({ host }) { - {recentPlaybookJobs?.length > 0 && ( - } - /> - )} + } + isEmpty={recentPlaybookJobs?.length === 0} + /> ', () => { expect(wrapper.find('Detail[label="Activity"] Sparkline')).toHaveLength(1); expect(wrapper.find('VariablesDetail')).toHaveLength(1); }); + + test('should not load Activity', () => { + wrapper = mountWithContexts( + + ); + const activity_detail = wrapper.find(`Detail[label="Activity"]`).at(0); + expect(activity_detail.prop('isEmpty')).toEqual(true); + }); }); diff --git a/awx/ui/src/screens/Job/JobDetail/JobDetail.js b/awx/ui/src/screens/Job/JobDetail/JobDetail.js index 262cec8d95..0202a7bcd3 100644 --- a/awx/ui/src/screens/Job/JobDetail/JobDetail.js +++ b/awx/ui/src/screens/Job/JobDetail/JobDetail.js @@ -268,15 +268,14 @@ function JobDetail({ job, inventorySourceLabels }) { } /> - {inventorySourceLabels.length > 0 && ( - - string === job.source ? label : null - )} - /> - )} + + string === job.source ? label : null + )} + isEmpty={inventorySourceLabels.length === 0} + /> )} {inventory_source && inventory_source.source === 'scm' && ( @@ -406,7 +405,7 @@ function JobDetail({ job, inventorySourceLabels }) { } /> )} - {credentials && credentials.length > 0 && ( + {credentials && ( } + isEmpty={credentials.length === 0} /> )} {labels && labels.count > 0 && ( @@ -451,7 +451,7 @@ function JobDetail({ job, inventorySourceLabels }) { } /> )} - {job.job_tags && job.job_tags.length > 0 && ( + {job.job_tags && ( } + isEmpty={job.job_tags.length === 0} /> )} - {job.skip_tags && job.skip_tags.length > 0 && ( + {job.skip_tags && ( } + isEmpty={job.skip_tags.length === 0} /> )} ', () => { assertDetail('Inventory', 'Demo Inventory'); assertDetail('Job Slice Parent', 'True'); }); + + test('should not load Source', () => { + wrapper = mountWithContexts( + + ); + const source_detail = wrapper.find(`Detail[label="Source"]`).at(0); + expect(source_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load Credentials', () => { + wrapper = mountWithContexts( + + ); + const credentials_detail = wrapper + .find(`Detail[label="Credentials"]`) + .at(0); + expect(credentials_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load Job Tags', () => { + wrapper = mountWithContexts( + + ); + expect(wrapper.find('Detail[label="Job Tags"]').length).toBe(0); + }); + + test('should not load Skip Tags', () => { + wrapper = mountWithContexts( + + ); + expect(wrapper.find('Detail[label="Skip Tags"]').length).toBe(0); + }); }); diff --git a/awx/ui/src/screens/Organization/OrganizationDetail/OrganizationDetail.js b/awx/ui/src/screens/Organization/OrganizationDetail/OrganizationDetail.js index 4dfbc4247e..99d4955f3d 100644 --- a/awx/ui/src/screens/Organization/OrganizationDetail/OrganizationDetail.js +++ b/awx/ui/src/screens/Organization/OrganizationDetail/OrganizationDetail.js @@ -30,7 +30,7 @@ function OrganizationDetail({ organization }) { created, modified, summary_fields, - galaxy_credentials, + galaxy_credentials = [], } = organization; const [contentError, setContentError] = useState(null); const [hasContentLoading, setHasContentLoading] = useState(true); @@ -121,7 +121,7 @@ function OrganizationDetail({ organization }) { date={modified} user={summary_fields.modified_by} /> - {instanceGroups && instanceGroups.length > 0 && ( + {instanceGroups && ( } + isEmpty={instanceGroups.length === 0} /> )} - {galaxy_credentials && galaxy_credentials.length > 0 && ( - - {galaxy_credentials.map((credential) => ( - + {galaxy_credentials?.map((credential) => ( + + - - - ))} - - } - /> - )} + isReadOnly + ouiaId={`galaxy-credential-${credential.id}-chip`} + /> + + ))} + + } + isEmpty={galaxy_credentials?.length === 0} + /> {summary_fields.user_capabilities.edit && ( diff --git a/awx/ui/src/screens/Organization/OrganizationDetail/OrganizationDetail.test.js b/awx/ui/src/screens/Organization/OrganizationDetail/OrganizationDetail.test.js index 3953cfeb5f..5141a198f8 100644 --- a/awx/ui/src/screens/Organization/OrganizationDetail/OrganizationDetail.test.js +++ b/awx/ui/src/screens/Organization/OrganizationDetail/OrganizationDetail.test.js @@ -216,4 +216,44 @@ describe('', () => { (el) => el.length === 0 ); }); + + test('should not load instance groups', async () => { + OrganizationsAPI.readInstanceGroups.mockResolvedValue({ + data: { + results: [], + }, + }); + + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + wrapper.update(); + const instance_groups_detail = wrapper + .find(`Detail[label="Instance Groups"]`) + .at(0); + expect(instance_groups_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load galaxy credentials', async () => { + OrganizationsAPI.readInstanceGroups.mockResolvedValue({ data: {} }); + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + wrapper.update(); + const galaxy_credentials_detail = wrapper + .find(`Detail[label="Galaxy Credentials"]`) + .at(0); + expect(galaxy_credentials_detail.prop('isEmpty')).toEqual(true); + }); }); diff --git a/awx/ui/src/screens/Template/JobTemplateDetail/JobTemplateDetail.js b/awx/ui/src/screens/Template/JobTemplateDetail/JobTemplateDetail.js index 3a4869d0f3..c99348c33c 100644 --- a/awx/ui/src/screens/Template/JobTemplateDetail/JobTemplateDetail.js +++ b/awx/ui/src/screens/Template/JobTemplateDetail/JobTemplateDetail.js @@ -354,7 +354,7 @@ function JobTemplateDetail({ template }) { helpText={helpText.enabledOptions} /> )} - {summary_fields.credentials && summary_fields.credentials.length > 0 && ( + {summary_fields.credentials && ( } + isEmpty={summary_fields.credentials.length === 0} /> )} - {summary_fields.labels && summary_fields.labels.results.length > 0 && ( + {summary_fields.labels && ( } + isEmpty={summary_fields.labels.results.length === 0} /> )} - {instanceGroups.length > 0 && ( - - {instanceGroups.map((ig) => ( - - - {ig.name} - - - ))} - - } - /> - )} - {job_tags && job_tags.length > 0 && ( + + {instanceGroups.map((ig) => ( + + + {ig.name} + + + ))} + + } + isEmpty={instanceGroups.length === 0} + /> + {job_tags && ( } + isEmpty={job_tags.length === 0} /> )} - {skip_tags && skip_tags.length > 0 && ( + {skip_tags && ( } + isEmpty={skip_tags.length === 0} /> )} ', () => { wrapper.find(`Detail[label="Execution Environment"] dd`).text() ).toBe('Default EE'); }); + + test('should not load credentials', async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + const credentials_detail = wrapper + .find(`Detail[label="Credentials"]`) + .at(0); + expect(credentials_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load labels', async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + const labels_detail = wrapper.find(`Detail[label="Labels"]`).at(0); + expect(labels_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load instance groups', async () => { + JobTemplatesAPI.readInstanceGroups.mockResolvedValue({ + data: { + results: [], + }, + }); + + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + wrapper.update(); + const instance_groups_detail = wrapper + .find(`Detail[label="Instance Groups"]`) + .at(0); + expect(instance_groups_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load job tags', async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + expect(wrapper.find('Detail[label="Job Tags"]').length).toBe(0); + }); + + test('should not load skip tags', async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + expect(wrapper.find('Detail[label="Skip Tags"]').length).toBe(0); + }); }); diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.js b/awx/ui/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.js index 511f3cae4d..a3316b527c 100644 --- a/awx/ui/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.js +++ b/awx/ui/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.js @@ -110,12 +110,11 @@ function WorkflowJobTemplateDetail({ template }) { - {summary_fields.recent_jobs?.length > 0 && ( - } - label={t`Activity`} - /> - )} + } + label={t`Activity`} + isEmpty={summary_fields.recent_jobs?.length === 0} + /> {summary_fields.organization && ( )} - {summary_fields.labels?.results?.length > 0 && ( - - {summary_fields.labels.results.map((l) => ( - - {l.name} - - ))} - - } - /> - )} + + {summary_fields.labels.results.map((l) => ( + + {l.name} + + ))} + + } + isEmpty={!summary_fields.labels?.results?.length} + /> ', () => { expect(inventory.prop('to')).toEqual('/inventories/inventory/1/details'); expect(organization.prop('to')).toEqual('/organizations/1/details'); }); + + test('should not load Activity', async () => { + await act(async () => { + wrapper = mountWithContexts( + {}} + /> + ); + }); + const activity_detail = wrapper.find(`Detail[label="Activity"]`).at(0); + expect(activity_detail.prop('isEmpty')).toEqual(true); + }); + + test('should not load Labels', async () => { + await act(async () => { + wrapper = mountWithContexts( + {}} + /> + ); + }); + const labels_detail = wrapper.find(`Detail[label="Labels"]`).at(0); + expect(labels_detail.prop('isEmpty')).toEqual(true); + }); }); diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/JobTemplatesList.js b/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/JobTemplatesList.js index eccd397cd9..2d206a39f9 100644 --- a/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/JobTemplatesList.js +++ b/awx/ui/src/screens/Template/WorkflowJobTemplateVisualizer/Modals/NodeModals/NodeTypeStep/JobTemplatesList.js @@ -44,30 +44,28 @@ function TemplatePopoverContent({ template }) { value={template?.playbook} dataCy={`template-${template.id}-playbook`} /> - {template.summary_fields?.credentials && - template.summary_fields.credentials.length ? ( - - {template.summary_fields.credentials.map((c) => ( - - ))} - - } - /> - ) : null} + + {template.summary_fields?.credentials?.map((c) => ( + + ))} + + } + isEmpty={template.summary_fields?.credentials?.length === 0} + /> ); } diff --git a/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalDetail/WorkflowApprovalDetail.js b/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalDetail/WorkflowApprovalDetail.js index ee8957c70a..e8eadad293 100644 --- a/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalDetail/WorkflowApprovalDetail.js +++ b/awx/ui/src/screens/WorkflowApproval/WorkflowApprovalDetail/WorkflowApprovalDetail.js @@ -309,25 +309,24 @@ function WorkflowApprovalDetail({ workflowApproval }) { dataCy="wa-detail-inventory" /> ) : null} - {workflowJob?.summary_fields?.labels?.results?.length > 0 && ( - - {workflowJob.summary_fields.labels.results.map((label) => ( - - {label.name} - - ))} - - } - /> - )} + + {workflowJob.summary_fields.labels.results.map((label) => ( + + {label.name} + + ))} + + } + isEmpty={!workflowJob?.summary_fields?.labels?.results?.length} + /> {workflowJob?.extra_vars ? ( ', () => { expect(wrapper.find('DeleteButton').length).toBe(1); }); + test('should not load Labels', async () => { + WorkflowJobTemplatesAPI.readDetail.mockResolvedValue({ + data: workflowJobTemplate, + }); + WorkflowJobsAPI.readDetail.mockResolvedValue({ + data: { + ...workflowApproval, + summary_fields: { + ...workflowApproval.summary_fields, + labels: { + results: [], + }, + }, + }, + }); + + let wrapper; + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + waitForElement(wrapper, 'WorkflowApprovalDetail', (el) => el.length > 0); + const labels_detail = wrapper.find(`Detail[label="Labels"]`).at(0); + expect(labels_detail.prop('isEmpty')).toEqual(true); + }); + test('Error dialog shown for failed approval', async () => { WorkflowApprovalsAPI.approve.mockImplementationOnce(() => Promise.reject(new Error())