Skip to content

Commit

Permalink
Merge pull request #2587 from mturley/dw-initial-cypress-tests
Browse files Browse the repository at this point in the history
Add missing cypress tests for initial Distributed Workloads views, fix minor architecture issues, factor out components for sections of the Workload Status tab
  • Loading branch information
openshift-merge-bot[bot] authored Mar 14, 2024
2 parents 7b8a60b + ef81fd9 commit b1673c0
Show file tree
Hide file tree
Showing 18 changed files with 387 additions and 157 deletions.
21 changes: 21 additions & 0 deletions frontend/src/__mocks__/mockPrometheusDWQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { PrometheusQueryResponse } from '~/types';

type MockPrometheusDWQueryType = {
result?: { value: [number, string] }[];
};

export const mockPrometheusDWQuery = ({
result,
}: MockPrometheusDWQueryType): {
code?: number;
response: PrometheusQueryResponse;
} => ({
code: 200,
response: {
status: 'success',
data: {
resultType: 'vector',
result: result ?? [],
},
},
});
26 changes: 26 additions & 0 deletions frontend/src/__mocks__/mockPrometheusDWQueryRange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { PrometheusQueryRangeResponse, PrometheusQueryRangeResponseDataResult } from '~/types';

type MockPrometheusDWQueryRangeType = {
result?: PrometheusQueryRangeResponseDataResult[];
};

