Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(cypress): extend test suite to cover creation and editing of selection questions #4438

Merged
merged 3 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
FormikSelectField,
FormikSwitchField,
FormLabel,
UserNotification,
} from '@uzh-bf/design-system'
import { useField } from 'formik'
import { useTranslations } from 'next-intl'
Expand Down Expand Up @@ -65,7 +66,7 @@ function SelectionOptions({ values }: SelectionOptionsProps) {

// udpate the selected correct answers if the answer collection changes
useEffect(() => {
if (!field.value) {
if (!field.value || !collectionAnswers || collectionAnswers.length === 0) {
return
}

Expand All @@ -82,6 +83,16 @@ function SelectionOptions({ values }: SelectionOptionsProps) {
return <Loader />
}

if (collections.length === 0) {
return (
<UserNotification
type="warning"
message={t('manage.questionForms.SEAnswerCollectionRequired')}
className={{ root: 'text-base' }}
/>
)
}

return (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-1 lg:flex-row lg:items-start lg:gap-3">
Expand All @@ -96,7 +107,7 @@ function SelectionOptions({ values }: SelectionOptionsProps) {
label: collection.name,
value: String(collection.id),
data: {
cy: `select-answer-collection-${collection.id}`,
cy: `select-answer-collection-${collection.name}`,
},
}))}
data={{ cy: 'select-answer-collection' }}
Expand Down Expand Up @@ -135,23 +146,24 @@ function SelectionOptions({ values }: SelectionOptionsProps) {
tooltip={t('manage.questionForms.correctAnswerOptionsTooltip')}
labelType="small"
/>
<Select
isClearable
isMulti
value={selectedAnswers}
options={collectionAnswers}
classNames={{
container: () => 'w-full',
}}
onChange={(newValue) =>
helpers.setValue(newValue.map((tag) => tag.value))
}
placeholder={t('manage.questionForms.selectAnswerOptions')}
noOptionsMessage={() =>
t('manage.questionForms.noMatchingOptionFound')
}
data-cy="select-correct-answers"
/>
<div data-cy="choose-correct-answer-options">
<Select
isClearable
isMulti
value={selectedAnswers}
options={collectionAnswers}
classNames={{
container: () => 'w-full',
}}
onChange={(newValue) =>
helpers.setValue(newValue.map((tag) => tag.value))
}
placeholder={t('manage.questionForms.selectAnswerOptions')}
noOptionsMessage={() =>
t('manage.questionForms.noMatchingOptionFound')
}
/>
</div>
</div>
) : null}
</div>
Expand Down
161 changes: 161 additions & 0 deletions cypress/cypress/e2e/D-questions-workflow.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ const FTSampleSolution = [
'Sample Solution 3',
]

const SETitle = 'Selection Question Title'
const SEContent = 'Selection Question Text'
const SEExplanation = 'Selection Question Explanation'
const SECollection = 'Private Collection (Vegetables)' // from seed (access otherwise is tested in resources workflow)
const SEInputs = 2
const SESolutions = ['Cabbage', 'Cucumber']
const SETitleEdited = 'Selection Question Title Edited'
const SEContentEdited = 'Selection Question Text Edited'
const SEExplanationEdited = 'Selection Question Explanation Edited'
const SECollectionEdited = 'Public Collection (Fruits)' // from seed
const SEInputsEdited = 1
const SESolutionsEdited = ['Apple', 'Banana']

describe('Create different types of elements (with and without sample solution) and edit them', () => {
beforeEach(() => {
cy.loginLecturer()
Expand Down Expand Up @@ -1054,6 +1067,154 @@ describe('Create different types of elements (with and without sample solution)
cy.get('[data-cy="close-question-modal"]').click()
})

it('Create a Selection question', () => {
cy.get('[data-cy="create-question"]').click()
cy.get('[data-cy="select-question-type"]')
.should('exist')
.contains(messages.shared.SC.typeLabel)
cy.get('[data-cy="select-question-type"]').click()
cy.get(
`[data-cy="select-question-type-${messages.shared.SELECTION.typeLabel}"]`
).click()
cy.get('[data-cy="select-question-type"]')
.should('exist')
.contains(messages.shared.SELECTION.typeLabel)
cy.get('[data-cy="save-new-question"]').should('be.disabled')

cy.get('[data-cy="insert-question-title"]').click().type(SETitle)
cy.get('[data-cy="save-new-question"]').should('be.disabled')
cy.get('[data-cy="select-question-status"]').click()
cy.get(
`[data-cy="select-question-status-${messages.shared.READY.statusLabel}"]`
).click()
cy.get('[data-cy="insert-question-text"]').realClick().type(SEContent)
cy.get('[data-cy="insert-question-explanation"]')
.realClick()
.type(SEExplanation)
cy.get('[data-cy="save-new-question"]').should('be.disabled')

cy.get('[data-cy="select-answer-collection"]').contains(
messages.manage.questionForms.selectCollection
)
cy.get('[data-cy="select-answer-collection"]').click()
cy.get(`[data-cy="select-answer-collection-${SECollection}"]`).click()
cy.get('[data-cy="select-answer-collection"]').contains(SECollection)
cy.get('[data-cy="save-new-question"]').should('be.disabled')
cy.get('[data-cy="configure-number-of-inputs"]')
.click()
.type(String(SEInputs))
cy.get('[data-cy="save-new-question"]').click()
cy.wait(500)

cy.get(`[data-cy="element-item-${SETitle}"]`).contains(SEContent)
cy.get(`[data-cy="element-item-${SETitle}"]`).contains(SETitle)
cy.get(`[data-cy="element-item-${SETitle}"]`).contains(
messages.shared.READY.statusLabel
)

cy.get(`[data-cy="edit-question-${SETitle}"]`).click()
// TODO: check that preview of selection question is visible and correct
})

it('Verify that the correct content has been saved', () => {
cy.get(`[data-cy="edit-question-${SETitle}"]`).click()
cy.get('[data-cy="insert-question-title"]').should('have.value', SETitle)
cy.get('[data-cy="select-question-status"]').contains(
messages.shared.READY.statusLabel
)
cy.get('[data-cy="insert-question-text"]').realClick().contains(SEContent)
cy.get('[data-cy="insert-question-explanation"]')
.realClick()
.contains(SEExplanation)
cy.get('[data-cy="select-answer-collection"]').contains(SECollection)
cy.get('[data-cy="configure-number-of-inputs"]').should(
'have.value',
SEInputs
)
cy.get('[data-cy="close-question-modal"]').click()
})

it('Add a sample solution to the created selection question', () => {
cy.get(`[data-cy="edit-question-${SETitle}"]`).click()
cy.get('[data-cy="insert-question-title"]').should('have.value', SETitle)

cy.get('[data-cy="configure-sample-solution"]').click()
cy.get('[data-cy="save-new-question"]').should('be.disabled') // at least one correct answer is required
cy.get('[data-cy="choose-correct-answer-options"]').click()
cy.findByText(SESolutions[0]).realClick()
cy.get('[data-cy="choose-correct-answer-options"]').contains(SESolutions[0])
cy.get('[data-cy="save-new-question"]').should('be.disabled') // number of solutions needs to be >= number of inputs
SESolutions.slice(1).forEach((solution) => {
cy.get('[data-cy="choose-correct-answer-options"]').click()
cy.findByText(solution).realClick()
cy.get('[data-cy="choose-correct-answer-options"]').contains(solution)
})
cy.get('[data-cy="save-new-question"]').click()
})

it('Verify that the sample solution has been stored correctly for the modified selection question', () => {
cy.get(`[data-cy="edit-question-${SETitle}"]`).click()
cy.get('[data-cy="insert-question-title"]').should('have.value', SETitle)

SESolutions.forEach((solution) => {
cy.get('[data-cy="choose-correct-answer-options"]').contains(solution)
})
cy.get('[data-cy="close-question-modal"]').click()
})

it('Edit the selection question and change the answer collection (including new sample solutions)', () => {
cy.get(`[data-cy="edit-question-${SETitle}"]`).click()
cy.get('[data-cy="insert-question-title"]')
.click()
.clear()
.type(SETitleEdited)
cy.get('[data-cy="insert-question-text"]')
.realClick()
.clear()
.type(SEContentEdited)
cy.get('[data-cy="insert-question-explanation"]')
.realClick()
.clear()
.type(SEExplanationEdited)

cy.get('[data-cy="select-answer-collection"]').click()
cy.get(`[data-cy="select-answer-collection-${SECollectionEdited}"]`).click()
cy.get('[data-cy="select-answer-collection"]').contains(SECollectionEdited)
cy.get('[data-cy="save-new-question"]').should('be.disabled') // answer options are cleared on collection change
cy.get('[data-cy="configure-number-of-inputs"]')
.click()
.clear()
.type(String(SEInputsEdited))
SESolutionsEdited.forEach((solution) => {
cy.get('[data-cy="choose-correct-answer-options"]').click()
cy.findByText(solution).realClick()
})

cy.get('[data-cy="save-new-question"]').click()
})

it('Verify that the edited state of the selection question persists', () => {
cy.get(`[data-cy="edit-question-${SETitleEdited}"]`).click()
cy.get('[data-cy="insert-question-title"]').should(
'have.value',
SETitleEdited
)
cy.get('[data-cy="insert-question-text"]')
.realClick()
.contains(SEContentEdited)
cy.get('[data-cy="insert-question-explanation"]')
.realClick()
.contains(SEExplanationEdited)
cy.get('[data-cy="select-answer-collection"]').contains(SECollectionEdited)
cy.get('[data-cy="configure-number-of-inputs"]').should(
'have.value',
SEInputsEdited
)
SESolutionsEdited.forEach((solution) => {
cy.get('[data-cy="choose-correct-answer-options"]').contains(solution)
})
})

it('Create a new question, duplicates it and then deletes the duplicate again', () => {
const randomNumber = uuid()
const questionTitle = 'A Single Choice ' + randomNumber
Expand Down
68 changes: 68 additions & 0 deletions cypress/cypress/e2e/K-resources-workflow.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,24 @@ describe('Create, edit and share answer collections', () => {
})
})

it('Verify that all three answer collections can be used in selection questions by owner', () => {
cy.loginLecturer()
cy.get('[data-cy="create-question"]').click()
cy.get('[data-cy="select-question-type"]').click()
cy.get(
`[data-cy="select-question-type-${messages.shared.SELECTION.typeLabel}"]`
).click()

cy.get('[data-cy="select-answer-collection"]').click()
cy.get(`[data-cy="select-answer-collection-${publicName}"]`).should('exist')
cy.get(`[data-cy="select-answer-collection-${restrictedName}"]`).should(
'exist'
)
cy.get(`[data-cy="select-answer-collection-${privateNameNew}"]`).should(
'exist'
)
})

it('Request access to the restricted answer catalogue for user pro1', () => {
cy.loginIndividualCatalyst()
cy.get('[data-cy="resources"]').click()
Expand Down Expand Up @@ -373,6 +391,26 @@ describe('Create, edit and share answer collections', () => {
})
})

it('Verify that only the shared and restricted answer catalogue is available during question creation for user pro1', () => {
cy.loginIndividualCatalyst()
cy.get('[data-cy="create-question"]').click()
cy.get('[data-cy="select-question-type"]').click()
cy.get(
`[data-cy="select-question-type-${messages.shared.SELECTION.typeLabel}"]`
).click()

cy.get('[data-cy="select-answer-collection"]').click()
cy.get(`[data-cy="select-answer-collection-${publicName}"]`).should(
'not.exist'
)
cy.get(`[data-cy="select-answer-collection-${restrictedName}"]`).should(
'exist'
)
cy.get(`[data-cy="select-answer-collection-${privateNameNew}"]`).should(
'not.exist'
)
})

it('Verify that user pro2 does not have access to the restricted answer catalogue', () => {
cy.loginInstitutionalCatalyst()
cy.get('[data-cy="resources"]').click()
Expand All @@ -381,6 +419,18 @@ describe('Create, edit and share answer collections', () => {
)
})

it('Verify that no answer catalogue is available for user pro2', () => {
cy.loginInstitutionalCatalyst()
cy.get('[data-cy="create-question"]').click()
cy.get('[data-cy="select-question-type"]').click()
cy.get(
`[data-cy="select-question-type-${messages.shared.SELECTION.typeLabel}"]`
).click()

cy.get('[data-cy="select-answer-collection"]').should('not.exist')
cy.findByText(messages.manage.questionForms.SEAnswerCollectionRequired)
})

it('Import the public answer catalogue for user pro1 and verify access to it', () => {
cy.loginIndividualCatalyst()
cy.get('[data-cy="resources"]').click()
Expand Down Expand Up @@ -430,6 +480,24 @@ describe('Create, edit and share answer collections', () => {
cy.get('[data-cy="close-viewing-collection-modal"]').click()
})

it('Verify that imported public answer collection is also available for during question creation user pro1', () => {
cy.loginIndividualCatalyst()
cy.get('[data-cy="create-question"]').click()
cy.get('[data-cy="select-question-type"]').click()
cy.get(
`[data-cy="select-question-type-${messages.shared.SELECTION.typeLabel}"]`
).click()

cy.get('[data-cy="select-answer-collection"]').click()
cy.get(`[data-cy="select-answer-collection-${publicName}"]`).should('exist')
cy.get(`[data-cy="select-answer-collection-${restrictedName}"]`).should(
'exist'
)
cy.get(`[data-cy="select-answer-collection-${privateNameNew}"]`).should(
'not.exist'
)
})

it('Login again as user pro1 and verify that the answer catalogues are still visible', () => {
cy.loginIndividualCatalyst()
cy.get('[data-cy="resources"]').click()
Expand Down
2 changes: 2 additions & 0 deletions packages/i18n/messages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,8 @@ Da die KlickerUZH-App noch nicht im iOS-App-Store verfügbar ist, folgen Sie die
'Nehmen Sie hier optionale Einstellungen für die numerische Frage vor. Bitte beachten Sie, dass der Antwortbereich von numerischen Fragen auf das Intervall [-1e30,1e30] begrenzt ist. Sollten Sie grössere Zahlen benötigen, verwenden Sie bitte eine Freitext-Frage.',
SELECTIONOptionsTooltip:
'Wählen Sie hier die Antwort-Sammlung aus welcher die Studierenden die korrekten Antworten auswählen sollen.',
SEAnswerCollectionRequired:
"Zur Erstellung von Auswahl-Fragen benötigen Sie Zugriff auf mindestens eine Antwort-Sammlung!. Sie können diese entweder unter dem Reiter 'Ressourcen' selbst erstellen oder dort eine bestehende Sammlung anderer Nutzer importieren.",
selectCollection: 'Sammlung auswählen...',
answerCollection: 'Antwort-Sammlung',
numberOfInputs: 'Anzahl Eingabefelder',
Expand Down
2 changes: 2 additions & 0 deletions packages/i18n/messages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,8 @@ Since the KlickerUZH app is not yet available on the iOS App Store, follow these
'Enter optional settings for the numerical question here. Please note that the range of numbers for numerical questions is limited to the interval [-1e30,1e30] for technical reasons. Should you require to use larger numbers, please use a free text question instead.',
SELECTIONOptionsTooltip:
'Please select the answer collection from which the students should select the correct answers.',
SEAnswerCollectionRequired:
'To create selection questions, you need access to at least one answer collection! You can either create one yourself under the "Resources" tab or import an existing collection from other users there.',
answerCollection: 'Answer collection',
selectCollection: 'Select collection...',
numberOfInputs: 'Number of inputs',
Expand Down
6 changes: 6 additions & 0 deletions packages/prisma/src/data/data/TEST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,12 @@ export const ANSWER_COLLECTIONS = [
{
value: 'Dill',
},
{
value: 'Cucumber',
},
{
value: 'Carrot',
},
],
},
{
Expand Down
Loading