diff --git a/client/src/templates/Challenges/components/completion-modal.test.tsx b/client/src/templates/Challenges/components/completion-modal.test.tsx
index 3f17a48f380..1469a053c27 100644
--- a/client/src/templates/Challenges/components/completion-modal.test.tsx
+++ b/client/src/templates/Challenges/components/completion-modal.test.tsx
@@ -1,15 +1,48 @@
import React from 'react';
+import { runSaga } from 'redux-saga';
import { render } from '../../../../utils/test-utils';
import { getCompletedPercentage } from '../../../utils/get-completion-percentage';
import { fireConfetti } from '../../../utils/fire-confetti';
import { createStore } from '../../../redux/create-store';
+import { executeChallengeSaga } from '../redux/execute-challenge-saga';
+import {
+ challengeDataSelector,
+ challengeMetaSelector,
+ challengeTestsSelector,
+ isBuildEnabledSelector,
+ isBlockNewlyCompletedSelector
+} from '../redux/selectors';
+import { buildChallenge, getTestRunner } from '../utils/build';
import CompletionModal from './completion-modal';
jest.mock('../../../analytics');
jest.mock('../../../utils/fire-confetti');
jest.mock('../../../components/ProgressBar');
+jest.mock('../redux/selectors');
+jest.mock('../utils/build');
const mockFireConfetti = fireConfetti as jest.Mock;
+const mockTestRunner = jest.fn().mockReturnValue({ pass: true });
+const mockBuildEnabledSelector = isBuildEnabledSelector as jest.Mock;
+const mockChallengeTestsSelector = challengeTestsSelector as jest.Mock;
+const mockChallengeMetaSelector = challengeMetaSelector as jest.Mock;
+const mockChallengeDataSelector = challengeDataSelector as jest.Mock;
+const mockIsBlockNewlyCompletedSelector =
+ isBlockNewlyCompletedSelector as jest.Mock;
+const mockBuildChallenge = buildChallenge as jest.Mock;
+const mockGetTestRunner = getTestRunner as jest.Mock;
+mockBuildEnabledSelector.mockReturnValue(true);
+mockChallengeTestsSelector.mockReturnValue([
+ { text: 'Test 1', testString: 'mock test code' }
+]);
+mockChallengeMetaSelector.mockReturnValue({
+ challengeType: 'mock_challenge_type'
+});
+mockChallengeDataSelector.mockReturnValue({
+ challengeFiles: ['mock_challenge_files']
+});
+mockBuildChallenge.mockReturnValue({ challengeType: 'mock_challenge_type' });
+mockGetTestRunner.mockReturnValue(mockTestRunner);
const completedChallengesIds = ['1', '3', '5'],
currentBlockIds = ['1', '3', '5', '7'],
@@ -21,7 +54,41 @@ describe('', () => {
beforeEach(() => {
mockFireConfetti.mockClear();
});
- test('should fire if certification project has been completed', () => {
+ test('should fire when block is completed', async () => {
+ const payload = { showCompletionModal: true };
+ const store = createStore({
+ challenge: {
+ modal: { completion: true },
+ challengeMeta: {
+ id: 'bd7158d8c442eddfaeb5bd18',
+ certification: 'responsive-web-design' // Make sure the certification matches
+ }
+ }
+ });
+ mockIsBlockNewlyCompletedSelector.mockReturnValue(true);
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ await runSaga(store, executeChallengeSaga, { payload }).done;
+ expect(mockFireConfetti).toHaveBeenCalledTimes(1);
+ });
+ test('should not fire when block is not completed', async () => {
+ const payload = { showCompletionModal: true };
+ const store = createStore({
+ challenge: {
+ modal: { completion: true },
+ challengeMeta: {
+ id: 'bd7158d8c442eddfaeb5bd18',
+ certification: 'responsive-web-design' // Make sure the certification matches
+ }
+ }
+ });
+ mockIsBlockNewlyCompletedSelector.mockReturnValue(false);
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-ignore
+ await runSaga(store, executeChallengeSaga, { payload }).done;
+ expect(mockFireConfetti).toHaveBeenCalledTimes(0);
+ });
+ test('should not fire if certification project has been completed', () => {
const store = createStore({
challenge: {
modal: { completion: true },
@@ -31,12 +98,10 @@ describe('', () => {
}
}
});
-
render(, store);
- expect(mockFireConfetti).toHaveBeenCalledTimes(1);
+ expect(mockFireConfetti).toHaveBeenCalledTimes(0);
});
-
test('should NOT fire if the challenge is not a project', () => {
const store = createStore({
challenge: {
diff --git a/client/src/templates/Challenges/components/completion-modal.tsx b/client/src/templates/Challenges/components/completion-modal.tsx
index c131df70666..40ea4aa6c34 100644
--- a/client/src/templates/Challenges/components/completion-modal.tsx
+++ b/client/src/templates/Challenges/components/completion-modal.tsx
@@ -28,8 +28,6 @@ import ProgressBar from '../../../components/ProgressBar';
import GreenPass from '../../../assets/icons/green-pass';
import './completion-modal.css';
-import { fireConfetti } from '../../../utils/fire-confetti';
-import { certsToProjects } from '../../../../config/cert-and-project-map';
const mapStateToProps = createSelector(
challengeFilesSelector,
@@ -81,11 +79,6 @@ interface CompletionModalState {
downloadURL: null | string;
}
-const isCertificationProject = (id: string) =>
- Object.values(certsToProjects).some(cert =>
- cert.some(project => project.id === id)
- );
-
class CompletionModal extends Component<
CompletionModalsProps,
CompletionModalState
@@ -157,26 +150,18 @@ class CompletionModal extends Component<
const {
close,
isOpen,
- id,
isSignedIn,
isSubmitting,
message,
t,
dashedName,
- submitChallenge,
- completedChallengesIds
+ submitChallenge
} = this.props;
if (isOpen) {
executeGA({ event: 'pageview', pagePath: '/completion-modal' });
- if (
- isCertificationProject(id) &&
- !completedChallengesIds.includes(id) &&
- !isSubmitting
- ) {
- fireConfetti();
- }
}
+
return (
test.pass && !test.err);
+ const isBlockCompleted = yield select(isBlockNewlyCompletedSelector);
if (challengeComplete) {
playTone('tests-completed');
+ if (isBlockCompleted) {
+ fireConfetti();
+ }
} else {
playTone('tests-failed');
if (challengeMeta.certification === 'responsive-web-design') {
@@ -141,6 +147,7 @@ function* executeChallengeSaga({ payload }) {
);
}
}
+
if (challengeComplete && payload?.showCompletionModal) {
yield put(openModal('completion'));
}