Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 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
49 changes: 49 additions & 0 deletions __tests__/shared/components/Dashboard/Challenges/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import renderer from 'react-test-renderer';

import ChallengesFeed from 'components/Dashboard/Challenges';

function renderChallenge(challenge) {
return renderer.create((
<ChallengesFeed
challenges={[challenge]}
loading={false}
/>
)).toJSON();
}

describe('Dashboard Challenges feed', () => {
test('shows Fun label for fun challenges', () => {
const view = renderChallenge({
id: 'challenge-fun',
name: 'Challenge one',
funChallenge: true,
prizeSets: [
{
type: 'PLACEMENT',
prizes: [{ type: 'USD', value: 0 }],
},
],
});

const text = JSON.stringify(view);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[⚠️ maintainability]
Using JSON.stringify to convert the component's output to a string for assertions may lead to brittle tests. Consider using a more robust method to inspect the rendered output, such as querying the rendered component tree for specific elements or text.

expect(text).toContain('Fun');
expect(text).not.toContain('$0');
});

test('shows calculated prize amount for regular challenges', () => {
const view = renderChallenge({
id: 'challenge-regular',
name: 'Challenge two',
funChallenge: false,
prizeSets: [
{
type: 'PLACEMENT',
prizes: [{ type: 'USD', value: 44 }],
},
],
});

expect(JSON.stringify(view)).toContain('$44');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[⚠️ maintainability]
Similar to the previous test, using JSON.stringify for assertions can make tests fragile. It's better to directly query the rendered component for specific content to ensure the test is more reliable and less prone to false positives.

});
});
30 changes: 30 additions & 0 deletions __tests__/shared/components/challenge-detail/Header/Prizes.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import Renderer from 'react-test-renderer/shallow';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[⚠️ maintainability]
Consider using @testing-library/react for testing React components instead of react-test-renderer/shallow. It provides more robust testing capabilities and better simulates user interactions.


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