export const mockPrometheusDWQueryRange = ({
result,
}: MockPrometheusDWQueryRangeType): {
code?: number;
response: PrometheusQueryRangeResponse;
} => ({
code: 200,
response: {
status: 'success',
data: {
resultType: 'matrix',
result: result ?? [
{
metric: {},
values: [],
},
],
},
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ import { mockStatus } from '~/__mocks__/mockStatus';
import { mockComponents } from '~/__mocks__/mockComponents';
import { explorePage } from '~/__tests__/cypress/cypress/pages/explore';
import { globalDistributedWorkloads } from '~/__tests__/cypress/cypress/pages/distributedWorkloads';
import { mockK8sResourceList } from '~/__mocks__/mockK8sResourceList';
import { mockProjectK8sResource } from '~/__mocks__/mockProjectK8sResource';
import { mockPrometheusDWQuery } from '~/__mocks__/mockPrometheusDWQuery';
import { mockPrometheusDWQueryRange } from '~/__mocks__/mockPrometheusDWQueryRange';

type HandlersProps = {
isKueueInstalled?: boolean;
disableDistributedWorkloads?: boolean;
hasProjects?: boolean;
};

const initIntercepts = ({
isKueueInstalled = true,
disableDistributedWorkloads = false,
hasProjects = true,
}: HandlersProps) => {
cy.intercept(
'/api/dsc/status',
Expand All @@ -28,8 +34,34 @@ const initIntercepts = ({
}),
);
cy.intercept('/api/components', mockComponents());

// TODO mturley other intercepts here
cy.intercept(
{
method: 'GET',
pathname: '/api/k8s/apis/project.openshift.io/v1/projects',
},
mockK8sResourceList(
hasProjects
? [
mockProjectK8sResource({ k8sName: 'test-project', displayName: 'Test Project' }),
mockProjectK8sResource({ k8sName: 'test-project-2', displayName: 'Test Project 2' }),
]
: [],
),
);
cy.intercept(
{
method: 'POST',
pathname: '/api/prometheus/query',
},
mockPrometheusDWQuery({ result: [] }),
);
cy.intercept(
{
method: 'POST',
pathname: '/api/prometheus/queryRange',
},
mockPrometheusDWQueryRange({ result: [] }),
);
};

describe('Workload Metrics', () => {
Expand Down Expand Up @@ -71,5 +103,51 @@ describe('Workload Metrics', () => {
globalDistributedWorkloads.findHeaderText().should('exist');
});

// TODO mturley other tests here for tab navigation, empty states, etc.
it('Defaults to Project Metrics tab and automatically selects a project', () => {
initIntercepts({});
globalDistributedWorkloads.visit();

cy.url().should('include', '/projectMetrics/test-project');
// TODO mturley replace this with real identifiable text on the loaded tab when it is completed
cy.findByText('TODO tab content for project metrics -- these are placeholders').should('exist');
});

it('Tabs navigate to corresponding routes and render their contents', () => {
initIntercepts({});
globalDistributedWorkloads.visit();

cy.findByLabelText('Workload status tab').click();
cy.url().should('include', '/workloadStatus/test-project');
cy.findByText('Status overview').should('exist');

cy.findByLabelText('Project metrics tab').click();
cy.url().should('include', '/projectMetrics/test-project');
// TODO mturley replace this with real identifiable text on the loaded tab when it is completed
cy.findByText('TODO tab content for project metrics -- these are placeholders').should('exist');
});

it('Changing the project and navigating between tabs or to the root of the page retains the new project', () => {
initIntercepts({});
globalDistributedWorkloads.visit();
cy.url().should('include', '/projectMetrics/test-project');

globalDistributedWorkloads.selectProjectByName('Test Project 2');
cy.url().should('include', '/projectMetrics/test-project-2');

cy.findByLabelText('Workload status tab').click();
cy.url().should('include', '/workloadStatus/test-project-2');

cy.findByLabelText('Project metrics tab').click();
cy.url().should('include', '/projectMetrics/test-project-2');

globalDistributedWorkloads.navigate();
cy.url().should('include', '/projectMetrics/test-project-2');
});

it('Should show an empty state if there are no projects', () => {
initIntercepts({ hasProjects: false });
globalDistributedWorkloads.visit();

cy.findByText('No data science projects').should('exist');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ class GlobalDistributedWorkloads {
return cy.findByText('Monitor the metrics of your active resources.');
}

findProjectSelect() {
return cy.findByTestId('project-selector-dropdown');
}

selectProjectByName(name: string) {
this.findProjectSelect().findDropdownItem(name).click();
}

private wait() {
this.findHeaderText();
cy.testA11y();
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/api/prometheus/distributedWorkloads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export const useDWWorkloadTrendMetrics = (
defaultResponsePredicate,
namespace || '',
'/api/prometheus/queryRange',
{ initialPromisePurity: true },
),
jobsInadmissibleTrend: useQueryRangeResourceData(
!!queries,
Expand All @@ -169,6 +170,7 @@ export const useDWWorkloadTrendMetrics = (
defaultResponsePredicate,
namespace || '',
'/api/prometheus/queryRange',
{ initialPromisePurity: true },
),
jobsPendingTrend: useQueryRangeResourceData(
!!queries,
Expand All @@ -178,6 +180,7 @@ export const useDWWorkloadTrendMetrics = (
defaultResponsePredicate,
namespace || '',
'/api/prometheus/queryRange',
{ initialPromisePurity: true },
),
};

Expand Down
4 changes: 3 additions & 1 deletion frontend/src/api/prometheus/usePrometheusQueryRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import axios from 'axios';

import useFetchState, {
FetchOptions,
FetchState,
FetchStateCallbackPromise,
NotReadyError,
Expand All @@ -26,6 +27,7 @@ const usePrometheusQueryRange = <T = PrometheusQueryRangeResultValue>(
step: number,
responsePredicate: ResponsePredicate<T>,
namespace: string,
fetchOptions?: Partial<FetchOptions>,
): [...FetchState<T[]>, boolean] => {
const pendingRef = React.useRef(active);
const fetchData = React.useCallback<FetchStateCallbackPromise<T[]>>(() => {
Expand Down Expand Up @@ -59,7 +61,7 @@ const usePrometheusQueryRange = <T = PrometheusQueryRangeResultValue>(
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [active, fetchData]);

return [...useFetchState<T[]>(fetchData, []), pendingRef.current];
return [...useFetchState<T[]>(fetchData, [], fetchOptions), pendingRef.current];
};

export const defaultResponsePredicate: ResponsePredicate = (data) => data.result?.[0]?.values || [];
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/api/prometheus/useQueryRangeResourceData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { TimeframeStep, TimeframeTimeRange } from '~/pages/modelServing/screens/const';
import { PrometheusQueryRangeResultValue } from '~/types';
import useRestructureContextResourceData from '~/utilities/useRestructureContextResourceData';
import { FetchOptions } from '~/utilities/useFetchState';
import { TimeframeTitle } from '~/pages/modelServing/screens/types';
import usePrometheusQueryRange, { ResponsePredicate } from './usePrometheusQueryRange';

Expand All @@ -14,6 +15,7 @@ const useQueryRangeResourceData = <T = PrometheusQueryRangeResultValue>(
responsePredicate: ResponsePredicate<T>,
namespace: string,
apiPath = '/api/prometheus/serving',
fetchOptions?: Partial<FetchOptions>,
): ReturnType<typeof useRestructureContextResourceData<T>> =>
useRestructureContextResourceData<T>(
usePrometheusQueryRange<T>(
Expand All @@ -25,6 +27,7 @@ const useQueryRangeResourceData = <T = PrometheusQueryRangeResultValue>(
TimeframeStep[timeframe],
responsePredicate,
namespace,
fetchOptions,
),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
POLL_INTERVAL,
} from '~/utilities/const';
import { SupportedArea, conditionalArea } from '~/concepts/areas';
import useSyncPreferredProject from '~/concepts/projects/useSyncPreferredProject';
import { ProjectsContext, byName } from '~/concepts/projects/ProjectsContext';
import { useMakeFetchObject } from '~/utilities/useMakeFetchObject';
import {
DWProjectMetrics,
Expand Down Expand Up @@ -81,6 +83,10 @@ export const DistributedWorkloadsContextProvider =
SupportedArea.DISTRIBUTED_WORKLOADS,
true,
)(({ children, namespace }) => {
const { projects } = React.useContext(ProjectsContext);
const project = projects.find(byName(namespace)) ?? null;
useSyncPreferredProject(project);

const [refreshRate, setRefreshRate] = React.useState(POLL_INTERVAL);
const [lastUpdateTime, setLastUpdateTime] = React.useState<number>(Date.now());

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/concepts/distributedWorkloads/useWorkloads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const useWorkloads = (namespace?: string, refreshRate = 0): FetchState<WorkloadK
return listWorkloads(namespace);
}, [dwEnabled, namespace]),
[],
{ refreshRate },
{ refreshRate, initialPromisePurity: true },
);
};

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import * as React from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { Tabs, Tab, TabTitleText, PageSection } from '@patternfly/react-core';
import {
Tabs,
Tab,
TabTitleText,
PageSection,
TabContent,
TabContentBody,
} from '@patternfly/react-core';
import {
DistributedWorkloadsTabId,
useDistributedWorkloadsTabs,
} from './useDistributedWorkloadsTabs';
import DistributedWorkloadsToolbar from './DistributedWorkloadsToolbar';

type GlobalDistributedWorkloadsTabsProps = {
activeTabId: DistributedWorkloadsTabId;
Expand Down Expand Up @@ -45,7 +53,25 @@ const GlobalDistributedWorkloadsTabs: React.FC<GlobalDistributedWorkloadsTabsPro
))}
</Tabs>
</PageSection>
{activeTab && <activeTab.ContentComponent tabConfig={activeTab} />}
{activeTab ? <DistributedWorkloadsToolbar tabConfig={activeTab} /> : null}
<PageSection isFilled>
{tabs
.filter((tab) => tab.isAvailable)
.map((tab) => {
const isActiveTab = tab.id === activeTab?.id;
return (
<TabContent
id={`${tab.id}-tab-content`}
activeKey={tab.id}
eventKey={tab.id}
key={tab.id}
hidden={!isActiveTab}
>
<TabContentBody>{isActiveTab ? <tab.ContentComponent /> : null}</TabContentBody>
</TabContent>
);
})}
</PageSection>
</>
);
};
Expand Down
Loading

0 comments on commit b1673c0

Please sign in to comment.