From c5bd1b2aea8d6897bc995dfb85fe2ff7ff8231cb Mon Sep 17 00:00:00 2001 From: Divyanshi Gupta Date: Wed, 14 Sep 2022 20:32:34 +0530 Subject: [PATCH] Add e2e tests --- .../topology/resource-quota-warning.feature | 13 ++++- .../support/page-objects/topology-po.ts | 1 + .../support/pages/topology/topology-page.ts | 5 ++ .../pages/topology/topology-side-pane-page.ts | 2 + .../topology/resource-quota-warning.ts | 11 +++- .../graph-view/components/nodes/BaseNode.tsx | 16 +++--- .../components/nodes/WorkloadNode.tsx | 3 +- .../side-bar/components/SideBarAlerts.tsx | 6 +- .../components/workload/resource-alert.tsx | 57 ++++++++++--------- 9 files changed, 73 insertions(+), 41 deletions(-) diff --git a/frontend/packages/topology/integration-tests/features/topology/resource-quota-warning.feature b/frontend/packages/topology/integration-tests/features/topology/resource-quota-warning.feature index 0d75bcc04b1..7bd6cea52f8 100644 --- a/frontend/packages/topology/integration-tests/features/topology/resource-quota-warning.feature +++ b/frontend/packages/topology/integration-tests/features/topology/resource-quota-warning.feature @@ -23,5 +23,16 @@ Feature: Update user in topology page if Quotas has been reached in a namespace When user navigates to Topology page And user clicks on link to view resource quota details Then user is redirected to resource quota list page - + + + @regression + Scenario: deployment node has yellow border around it and side-panel shows alert when resource quota is reached: T-19-TC03 + Given user is at Add page + And user has created workload "ex-node-js1" with resource type "deployment" + And user is at Topology page + When user clicks on workload 'ex-node-js1' + And user can see sidebar opens with Resources tab selected by default + Then user is able to see resource quota alert + And user is able to see yellow border around 'ex-node-js1' workload + \ No newline at end of file diff --git a/frontend/packages/topology/integration-tests/support/page-objects/topology-po.ts b/frontend/packages/topology/integration-tests/support/page-objects/topology-po.ts index c9d3bc67c0e..895947a8ac9 100644 --- a/frontend/packages/topology/integration-tests/support/page-objects/topology-po.ts +++ b/frontend/packages/topology/integration-tests/support/page-objects/topology-po.ts @@ -89,6 +89,7 @@ export const topologyPO = { editAnnotations: '[data-test="edit-annotations"]', tabName: '[role="dialog"] li button', healthCheckAlert: 'div.odc-topology-sidebar-alert', + resourceQuotaAlert: 'div.odc-topology-sidebar-alert [aria-label="Warning Alert"]', podScale: 'button.pf-c-button.pf-m-plain.pf-m-block', podText: 'text.pf-chart-donut-title.pod-ring__center-text', applicationGroupingsTitle: '.overview__sidebar-pane-head.resource-overview__heading', diff --git a/frontend/packages/topology/integration-tests/support/pages/topology/topology-page.ts b/frontend/packages/topology/integration-tests/support/pages/topology/topology-page.ts index 0d54ccb19c3..3c7f1e3c4eb 100644 --- a/frontend/packages/topology/integration-tests/support/pages/topology/topology-page.ts +++ b/frontend/packages/topology/integration-tests/support/pages/topology/topology-page.ts @@ -436,6 +436,11 @@ export const topologyPage = { .should('be.visible') .click({ force: true }); }, + verifyNodeAlert: (nodeName: string) => { + cy.get('[data-type="workload"]') + .find('.pf-topology__node.pf-m-warning') + .contains(nodeName); + }, }; export const addGitWorkload = ( diff --git a/frontend/packages/topology/integration-tests/support/pages/topology/topology-side-pane-page.ts b/frontend/packages/topology/integration-tests/support/pages/topology/topology-side-pane-page.ts index fd57f861b28..d32a8d6fd66 100644 --- a/frontend/packages/topology/integration-tests/support/pages/topology/topology-side-pane-page.ts +++ b/frontend/packages/topology/integration-tests/support/pages/topology/topology-side-pane-page.ts @@ -81,6 +81,8 @@ export const topologySidePane = { cy.get(topologyPO.sidePane.podText, { timeout: 120000 }).should('contain.text', scaleNumber); }, verifyHealthCheckAlert: () => cy.get(topologyPO.sidePane.healthCheckAlert).should('be.visible'), + verifyResourceQuotaAlert: () => + cy.get(topologyPO.sidePane.resourceQuotaAlert).should('be.visible'), verifyWorkloadInAppSideBar: (workloadName: string) => cy .get(topologyPO.sidePane.dialog) diff --git a/frontend/packages/topology/integration-tests/support/step-definitions/topology/resource-quota-warning.ts b/frontend/packages/topology/integration-tests/support/step-definitions/topology/resource-quota-warning.ts index bb08e9e2071..5b091308d14 100644 --- a/frontend/packages/topology/integration-tests/support/step-definitions/topology/resource-quota-warning.ts +++ b/frontend/packages/topology/integration-tests/support/step-definitions/topology/resource-quota-warning.ts @@ -1,4 +1,4 @@ -import { Given, When, Then } from 'cypress-cucumber-preprocessor/steps'; +import { Given, When, Then, And } from 'cypress-cucumber-preprocessor/steps'; import { detailsPage } from '@console/cypress-integration-tests/views/details-page'; import { listPage } from '@console/cypress-integration-tests/views/list-page'; import { modal } from '@console/cypress-integration-tests/views/modal'; @@ -10,6 +10,7 @@ import { topologyHelper, topologyPage, app, + topologySidePane, } from '@console/dev-console/integration-tests/support/pages'; const deteleResourceQuota = () => { @@ -68,3 +69,11 @@ When( cy.get('h2').should('contain.text', 'ResourceQuota details'); }, ); + +Then('user is able to see resource quota alert', () => { + topologySidePane.verifyResourceQuotaAlert(); +}); + +And('user is able to see yellow border around {string} workload', (workloadName: string) => { + topologyPage.verifyNodeAlert(workloadName); +}); diff --git a/frontend/packages/topology/src/components/graph-view/components/nodes/BaseNode.tsx b/frontend/packages/topology/src/components/graph-view/components/nodes/BaseNode.tsx index a603912c48e..4da79e509e3 100644 --- a/frontend/packages/topology/src/components/graph-view/components/nodes/BaseNode.tsx +++ b/frontend/packages/topology/src/components/graph-view/components/nodes/BaseNode.tsx @@ -54,8 +54,7 @@ type BaseNodeProps = { createConnectorAccessVerb?: K8sVerb; nodeStatus?: NodeStatus; showStatusBackground?: boolean; - showAlertStatus?: boolean; - alertVariant?: string; + alertVariant?: NodeStatus; } & Partial & Partial & Partial & @@ -75,7 +74,6 @@ const BaseNode: React.FC = ({ contextMenuOpen, createConnectorAccessVerb = 'patch', createConnectorDrag, - showAlertStatus, alertVariant, ...rest }) => { @@ -118,10 +116,14 @@ const BaseNode: React.FC = ({ = observer(function Work const buildStatus = buildConfigs?.[0]?.builds?.[0]?.status?.phase; const pipelineStatus = element.getData()?.resources?.pipelineRunStatus ?? 'Unknown'; const workloadRqAlert = useResourceQuotaAlert(element); - const workloadRqAlertVariant = workloadRqAlert?.variant || 'default'; + const workloadRqAlertVariant = (workloadRqAlert?.variant as NodeStatus) || NodeStatus.default; return ( @@ -219,7 +219,6 @@ const WorkloadPodsNode: React.FC = observer(function Work } attachments={nodeDecorators} contextMenuOpen={contextMenuOpen} - showAlertStatus={!!workloadRqAlert} alertVariant={workloadRqAlertVariant} {...rest} > diff --git a/frontend/packages/topology/src/components/side-bar/components/SideBarAlerts.tsx b/frontend/packages/topology/src/components/side-bar/components/SideBarAlerts.tsx index 375f3c57df1..401114354cb 100644 --- a/frontend/packages/topology/src/components/side-bar/components/SideBarAlerts.tsx +++ b/frontend/packages/topology/src/components/side-bar/components/SideBarAlerts.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { Alert, AlertActionCloseButton } from '@patternfly/react-core'; -import { GraphElement } from '@patternfly/react-topology'; +import { GraphElement, observer } from '@patternfly/react-topology'; import { DetailsResourceAlert, DetailsResourceAlertContent, @@ -15,7 +15,7 @@ const ResolveResourceAlerts: React.FC<{ id?: string; useResourceAlertsContent?: (element: GraphElement) => DetailsResourceAlertContent; element: GraphElement; -}> = ({ id, useResourceAlertsContent, element }) => { +}> = observer(function ResolveResourceAlerts({ id, useResourceAlertsContent, element }) { const [showAlert, setShowAlert, loaded] = useUserSettings( `${USERSETTINGS_PREFIX}.${SIDEBAR_ALERTS}.${id}.${element.getId()}`, true, @@ -42,7 +42,7 @@ const ResolveResourceAlerts: React.FC<{ {content} ) : null; -}; +}); const SideBarAlerts: React.FC<{ element: GraphElement }> = ({ element }) => { const [resourceAlertsExtension, resolved] = useResolvedExtensions( diff --git a/frontend/packages/topology/src/components/workload/resource-alert.tsx b/frontend/packages/topology/src/components/workload/resource-alert.tsx index 15228d9ef32..037666ec895 100644 --- a/frontend/packages/topology/src/components/workload/resource-alert.tsx +++ b/frontend/packages/topology/src/components/workload/resource-alert.tsx @@ -1,19 +1,23 @@ import * as React from 'react'; import { AlertActionLink } from '@patternfly/react-core'; import { GraphElement } from '@patternfly/react-topology'; -import * as _ from 'lodash'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { CommonActionFactory } from '@console/app/src/actions/creators/common-factory'; import { DeploymentActionFactory } from '@console/app/src/actions/creators/deployment-factory'; -import { DetailsResourceAlertContent, useAccessReview } from '@console/dynamic-plugin-sdk'; +import { Action, DetailsResourceAlertContent, useAccessReview } from '@console/dynamic-plugin-sdk'; import { DaemonSetModel, DeploymentConfigModel, DeploymentModel, StatefulSetModel, } from '@console/internal/models'; -import { modelFor, referenceFor, referenceForModel } from '@console/internal/module/k8s'; +import { + K8sResourceCondition, + modelFor, + referenceFor, + referenceForModel, +} from '@console/internal/module/k8s'; import { ServiceModel as KnativeServiceModel } from '@console/knative-plugin'; import { getResource } from '../../utils'; @@ -35,7 +39,7 @@ export const useHealthChecksAlert = (element: GraphElement): DetailsResourceAler const resourceModel = kindForCRDResource ? modelFor(kindForCRDResource) : undefined; const resourceKind = resourceModel?.crd ? kindForCRDResource : kind; - const canAddHealthChecks = useAccessReview({ + const [canAddHealthChecks, canAddHealthChecksLoading] = useAccessReview({ group: resourceModel?.apiGroup, resource: resourceModel?.plural, namespace, @@ -53,7 +57,7 @@ export const useHealthChecksAlert = (element: GraphElement): DetailsResourceAler (container) => container.readinessProbe || container.livenessProbe || container.startupProbe, ); - const showAlert = !healthCheckAdded && canAddHealthChecks; + const showAlert = !healthCheckAdded && canAddHealthChecks && !canAddHealthChecksLoading; const addHealthChecksLink = `/k8s/ns/${namespace}/${resourceKind}/${name}/containers/${containersName[0]}/health-checks`; @@ -87,9 +91,9 @@ export const useResourceQuotaAlert = (element: GraphElement): DetailsResourceAle const name = resource?.metadata?.name; const namespace = resource?.metadata?.namespace; - const canUseAlertAction = useAccessReview({ - group: DeploymentModel?.apiGroup, - resource: DeploymentModel?.plural, + const [canUseAlertAction, canUseAlertActionLoading] = useAccessReview({ + group: DeploymentModel.apiGroup, + resource: DeploymentModel.plural, namespace, name, verb: 'patch', @@ -97,32 +101,31 @@ export const useResourceQuotaAlert = (element: GraphElement): DetailsResourceAle if (!resource || referenceForModel(DeploymentModel) !== referenceFor(resource)) return null; - const statusConditions = resource.status?.conditions ?? []; - const replicaFailure = !_.isEmpty(statusConditions) - ? _.find(statusConditions, (condition) => condition.type === 'ReplicaFailure') - : undefined; + const statusConditions: K8sResourceCondition[] = resource.status?.conditions ?? []; + const replicaFailure = statusConditions.find((condition) => condition.type === 'ReplicaFailure'); const replicaFailureMsg: string = replicaFailure?.message ?? ''; const resourceQuotaRequested = replicaFailureMsg.split(':')?.[3] ?? ''; - const resourceQuotaType = resourceQuotaRequested.includes('limits') - ? 'Limits' - : resourceQuotaRequested.includes('pods') - ? 'Pods' - : ''; - const showAlert = resourceQuotaType && canUseAlertAction; - const alertAction = - resourceQuotaType === 'Limits' - ? DeploymentActionFactory.EditResourceLimits(DeploymentModel, resource) - : CommonActionFactory.ModifyCount(DeploymentModel, resource); - - const alertActionCta = alertAction.cta as () => void; - - const alertActionLink = ( + + let alertAction: Action; + if (resourceQuotaRequested.includes('limits')) { + alertAction = DeploymentActionFactory.EditResourceLimits(DeploymentModel, resource); + } else if (resourceQuotaRequested.includes('pods')) { + alertAction = CommonActionFactory.ModifyCount(DeploymentModel, resource); + } + + const showAlertActionLink = alertAction && canUseAlertAction && !canUseAlertActionLoading; + + const alertActionCta = alertAction?.cta as () => void; + + const alertActionLink = showAlertActionLink ? ( alertActionCta()}> {alertAction.label as string} + ) : ( + undefined ); - return showAlert + return replicaFailure ? { title: t('topology~Resource Quotas'), dismissible: true,