Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion .github/workflows/trivy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
uses: actions/checkout@v4

- name: Run Trivy scanner in repo mode
uses: aquasecurity/trivy-action@0.33.1
uses: aquasecurity/trivy-action@0.35.0
with:
scan-type: "fs"
ignore-unfixed: true
Expand Down
127 changes: 127 additions & 0 deletions __tests__/shared/components/challenge-detail/Header/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import React from 'react';
import Renderer from 'react-test-renderer/shallow';

import Header from 'components/challenge-detail/Header';

function collectText(node) {
if (typeof node === 'string') {
return [node];
}

if (!React.isValidElement(node)) {
return [];
}

return React.Children.toArray(node.props.children)
.reduce((acc, child) => acc.concat(collectText(child)), []);
}

function renderHeader(challengeOverrides = {}) {
const renderer = new Renderer();
renderer.render(
<Header
challenge={{
drPoints: null,
events: [],
funChallenge: false,
id: 'challenge-id',
legacy: {},
metadata: [],
name: 'Challenge title',
numOfCheckpointSubmissions: 0,
numOfRegistrants: 0,
numOfSubmissions: 0,
phases: [
{
isOpen: true,
name: 'Registration',
scheduledEndDate: '2030-01-02T00:00:00.000Z',
scheduledStartDate: '2030-01-01T00:00:00.000Z',
},
],
pointPrizes: [],
prizeSets: [
{
type: 'placement',
prizes: [{ type: 'USD', value: 1000 }],
},
],
reliabilityBonus: 0,
skills: [],
status: 'ACTIVE',
tags: [],
track: 'Development',
type: 'Challenge',
...challengeOverrides,
}}
challengeTypesMap={{}}
challengesUrl="/challenges"
checkpoints={{}}
hasFirstPlacement={false}
hasRecommendedChallenges={false}
hasRegistered={false}
hasThriveArticles={false}
isLoggedIn
mySubmissions={[]}
numWinners={1}
onSelectorClicked={jest.fn()}
onSort={jest.fn()}
onToggleDeadlines={jest.fn()}
openForRegistrationChallenges={{}}
registerForChallenge={jest.fn()}
registering={false}
selectedView="details"
setChallengeListingFilter={jest.fn()}
showDeadlineDetail={false}
submissionEnded={false}
unregisterFromChallenge={jest.fn()}
unregistering={false}
viewAsTable={false}
/>,
);

return renderer.getRenderOutput();
}

