Skip to content

Commit

Permalink
feat: disable save btn when question is not valid and display warning (
Browse files Browse the repository at this point in the history
  • Loading branch information
ReidyT authored Jan 17, 2024
1 parent b09bef1 commit 84553a7
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 66 deletions.
22 changes: 10 additions & 12 deletions cypress/e2e/Admin/create/fillBlanks.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import i18n from '../../../../src/config/i18n';
import {
CREATE_QUESTION_SELECT_TYPE_CY,
CREATE_QUESTION_TITLE_CY,
CREATE_VIEW_ERROR_ALERT_CY,
CREATE_VIEW_SAVE_BUTTON_CY,
FILL_BLANKS_TEXT_FIELD_CY,
QUESTION_BAR_ADD_NEW_BUTTON_CLASSNAME,
Expand Down Expand Up @@ -61,6 +60,8 @@ const fillBlanksQuestion = (
// save
if (shouldSave) {
cy.get(dataCyWrapper(CREATE_VIEW_SAVE_BUTTON_CY)).click();
} else {
cy.get(dataCyWrapper(CREATE_VIEW_SAVE_BUTTON_CY)).should('be.disabled');
}
};

Expand All @@ -81,26 +82,23 @@ describe('Fill in the Blanks', () => {

// empty text
const new1 = { ...newFillBlanksData, text: '' };
fillBlanksQuestion(new1);
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should(
'contain',
t(FAILURE_MESSAGES.FILL_BLANKS_EMPTY_TEXT)
);
fillBlanksQuestion(new1, { shouldSave: false });
cy.checkErrorMessage({
errorMessage: t(FAILURE_MESSAGES.FILL_BLANKS_EMPTY_TEXT),
});

// faulty text
const new2 = {
...newFillBlanksData,
text: 'my <faulty< text with <blanks>',
};
fillBlanksQuestion(new2, { shouldSave: false });
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should(
'contain',
t(FAILURE_MESSAGES.FILL_BLANKS_UNMATCHING_TAGS)
);
cy.checkErrorMessage({
errorMessage: t(FAILURE_MESSAGES.FILL_BLANKS_UNMATCHING_TAGS),
});

fillBlanksQuestion(newFillBlanksData);

cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should('not.exist');
cy.checkErrorMessage({});
});

describe('Display saved settings', () => {
Expand Down
31 changes: 15 additions & 16 deletions cypress/e2e/Admin/create/multipleChoices.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import i18n from '../../../../src/config/i18n';
import {
CREATE_QUESTION_SELECT_TYPE_CY,
CREATE_QUESTION_TITLE_CY,
CREATE_VIEW_ERROR_ALERT_CY,
CREATE_VIEW_SAVE_BUTTON_CY,
MULTIPLE_CHOICES_ADD_ANSWER_BUTTON_CY,
MULTIPLE_CHOICES_ANSWER_CORRECTNESS_CLASSNAME,
Expand All @@ -24,7 +23,10 @@ import {
buildQuestionStepCy,
dataCyWrapper,
} from '../../../../src/config/selectors';
import { APP_SETTINGS, QUESTION_APP_SETTINGS } from '../../../fixtures/appSettings';
import {
APP_SETTINGS,
QUESTION_APP_SETTINGS,
} from '../../../fixtures/appSettings';

const t = i18n.t;

Expand Down Expand Up @@ -129,6 +131,8 @@ export const fillMultipleChoiceQuestion = (
// save
if (shouldSave) {
cy.get(dataCyWrapper(CREATE_VIEW_SAVE_BUTTON_CY)).click();
} else {
cy.get(dataCyWrapper(CREATE_VIEW_SAVE_BUTTON_CY)).should('be.disabled');
}
};

Expand All @@ -147,11 +151,8 @@ describe('Multiple Choices', () => {

// no question text
const new1 = { ...newMultipleChoiceData, question: '' };
fillMultipleChoiceQuestion(new1);
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should(
'contain',
t(FAILURE_MESSAGES.EMPTY_QUESTION)
);
fillMultipleChoiceQuestion(new1, { shouldSave: false });
cy.checkErrorMessage({ errorMessage: t(FAILURE_MESSAGES.EMPTY_QUESTION) });

// empty answer
const new2 = {
Expand All @@ -162,10 +163,9 @@ describe('Multiple Choices', () => {
],
};
fillMultipleChoiceQuestion(new2, { shouldSave: false });
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should(
'contain',
t(FAILURE_MESSAGES.MULTIPLE_CHOICES_EMPTY_CHOICE)
);
cy.checkErrorMessage({
errorMessage: t(FAILURE_MESSAGES.MULTIPLE_CHOICES_EMPTY_CHOICE),
});

// no correct answer
const new3 = {
Expand All @@ -177,13 +177,12 @@ describe('Multiple Choices', () => {
],
};
fillMultipleChoiceQuestion(new3, { shouldSave: false });
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should(
'contain',
t(FAILURE_MESSAGES.MULTIPLE_CHOICES_CORRECT_ANSWER)
);
cy.checkErrorMessage({
errorMessage: t(FAILURE_MESSAGES.MULTIPLE_CHOICES_CORRECT_ANSWER),
});

fillMultipleChoiceQuestion(newMultipleChoiceData);
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should('not.exist');
cy.checkErrorMessage({});

cy.checkExplanationField(newMultipleChoiceData.explanation);
});
Expand Down
31 changes: 17 additions & 14 deletions cypress/e2e/Admin/create/slider.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ import {
buildQuestionStepCy,
dataCyWrapper,
} from '../../../../src/config/selectors';
import { APP_SETTINGS, QUESTION_APP_SETTINGS } from '../../../fixtures/appSettings';
import {
APP_SETTINGS,
QUESTION_APP_SETTINGS,
} from '../../../fixtures/appSettings';

const t = i18n.t;

Expand Down Expand Up @@ -80,6 +83,8 @@ const fillSliderQuestion = (
// save
if (shouldSave) {
cy.get(dataCyWrapper(CREATE_VIEW_SAVE_BUTTON_CY)).click();
} else {
cy.get(dataCyWrapper(CREATE_VIEW_SAVE_BUTTON_CY)).should('be.disabled');
}
};

Expand All @@ -101,25 +106,23 @@ describe('Slider', () => {
// empty min
const new1 = { ...newSliderData, min: null } as SliderValues;
const dataValue = (data as SliderAppDataData).value;
fillSliderQuestion(new1, dataValue);
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should(
'contain',
t(FAILURE_MESSAGES.SLIDER_UNDEFINED_MIN_MAX)
);
fillSliderQuestion(new1, dataValue, { shouldSave: false });
cy.checkErrorMessage({
errorMessage: t(FAILURE_MESSAGES.SLIDER_UNDEFINED_MIN_MAX),
});

// empty max
const new2 = { ...newSliderData, max: null } as SliderValues;
fillSliderQuestion(new2, new1.value, { shouldSave: false });
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should(
'contain',
t(FAILURE_MESSAGES.SLIDER_UNDEFINED_MIN_MAX)
);
cy.checkErrorMessage({
errorMessage: t(FAILURE_MESSAGES.SLIDER_UNDEFINED_MIN_MAX),
});
// // min higher than max
const new3 = { ...newSliderData, min: 100, max: 30 };
fillSliderQuestion(new3, new2.value, { shouldSave: false });
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should(
'contain',
t(FAILURE_MESSAGES.SLIDER_MIN_SMALLER_THAN_MAX)
);
cy.checkErrorMessage({
errorMessage: t(FAILURE_MESSAGES.SLIDER_MIN_SMALLER_THAN_MAX),
});

fillSliderQuestion(newSliderData, new3.value);
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should('not.exist');
Expand Down
21 changes: 15 additions & 6 deletions cypress/e2e/Admin/create/textInput.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@ import { Context, PermissionLevel } from '@graasp/sdk';

import {
APP_SETTING_NAMES,
FAILURE_MESSAGES,
QuestionType,
} from '../../../../src/config/constants';
import i18n from '../../../../src/config/i18n';
import {
CREATE_QUESTION_SELECT_TYPE_CY,
CREATE_QUESTION_TITLE_CY,
CREATE_VIEW_ERROR_ALERT_CY,
CREATE_VIEW_SAVE_BUTTON_CY,
QUESTION_BAR_ADD_NEW_BUTTON_CLASSNAME,
TEXT_INPUT_FIELD_CY,
buildQuestionStepCy,
dataCyWrapper,
} from '../../../../src/config/selectors';
import { APP_SETTINGS, QUESTION_APP_SETTINGS } from '../../../fixtures/appSettings';
import {
APP_SETTINGS,
QUESTION_APP_SETTINGS,
} from '../../../fixtures/appSettings';

const t = i18n.t;

const newTextInputData = {
question: 'new question text',
Expand Down Expand Up @@ -55,6 +61,8 @@ const fillTextInputQuestion = (
// save
if (shouldSave) {
cy.get(dataCyWrapper(CREATE_VIEW_SAVE_BUTTON_CY)).click();
} else {
cy.get(dataCyWrapper(CREATE_VIEW_SAVE_BUTTON_CY)).should('be.disabled');
}
};

Expand All @@ -78,9 +86,10 @@ describe('Text Input', () => {
...newTextInputData,
text: '',
};
fillTextInputQuestion(new1);

cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should('not.exist');
fillTextInputQuestion(new1, { shouldSave: false });
cy.checkErrorMessage({
errorMessage: t(FAILURE_MESSAGES.TEXT_INPUT_NOT_EMPTY),
});
});

it('Start with empty data and save question with non-empty response', () => {
Expand All @@ -98,7 +107,7 @@ describe('Text Input', () => {
cy.switchQuestionType(QuestionType.TEXT_INPUT);

fillTextInputQuestion(newTextInputData);
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should('not.exist');
cy.checkErrorMessage({});
});

describe('Display saved settings', () => {
Expand Down
34 changes: 34 additions & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Context, PermissionLevel } from '@graasp/sdk';
import { API_HOST } from '../../src/config/constants';
import {
CREATE_QUESTION_SELECT_TYPE_CY,
CREATE_VIEW_ERROR_ALERT_CY,
EXPLANATION_CY,
EXPLANATION_PLAY_CY,
NAVIGATION_ANALYTICS_BUTTON_CY,
Expand Down Expand Up @@ -198,3 +199,36 @@ Cypress.Commands.add(
}
}
);

Cypress.Commands.add(
'checkErrorMessage',
({
errorMessage,
severity = 'warning',
}: {
errorMessage?: string;
severity?: 'error' | 'warning';
} = {}) => {
const MUI_ALERT_WARNING = 'MuiAlert-standardWarning'.toLowerCase();
const MUI_ALERT_ERROR = 'MuiAlert-standardError'.toLowerCase();

if (errorMessage) {
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should(
'contain',
errorMessage
);

cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).then(($el) => {
if (severity === 'warning') {
expect($el.attr('class').toLowerCase()).to.contain(MUI_ALERT_WARNING);
} else if (severity === 'error') {
expect($el.attr('class').toLowerCase()).to.contain(
MUI_ALERT_ERROR
);
}
});
} else {
cy.get(dataCyWrapper(CREATE_VIEW_ERROR_ALERT_CY)).should('not.exist');
}
}
);
8 changes: 8 additions & 0 deletions cypress/support/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ declare global {
currentAttempts: number;
isCorrect?: boolean;
}): Chainable;

checkErrorMessage({
errorMessage,
severity,
}: {
errorMessage?: string;
severity?: 'error' | 'warning';
}): Chainable;
}
}
}
10 changes: 5 additions & 5 deletions src/components/context/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,11 @@ export const validateQuestionData = (data: QuestionData) => {

break;
// enable following lines to prevent empty correct answer
// case QuestionType.TEXT_INPUT:
// if (!data?.text?.length) {
// throw FAILURE_MESSAGES.TEXT_INPUT_NOT_EMPTY;
// }
// break;
case QuestionType.TEXT_INPUT:
if (!data?.text?.length) {
throw FAILURE_MESSAGES.TEXT_INPUT_NOT_EMPTY;
}
break;
default:
return true;
}
Expand Down
44 changes: 32 additions & 12 deletions src/components/create/CreateView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,42 @@ import QuestionTypeSelect from './QuestionTypeSelect';
import Slider from './Slider';
import TextInput from './TextInput';

type ValidationSeverity = 'warning' | 'error';

type ValidationMessage = {
msg: string;
severity: ValidationSeverity;
};

const CreateView = () => {
const { t } = useTranslation();
const { currentQuestion, deleteQuestion, saveQuestion } =
const { currentQuestion, deleteQuestion, saveQuestion, currentIdx } =
useContext(QuizContext);

const [newData, setNewData] = useState<QuestionData>(currentQuestion.data);
const [errorMessage, setErrorMessage] = useState<string | null>();
const [errorMessage, setErrorMessage] = useState<ValidationMessage | null>();
const [isSubmitted, setIsSubmitted] = useState(false);

// Reset is submitted when currentIdx changed to
// display errorMessage with the correct severity.
useEffect(() => {
setIsSubmitted(false);
}, [currentIdx]);

useEffect(() => {
setNewData(currentQuestion.data as QuestionData);
}, [currentQuestion]);

// validate data to enable save
useEffect(() => {
if (isSubmitted) {
try {
validateQuestionData(newData);
setErrorMessage(null);
} catch (e) {
setErrorMessage(e as string);
}
try {
validateQuestionData(newData);
setErrorMessage(null);
} catch (e) {
setErrorMessage({
msg: e as string,
severity: isSubmitted ? 'error' : 'warning',
});
}
}, [newData, isSubmitted]);

Expand All @@ -62,7 +76,10 @@ const CreateView = () => {
setErrorMessage(null);
saveQuestion(newData);
} catch (e) {
setErrorMessage(e as string);
setErrorMessage({
msg: e as string,
severity: 'error',
});
}
};

Expand Down Expand Up @@ -199,8 +216,11 @@ const CreateView = () => {
</Grid>
{errorMessage && (
<Grid item>
<Alert severity="error" data-cy={CREATE_VIEW_ERROR_ALERT_CY}>
{t(errorMessage)}
<Alert
severity={errorMessage.severity}
data-cy={CREATE_VIEW_ERROR_ALERT_CY}
>
{t(errorMessage.msg)}
</Alert>
</Grid>
)}
Expand Down
Loading

0 comments on commit 84553a7

Please sign in to comment.