diff --git a/AUTHORS.md b/AUTHORS.md index bb1ac22f5a8..c35fcbba7d2 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -102,3 +102,4 @@ If you have contributed to Kolibri, feel free to add your name and Github accoun | Shivang Rawat | ShivangRawat30 | | Alex VĂ©lez | AlexVelezLl | | Mazen Oweiss | moweiss | +| Eshaan Aggarwal | EshaanAgg | diff --git a/kolibri/core/assets/src/views/ExamReport/TriesOverview.vue b/kolibri/core/assets/src/views/ExamReport/TriesOverview.vue index 83a3b85a4ff..21cc1d8f145 100644 --- a/kolibri/core/assets/src/views/ExamReport/TriesOverview.vue +++ b/kolibri/core/assets/src/views/ExamReport/TriesOverview.vue @@ -115,6 +115,7 @@ return 0.0; } }, + // Returns the time spent on the best attempt or null if there are no attempts bestTimeSpent() { const bestScoreAttempt = this.pastTries.find(t => t.correct === this.maxQuestionsCorrect); if (!bestScoreAttempt) { @@ -122,23 +123,19 @@ } return bestScoreAttempt.time_spent; }, + // Returns the number of questions correct in the best attempt or 0 if there are no attempts maxQuestionsCorrect() { - return this.pastTries.length ? Math.max(...this.pastTries.map(t => t.correct)) : null; + return this.pastTries.length ? Math.max(...this.pastTries.map(t => t.correct)) : 0; }, bestScore() { - return this.maxQuestionsCorrect !== null - ? this.maxQuestionsCorrect / this.totalQuestions - : null; + return this.maxQuestionsCorrect / this.totalQuestions; }, suggestedTimeAnnotation() { - if (!this.suggestedTime || this.bestTimeSpent === null) { + if (!this.suggestedTime || !this.bestTimeSpent) { return null; } const diff = Math.floor((this.bestTimeSpent - this.suggestedTime) / 60); - if (!diff) { - return null; - } return diff >= 1 ? this.$tr('practiceQuizReportSlowerSuggestedLabel', { value: diff }) diff --git a/kolibri/core/assets/src/views/ExamReport/__tests__/TriesOverview.spec.js b/kolibri/core/assets/src/views/ExamReport/__tests__/TriesOverview.spec.js index 05acce0b770..eff15339625 100644 --- a/kolibri/core/assets/src/views/ExamReport/__tests__/TriesOverview.spec.js +++ b/kolibri/core/assets/src/views/ExamReport/__tests__/TriesOverview.spec.js @@ -12,7 +12,13 @@ tryValidatorModule.tryValidator = jest.fn(() => true); const renderComponent = props => { const commonCoreStrings = { methods: { - coreString: x => x, + coreString: (x, options) => + !options + ? x + : // Add comma seperated options as key value pairs at the end of the label + `${x} ${Object.keys(options) + .map(key => `${key}=${options[key]}`) + .join(', ')}`, }, }; @@ -20,7 +26,6 @@ const renderComponent = props => { pastTries: [], totalQuestions: 20, suggestedTime: 240, - ...props, }; return render(TriesOverview, { @@ -40,6 +45,8 @@ describe('TriesOverview', () => { pastTries: [ { id: '1', + correct: 5, + time_spent: 100, completion_timestamp: 100, }, ], @@ -51,7 +58,13 @@ describe('TriesOverview', () => { test('renders progress icon and in-progress label when there is an in-progress try', () => { renderComponent({ - pastTries: [{ id: '2' }], + pastTries: [ + { + id: '2', + correct: 5, + time_spent: 100, + }, + ], }); expect(screen.getByTestId('progress-icon-0.5')).toBeInTheDocument(); @@ -59,10 +72,120 @@ describe('TriesOverview', () => { }); test('renders progress icon and not started label when there are no past tries', () => { - renderComponent(); + renderComponent({ + pastTries: [], + totalQuestions: 20, + }); expect(screen.getByTestId('progress-icon-0')).toBeInTheDocument(); expect(screen.getByText('notStartedLabel')).toBeInTheDocument(); }); }); + + describe("Test the 'Best Score' table row", () => { + test('renders the best score when there are past tries', () => { + renderComponent({ + pastTries: [ + { + id: '1', + correct: 8, + time_spent: 100, + }, + { + id: '2', + correct: 9, + time_spent: 100, + }, + ], + totalQuestions: 10, + }); + + expect(screen.getByText('Best score')).toBeInTheDocument(); + expect(screen.getByText('90%')).toBeInTheDocument(); + + expect(screen.getByText('questionsCorrectLabel')).toBeInTheDocument(); + expect(screen.getByText('questionsCorrectValue correct=9, total=10')).toBeInTheDocument(); + }); + + test('renders the best score as 0 when there are no past tries', () => { + renderComponent(); + + expect(screen.getByText('Best score')).toBeInTheDocument(); + expect(screen.getByText('0%')).toBeInTheDocument(); + + expect(screen.getByText('questionsCorrectLabel')).toBeInTheDocument(); + expect(screen.getByText('questionsCorrectValue correct=0, total=20')).toBeInTheDocument(); + }); + }); + + describe("Test the 'Time Spent' table row", () => { + test('shows the time spent when there are past tries [Faster Quiz Report]', () => { + renderComponent({ + pastTries: [ + { + id: '1', + correct: 8, + time_spent: 100, + }, + { + id: '2', + correct: 9, + time_spent: 20, + }, + ], + suggestedTime: 100, + }); + + expect(screen.getByText('Best score time')).toBeInTheDocument(); + expect(screen.getByText('20 seconds')).toBeInTheDocument(); + expect(screen.getByText('2 minutes faster than the suggested time')).toBeInTheDocument(); + }); + + test('shows the time spent when there are past tries [Slower Quiz Report]', () => { + renderComponent({ + pastTries: [ + { + id: '1', + correct: 8, + time_spent: 100, + }, + { + id: '2', + correct: 9, + time_spent: 160, + }, + ], + suggestedTime: 100, + }); + + expect(screen.getByText('Best score time')).toBeInTheDocument(); + expect(screen.getByText('2 minutes')).toBeInTheDocument(); + expect(screen.getByText('1 minute slower than the suggested time')).toBeInTheDocument(); + }); + + test('shows the time spent when there are past tries but no suggested time [No Quiz Report]', () => { + renderComponent({ + pastTries: [ + { + id: '1', + correct: 8, + time_spent: 100, + }, + { + id: '2', + correct: 9, + time_spent: 20, + }, + ], + }); + + expect(screen.getByText('Best score time')).toBeInTheDocument(); + expect(screen.getByText('20 seconds')).toBeInTheDocument(); + }); + + test('does not render the row if there are no past tries', () => { + renderComponent({ pastTries: [] }); + expect(screen.queryByText('Best score time')).not.toBeInTheDocument(); + }); + }); });