Skip to content
Merged
4 changes: 2 additions & 2 deletions cypress/support/routeSelectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ export const routeSelectors = {
AUTH_TOKENS: "**/identity/auth/tokens",
CONN_SCHEMA_OPENSTACK: "**/coriolis/**/providers/openstack/schemas/16",
ENDPOINTS: "**/coriolis/**/endpoints",
DEPLOYMENTS: "**/coriolis/**/deployments",
DEPLOYMENTS: "**/coriolis/**/deployments*",
PROJECTS: "**/identity/auth/projects",
PROVIDERS: "**/coriolis/**/providers",
REGIONS: "**/coriolis/**/regions",
TRANSFERS: "**/coriolis/**/transfers",
TRANSFERS: "**/coriolis/**/transfers*",
ROLE_ASSIGNMENTS: "**/identity/role_assignments*",
SCHEDULES: "**/coriolis/**/transfers/**/schedules",
SECRETS: "**/barbican/**/secrets",
Expand Down
130 changes: 130 additions & 0 deletions src/components/modules/TransferModule/Executions/Executions.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,136 @@ describe("Executions", () => {
expect(TestUtils.select("Executions__LoadingWrapper")).toBeTruthy();
});

it("triggers onLoadOlderExecutions when left arrow is clicked at the first execution", async () => {
const onLoadOlderExecutions = jest.fn();
render(
<Executions
{...defaultProps}
hasOlderExecutions
onLoadOlderExecutions={onLoadOlderExecutions}
/>,
);
const previousArrow = TestUtils.selectAll(
"Arrow__Wrapper",
TestUtils.select("Timeline__Wrapper")!,
)[0];
await act(async () => {
previousArrow.click();
});
expect(onLoadOlderExecutions).toHaveBeenCalled();
});

it("does not trigger onLoadOlderExecutions when not at the first execution", async () => {
const onLoadOlderExecutions = jest.fn();
render(
<Executions
{...defaultProps}
executions={[
EXECUTION_MOCK,
{ ...EXECUTION_MOCK, id: "second-id", status: "COMPLETED" },
]}
hasOlderExecutions
onLoadOlderExecutions={onLoadOlderExecutions}
/>,
);
const nextArrow = TestUtils.selectAll(
"Arrow__Wrapper",
TestUtils.select("Timeline__Wrapper")!,
)[1];
await act(async () => {
nextArrow.click();
});
const previousArrow = TestUtils.selectAll(
"Arrow__Wrapper",
TestUtils.select("Timeline__Wrapper")!,
)[0];
await act(async () => {
previousArrow.click();
});
expect(onLoadOlderExecutions).not.toHaveBeenCalled();
});

it("triggers onLoadOlderExecutions when the oldest bullet is clicked", async () => {
const onLoadOlderExecutions = jest.fn();
render(
<Executions
{...defaultProps}
hasOlderExecutions
onLoadOlderExecutions={onLoadOlderExecutions}
/>,
);
const timelineItem = TestUtils.select("Timeline__Item-");
expect(timelineItem).toBeTruthy();
await act(async () => {
timelineItem!.click();
});
expect(onLoadOlderExecutions).toHaveBeenCalled();
});

it("navigates to newest of prepended older executions after previous arrow click", async () => {
const onLoadOlderExecutions = jest.fn();
const { rerender } = render(
<Executions
{...defaultProps}
hasOlderExecutions
onLoadOlderExecutions={onLoadOlderExecutions}
/>,
);
const previousArrow = TestUtils.selectAll(
"Arrow__Wrapper",
TestUtils.select("Timeline__Wrapper")!,
)[0];
await act(async () => {
previousArrow.click();
});
expect(onLoadOlderExecutions).toHaveBeenCalled();

const olderExec = { ...EXECUTION_MOCK, id: "older-id", number: 0 };
rerender(
<Executions
{...defaultProps}
executions={[olderExec, EXECUTION_MOCK]}
hasOlderExecutions
onLoadOlderExecutions={onLoadOlderExecutions}
/>,
);
expect(defaultProps.onChange).toHaveBeenLastCalledWith(olderExec.id);
});

it("keeps the clicked execution selected when older executions are prepended", async () => {
const onLoadOlderExecutions = jest.fn();
const newerExec = { ...EXECUTION_MOCK, id: "newer-id", number: 2 };
const { rerender } = render(
<Executions
{...defaultProps}
executions={[EXECUTION_MOCK, newerExec]}
hasOlderExecutions
onLoadOlderExecutions={onLoadOlderExecutions}
/>,
);
const oldestBullet = TestUtils.select("Timeline__Item-");
expect(oldestBullet).toBeTruthy();
await act(async () => {
oldestBullet!.click();
});
expect(onLoadOlderExecutions).toHaveBeenCalled();
expect(defaultProps.onChange).toHaveBeenLastCalledWith(EXECUTION_MOCK.id);

const olderExecs = [
{ ...EXECUTION_MOCK, id: "older-id-1", number: -1 },
{ ...EXECUTION_MOCK, id: "older-id-2", number: 0 },
];
rerender(
<Executions
{...defaultProps}
executions={[...olderExecs, EXECUTION_MOCK, newerExec]}
hasOlderExecutions
onLoadOlderExecutions={onLoadOlderExecutions}
/>,
);
expect(defaultProps.onChange).toHaveBeenLastCalledWith(EXECUTION_MOCK.id);
});

it("deletes execution", async () => {
const deleteExecution = jest.fn();
render(
Expand Down
39 changes: 39 additions & 0 deletions src/components/modules/TransferModule/Executions/Executions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,11 @@ type Props = {
executions: Execution[];
executionsTasks: ExecutionTasks[];
loading: boolean;
paginationLoading?: boolean;
tasksLoading: boolean;
instancesDetails: Instance[];
hasOlderExecutions?: boolean;
onLoadOlderExecutions?: () => void;
onChange: (executionId: string) => void;
onCancelExecutionClick: (
execution: Execution | null,
Expand All @@ -106,6 +109,8 @@ class Executions extends React.Component<Props, State> {
selectedExecution: null,
};

selectPreviousOnOlderLoad = false;

UNSAFE_componentWillMount() {
this.setSelectedExecution(this.props);
}
Expand Down Expand Up @@ -146,6 +151,26 @@ class Executions extends React.Component<Props, State> {
}
}
}

const prevFirstId =
this.props.executions.length > 0
? this.props.executions[0].id
: undefined;
const newFirstId =
props.executions.length > 0 ? props.executions[0].id : undefined;
if (
props.executions.length > this.props.executions.length &&
prevFirstId !== undefined &&
prevFirstId !== newFirstId &&
!(lastExecution && lastExecution.status === "RUNNING")
) {
if (this.selectPreviousOnOlderLoad) {
const newCount =
props.executions.length - this.props.executions.length;
selectExecution = props.executions[newCount - 1];
}
this.selectPreviousOnOlderLoad = false;
}
}
const currentSelectedExecution = this.state.selectedExecution;
if (!currentSelectedExecution) {
Expand Down Expand Up @@ -213,6 +238,10 @@ class Executions extends React.Component<Props, State> {
);

if (selectedIndex === 0) {
if (this.props.hasOlderExecutions && this.props.onLoadOlderExecutions) {
this.selectPreviousOnOlderLoad = true;
this.props.onLoadOlderExecutions();
}
return;
}

Expand Down Expand Up @@ -251,6 +280,14 @@ class Executions extends React.Component<Props, State> {
this.setState({ selectedExecution: item }, () => {
this.handleChange(item);
});

if (
item.id === this.props.executions[0]?.id &&
this.props.hasOlderExecutions &&
this.props.onLoadOlderExecutions
) {
this.props.onLoadOlderExecutions();
}
}

handleCancelExecutionClick() {
Expand Down Expand Up @@ -283,6 +320,8 @@ class Executions extends React.Component<Props, State> {
<Timeline
items={this.props.executions}
selectedItem={this.state.selectedExecution}
hasOlderItems={this.props.hasOlderExecutions}
loading={this.props.paginationLoading}
onPreviousClick={() => {
this.handlePreviousExecutionClick();
}}
Expand Down
27 changes: 21 additions & 6 deletions src/components/modules/TransferModule/Timeline/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ const Wrapper = styled.div<any>`
opacity: 1;
}
`;
const LoadingIcon = styled(StatusIcon)`
position: absolute;
top: 0;
left: -19px;
`;
const MainLine = styled.div<any>`
width: 100%;
padding-top: 7px;
Expand Down Expand Up @@ -84,6 +89,8 @@ const ItemLabel = styled.div<any>`
type Props = {
items?: Execution[] | null;
selectedItem?: Execution | null;
hasOlderItems?: boolean;
loading?: boolean;
onPreviousClick?: () => void;
onNextClick?: () => void;
onItemClick?: (item: Execution) => void;
Expand Down Expand Up @@ -214,12 +221,20 @@ class Timeline extends React.Component<Props> {
this.wrapperRef = w;
}}
>
<ArrowStyled
orientation="left"
forceShow={!this.props.items || !this.props.items.length}
primary={Boolean(this.props.items && this.props.items.length)}
onClick={this.props.onPreviousClick}
/>
{this.props.loading ? (
<LoadingIcon status="RUNNING" />
) : (
<ArrowStyled
orientation="left"
forceShow={
!this.props.items ||
!this.props.items.length ||
this.props.hasOlderItems
}
primary={Boolean(this.props.items && this.props.items.length)}
onClick={this.props.onPreviousClick}
/>
)}
{this.renderMainLine()}
{this.renderItems()}
<ArrowStyled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,13 @@ type Props = {
detailsLoading: boolean;
executions: Execution[];
executionsLoading: boolean;
executionsPaginationLoading?: boolean;
executionsTasksLoading: boolean;
executionsTasks: ExecutionTasks[];
minionPools: MinionPool[];
storageBackends: StorageBackend[];
hasOlderExecutions?: boolean;
onLoadOlderExecutions?: () => void;
onExecutionChange: (executionId: string) => void;
onCancelExecutionClick: (
execution: Execution | null,
Expand Down Expand Up @@ -209,9 +212,12 @@ class TransferDetailsContent extends React.Component<Props, State> {
onDeleteExecutionClick={this.props.onDeleteExecutionClick}
onExecuteClick={this.props.onExecuteClick}
loading={this.props.executionsLoading || this.props.detailsLoading}
paginationLoading={this.props.executionsPaginationLoading}
onChange={this.props.onExecutionChange}
tasksLoading={this.props.executionsTasksLoading}
instancesDetails={this.props.instancesDetails}
hasOlderExecutions={this.props.hasOlderExecutions}
onLoadOlderExecutions={this.props.onLoadOlderExecutions}
/>
);
}
Expand Down
16 changes: 16 additions & 0 deletions src/components/smart/DeploymentsPage/DeploymentsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ class DeploymentsPage extends React.Component<Props, State> {
componentDidMount() {
document.title = "Coriolis Deployments";

deploymentStore.resetDeploymentPagination();

projectStore.getProjects();
endpointStore.getEndpoints({ showLoading: true });
userStore.getAllUsers({
Expand Down Expand Up @@ -112,6 +114,7 @@ class DeploymentsPage extends React.Component<Props, State> {
}

handleProjectChange() {
deploymentStore.resetDeploymentPagination();
endpointStore.getEndpoints({ showLoading: true });
deploymentStore.getDeployments({ showLoading: true });
}
Expand Down Expand Up @@ -297,6 +300,19 @@ class DeploymentsPage extends React.Component<Props, State> {
this.setState({ selectedDeployments });
}}
dropdownActions={BulkActions}
apiPagination={{
currentPage: deploymentStore.deploymentsPage,
hasNextPage: deploymentStore.deploymentsHasNextPage,
itemsPerPage: deploymentStore.deploymentsItemsPerPage,
onPageChange: page => {
deploymentStore.setDeploymentsPage(page);
},
onItemsPerPageChange: e => {
deploymentStore.setDeploymentsItemsPerPage(
parseInt(e.target.value, 10),
);
},
}}
renderItemComponent={options => (
<TransferListItem
{...options}
Expand Down
Loading
Loading