From 997de00f4c77d9bf7d7cae347d6ba3f7753af03e Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Fri, 10 Nov 2023 23:26:24 +0800 Subject: [PATCH 01/10] Save single question with no extra choice --- src/app/components/mc/editor.test.tsx | 30 +++- src/app/components/mc/editor.tsx | 229 ++++++++++++++++---------- src/app/mc/edit/page.tsx | 1 + src/repo/question_set.ts | 12 +- 4 files changed, 171 insertions(+), 101 deletions(-) diff --git a/src/app/components/mc/editor.test.tsx b/src/app/components/mc/editor.test.tsx index 5e05288..ae32d78 100644 --- a/src/app/components/mc/editor.test.tsx +++ b/src/app/components/mc/editor.test.tsx @@ -1,9 +1,10 @@ import { fireEvent, render } from '@testing-library/react' import { QuestionSetRepoFactory } from '../../../repo/question_set' import { QuestionSetEditorUIService } from './editor' +import { MultipleChoice } from '../../../model/mc' describe('QuestionSetEditor', () => { - it('should save question set', () => { + it('should save question set successfully when no extra choice or question added', () => { const editorRepo = QuestionSetRepoFactory.createTestInstance() const { getByLabelText, getByText } = render( QuestionSetEditorUIService.createTestInstance({ @@ -14,27 +15,42 @@ describe('QuestionSetEditor', () => { const questionInput = getByLabelText('Question 1:') const choice1Input = getByLabelText('Choice 1:') - const isChoice1CorrectInput = getByLabelText('Choice 1 is correct answer') const isChoice1FixedPositionInput = getByLabelText( 'Choice 1 is fixed position', ) const choice2Input = getByLabelText('Choice 2:') + const isChoice2CorrectInput = getByLabelText('Choice 2 is correct answer') fireEvent.change(questionSetNameInput, { target: { value: 'Test name' } }) fireEvent.change(questionInput, { target: { value: 'Am I handsome?' } }) fireEvent.change(choice1Input, { target: { value: 'True' } }) - fireEvent.change(isChoice1CorrectInput, { target: { value: 'true' } }) - fireEvent.change(isChoice1FixedPositionInput, { - target: { value: 'true' }, - }) + fireEvent.click(isChoice1FixedPositionInput) fireEvent.change(choice2Input, { target: { value: 'False' } }) + fireEvent.click(isChoice2CorrectInput) fireEvent.click(getByText('Save')) const actualQuestionSet = editorRepo.getQuestionSetByName('Test name') - // TODO: Assert the question set is saved with expected values + expect(actualQuestionSet.name).toBe('Test name') + expect(actualQuestionSet.questions.length).toBe(1) + expect(actualQuestionSet.questions[0].title).toBe('Am I handsome?') + expect(actualQuestionSet.questions[0].mc).toEqual( + new MultipleChoice({ + choices: [ + { + answer: 'True', + isFixedPosition: true, + }, + { + answer: 'False', + isFixedPosition: false, + }, + ], + correctChoiceIndex: 1, + }), + ) }) }) diff --git a/src/app/components/mc/editor.tsx b/src/app/components/mc/editor.tsx index 851c95a..f8285a9 100644 --- a/src/app/components/mc/editor.tsx +++ b/src/app/components/mc/editor.tsx @@ -1,7 +1,12 @@ +'use client' + +import { useState } from 'react' import { QuestionSetRepo, QuestionSetRepoFactory, } from '../../../repo/question_set' +import { QuestionSet } from '../../../model/question_set' +import { MultipleChoiceBuilder } from '../../../model/mc' export class QuestionSetEditorUIService { static create() { @@ -22,113 +27,157 @@ export class QuestionSetEditorUIService { this.editorRepo = editorRepo } + private handleSave = (questionSet: QuestionSet) => { + this.editorRepo.save(questionSet) + } + getElement() { - return + return } } -function QuestionSetEditor() { - return ( -
-
- - -
+function QuestionSetEditor({ + onSave, +}: { + onSave: (questionSet: QuestionSet) => void +}) { + const [questionIndexToNumOfChoices, setQuestionIndexToNumOfChoices] = + useState>(new Map([[0, 2]])) + + const renderChoiceInputs = (questionIndex: number, numOfChoices: number) => { + const choiceInputs = [] + for (let i = 0; i < numOfChoices; i++) { + // TODO: turn these into one component and use one key only -
-
+

Choices:

- + From cb561dc0bfb6b371499a818f58abd84c212572fd Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Mon, 13 Nov 2023 18:06:51 +0800 Subject: [PATCH 05/10] Support add questions --- src/app/components/mc/editor.test.tsx | 123 ++++++++++++++++++ src/app/components/mc/editor.tsx | 173 ++++++++++++++++---------- 2 files changed, 227 insertions(+), 69 deletions(-) diff --git a/src/app/components/mc/editor.test.tsx b/src/app/components/mc/editor.test.tsx index 7aec814..47c6658 100644 --- a/src/app/components/mc/editor.test.tsx +++ b/src/app/components/mc/editor.test.tsx @@ -105,4 +105,127 @@ describe('QuestionSetEditor', () => { }), ) }) + + it('should save question set after multiple clicks of add choice and add question', () => { + const editorRepo = QuestionSetRepoFactory.createTestInstance() + const { getAllByText, getByLabelText, getByText } = render( + QuestionSetEditorUIService.createTestInstance({ + editorRepo, + }).getElement(), + ) + fireEvent.change(getByLabelText('Question Set Name:'), { + target: { value: 'Test name' }, + }) + + fireEvent.change(getByLabelText('Question 1:'), { + target: { value: '1 + 1 = ?' }, + }) + fireEvent.change(getByLabelText('answer of question 1 choice 1'), { + target: { value: '2' }, + }) + fireEvent.change(getByLabelText('answer of question 1 choice 2'), { + target: { value: '0' }, + }) + fireEvent.click(getByLabelText('question 1 choice 1 is correct answer')) + + fireEvent.click(getByText('Add Question')) + + fireEvent.change(getByLabelText('Question 2:'), { + target: { value: '1 + 2 = ?' }, + }) + fireEvent.change(getByLabelText('answer of question 2 choice 1'), { + target: { value: '3' }, + }) + fireEvent.change(getByLabelText('answer of question 2 choice 2'), { + target: { value: '0' }, + }) + fireEvent.click(getByLabelText('question 2 choice 1 is correct answer')) + + fireEvent.click(getByText('Add Question')) + + fireEvent.change(getByLabelText('Question 3:'), { + target: { value: '1 + 3 = ?' }, + }) + fireEvent.change(getByLabelText('answer of question 3 choice 1'), { + target: { value: '1' }, + }) + fireEvent.change(getByLabelText('answer of question 3 choice 2'), { + target: { value: '3' }, + }) + const question3AddChoice = getAllByText( + 'Add Choice', + ).pop() as HTMLButtonElement + fireEvent.click(question3AddChoice) + fireEvent.click(question3AddChoice) + fireEvent.change(getByLabelText('answer of question 3 choice 3'), { + target: { value: '0' }, + }) + fireEvent.change(getByLabelText('answer of question 3 choice 4'), { + target: { value: 'None of the above' }, + }) + fireEvent.click(getByLabelText('question 3 choice 4 is correct answer')) + fireEvent.click(getByLabelText('question 3 choice 4 is fixed position')) + + fireEvent.click(getByText('Save')) + + const actualQuestionSet = editorRepo.getQuestionSetByName('Test name') + expect(actualQuestionSet.questions).toEqual([ + { + title: '1 + 1 = ?', + mc: new MultipleChoice({ + choices: [ + { + answer: '2', + isFixedPosition: false, + }, + { + answer: '0', + isFixedPosition: false, + }, + ], + correctChoiceIndex: 0, + }), + }, + { + title: '1 + 2 = ?', + mc: new MultipleChoice({ + choices: [ + { + answer: '3', + isFixedPosition: false, + }, + { + answer: '0', + isFixedPosition: false, + }, + ], + correctChoiceIndex: 0, + }), + }, + { + title: '1 + 3 = ?', + mc: new MultipleChoice({ + choices: [ + { + answer: '1', + isFixedPosition: false, + }, + { + answer: '3', + isFixedPosition: false, + }, + { + answer: '0', + isFixedPosition: false, + }, + { + answer: 'None of the above', + isFixedPosition: true, + }, + ], + correctChoiceIndex: 3, + }), + }, + ]) + }) }) diff --git a/src/app/components/mc/editor.tsx b/src/app/components/mc/editor.tsx index a676999..14efafe 100644 --- a/src/app/components/mc/editor.tsx +++ b/src/app/components/mc/editor.tsx @@ -6,7 +6,7 @@ import { QuestionSetRepoFactory, } from '../../../repo/question_set' import { QuestionSet } from '../../../model/question_set' -import { MultipleChoiceBuilder } from '../../../model/mc' +import { MultipleChoice, MultipleChoiceBuilder } from '../../../model/mc' export class QuestionSetEditorUIService { static create() { @@ -85,30 +85,51 @@ function QuestionSetEditor({ return choiceInputs } - const mapFormDataToQuestionSet = (formData: FormData): QuestionSet => { + const mapFormDataToQuestionSet = ( + formData: FormData, + numOfQuestions: number, + ): QuestionSet => { + const questions: { + title: string + mc: MultipleChoice + }[] = [] + for ( + let questionIndex = 0; + questionIndex < numOfQuestions; + questionIndex++ + ) { + const numOfChoices = questionIndexToNumOfChoices.get( + questionIndex, + ) as number + const mcBuilder = new MultipleChoiceBuilder() + for (let choiceIndex = 0; choiceIndex < numOfChoices; choiceIndex++) { + mcBuilder.appendChoice({ + answer: formData.get( + `question-${questionIndex}-choice-${choiceIndex}-answer`, + ) as string, + isFixedPosition: + formData.get( + `question-${questionIndex}-choice-${choiceIndex}-is-fixed-position`, + ) === 'on', + }) + if ( + formData.get( + `question-${questionIndex}-choice-${choiceIndex}-is-correct`, + ) === 'on' + ) { + mcBuilder.setCorrectChoiceIndex(choiceIndex) + } + } + + questions.push({ + title: formData.get(`question-${questionIndex}-title`) as string, + mc: mcBuilder.build(), + }) + } + return { name: formData.get('question-set-name') as string, - questions: [ - { - title: formData.get('question-0-title') as string, - mc: (() => { - const numOfChoices = questionIndexToNumOfChoices.get(0) as number - const mcBuilder = new MultipleChoiceBuilder() - for (let i = 0; i < numOfChoices; i++) { - mcBuilder.appendChoice({ - answer: formData.get(`question-0-choice-${i}-answer`) as string, - isFixedPosition: - formData.get(`question-0-choice-${i}-is-fixed-position`) === - 'on', - }) - if (formData.get(`question-0-choice-${i}-is-correct`) === 'on') { - mcBuilder.setCorrectChoiceIndex(i) - } - } - return mcBuilder.build() - })(), - }, - ], + questions, } } @@ -126,54 +147,63 @@ function QuestionSetEditor({ -
- -
-

Choices:

-
ChoiceAnswer Correct Fixed Position
- - - - - - - - - {renderChoiceInputs( - 0, - questionIndexToNumOfChoices.get(0) as number, - )} - -
AnswerCorrectFixed Position
- - -
-
+ {Array.from(questionIndexToNumOfChoices).map( + ([questionIndex, numOfChoices]) => { + return ( +
+ +
+

Choices:

+ + + + + + + + + + {renderChoiceInputs(questionIndex, numOfChoices)} + +
AnswerCorrect + Fixed Position +
+ + +
+
+ ) + }, + )} @@ -183,7 +213,12 @@ function QuestionSetEditor({ type="button" className="bg-green-500 text-white px-4 py-2 rounded" onClick={() => - onSave(mapFormDataToQuestionSet(new FormData(document.forms[0]))) + onSave( + mapFormDataToQuestionSet( + new FormData(document.forms[0]), + questionIndexToNumOfChoices.size, + ), + ) } > Save From 22a31686bc1680a40d5a9d61008b736095c723f8 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Mon, 13 Nov 2023 18:17:09 +0800 Subject: [PATCH 06/10] Refactor for removing extra lines --- src/app/components/mc/editor.test.tsx | 101 +++++++++++++------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/src/app/components/mc/editor.test.tsx b/src/app/components/mc/editor.test.tsx index 47c6658..5699861 100644 --- a/src/app/components/mc/editor.test.tsx +++ b/src/app/components/mc/editor.test.tsx @@ -11,49 +11,46 @@ describe('QuestionSetEditor', () => { editorRepo, }).getElement(), ) - const questionSetNameInput = getByLabelText('Question Set Name:') - const questionInput = getByLabelText('Question 1:') - const choice1Input = getByLabelText('answer of question 1 choice 1') - const isChoice1FixedPositionInput = getByLabelText( - 'question 1 choice 1 is fixed position', - ) - - const choice2Input = getByLabelText('answer of question 1 choice 2') - const isChoice2CorrectInput = getByLabelText( - 'question 1 choice 2 is correct answer', - ) - - fireEvent.change(questionSetNameInput, { target: { value: 'Test name' } }) - fireEvent.change(questionInput, { target: { value: 'Am I handsome?' } }) - fireEvent.change(choice1Input, { target: { value: 'True' } }) - fireEvent.click(isChoice1FixedPositionInput) + fireEvent.change(getByLabelText('Question Set Name:'), { + target: { value: 'Test name' }, + }) + fireEvent.change(getByLabelText('Question 1:'), { + target: { value: 'Am I handsome?' }, + }) + fireEvent.change(getByLabelText('answer of question 1 choice 1'), { + target: { value: 'True' }, + }) + fireEvent.click(getByLabelText('question 1 choice 1 is fixed position')) - fireEvent.change(choice2Input, { target: { value: 'False' } }) - fireEvent.click(isChoice2CorrectInput) + fireEvent.change(getByLabelText('answer of question 1 choice 2'), { + target: { value: 'False' }, + }) + fireEvent.click(getByLabelText('question 1 choice 2 is correct answer')) fireEvent.click(getByText('Save')) const actualQuestionSet = editorRepo.getQuestionSetByName('Test name') expect(actualQuestionSet.name).toBe('Test name') - expect(actualQuestionSet.questions.length).toBe(1) - expect(actualQuestionSet.questions[0].title).toBe('Am I handsome?') - expect(actualQuestionSet.questions[0].mc).toEqual( - new MultipleChoice({ - choices: [ - { - answer: 'True', - isFixedPosition: true, - }, - { - answer: 'False', - isFixedPosition: false, - }, - ], - correctChoiceIndex: 1, - }), - ) + expect(actualQuestionSet.questions).toEqual([ + { + title: 'Am I handsome?', + mc: new MultipleChoice({ + choices: [ + { + answer: 'True', + isFixedPosition: true, + }, + { + answer: 'False', + isFixedPosition: false, + }, + ], + correctChoiceIndex: 1, + }), + }, + ]) }) it('should also save all input specified in extra option', () => { @@ -63,30 +60,34 @@ describe('QuestionSetEditor', () => { editorRepo, }).getElement(), ) - const questionSetNameInput = getByLabelText('Question Set Name:') - const questionInput = getByLabelText('Question 1:') - - const choice1Input = getByLabelText('answer of question 1 choice 1') - const choice2Input = getByLabelText('answer of question 1 choice 2') - fireEvent.change(questionSetNameInput, { target: { value: 'Test name' } }) - fireEvent.change(questionInput, { target: { value: '1 + 1 = ?' } }) - fireEvent.change(choice1Input, { target: { value: '1' } }) - fireEvent.change(choice2Input, { target: { value: '0' } }) + fireEvent.change(getByLabelText('Question Set Name:'), { + target: { value: 'Test name' }, + }) + fireEvent.change(getByLabelText('Question 1:'), { + target: { value: '1 + 1 = ?' }, + }) + fireEvent.change(getByLabelText('answer of question 1 choice 1'), { + target: { value: '1' }, + }) + fireEvent.change(getByLabelText('answer of question 1 choice 2'), { + target: { value: '0' }, + }) fireEvent.click(getByText('Add Choice')) - const choice3Input = getByLabelText('answer of question 1 choice 3') - fireEvent.change(choice3Input, { target: { value: 'None of the above' } }) + fireEvent.change(getByLabelText('answer of question 1 choice 3'), { + target: { value: 'None of the above' }, + }) fireEvent.click(getByLabelText('question 1 choice 3 is correct answer')) fireEvent.click(getByLabelText('question 1 choice 3 is fixed position')) fireEvent.click(getByText('Save')) const actualQuestionSet = editorRepo.getQuestionSetByName('Test name') - expect(actualQuestionSet.questions[0].title).toBe('1 + 1 = ?') - expect(actualQuestionSet.questions[0].mc).toEqual( - new MultipleChoice({ + expect(actualQuestionSet.questions[0]).toEqual({ + title: '1 + 1 = ?', + mc: new MultipleChoice({ choices: [ { answer: '1', @@ -103,7 +104,7 @@ describe('QuestionSetEditor', () => { ], correctChoiceIndex: 2, }), - ) + }) }) it('should save question set after multiple clicks of add choice and add question', () => { From a7f10e93ce643452b14a1d1411e1c06b368bf87d Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Tue, 14 Nov 2023 17:24:52 +0800 Subject: [PATCH 07/10] Trial for refractor of the ui interaction --- src/app/components/mc/editor.test.tsx | 131 +++++++++++++++++++++----- 1 file changed, 109 insertions(+), 22 deletions(-) diff --git a/src/app/components/mc/editor.test.tsx b/src/app/components/mc/editor.test.tsx index 5699861..823c9fd 100644 --- a/src/app/components/mc/editor.test.tsx +++ b/src/app/components/mc/editor.test.tsx @@ -1,36 +1,123 @@ -import { fireEvent, render } from '@testing-library/react' -import { QuestionSetRepoFactory } from '../../../repo/question_set' +import { fireEvent, render, screen } from '@testing-library/react' +import { + QuestionSetRepo, + QuestionSetRepoFactory, +} from '../../../repo/question_set' import { QuestionSetEditorUIService } from './editor' import { MultipleChoice } from '../../../model/mc' -describe('QuestionSetEditor', () => { - it('should save question set successfully when no extra choice or question added', () => { - const editorRepo = QuestionSetRepoFactory.createTestInstance() - const { getByLabelText, getByText } = render( +class UIServiceInteractor { + readonly screen + private readonly editorRepo: QuestionSetRepo + private questionSetName: string + private questionNumberFocus = 1 + + constructor({ questionSetName = 'Dummy name' }) { + this.editorRepo = QuestionSetRepoFactory.createTestInstance() + render( QuestionSetEditorUIService.createTestInstance({ - editorRepo, + editorRepo: this.editorRepo, }).getElement(), ) + this.screen = screen - fireEvent.change(getByLabelText('Question Set Name:'), { - target: { value: 'Test name' }, - }) - fireEvent.change(getByLabelText('Question 1:'), { - target: { value: 'Am I handsome?' }, - }) - fireEvent.change(getByLabelText('answer of question 1 choice 1'), { - target: { value: 'True' }, - }) - fireEvent.click(getByLabelText('question 1 choice 1 is fixed position')) + this.questionSetName = questionSetName + this.syncQuestionSetName() + } - fireEvent.change(getByLabelText('answer of question 1 choice 2'), { - target: { value: 'False' }, + private syncQuestionSetName() { + fireEvent.change(screen.getByLabelText('Question Set Name:'), { + target: { value: this.questionSetName }, }) - fireEvent.click(getByLabelText('question 1 choice 2 is correct answer')) + return this + } - fireEvent.click(getByText('Save')) + setQuestionNumberFocus(questionNumber: number) { + this.questionNumberFocus = questionNumber + return this + } - const actualQuestionSet = editorRepo.getQuestionSetByName('Test name') + getSavedQuestionSet() { + return this.editorRepo.getQuestionSetByName(this.questionSetName) + } + + inputQuestionDescription({ description }: { description: string }) { + fireEvent.change( + screen.getByLabelText(`Question ${this.questionNumberFocus}:`), + { + target: { value: description }, + }, + ) + return this + } + + inputAnswer({ + choiceNumber, + answer, + }: { + choiceNumber: number + answer: string + }) { + fireEvent.change( + screen.getByLabelText( + `answer of question ${this.questionNumberFocus} choice ${choiceNumber}`, + ), + { + target: { value: answer }, + }, + ) + return this + } + + clickFixedPosition({ choiceNumber }: { choiceNumber: number }) { + fireEvent.click( + screen.getByLabelText( + `question ${this.questionNumberFocus} choice ${choiceNumber} is fixed position`, + ), + ) + return this + } + + clickCorrectAnswer({ choiceNumber }: { choiceNumber: number }) { + fireEvent.click( + screen.getByLabelText( + `question ${this.questionNumberFocus} choice ${choiceNumber} is correct answer`, + ), + ) + return this + } + + clickSave() { + fireEvent.click(screen.getByText('Save')) + return this + } +} + +describe('QuestionSetEditorUIService', () => { + it('should save question set successfully when no extra choice or question added', () => { + const interactor = new UIServiceInteractor({ questionSetName: 'Test name' }) + interactor + .setQuestionNumberFocus(1) + .inputQuestionDescription({ + description: 'Am I handsome?', + }) + .inputAnswer({ + choiceNumber: 1, + answer: 'True', + }) + .clickFixedPosition({ + choiceNumber: 1, + }) + .inputAnswer({ + choiceNumber: 2, + answer: 'False', + }) + .clickCorrectAnswer({ + choiceNumber: 2, + }) + .clickSave() + + const actualQuestionSet = interactor.getSavedQuestionSet() expect(actualQuestionSet.name).toBe('Test name') expect(actualQuestionSet.questions).toEqual([ From 51e96d1594be212c18332d1f1150baf9180ceee5 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Tue, 14 Nov 2023 18:16:01 +0800 Subject: [PATCH 08/10] Refactor remaining tests to use interactor --- src/app/components/mc/editor.test.tsx | 136 ++++++++++---------------- 1 file changed, 52 insertions(+), 84 deletions(-) diff --git a/src/app/components/mc/editor.test.tsx b/src/app/components/mc/editor.test.tsx index 823c9fd..c06bdc9 100644 --- a/src/app/components/mc/editor.test.tsx +++ b/src/app/components/mc/editor.test.tsx @@ -87,6 +87,17 @@ class UIServiceInteractor { return this } + clickAddChoice() { + const addChoiceButtons = screen.getAllByText('Add Choice') + fireEvent.click(addChoiceButtons[this.questionNumberFocus - 1]) + return this + } + + clickAddQuestion() { + fireEvent.click(screen.getByText('Add Question')) + return this + } + clickSave() { fireEvent.click(screen.getByText('Save')) return this @@ -141,37 +152,21 @@ describe('QuestionSetEditorUIService', () => { }) it('should also save all input specified in extra option', () => { - const editorRepo = QuestionSetRepoFactory.createTestInstance() - const { getByLabelText, getByText } = render( - QuestionSetEditorUIService.createTestInstance({ - editorRepo, - }).getElement(), - ) - - fireEvent.change(getByLabelText('Question Set Name:'), { - target: { value: 'Test name' }, - }) - fireEvent.change(getByLabelText('Question 1:'), { - target: { value: '1 + 1 = ?' }, - }) - fireEvent.change(getByLabelText('answer of question 1 choice 1'), { - target: { value: '1' }, - }) - fireEvent.change(getByLabelText('answer of question 1 choice 2'), { - target: { value: '0' }, - }) - - fireEvent.click(getByText('Add Choice')) - - fireEvent.change(getByLabelText('answer of question 1 choice 3'), { - target: { value: 'None of the above' }, - }) - fireEvent.click(getByLabelText('question 1 choice 3 is correct answer')) - fireEvent.click(getByLabelText('question 1 choice 3 is fixed position')) + const interactor = new UIServiceInteractor({}) + interactor + .setQuestionNumberFocus(1) + .inputQuestionDescription({ description: '1 + 1 = ?' }) - fireEvent.click(getByText('Save')) + interactor + .inputAnswer({ choiceNumber: 1, answer: '1' }) + .inputAnswer({ choiceNumber: 2, answer: '0' }) + .clickAddChoice() + .inputAnswer({ choiceNumber: 3, answer: 'None of the above' }) + .clickCorrectAnswer({ choiceNumber: 3 }) + .clickFixedPosition({ choiceNumber: 3 }) + .clickSave() - const actualQuestionSet = editorRepo.getQuestionSetByName('Test name') + const actualQuestionSet = interactor.getSavedQuestionSet() expect(actualQuestionSet.questions[0]).toEqual({ title: '1 + 1 = ?', mc: new MultipleChoice({ @@ -195,68 +190,41 @@ describe('QuestionSetEditorUIService', () => { }) it('should save question set after multiple clicks of add choice and add question', () => { - const editorRepo = QuestionSetRepoFactory.createTestInstance() - const { getAllByText, getByLabelText, getByText } = render( - QuestionSetEditorUIService.createTestInstance({ - editorRepo, - }).getElement(), - ) - fireEvent.change(getByLabelText('Question Set Name:'), { - target: { value: 'Test name' }, - }) + const interactor = new UIServiceInteractor({}) - fireEvent.change(getByLabelText('Question 1:'), { - target: { value: '1 + 1 = ?' }, - }) - fireEvent.change(getByLabelText('answer of question 1 choice 1'), { - target: { value: '2' }, - }) - fireEvent.change(getByLabelText('answer of question 1 choice 2'), { - target: { value: '0' }, - }) - fireEvent.click(getByLabelText('question 1 choice 1 is correct answer')) + interactor + .setQuestionNumberFocus(1) + .inputQuestionDescription({ description: '1 + 1 = ?' }) + .inputAnswer({ choiceNumber: 1, answer: '2' }) + .inputAnswer({ choiceNumber: 2, answer: '0' }) + .clickCorrectAnswer({ choiceNumber: 1 }) - fireEvent.click(getByText('Add Question')) + interactor.clickAddQuestion() - fireEvent.change(getByLabelText('Question 2:'), { - target: { value: '1 + 2 = ?' }, - }) - fireEvent.change(getByLabelText('answer of question 2 choice 1'), { - target: { value: '3' }, - }) - fireEvent.change(getByLabelText('answer of question 2 choice 2'), { - target: { value: '0' }, - }) - fireEvent.click(getByLabelText('question 2 choice 1 is correct answer')) + interactor + .setQuestionNumberFocus(2) + .inputQuestionDescription({ description: '1 + 2 = ?' }) + .inputAnswer({ choiceNumber: 1, answer: '3' }) + .inputAnswer({ choiceNumber: 2, answer: '0' }) + .clickCorrectAnswer({ choiceNumber: 1 }) - fireEvent.click(getByText('Add Question')) + interactor.clickAddQuestion() - fireEvent.change(getByLabelText('Question 3:'), { - target: { value: '1 + 3 = ?' }, - }) - fireEvent.change(getByLabelText('answer of question 3 choice 1'), { - target: { value: '1' }, - }) - fireEvent.change(getByLabelText('answer of question 3 choice 2'), { - target: { value: '3' }, - }) - const question3AddChoice = getAllByText( - 'Add Choice', - ).pop() as HTMLButtonElement - fireEvent.click(question3AddChoice) - fireEvent.click(question3AddChoice) - fireEvent.change(getByLabelText('answer of question 3 choice 3'), { - target: { value: '0' }, - }) - fireEvent.change(getByLabelText('answer of question 3 choice 4'), { - target: { value: 'None of the above' }, - }) - fireEvent.click(getByLabelText('question 3 choice 4 is correct answer')) - fireEvent.click(getByLabelText('question 3 choice 4 is fixed position')) + interactor + .setQuestionNumberFocus(3) + .inputQuestionDescription({ description: '1 + 3 = ?' }) + .inputAnswer({ choiceNumber: 1, answer: '1' }) + .inputAnswer({ choiceNumber: 2, answer: '3' }) + .clickAddChoice() + .clickAddChoice() + .inputAnswer({ choiceNumber: 3, answer: '0' }) + .inputAnswer({ choiceNumber: 4, answer: 'None of the above' }) + .clickCorrectAnswer({ choiceNumber: 4 }) + .clickFixedPosition({ choiceNumber: 4 }) - fireEvent.click(getByText('Save')) + interactor.clickSave() - const actualQuestionSet = editorRepo.getQuestionSetByName('Test name') + const actualQuestionSet = interactor.getSavedQuestionSet() expect(actualQuestionSet.questions).toEqual([ { title: '1 + 1 = ?', From ef2120cdc1fa9fa87ee6112b308ee9f3d4d4ac45 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Tue, 14 Nov 2023 18:17:00 +0800 Subject: [PATCH 09/10] Move to single line --- src/app/components/mc/editor.test.tsx | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/app/components/mc/editor.test.tsx b/src/app/components/mc/editor.test.tsx index c06bdc9..53cd932 100644 --- a/src/app/components/mc/editor.test.tsx +++ b/src/app/components/mc/editor.test.tsx @@ -109,23 +109,11 @@ describe('QuestionSetEditorUIService', () => { const interactor = new UIServiceInteractor({ questionSetName: 'Test name' }) interactor .setQuestionNumberFocus(1) - .inputQuestionDescription({ - description: 'Am I handsome?', - }) - .inputAnswer({ - choiceNumber: 1, - answer: 'True', - }) - .clickFixedPosition({ - choiceNumber: 1, - }) - .inputAnswer({ - choiceNumber: 2, - answer: 'False', - }) - .clickCorrectAnswer({ - choiceNumber: 2, - }) + .inputQuestionDescription({ description: 'Am I handsome?' }) + .inputAnswer({ choiceNumber: 1, answer: 'True' }) + .clickFixedPosition({ choiceNumber: 1 }) + .inputAnswer({ choiceNumber: 2, answer: 'False' }) + .clickCorrectAnswer({ choiceNumber: 2 }) .clickSave() const actualQuestionSet = interactor.getSavedQuestionSet() From 320532802b45bc0fc36f22c6bbf719b5187040d3 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Tue, 14 Nov 2023 18:20:11 +0800 Subject: [PATCH 10/10] Rename to description --- src/app/components/mc/editor.test.tsx | 10 +++++----- src/app/components/mc/editor.tsx | 4 ++-- src/app/components/mc/quiz.test.tsx | 2 +- src/model/question_set.ts | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/app/components/mc/editor.test.tsx b/src/app/components/mc/editor.test.tsx index 53cd932..3e2ffa1 100644 --- a/src/app/components/mc/editor.test.tsx +++ b/src/app/components/mc/editor.test.tsx @@ -121,7 +121,7 @@ describe('QuestionSetEditorUIService', () => { expect(actualQuestionSet.name).toBe('Test name') expect(actualQuestionSet.questions).toEqual([ { - title: 'Am I handsome?', + description: 'Am I handsome?', mc: new MultipleChoice({ choices: [ { @@ -156,7 +156,7 @@ describe('QuestionSetEditorUIService', () => { const actualQuestionSet = interactor.getSavedQuestionSet() expect(actualQuestionSet.questions[0]).toEqual({ - title: '1 + 1 = ?', + description: '1 + 1 = ?', mc: new MultipleChoice({ choices: [ { @@ -215,7 +215,7 @@ describe('QuestionSetEditorUIService', () => { const actualQuestionSet = interactor.getSavedQuestionSet() expect(actualQuestionSet.questions).toEqual([ { - title: '1 + 1 = ?', + description: '1 + 1 = ?', mc: new MultipleChoice({ choices: [ { @@ -231,7 +231,7 @@ describe('QuestionSetEditorUIService', () => { }), }, { - title: '1 + 2 = ?', + description: '1 + 2 = ?', mc: new MultipleChoice({ choices: [ { @@ -247,7 +247,7 @@ describe('QuestionSetEditorUIService', () => { }), }, { - title: '1 + 3 = ?', + description: '1 + 3 = ?', mc: new MultipleChoice({ choices: [ { diff --git a/src/app/components/mc/editor.tsx b/src/app/components/mc/editor.tsx index 14efafe..74d265d 100644 --- a/src/app/components/mc/editor.tsx +++ b/src/app/components/mc/editor.tsx @@ -90,7 +90,7 @@ function QuestionSetEditor({ numOfQuestions: number, ): QuestionSet => { const questions: { - title: string + description: string mc: MultipleChoice }[] = [] for ( @@ -122,7 +122,7 @@ function QuestionSetEditor({ } questions.push({ - title: formData.get(`question-${questionIndex}-title`) as string, + description: formData.get(`question-${questionIndex}-title`) as string, mc: mcBuilder.build(), }) } diff --git a/src/app/components/mc/quiz.test.tsx b/src/app/components/mc/quiz.test.tsx index b28d900..923519a 100644 --- a/src/app/components/mc/quiz.test.tsx +++ b/src/app/components/mc/quiz.test.tsx @@ -109,7 +109,7 @@ function renderMultipleChoicePage({ // TODO: move this mapping to MultipleChoiceQuizUIService const questions = questionSet.questions.map((question) => { return { - title: question.title, + title: question.description, mc: { choices: question.mc.choices.map((choice) => choice.answer), correctChoiceIndex: question.mc.correctChoiceIndex, diff --git a/src/model/question_set.ts b/src/model/question_set.ts index 9ec88a3..bd35edf 100644 --- a/src/model/question_set.ts +++ b/src/model/question_set.ts @@ -3,7 +3,7 @@ import { MultipleChoiceBuilder, MultipleChoice } from './mc' export interface QuestionSet { name: string questions: ReadonlyArray<{ - title: string + description: string mc: MultipleChoice }> } @@ -11,7 +11,7 @@ export interface QuestionSet { export class QuestionSetBuilderForTest { private name: string = 'Dummy question set' private questions: { - title: string + description: string mc: MultipleChoice }[] = [] @@ -24,7 +24,7 @@ export class QuestionSetBuilderForTest { .build(), } = {}): QuestionSetBuilderForTest { this.questions.push({ - title: title, + description: title, mc: mc, }) return this