describe('Challenge detail header prizes', () => {
test('renders leaderboard-scoring label for fun challenges', () => {
const renderer = new Renderer();
renderer.render((
<Prizes
isFunChallenge
pointPrizes={[]}
prizes={[{ type: 'USD', value: 1 }]}
/>
));
expect(renderer.getRenderOutput()).toMatchSnapshot();
});

test('renders normal placement prizes when fun challenge is false', () => {
const renderer = new Renderer();
renderer.render((
<Prizes
isFunChallenge={false}
pointPrizes={[]}
prizes={[{ type: 'USD', value: 1000 }]}
/>
));
expect(renderer.getRenderOutput()).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Challenge detail header prizes renders leaderboard-scoring label for fun challenges 1`] = `
<div
className="src-shared-components-challenge-detail-Header-___style__prizes-container___3iYoT"
>
<p
className="src-shared-components-challenge-detail-Header-___style__fun-challenge-prize___3T3KZ"
>
No individual prize - leaderboard scoring
</p>
</div>
`;

exports[`Challenge detail header prizes renders normal placement prizes when fun challenge is false 1`] = `
<div
className="src-shared-components-challenge-detail-Header-___style__prizes-container___3iYoT"
>
<div
className="src-shared-components-challenge-detail-Header-___style__prize-fill___2MJPB"
>
<div
aria-label="1st prize is $$1,000"
className="src-shared-components-challenge-detail-Header-___style__prize-card___24y2m"
id="rank1"
tabIndex={0}
>
<p
aria-hidden="true"
className="src-shared-components-challenge-detail-Header-___style__prize-rank___3hC_J"
>
1
<span
className="src-shared-components-challenge-detail-Header-___style__rank-ordinal___1K53t"
>
st
</span>
</p>
</div>
<p
aria-hidden="true"
className="src-shared-components-challenge-detail-Header-___style__prize-money___3raXa"
>
$1,000
</p>
</div>
</div>
`;
137 changes: 122 additions & 15 deletions __tests__/shared/components/challenge-listing/Listing/Bucket.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import _ from 'lodash';
import Renderer from 'react-test-renderer/shallow';
// import TU from 'react-dom/test-utils';
import Bucket from 'components/challenge-listing/Listing/Bucket';
import ChallengeCard from 'components/challenge-listing/ChallengeCard';
import reduxStoreFactory from 'redux-mock-store';
import { Provider } from 'react-redux';
import { StaticRouter } from 'react-router-dom';
Expand All @@ -15,6 +16,23 @@ const expand = jest.fn();
const loadMore = jest.fn();
const setFilterState = jest.fn();
const setSort = jest.fn();
const setSearchText = jest.fn();

const challengeTypes = [
{
name: 'Challenge',
abbreviation: 'CH',
}, {
name: 'First2Finish',
abbreviation: 'F2F',
}, {
name: 'Task',
abbreviation: 'TSK',
}, {
name: 'Marathon Match',
abbreviation: 'MM',
},
];

const mockDatas = [{
bucket: 'all',
Expand Down Expand Up @@ -47,21 +65,7 @@ const mockDatas = [{
totalPrize: 1800,
users: {},
}],
challengeTypes: [
{
name: 'Challenge',
abbreviation: 'CH',
}, {
name: 'First2Finish',
abbreviation: 'F2F',
}, {
name: 'Task',
abbreviation: 'TSK',
}, {
name: 'Marathon Match',
abbreviation: 'MM',
},
],
challengeTypes,
loading: false,
loadMore,
setFilterState,
Expand All @@ -84,6 +88,109 @@ test('Matches shallow shapshot', () => {
});
});

function countElementsByType(element, type) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[⚠️ performance]
The countElementsByType function uses recursion to count elements, which could lead to a stack overflow if the component tree is too deep. Consider using an iterative approach to avoid potential performance issues.

if (!React.isValidElement(element)) {
return 0;
}

let count = element.type === type ? 1 : 0;
React.Children.forEach(element.props.children, (child) => {
count += countElementsByType(child, type);
});

return count;
}

test('Shows assigned task when memberId and userId are matching strings', () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[⚠️ correctness]
The test case for showing assigned tasks when memberId and userId match is correct, but it would be more robust to also test for cases where memberId is null or undefined to ensure the component handles these edge cases gracefully.

const renderer = new Renderer();
renderer.render((
<Bucket
activeBucket="openForRegistration"
auth={{ user: { roles: [] } }}
bucket="openForRegistration"
challenges={[
{
id: 'task-1',
name: 'Assigned task',
status: 'ACTIVE',
type: { name: 'Task' },
tags: [],
prizes: [],
task: {
isTask: true,
isAssigned: true,
memberId: 'c0ffee-123',
},
},
]}
challengeTypes={challengeTypes}
challengesUrl="/challenges"
expand={_.noop}
expanded
expandTag={_.noop}
expandedTags={[]}
expanding={false}
filterState={{}}
isLoggedIn
needLoad={false}
prizeMode="money-usd"
selectChallengeDetailsTab={_.noop}
setFilterState={setFilterState}
setSearchText={setSearchText}
setSort={setSort}
sort=""
userId="c0ffee-123"
/>
));

expect(countElementsByType(renderer.getRenderOutput(), ChallengeCard)).toBe(1);
});

test('Hides assigned task when memberId and userId do not match', () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[⚠️ correctness]
The test case for hiding assigned tasks when memberId and userId do not match is correct, but consider adding a test for when memberId is null or undefined to ensure the component behaves as expected in these scenarios.

const renderer = new Renderer();
renderer.render((
<Bucket
activeBucket="openForRegistration"
auth={{ user: { roles: [] } }}
bucket="openForRegistration"
challenges={[
{
id: 'task-2',
name: 'Assigned task',
status: 'ACTIVE',
type: { name: 'Task' },
tags: [],
prizes: [],
task: {
isTask: true,
isAssigned: true,
memberId: 'owner-user',
},
},
]}
challengeTypes={challengeTypes}
challengesUrl="/challenges"
expand={_.noop}
expanded
expandTag={_.noop}
expandedTags={[]}
expanding={false}
filterState={{}}
isLoggedIn
needLoad={false}
prizeMode="money-usd"
selectChallengeDetailsTab={_.noop}
setFilterState={setFilterState}
setSearchText={setSearchText}
setSort={setSort}
sort=""
userId="different-user"
/>
));

expect(countElementsByType(renderer.getRenderOutput(), ChallengeCard)).toBe(0);
});

// class Wrapper extends React.Component {
// componentDidMount() {}

Expand Down
50 changes: 50 additions & 0 deletions __tests__/shared/containers/challenge-detail/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { getDisplayWinners, isWiproRegistrationBlocked } from 'containers/challenge-detail';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[⚠️ correctness]
Consider adding tests for edge cases, such as when the email is null or an empty string, to ensure isWiproRegistrationBlocked handles these scenarios correctly.


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

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

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

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

describe('Challenge detail winners filter', () => {
test('includes legacy winners with "Final" type for non-task challenges', () => {
const winners = getDisplayWinners({
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[⚠️ correctness]
The test for getDisplayWinners assumes that the type field is case-sensitive. If the type field is intended to be case-insensitive, additional tests should be added to verify this behavior.

type: 'Challenge',
winners: [
{ handle: 'legacyFinal', type: 'Final' },
{ handle: 'newFinal', type: 'final' },
{ handle: 'provisionalWinner', type: 'provisional' },
],
});

expect(winners).toEqual([
{ handle: 'legacyFinal', type: 'Final' },
{ handle: 'newFinal', type: 'final' },
]);
});

test('does not filter winners for task challenges', () => {
const winners = getDisplayWinners({
type: 'Task',
winners: [
{ handle: 'taskWinner', type: 'provisional' },
],
});

expect(winners).toEqual([
{ handle: 'taskWinner', type: 'provisional' },
]);
});
});
Loading
Loading