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
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
.filterWrap {
min-width: 280px;
max-width: 360px;

@include ltemd {
max-width: unset;
min-width: unset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,38 @@
}
}

.testStatusCell {
text-align: center !important;
}

.testStatusIcon {
align-items: center;
display: inline-flex;
height: 20px;
justify-content: center;
vertical-align: middle;
width: 20px;

svg {
display: block;
fill: currentcolor;
height: 18px;
width: 18px;
}
}

.testStatusInProgress {
color: #d97706;
}

.testStatusSuccess {
color: #137d60;
}

.testStatusFailed {
color: #ea1900;
}

.level-1 {
color: #555 !important;
}
Expand All @@ -144,6 +176,6 @@
}

.table {
min-width: 1080px;
min-width: 1180px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import { render, screen } from '@testing-library/react'
import { SubmissionsTable } from './SubmissionsTable'

jest.mock('~/libs/ui', () => ({
IconOutline: {
ClockIcon: (): JSX.Element => <svg data-testid='clock-icon' />,
XCircleIcon: (): JSX.Element => <svg data-testid='x-circle-icon' />,
},
IconSolid: {
CheckCircleIcon: (): JSX.Element => <svg data-testid='check-circle-icon' />,
},
LoadingSpinner: () => <div>Loading</div>,
}), {
virtual: true,
Expand All @@ -21,6 +28,28 @@ jest.mock('../../utils', () => ({
getSubmissionInitialScore: (submission: { review?: Array<{ initialScore?: number }> }) => (
submission.review?.[0]?.initialScore ?? 0
),
getSubmissionTestProgress: (
submission: {
reviewSummation?: Array<{
metadata?: {
testProcess?: 'provisional' | 'system'
testProgress?: number
testStatus?: 'FAILED' | 'IN PROGRESS' | 'SUCCESS'
}
}>
},
) => {
const metadata = submission.reviewSummation?.[0]?.metadata
const progress = metadata?.testProgress

return {
process: metadata?.testProcess,
progressPercent: typeof progress === 'number'
? `${Math.round(progress * 100)}%`
: undefined,
status: metadata?.testStatus,
}
},
}))
jest.mock('../../assets/icons/IconDownloadArtifacts.svg', () => ({
ReactComponent: () => <svg aria-hidden='true' />,
Expand Down Expand Up @@ -142,4 +171,85 @@ describe('SubmissionsTable', () => {
expect(screen.getByRole('button', { name: 'Download submission artifacts' }))
.toBeTruthy()
})

it('renders marathon test progress columns when enabled', () => {
render(
<SubmissionsTable
canDownloadSubmissions
challengeId='challenge-123'
onDownloadSubmission={jest.fn()}
onOpenArtifacts={jest.fn()}
onSort={jest.fn()}
showMarathonMatchTestProgress
sortBy='createdAt'
sortOrder='desc'
submissions={[
{
challengeId: 'challenge-123',
createdBy: 'member-1',
id: 'submission-1',
reviewSummation: [
{
metadata: {
testProcess: 'system',
testProgress: 0.75,
testStatus: 'IN PROGRESS',
},
},
],
type: 'SUBMISSION',
},
{
challengeId: 'challenge-123',
createdBy: 'member-2',
id: 'submission-2',
reviewSummation: [
{
metadata: {
testProcess: 'provisional',
testProgress: 1,
testStatus: 'SUCCESS',
},
},
],
type: 'SUBMISSION',
},
{
challengeId: 'challenge-123',
createdBy: 'member-3',
id: 'submission-3',
reviewSummation: [
{
metadata: {
testProcess: 'system',
testProgress: 0.2,
testStatus: 'FAILED',
},
},
],
type: 'SUBMISSION',
},
]}
/>,
)

expect(screen.getByText('Current tests process'))
.toBeTruthy()
expect(screen.getByText('Test status'))
.toBeTruthy()
expect(screen.getByText('Test progress'))
.toBeTruthy()
expect(screen.getByText('75%'))
.toBeTruthy()
expect(screen.getByText('100%'))
.toBeTruthy()
expect(screen.getByText('20%'))
.toBeTruthy()
expect(screen.getByRole('img', { name: 'Test status: IN PROGRESS' }))
.toBeTruthy()
expect(screen.getByRole('img', { name: 'Test status: SUCCESS' }))
.toBeTruthy()
expect(screen.getByRole('img', { name: 'Test status: FAILED' }))
.toBeTruthy()
})
})
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import {
FC,
MouseEvent,
ReactElement,
} from 'react'
import classNames from 'classnames'

import { LoadingSpinner } from '~/libs/ui'
import {
IconOutline,
IconSolid,
LoadingSpinner,
} from '~/libs/ui'

import { COMMUNITY_APP_URL, REVIEW_APP_URL } from '../../constants'
import { ReactComponent as IconDownloadArtifacts } from '../../assets/icons/IconDownloadArtifacts.svg'
Expand All @@ -15,6 +20,7 @@ import {
getRatingLevel,
getSubmissionFinalScore,
getSubmissionInitialScore,
getSubmissionTestProgress,
} from '../../utils'

import styles from './SubmissionsTable.module.scss'
Expand Down Expand Up @@ -45,6 +51,7 @@ interface SubmissionsTableProps {
onSort: (fieldName: SubmissionSortBy) => void
sortBy: SubmissionSortBy
sortOrder: SortOrder
showMarathonMatchTestProgress?: boolean
submissionDownloadLoading?: Record<string, boolean>
submissions: Submission[]
}
Expand All @@ -70,6 +77,21 @@ const BASE_COLUMNS: ColumnConfig[] = [
label: 'Initial / Final Score',
sortable: true,
},
]

const MARATHON_MATCH_TEST_COLUMNS: ColumnConfig[] = [
{
label: 'Current tests process',
},
{
label: 'Test status',
},
{
label: 'Test progress',
},
]

const TRAILING_COLUMNS: ColumnConfig[] = [
{
fieldName: 'submissionId',
label: 'Submission ID (UUID)',
Expand All @@ -80,6 +102,25 @@ const BASE_COLUMNS: ColumnConfig[] = [
},
]

/**
* Builds the table columns for standard and marathon submission rows.
* @param showMarathonMatchTestProgress Whether marathon test-progress metadata should be displayed.
* @returns Column config used by the table header and empty/loading colspans.
* Used by `SubmissionsTable` to insert marathon-only progress columns before actions.
*/
function getColumns(showMarathonMatchTestProgress: boolean): ColumnConfig[] {
return showMarathonMatchTestProgress
? [
...BASE_COLUMNS,
...MARATHON_MATCH_TEST_COLUMNS,
...TRAILING_COLUMNS,
]
: [
...BASE_COLUMNS,
...TRAILING_COLUMNS,
]
}

function getCreatedAt(submission: Submission): string {
return submission.createdAt
|| submission.created
Expand Down Expand Up @@ -139,10 +180,59 @@ function getEmailDisplay(
: '-'
}

/**
* Renders the marathon test status icon for a submission row.
* @param status Normalized test status from review summation metadata.
* @returns Status icon element or `undefined` when no status is available.
* Used by `SubmissionsTable` to keep empty status cells blank.
*/
function renderTestStatusIcon(status: string | undefined): ReactElement | undefined {
if (status === 'IN PROGRESS') {
return (
<span
aria-label='Test status: IN PROGRESS'
className={classNames(styles.testStatusIcon, styles.testStatusInProgress)}
role='img'
title='IN PROGRESS'
>
<IconOutline.ClockIcon aria-hidden='true' />
</span>
)
}

if (status === 'SUCCESS') {
return (
<span
aria-label='Test status: SUCCESS'
className={classNames(styles.testStatusIcon, styles.testStatusSuccess)}
role='img'
title='SUCCESS'
>
<IconSolid.CheckCircleIcon aria-hidden='true' />
</span>
)
}

if (status === 'FAILED') {
return (
<span
aria-label='Test status: FAILED'
className={classNames(styles.testStatusIcon, styles.testStatusFailed)}
role='img'
title='FAILED'
>
<IconOutline.XCircleIcon aria-hidden='true' />
</span>
)
}

return undefined
}

export const SubmissionsTable: FC<SubmissionsTableProps> = (
props: SubmissionsTableProps,
) => {
const columns = BASE_COLUMNS
const columns = getColumns(!!props.showMarathonMatchTestProgress)

function handleSortButtonClick(event: MouseEvent<HTMLButtonElement>): void {
const sortBy = event.currentTarget.dataset.fieldName as SubmissionSortBy | undefined
Expand Down Expand Up @@ -225,6 +315,9 @@ export const SubmissionsTable: FC<SubmissionsTableProps> = (
const submissionDate = formatDateTime(getCreatedAt(submission))
const initialScore = formatScore(getSubmissionInitialScore(submission))
const finalScore = formatScore(getSubmissionFinalScore(submission))
const testProgress = props.showMarathonMatchTestProgress
? getSubmissionTestProgress(submission)
: undefined
const reviewTab = submission.type === 'CHECKPOINT_SUBMISSION'
? 'checkpoint-submission'
: 'submission'
Expand Down Expand Up @@ -276,6 +369,24 @@ export const SubmissionsTable: FC<SubmissionsTableProps> = (
</a>
</td>

{props.showMarathonMatchTestProgress
? (
<>
<td>
<span>{testProgress?.process || ''}</span>
</td>

<td className={styles.testStatusCell}>
{renderTestStatusIcon(testProgress?.status)}
</td>

<td>
<span>{testProgress?.progressPercent || ''}</span>
</td>
</>
)
: undefined}

<td>
<span title={submission.id}>{submission.id}</span>
</td>
Expand Down
Loading
Loading