From a9276181ed940ac71430c44cdadfaff924cae177 Mon Sep 17 00:00:00 2001 From: Yoan VALLET <ext.sopra.yvallet@grandlyon.com> Date: Mon, 8 Feb 2021 15:46:40 +0100 Subject: [PATCH] fix: end of duel --- .../Challenge/ChallengeCardOnGoing.tsx | 1 + .../Challenge/challengeCardOnGoing.scss | 1 + src/components/Duel/DuelResultModal.tsx | 20 +- src/db/duelEntity.json | 2 +- src/locales/fr.json | 4 +- src/services/challenge.service.spec.ts | 250 +++++++++++++----- src/services/challenge.service.ts | 24 +- 7 files changed, 216 insertions(+), 86 deletions(-) diff --git a/src/components/Challenge/ChallengeCardOnGoing.tsx b/src/components/Challenge/ChallengeCardOnGoing.tsx index 9a182d5ba..368d7cb21 100644 --- a/src/components/Challenge/ChallengeCardOnGoing.tsx +++ b/src/components/Challenge/ChallengeCardOnGoing.tsx @@ -233,6 +233,7 @@ const ChallengeCardOnGoing: React.FC<ChallengeCardOnGoingProps> = ({ <span>{t('challenge.card.ongoing.duelDone')}</span> </div> <StyledIcon className="duelLocked" icon={challengeIcon} size={60} /> + <div className="notifChallenge">1</div> </div> ) : ( <div className={'smallCard duelCard'}> diff --git a/src/components/Challenge/challengeCardOnGoing.scss b/src/components/Challenge/challengeCardOnGoing.scss index a9ed15eb3..2b54c4989 100644 --- a/src/components/Challenge/challengeCardOnGoing.scss +++ b/src/components/Challenge/challengeCardOnGoing.scss @@ -114,6 +114,7 @@ border: solid 1px rgba(97, 240, 242, 0.5); align-items: center; justify-content: space-between; + position: relative; &.active { background: $grey-linear-gradient-background; } diff --git a/src/components/Duel/DuelResultModal.tsx b/src/components/Duel/DuelResultModal.tsx index 53113cef4..7a6fbc42f 100644 --- a/src/components/Duel/DuelResultModal.tsx +++ b/src/components/Duel/DuelResultModal.tsx @@ -26,7 +26,7 @@ const DuelResultModal: React.FC<DuelResultModalProps> = ({ const { t } = useI18n() const [winIcon, setWinIcon] = useState<string>(defaultIcon) const [lossIcon, setLossIcon] = useState<string>(defaultIcon) - const [emptyIcon, setEmptyIcon] = useState<string>(defaultIcon) + const [, setEmptyIcon] = useState<string>(defaultIcon) const result: string | number = formatNumberValues( Math.abs(userChallenge.duel.threshold - userChallenge.duel.userConsumption) ) @@ -49,15 +49,11 @@ const DuelResultModal: React.FC<DuelResultModalProps> = ({ <div className="em-content"> <StyledIcon className="imgResult" - icon={empty ? emptyIcon : win ? winIcon : lossIcon} + icon={win ? winIcon : lossIcon} size={208} /> <div className="text-28-normal-uppercase title"> - {empty - ? t('duel.empty.title') - : win - ? t('duel.sucess.title') - : t('duel.lost.title')} + {win ? t('duel.sucess.title') : t('duel.lost.title')} </div> <div className="text-18-normal"> {empty @@ -67,9 +63,7 @@ const DuelResultModal: React.FC<DuelResultModalProps> = ({ : t('duel.lost.message1') + result + ' €'} </div> <div className="text-18-normal"> - {empty - ? t('duel.empty.message2') + userChallenge.title - : win + {win ? t('duel.sucess.message2') + userChallenge.title : t('duel.lost.message2') + userChallenge.title + '...'} </div> @@ -81,11 +75,7 @@ const DuelResultModal: React.FC<DuelResultModalProps> = ({ label: 'text-16-normal', }} > - {empty - ? t('duel.empty.button') - : win - ? t('duel.sucess.button') - : t('duel.lost.button')} + {win ? t('duel.sucess.button') : t('duel.lost.button')} </MuiButton> </div> </div> diff --git a/src/db/duelEntity.json b/src/db/duelEntity.json index f446ee274..19a418a0d 100644 --- a/src/db/duelEntity.json +++ b/src/db/duelEntity.json @@ -2,7 +2,7 @@ { "_id": "DUEL001", "title": "Nicolas Hublot", - "description": "Je parie un ours polaire que vous ne pouvez pas consommer moins que #CONSUMPTION € en 1 semaine", + "description": "Je vous défie de consommer moins que #CONSUMPTION € en 1 semaine", "duration": { "days": 7 } }, { diff --git a/src/locales/fr.json b/src/locales/fr.json index f24f7ccdd..e8d0dbdec 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -371,8 +371,8 @@ "lost": { "title": "Presque !", "message1": "Vous avez dépassé de ", - "message2": "et presque manqué le badge ", - "button": "Dommage..." + "message2": "et manqué le badge ", + "button": "Zut alors" }, "empty": { "title": "Félicitations !", diff --git a/src/services/challenge.service.spec.ts b/src/services/challenge.service.spec.ts index f658048b4..010de953e 100644 --- a/src/services/challenge.service.spec.ts +++ b/src/services/challenge.service.spec.ts @@ -6,6 +6,7 @@ import { UserChallenge, QuizEntity, ExplorationEntity, + Dataload, } from 'models' import { UserChallengeState, @@ -46,6 +47,7 @@ import { UserExplorationUnlocked, } from '../../test/__mocks__/explorationData.mock' import { fluidStatusData } from '../../test/__mocks__/fluidStatusData.mock' +import { cloneDeep } from 'lodash' const mockGetExplorationEntityById = jest.fn() const mockParseExplorationEntityToUserExploration = jest.fn() @@ -152,7 +154,6 @@ describe('Challenge service', () => { skip: 0, } it('should return all user challenge', async () => { - mockClient mockClient.query.mockResolvedValueOnce(mockQueryResult) mockClient.query.mockResolvedValueOnce(mockQueryResultExploration) mockClient.query.mockResolvedValueOnce(mockQueryResultQuiz) @@ -170,14 +171,14 @@ describe('Challenge service', () => { expect(result).toEqual(userChallengeData) }) it('should return all user challenge plus create one missing', async () => { - const mockQueryResult: QueryResult<ChallengeEntity[], DuelEntity[]> = { + const _mockQueryResult: QueryResult<ChallengeEntity[], DuelEntity[]> = { data: allChallengeEntityData, included: allDuelEntity, bookmark: '', next: false, skip: 0, } - mockClient.query.mockResolvedValueOnce(mockQueryResult) + mockClient.query.mockResolvedValueOnce(_mockQueryResult) mockClient.query.mockResolvedValueOnce(mockQueryResultQuiz) mockClient.query.mockResolvedValueOnce(mockQueryResultExploration) mockClient.query.mockResolvedValueOnce(mockQueryResultUser) @@ -326,6 +327,15 @@ describe('Challenge service', () => { ) expect(result).toEqual(userChallengeData[0]) }) + it('should throw error when failed to save the updateUserChallenge', () => { + mockClient.save.mockRejectedValueOnce(new Error()) + expect( + challengeService.updateUserChallenge( + userChallengeData[0], + UserChallengeUpdateFlag.DUEL_START + ) + ).rejects.toEqual(new Error()) + }) }) describe('getUserChallengeDataload method', () => { @@ -361,71 +371,193 @@ describe('Challenge service', () => { }) describe('isChallengeDone method', () => { - const userChallenge = { - ...userChallengeData[0], - state: UserChallengeState.DUEL, - duel: { - ...userChallengeData[0].duel, - state: UserDuelState.ONGOING, - duration: Duration.fromObject({ day: 3 }), - threshold: 200, - userConsumption: 199, - startDate: DateTime.local() - .setZone('utc', { - keepLocalTime: true, - }) - .minus({ days: 5 }), - }, - } - const dataloads = graphData.actualData - dataloads[2].value = 50 - it('should return isDone = true and isWin = true', async () => { - const result = await challengeService.isChallengeDone( - userChallenge, - dataloads - ) - expect(result).toEqual({ isDone: true, isWin: true, isEmpty: false }) - }) - it('should return isDone = true and isWin = false when threshold < consumption ', async () => { - const updatedUserChallenge = { - ...userChallenge, + describe('case date + lags + 1 is reached', () => { + const userChallenge = { + ...userChallengeData[0], + state: UserChallengeState.DUEL, duel: { - ...userChallenge.duel, - threshold: 100, - userConsumption: 200, + ...userChallengeData[0].duel, + state: UserDuelState.ONGOING, + duration: Duration.fromObject({ day: 3 }), + threshold: 200, + userConsumption: 199, + startDate: DateTime.local() + .setZone('utc', { + keepLocalTime: true, + }) + .minus({ days: 5 }), }, } - const result = await challengeService.isChallengeDone( - updatedUserChallenge, - dataloads - ) - expect(result).toEqual({ isDone: true, isWin: false, isEmpty: false }) - }) - it('should return isDone = false and isWin = true with last dataload = -1', async () => { - const updatedDataloads = [...dataloads] - updatedDataloads[2].value = -1 - updatedDataloads[2].valueDetail = null - const result = await challengeService.isChallengeDone( - userChallenge, - updatedDataloads - ) - expect(result).toEqual({ isDone: true, isWin: true, isEmpty: true }) + const dataloads: Dataload[] = [ + { + date: DateTime.local() + .setZone('utc', { + keepLocalTime: true, + }) + .minus({ days: 5 }), + value: 69.18029999999999, + valueDetail: [ + 45.127739999999996, + 0.9048899999999999, + 23.147669999999998, + ], + }, + { + date: DateTime.local() + .setZone('utc', { + keepLocalTime: true, + }) + .minus({ days: 4 }), + value: 61.65554999999999, + valueDetail: [ + 40.21918999999999, + 0.8064649999999999, + 20.629894999999998, + ], + }, + { + date: DateTime.local() + .setZone('utc', { + keepLocalTime: true, + }) + .minus({ days: 3 }), + value: 50.0, + valueDetail: [25.0, 5.0, 20.0], + }, + ] + it('should return isDone = true, isWin = true and isEmpty=false when userConsumption < threshold', async () => { + const result = await challengeService.isChallengeDone( + userChallenge, + dataloads + ) + expect(result).toEqual({ isDone: true, isWin: true, isEmpty: false }) + }) + it('should return isDone = true, isWin = true and isEmpty=true when missing value and userConsumption < threshold', async () => { + const updatedDataloads = cloneDeep(dataloads) + updatedDataloads[2].value = -1 + updatedDataloads[2].valueDetail = null + const result = await challengeService.isChallengeDone( + userChallenge, + updatedDataloads + ) + expect(result).toEqual({ isDone: true, isWin: true, isEmpty: true }) + }) + it('should return isDone = true, isWin = false and isEmpty=false when userConsumption >= threshold ', async () => { + const updatedUserChallenge = { + ...userChallenge, + duel: { + ...userChallenge.duel, + threshold: 100, + userConsumption: 200, + }, + } + const result = await challengeService.isChallengeDone( + updatedUserChallenge, + dataloads + ) + expect(result).toEqual({ isDone: true, isWin: false, isEmpty: false }) + }) }) - it('should return isDone = false and isWin = false with dataload not complete', async () => { - const updatedUserChallenge = { - ...userChallenge, + + describe('case date + lags + 1 is not reached', () => { + const userChallenge = { + ...userChallengeData[0], + state: UserChallengeState.DUEL, duel: { - ...userChallenge.duel, - duration: Duration.fromObject({ day: 4 }), + ...userChallengeData[0].duel, + state: UserDuelState.ONGOING, + duration: Duration.fromObject({ day: 3 }), + threshold: 200, + userConsumption: 199, + startDate: DateTime.local() + .setZone('utc', { + keepLocalTime: true, + }) + .minus({ days: 3 }), }, } - const result = await challengeService.isChallengeDone( - updatedUserChallenge, - dataloads - ) - expect(result).toEqual({ isDone: false, isWin: false, isEmpty: false }) + const dataloads: Dataload[] = [ + { + date: DateTime.local() + .setZone('utc', { + keepLocalTime: true, + }) + .minus({ days: 3 }), + value: 69.18029999999999, + valueDetail: [ + 45.127739999999996, + 0.9048899999999999, + 23.147669999999998, + ], + }, + { + date: DateTime.local() + .setZone('utc', { + keepLocalTime: true, + }) + .minus({ days: 2 }), + value: 61.65554999999999, + valueDetail: [ + 40.21918999999999, + 0.8064649999999999, + 20.629894999999998, + ], + }, + { + date: DateTime.local() + .setZone('utc', { + keepLocalTime: true, + }) + .minus({ days: 1 }), + value: 50, + valueDetail: [25.0, 5.0, 20.0], + }, + ] + it('should return isDone = true, isWin = true and isEmpty=false when all data are available and userConsumption < threshold', async () => { + const result = await challengeService.isChallengeDone( + userChallenge, + dataloads + ) + expect(result).toEqual({ isDone: true, isWin: true, isEmpty: false }) + }) + + it('should return isDone = true and isWin = false and isEmpty=false when all data are available and userConsumption >= threshold ', async () => { + const updatedUserChallenge = { + ...userChallenge, + duel: { + ...userChallenge.duel, + threshold: 100, + userConsumption: 200, + }, + } + const result = await challengeService.isChallengeDone( + updatedUserChallenge, + dataloads + ) + expect(result).toEqual({ isDone: true, isWin: false, isEmpty: false }) + }) + it('should return isDone = false and isWin = false and isEmpty = false when last data is not available', async () => { + const updatedDataloads = cloneDeep(dataloads) + updatedDataloads[2].value = -1 + updatedDataloads[2].valueDetail = null + const result = await challengeService.isChallengeDone( + userChallenge, + updatedDataloads + ) + expect(result).toEqual({ isDone: false, isWin: false, isEmpty: false }) + }) + it('should return isDone = false and isWin = false and isEmpty = false when data in the middle is not available', async () => { + const updatedDataloads = cloneDeep(dataloads) + updatedDataloads[1].valueDetail = [20.0, -1, 10.0] + const result = await challengeService.isChallengeDone( + userChallenge, + updatedDataloads + ) + expect(result).toEqual({ isDone: false, isWin: false, isEmpty: false }) + }) }) }) + describe('loopVerificationExplorationCondition method', () => { it('should return updated userChallenge with non-conditional exploration', async () => { mockGetExplorationEntityById.mockResolvedValue(explorationEntity) diff --git a/src/services/challenge.service.ts b/src/services/challenge.service.ts index bcfc7f5d2..130fbaee5 100644 --- a/src/services/challenge.service.ts +++ b/src/services/challenge.service.ts @@ -146,8 +146,6 @@ export default class ChallengeService { fluidStatus[fluidCondition[0]].status !== FluidState.NOT_CONNECTED ) { isValid = true - } else { - isValid = false } return isValid } @@ -759,14 +757,14 @@ export default class ChallengeService { if (diffFromNow >= fullDuration) { isDone = true dataloads.forEach((d: Dataload) => { - if (d.value === -1) isEmpty = true - else if (d.valueDetail) { - d.valueDetail.forEach((detail: number) => { - if (detail === -1) isEmpty = true - }) + if (d.value === -1 || (d.valueDetail && d.valueDetail.includes(-1))) { + isEmpty = true } }) - if (userChallenge.duel.userConsumption < userChallenge.duel.threshold) { + if ( + isDone && + userChallenge.duel.userConsumption < userChallenge.duel.threshold + ) { isWin = true } } else { @@ -775,9 +773,17 @@ export default class ChallengeService { dataloads.length === duration && dataloads[duration - 1].value !== -1 ) { - console.log(dataloads[duration - 1].value) isDone = true + dataloads.forEach((d: Dataload) => { + if ( + d.value === -1 || + (d.valueDetail && d.valueDetail.includes(-1)) + ) { + isDone = false + } + }) if ( + isDone && userChallenge.duel.userConsumption < userChallenge.duel.threshold ) { isWin = true -- GitLab