Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/libs/ajax/teaspoons/teaspoons-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export interface PipelineRun {
timeSubmitted: string;
timeCompleted?: string;
quotaConsumed?: number;
outputExpirationDate?: string;
}

export interface GetPipelineRunsResponse {
Expand Down
2 changes: 2 additions & 0 deletions src/pages/scientificServices/pipelines/utils/mock-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,7 @@ export function mockPipelineRun(status: PipelineRunStatus): PipelineRun {
timeSubmitted: '2023-10-01T00:00:00Z',
timeCompleted: status === 'SUCCEEDED' || status === 'FAILED' ? new Date().toISOString() : undefined,
quotaConsumed: status === 'SUCCEEDED' ? 500 : undefined,
outputExpirationDate:
status === 'SUCCEEDED' ? new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString() : undefined, // job outputs expire in 14 days
};
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { screen, waitFor } from '@testing-library/react';
import { formatDatetime } from '@terra-ui-packages/core-utils';
import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Teaspoons, TeaspoonsContract } from 'src/libs/ajax/teaspoons/Teaspoons';
Expand Down Expand Up @@ -174,6 +175,7 @@ describe('job history table', () => {
...mockPipelineRun('SUCCEEDED'),
timeSubmitted: fifteenDaysAgo,
timeCompleted: fifteenDaysAgo,
outputExpirationDate: new Date(Date.now()).toISOString(),
};
const pipelineRuns = [pipelineRun];

Expand All @@ -195,9 +197,21 @@ describe('job history table', () => {
expect(screen.getAllByText(pipelineRun.jobId)).toHaveLength(2);
});

const viewOutputsButton = screen.getByText('View Outputs');
const table = screen.getByRole('table');
const rows = within(table).getAllByRole('row');
const dataRow = within(rows[1]).getAllByRole('cell');

const viewOutputsButton = within(dataRow[7]).getByText('View Outputs');
expect(viewOutputsButton).toBeInTheDocument();
expect(viewOutputsButton).toBeDisabled();

const user = userEvent.setup();
await user.hover(viewOutputsButton);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so cool to test the hover text!


const tooltip = await within(dataRow[7]).findByText(
'The outputs for this job have been deleted. Outputs are available for 14 days after job completion.'
);
expect(tooltip).toBeInTheDocument();
});

it('shows View Error button for FAILED jobs', async () => {
Expand Down Expand Up @@ -462,6 +476,41 @@ describe('job history table', () => {
const pipelineRun = {
...mockPipelineRun('SUCCEEDED'),
timeCompleted: '2023-10-15T14:00:00Z',
outputExpirationDate: '2023-10-29T14:00:00Z',
};
const pipelineRuns = [pipelineRun];

const mockPipelineRunResponse = {
pageToken: null,
results: pipelineRuns,
totalResults: 1,
};

asMockedFn(Teaspoons).mockReturnValue(
partial<TeaspoonsContract>({
getAllPipelineRuns: jest.fn().mockReturnValue(mockPipelineRunResponse),
})
);

render(<JobHistory />);

await waitFor(() => {
expect(screen.getAllByText(pipelineRun.jobId)).toHaveLength(2);
});

const table = screen.getByRole('table');
const rows = within(table).getAllByRole('row');
const cells = within(rows[1]).getAllByRole('cell');
expect(cells[5]).toHaveTextContent('Oct 29, 2023');
});

it('displays the deletion date in red for job outputs expiring within 3 days', async () => {
const twoDaysFromNow = new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toISOString();
const twelveDaysAgo = new Date(Date.now() - 12 * 24 * 60 * 60 * 1000).toISOString();
const pipelineRun = {
...mockPipelineRun('SUCCEEDED'),
timeCompleted: twelveDaysAgo,
outputExpirationDate: twoDaysFromNow,
};
const pipelineRuns = [pipelineRun];

Expand All @@ -483,7 +532,15 @@ describe('job history table', () => {
expect(screen.getAllByText(pipelineRun.jobId)).toHaveLength(2);
});

expect(screen.getByText('Oct 15, 2023')).toBeInTheDocument();
const table = screen.getByRole('table');
const rows = within(table).getAllByRole('row');
const cells = within(rows[1]).getAllByRole('cell');
expect(cells[5]).toHaveTextContent(formatDatetime(twoDaysFromNow));

// assert the date text is in red
const deletionDateDiv = within(cells[5]).getByText(formatDatetime(twoDaysFromNow)).parentElement!;
const style = window.getComputedStyle(deletionDateDiv);
expect(style.color).toBe('rgb(219, 50, 20)');
});
});
});
22 changes: 7 additions & 15 deletions src/pages/scientificServices/pipelines/views/JobHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -284,13 +284,11 @@ const CompletedCell = ({ pipelineRun }: CellProps): ReactNode => {
};

const DataDeletionDateCell = ({ pipelineRun }: CellProps): ReactNode => {
if (!pipelineRun.timeCompleted || !(pipelineRun.status === 'SUCCEEDED')) {
if (!pipelineRun.outputExpirationDate || !(pipelineRun.status === 'SUCCEEDED')) {
return <div>N/A</div>;
}

const completionDate = new Date(pipelineRun?.timeCompleted);
const deletionDate = new Date(completionDate);
deletionDate.setDate(deletionDate.getDate() + TEASPOONS_FILE_OUTPUT_TTL_DAYS);
const deletionDate = new Date(pipelineRun?.outputExpirationDate);

const today = new Date();
const threeDaysFromNow = new Date();
Expand Down Expand Up @@ -339,9 +337,12 @@ const ActionCell = ({ pipelineRun }: CellProps): ReactNode => {
return <ViewErrorModal pipelineRun={pipelineRun} onDismiss={errorModal.close} />;
});

const jobOutputsDeleted =
// `!!` converts the expression to a boolean
const jobOutputsDeleted = !!(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having trouble understanding why the addition of !! here didn't change the pipelineRun.status === 'SUCCEEDED' logic. doesn't this mean that jobOutputsDeleted will be True for failed/running jobs? does it not matter because jobOutputsDeleted is only used further down if pipelineRun.status === 'SUCCEEDED' (line 348)? this is still confusing to me to read, but I might be misunderstanding this logic

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The !! here converts that expression into a strict boolean value other wise the data type of jobOutputsDeleted will be boolean | " " | undefined. I can add that as a comment as it can be definitely confusing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh goodness, sorry, that's just me not understanding what !! means. thanks for the explanation - I don't think you need a comment here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries. I did add it as a comment because javascript is confusing and I too as code author did get confused initially.

pipelineRun.status === 'SUCCEEDED' &&
(hoursElapsedSinceCompletion(pipelineRun) ?? -1) > 24 * TEASPOONS_FILE_OUTPUT_TTL_DAYS; // 24 hours * 14 days
pipelineRun.outputExpirationDate &&
new Date() >= new Date(pipelineRun.outputExpirationDate)
);

return (
<div>
Expand Down Expand Up @@ -505,12 +506,3 @@ const hoursElapsedSinceSubmission = (pipelineRun: PipelineRun): number => {
const currentTime = new Date();
return (currentTime.getTime() - submittedTime.getTime()) / (1000 * 60 * 60);
};

const hoursElapsedSinceCompletion = (pipelineRun: PipelineRun): number | undefined => {
if (!pipelineRun.timeCompleted) {
return undefined;
}
const completedTime = new Date(pipelineRun.timeCompleted);
const currentTime = new Date();
return (currentTime.getTime() - completedTime.getTime()) / (1000 * 60 * 60);
};
Loading