describe('Challenge detail header actions', () => {
test('hides registration and submission actions for classic task challenges', () => {
const output = renderHeader({
type: 'Task',
});

expect(collectText(output)).not.toContain('Register');
expect(collectText(output)).not.toContain('Unregister');
expect(collectText(output)).not.toContain('Submit a solution');
});

test('hides registration and submission actions for work-app task payloads', () => {
const output = renderHeader({
task: {
isTask: true,
},
});

expect(collectText(output)).not.toContain('Register');
expect(collectText(output)).not.toContain('Unregister');
expect(collectText(output)).not.toContain('Submit a solution');
});

test('hides registration and submission actions for pure v5 task payloads', () => {
const output = renderHeader({
legacy: {
pureV5Task: true,
},
});

expect(collectText(output)).not.toContain('Register');
expect(collectText(output)).not.toContain('Unregister');
expect(collectText(output)).not.toContain('Submit a solution');
});

test('shows registration and submission actions for non-task challenges', () => {
const output = renderHeader();

expect(collectText(output)).toContain('Register');
expect(collectText(output)).toContain('Submit a solution');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { getDisplayedScores } from '../../../../../../src/shared/components/challenge-detail/MySubmissions/SubmissionsList';

describe('getDisplayedScores', () => {
test('shows final scores when a system review already produced one before review completes', () => {
expect(getDisplayedScores(
{
finalScore: 100,
initialScore: 100,
provisionalScore: 0,
},
{
phases: [
{
isOpen: true,
name: 'Registration',
scheduledStartDate: '2030-01-01T00:00:00.000Z',
},
],
},
)).toEqual({
finalScore: 100,
provisionalScore: 100,
});
});

test('shows final scores once the review phase is complete', () => {
expect(getDisplayedScores(
{
finalScore: 100,
initialScore: 95,
provisionalScore: 0,
},
{
phases: [
{
isOpen: false,
name: 'Review',
scheduledStartDate: '2000-01-01T00:00:00.000Z',
},
],
},
)).toEqual({
finalScore: 100,
provisionalScore: 95,
});
});
Comment on lines +1 to +46

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

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

This test file duplicates the getDisplayedScores coverage already added in __tests__/shared/utils/challenge-detail/my-submission-scores.test.js (and the utils test is more complete). To avoid redundant test maintenance and slower runs, consider removing this duplicate suite or consolidating the unique assertions into a single location.

Suggested change
import { getDisplayedScores } from '../../../../../../src/shared/components/challenge-detail/MySubmissions/SubmissionsList';
describe('getDisplayedScores', () => {
test('shows final scores when a system review already produced one before review completes', () => {
expect(getDisplayedScores(
{
finalScore: 100,
initialScore: 100,
provisionalScore: 0,
},
{
phases: [
{
isOpen: true,
name: 'Registration',
scheduledStartDate: '2030-01-01T00:00:00.000Z',
},
],
},
)).toEqual({
finalScore: 100,
provisionalScore: 100,
});
});
test('shows final scores once the review phase is complete', () => {
expect(getDisplayedScores(
{
finalScore: 100,
initialScore: 95,
provisionalScore: 0,
},
{
phases: [
{
isOpen: false,
name: 'Review',
scheduledStartDate: '2000-01-01T00:00:00.000Z',
},
],
},
)).toEqual({
finalScore: 100,
provisionalScore: 95,
});
});
describe('getDisplayedScores', () => {
test.todo(
'coverage for getDisplayedScores lives in __tests__/shared/utils/challenge-detail/my-submission-scores.test.js',
);

Copilot uses AI. Check for mistakes.
});
27 changes: 23 additions & 4 deletions __tests__/shared/containers/challenge-detail/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,38 @@ import { getDisplayWinners, isWiproRegistrationBlocked } from 'containers/challe

describe('Challenge detail Wipro registration guard', () => {
test('blocks Wipro members when challenge disallows Wipro participation', () => {
expect(isWiproRegistrationBlocked('member@wipro.com', false)).toBe(true);
expect(isWiproRegistrationBlocked('member@wipro.com', {
wiproAllowed: false,
type: 'Challenge',
})).toBe(true);
});

test('does not block Wipro members when challenge allows Wipro participation', () => {
expect(isWiproRegistrationBlocked('member@wipro.com', true)).toBe(false);
expect(isWiproRegistrationBlocked('member@wipro.com', {
wiproAllowed: true,
type: 'Challenge',
})).toBe(false);
});

test('does not block non-Wipro members when challenge disallows Wipro participation', () => {
expect(isWiproRegistrationBlocked('member@example.com', false)).toBe(false);
expect(isWiproRegistrationBlocked('member@example.com', {
wiproAllowed: false,
type: 'Challenge',
})).toBe(false);
});

test('matches Wipro domain case-insensitively and ignores surrounding spaces', () => {
expect(isWiproRegistrationBlocked(' MEMBER@WIPRO.COM ', false)).toBe(true);
expect(isWiproRegistrationBlocked(' MEMBER@WIPRO.COM ', {
wiproAllowed: false,
type: 'Challenge',
})).toBe(true);
});

test('does not block Wipro members for Topgear Task even when the flag is false', () => {
expect(isWiproRegistrationBlocked('member@wipro.com', {
wiproAllowed: false,
type: 'Topgear Task',
})).toBe(false);
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* eslint-env jest */
import {
hasVisibleMmFinalResults,
isReviewPhaseComplete,
shouldShowFinalMmResults,
} from '../../../../src/shared/utils/challenge-detail/mm-final-results';

describe('mm-final-results utilities', () => {
const activeReviewChallenge = {
phases: [
{
isOpen: true,
name: 'Review',
scheduledStartDate: '2030-01-01T00:00:00.000Z',
},
],
};

it('detects when the review phase has completed', () => {
expect(isReviewPhaseComplete({
phases: [
{
isOpen: false,
name: 'Review',
scheduledStartDate: '2000-01-01T00:00:00.000Z',
},
],
})).toBe(true);
});

it('keeps final Marathon Match results hidden while review is active and no final score exists', () => {
expect(shouldShowFinalMmResults(activeReviewChallenge, [
{
finalRank: null,
submissions: [
{
finalScore: null,
},
],
},
])).toBe(false);
});

it('shows final Marathon Match results as soon as a final score is available', () => {
const mmSubmissions = [
{
finalRank: 1,
submissions: [
{
finalScore: 100,
},
],
},
];

expect(hasVisibleMmFinalResults(mmSubmissions)).toBe(true);
expect(shouldShowFinalMmResults(activeReviewChallenge, mmSubmissions)).toBe(true);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* eslint-env jest */
import { getDisplayedScores } from '../../../../src/shared/components/challenge-detail/MySubmissions/SubmissionsList';

describe('getDisplayedScores', () => {
it('shows final scores when a system review has already produced one', () => {
expect(getDisplayedScores(
{
finalScore: 100,
initialScore: 100,
provisionalScore: 0,
},
{
phases: [
{
isOpen: true,
name: 'Registration',
scheduledStartDate: '2030-01-01T00:00:00.000Z',
},
],
},
)).toEqual({
finalScore: 100,
provisionalScore: 100,
});
});

it('hides final scores while review is active and no final result exists yet', () => {
expect(getDisplayedScores(
{
finalScore: null,
initialScore: 95,
provisionalScore: 0,
},
{
phases: [
{
isOpen: true,
name: 'Registration',
scheduledStartDate: '2030-01-01T00:00:00.000Z',
},
],
},
)).toEqual({
finalScore: null,
provisionalScore: 95,
});
});

it('shows final scores once the review phase is complete', () => {
expect(getDisplayedScores(
{
finalScore: 100,
initialScore: 95,
provisionalScore: 0,
},
{
phases: [
{
isOpen: false,
name: 'Review',
scheduledStartDate: '2000-01-01T00:00:00.000Z',
},
],
},
)).toEqual({
finalScore: 100,
provisionalScore: 95,
});
});
});
Loading
Loading