refactor(client): migrate C# modals (#54176)

This commit is contained in:
Huyen Nguyen
2024-04-03 20:01:50 +07:00
committed by GitHub
parent a39e74052c
commit 595dd5f653
11 changed files with 74 additions and 65 deletions
@@ -59,7 +59,7 @@ const ExamResultsModal = ({
<Modal.Header showCloseButton={true} closeButtonClassNames='close'> <Modal.Header showCloseButton={true} closeButtonClassNames='close'>
{t('settings.labels.results-for', { projectTitle })} {t('settings.labels.results-for', { projectTitle })}
</Modal.Header> </Modal.Header>
<Modal.Body> <Modal.Body alignment='start'>
<Spacer size='medium' /> <Spacer size='medium' />
<div style={{ paddingLeft: '30px' }}> <div style={{ paddingLeft: '30px' }}>
<Row> <Row>
@@ -82,7 +82,7 @@ const ExamResultsModal = ({
</div> </div>
<Spacer size='medium' /> <Spacer size='medium' />
</Modal.Body> </Modal.Body>
<Modal.Footer> <Modal.Footer alignment='end'>
<Button <Button
onClick={() => { onClick={() => {
closeModal('examResults'); closeModal('examResults');
@@ -1,11 +1,10 @@
// Package Utilities // Package Utilities
import { Modal } from '@freecodecamp/react-bootstrap';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux'; import { bindActionCreators, Dispatch } from 'redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button } from '@freecodecamp/ui'; import { Button, Modal } from '@freecodecamp/ui';
// Local Utilities // Local Utilities
import { closeModal } from '../../redux/actions'; import { closeModal } from '../../redux/actions';
@@ -45,21 +44,17 @@ function ExitExamModal({
return ( return (
<Modal <Modal
animation={false} onClose={closeExitExamModal}
dialogClassName='exit-exam-modal' open={isExitExamModalOpen}
keyboard={true} variant='danger'
onHide={closeExitExamModal}
show={isExitExamModalOpen}
> >
<Modal.Header className='exit-exam-modal-header' closeButton={true}> <Modal.Header closeButtonClassNames='close'>
<Modal.Title className='text-center'> {t('learn.exam.exit-header')}
{t('learn.exam.exit-header')}
</Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body className='reset-modal-body'> <Modal.Body>
<div className='text-center'>{t('learn.exam.exit')}</div> <div className='text-center'>{t('learn.exam.exit')}</div>
</Modal.Body> </Modal.Body>
<Modal.Footer className='reset-modal-footer'> <Modal.Footer>
<Button <Button
data-cy='exit-exam-modal-deny' data-cy='exit-exam-modal-deny'
block={true} block={true}
@@ -1,11 +1,10 @@
// Package Utilities // Package Utilities
import { Modal } from '@freecodecamp/react-bootstrap';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux'; import { bindActionCreators, Dispatch } from 'redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button } from '@freecodecamp/ui'; import { Button, Modal } from '@freecodecamp/ui';
// Local Utilities // Local Utilities
import { closeModal } from '../../redux/actions'; import { closeModal } from '../../redux/actions';
@@ -44,22 +43,14 @@ function FinishExamModal({
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Modal <Modal onClose={closeFinishExamModal} open={isFinishExamModalOpen}>
animation={false} <Modal.Header closeButtonClassNames='close'>
dialogClassName='finish-exam-modal' {t('learn.exam.finish-header')}
keyboard={true}
onHide={closeFinishExamModal}
show={isFinishExamModalOpen}
>
<Modal.Header className='finish-exam-modal-header' closeButton={true}>
<Modal.Title className='text-center'>
{t('learn.exam.finish-header')}
</Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body className='reset-modal-body'> <Modal.Body>
<div className='text-center'>{t('learn.exam.finish')}</div> <div className='text-center'>{t('learn.exam.finish')}</div>
</Modal.Body> </Modal.Body>
<Modal.Footer className='reset-modal-footer'> <Modal.Footer>
<Button <Button
data-cy='finish-exam-modal-confirm' data-cy='finish-exam-modal-confirm'
block={true} block={true}
@@ -19,7 +19,7 @@ interface FoudationalCSharpSurveyAlertProps {
openSurveyModal: () => void; openSurveyModal: () => void;
} }
function FoudationalCSharpSurveyAlert({ function FoundationalCSharpSurveyAlert({
openSurveyModal openSurveyModal
}: FoudationalCSharpSurveyAlertProps): JSX.Element { }: FoudationalCSharpSurveyAlertProps): JSX.Element {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -45,6 +45,6 @@ function FoudationalCSharpSurveyAlert({
); );
} }
FoudationalCSharpSurveyAlert.displayName = 'FoundationalCSharpSurveyAlert'; FoundationalCSharpSurveyAlert.displayName = 'FoundationalCSharpSurveyAlert';
export default connect(null, mapDispatchToProps)(FoudationalCSharpSurveyAlert); export default connect(null, mapDispatchToProps)(FoundationalCSharpSurveyAlert);
@@ -1,11 +1,10 @@
import { Modal } from '@freecodecamp/react-bootstrap';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import type { Dispatch } from 'redux'; import type { Dispatch } from 'redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { Button } from '@freecodecamp/ui'; import { Button, Modal } from '@freecodecamp/ui';
import { SurveyResults, SurveyResponse } from '../../../../redux/prop-types'; import { SurveyResults, SurveyResponse } from '../../../../redux/prop-types';
import { Spacer } from '../../../../components/helpers'; import { Spacer } from '../../../../components/helpers';
@@ -132,12 +131,10 @@ function FoundationalCSharpSurvey({
return ( return (
<> <>
<Modal.Header data-cy='c-sharp-survey-modal' closeButton={true}> <Modal.Header closeButtonClassNames='close'>
<Modal.Title className='text-center'> {t('survey.foundational-c-sharp.title')}
{t('survey.foundational-c-sharp.title')}
</Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body className='reset-modal-body'> <Modal.Body>
{i18nSurvey.map((question, i) => ( {i18nSurvey.map((question, i) => (
<div key={i}> <div key={i}>
<Spacer size='medium' /> <Spacer size='medium' />
@@ -147,7 +144,6 @@ function FoundationalCSharpSurvey({
{question.options.map((option, j) => ( {question.options.map((option, j) => (
<label className='video-quiz-option-label' key={j}> <label className='video-quiz-option-label' key={j}>
<input <input
aria-label={t('aria.answer')}
checked={surveyResponses[i].responseIndex === j} checked={surveyResponses[i].responseIndex === j}
className='sr-only' className='sr-only'
name={question.question} name={question.question}
@@ -167,7 +163,7 @@ function FoundationalCSharpSurvey({
</div> </div>
))} ))}
</Modal.Body> </Modal.Body>
<Modal.Footer className='reset-modal-footer'> <Modal.Footer>
<Button <Button
block={true} block={true}
size='medium' size='medium'
@@ -1,8 +1,8 @@
import { Modal } from '@freecodecamp/react-bootstrap';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux'; import { bindActionCreators, Dispatch } from 'redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { Modal } from '@freecodecamp/ui';
import { closeModal } from '../../redux/actions'; import { closeModal } from '../../redux/actions';
import { isSurveyModalOpenSelector } from '../../redux/selectors'; import { isSurveyModalOpenSelector } from '../../redux/selectors';
@@ -39,11 +39,8 @@ function SurveyModal({
}: SurveyModalProps): JSX.Element { }: SurveyModalProps): JSX.Element {
return ( return (
<Modal <Modal
animation={false} onClose={() => (isProcessing ? '' : closeSurveyModal())}
dialogClassName='survey-modal' open={isSurveyModalOpen}
keyboard={true}
onHide={() => (isProcessing ? '' : closeSurveyModal())}
show={isSurveyModalOpen}
> >
<FoundationalCSharpSurvey /> <FoundationalCSharpSurvey />
</Modal> </Modal>
@@ -57,7 +57,7 @@ import ExitExamModal from './components/exit-exam-modal';
import FinishExamModal from './components/finish-exam-modal'; import FinishExamModal from './components/finish-exam-modal';
import ExamResults from './components/exam-results'; import ExamResults from './components/exam-results';
import MissingPrerequisites from './components/missing-prerequisites'; import MissingPrerequisites from './components/missing-prerequisites';
import FoundationCSharpSurveyAlert from './components/foundational-c-sharp-survey-alert'; import FoundationalCSharpSurveyAlert from './components/foundational-c-sharp-survey-alert';
import './exam.css'; import './exam.css';
@@ -554,7 +554,7 @@ class ShowExam extends Component<ShowExamProps, ShowExamState> {
missingPrerequisites={missingPrerequisites} missingPrerequisites={missingPrerequisites}
/> />
) : ( ) : (
<FoundationCSharpSurveyAlert /> <FoundationalCSharpSurveyAlert />
)} )}
</> </>
)} )}
@@ -7,7 +7,6 @@ const el = {
surveyAlert: "[data-cy='c-sharp-survey-alert']", surveyAlert: "[data-cy='c-sharp-survey-alert']",
startSurveyBtn: "[data-cy='start-csharp-survey-btn']", startSurveyBtn: "[data-cy='start-csharp-survey-btn']",
submitSurveyBtn: "[data-cy='submit-csharp-survey-btn']", submitSurveyBtn: "[data-cy='submit-csharp-survey-btn']",
surveyModal: "[data-cy='c-sharp-survey-modal']",
startExamBtn: "[data-cy='start-exam-btn']", startExamBtn: "[data-cy='start-exam-btn']",
examTime: "[data-cy='exam-time']", examTime: "[data-cy='exam-time']",
examInput: '.exam-answer-input-visible', examInput: '.exam-answer-input-visible',
@@ -53,7 +52,7 @@ describe('C# Exam Challenge', () => {
cy.get(el.surveyAlert).should('be.visible'); cy.get(el.surveyAlert).should('be.visible');
cy.get(el.startExamBtn).should('have.attr', 'aria-disabled'); cy.get(el.startExamBtn).should('have.attr', 'aria-disabled');
cy.get(el.startSurveyBtn).click(); cy.get(el.startSurveyBtn).click();
cy.get(el.surveyModal).should('be.visible'); cy.contains('Foundational C# with Microsoft Survey').should('be.visible');
cy.get(el.submitSurveyBtn).should('have.attr', 'aria-disabled'); cy.get(el.submitSurveyBtn).should('have.attr', 'aria-disabled');
cy.contains('Student developer').click(); cy.contains('Student developer').click();
cy.contains('Novice (no prior experience').click(); cy.contains('Novice (no prior experience').click();
+11 -5
View File
@@ -15,10 +15,8 @@ test.describe('Exam results modal', () => {
await expect(viewButton).toBeVisible(); await expect(viewButton).toBeVisible();
await viewButton.click(); await viewButton.click();
await expect(page.getByRole('dialog')).toHaveCount(1);
await expect( await expect(
page.getByRole('heading', { page.getByRole('dialog', {
name: 'Results for Foundational C# with Microsoft Certification Exam' name: 'Results for Foundational C# with Microsoft Certification Exam'
}) })
).toBeVisible(); ).toBeVisible();
@@ -36,7 +34,11 @@ test.describe('Exam results modal', () => {
await expect(viewButton).toBeVisible(); await expect(viewButton).toBeVisible();
await viewButton.click(); await viewButton.click();
await expect(page.getByRole('dialog')).toHaveCount(1); await expect(
page.getByRole('dialog', {
name: 'Results for Foundational C# with Microsoft Certification Exam'
})
).toBeVisible();
// There are 2 close buttons on the page, and the x button is the first // There are 2 close buttons on the page, and the x button is the first
const closeButton = page.getByRole('button', { name: 'Close' }).first(); const closeButton = page.getByRole('button', { name: 'Close' }).first();
@@ -52,7 +54,11 @@ test.describe('Exam results modal', () => {
await expect(viewButton).toBeVisible(); await expect(viewButton).toBeVisible();
await viewButton.click(); await viewButton.click();
await expect(page.getByRole('dialog')).toHaveCount(1); await expect(
page.getByRole('dialog', {
name: 'Results for Foundational C# with Microsoft Certification Exam'
})
).toBeVisible();
// There are 2 close buttons on the page, and the close button is the last // There are 2 close buttons on the page, and the close button is the last
const closeButton = page.getByRole('button', { name: 'Close' }).last(); const closeButton = page.getByRole('button', { name: 'Close' }).last();
+14 -4
View File
@@ -15,6 +15,7 @@ test.beforeEach(async ({ page }) => {
name: translations.buttons['click-start-exam'] name: translations.buttons['click-start-exam']
}) })
.click(); .click();
await page await page
.getByRole('button', { .getByRole('button', {
name: translations.buttons['exit-exam'] name: translations.buttons['exit-exam']
@@ -27,19 +28,23 @@ test.describe('Exit exam Modal E2E Test Suite', () => {
page page
}) => { }) => {
await expect( await expect(
page.getByText(translations.learn.exam['exit-header']) page.getByRole('dialog', { name: translations.learn.exam['exit-header'] })
).toBeVisible(); ).toBeVisible();
await expect(page.getByText(translations.learn.exam.exit)).toBeVisible(); await expect(page.getByText(translations.learn.exam.exit)).toBeVisible();
await expect( await expect(
page.getByRole('button', { page.getByRole('button', {
name: translations.learn.exam['exit-yes'] name: translations.learn.exam['exit-yes']
}) })
).toBeVisible(); ).toBeVisible();
await expect( await expect(
page.getByRole('button', { page.getByRole('button', {
name: translations.buttons.close name: translations.buttons.close
}) })
).toBeVisible(); ).toBeVisible();
await expect( await expect(
page.getByRole('button', { page.getByRole('button', {
name: translations.learn.exam['exit-no'] name: translations.learn.exam['exit-no']
@@ -53,9 +58,11 @@ test.describe('Exit exam Modal E2E Test Suite', () => {
await page await page
.getByRole('button', { name: translations.learn.exam['exit-no'] }) .getByRole('button', { name: translations.learn.exam['exit-no'] })
.click(); .click();
await expect( await expect(
page.getByText(translations.learn.exam['exit-header']) page.getByRole('dialog', { name: translations.learn.exam['exit-header'] })
).not.toBeVisible(); ).not.toBeVisible();
await expect(page).toHaveURL(examUrl); await expect(page).toHaveURL(examUrl);
}); });
@@ -65,8 +72,9 @@ test.describe('Exit exam Modal E2E Test Suite', () => {
await page await page
.getByRole('button', { name: translations.learn.exam['exit-yes'] }) .getByRole('button', { name: translations.learn.exam['exit-yes'] })
.click(); .click();
await expect( await expect(
page.getByText(translations.learn.exam['exit-header']) page.getByRole('dialog', { name: translations.learn.exam['exit-header'] })
).not.toBeVisible(); ).not.toBeVisible();
await expect(page).toHaveURL(cancelExamUrl); await expect(page).toHaveURL(cancelExamUrl);
@@ -80,9 +88,11 @@ test.describe('Exit exam Modal E2E Test Suite', () => {
name: translations.buttons.close name: translations.buttons.close
}) })
.click(); .click();
await expect( await expect(
page.getByText(translations.learn.exam['exit-header']) page.getByRole('dialog', { name: translations.learn.exam['exit-header'] })
).not.toBeVisible(); ).not.toBeVisible();
await expect(page).toHaveURL(examUrl); await expect(page).toHaveURL(examUrl);
}); });
}); });
+19 -4
View File
@@ -38,19 +38,25 @@ test.describe('Finish Exit exam Modal E2E Test Suite', () => {
page page
}) => { }) => {
await expect( await expect(
page.getByText(translations.learn.exam['finish-header']) page.getByRole('dialog', {
name: translations.learn.exam['finish-header']
})
).toBeVisible(); ).toBeVisible();
await expect( await expect(
page.getByRole('button', { page.getByRole('button', {
name: translations.buttons.close name: translations.buttons.close
}) })
).toBeVisible(); ).toBeVisible();
await expect(page.getByText(translations.learn.exam.finish)).toBeVisible(); await expect(page.getByText(translations.learn.exam.finish)).toBeVisible();
await expect( await expect(
page.getByRole('button', { page.getByRole('button', {
name: translations.learn.exam['finish-yes'] name: translations.learn.exam['finish-yes']
}) })
).toBeVisible(); ).toBeVisible();
await expect( await expect(
page.getByRole('button', { page.getByRole('button', {
name: translations.learn.exam['finish-no'] name: translations.learn.exam['finish-no']
@@ -64,8 +70,11 @@ test.describe('Finish Exit exam Modal E2E Test Suite', () => {
await page await page
.getByRole('button', { name: translations.learn.exam['exit-no'] }) .getByRole('button', { name: translations.learn.exam['exit-no'] })
.click(); .click();
await expect( await expect(
page.getByText(translations.learn.exam['exit-header']) page.getByRole('dialog', {
name: translations.learn.exam['finish-header']
})
).not.toBeVisible(); ).not.toBeVisible();
}); });
@@ -75,8 +84,11 @@ test.describe('Finish Exit exam Modal E2E Test Suite', () => {
await page await page
.getByRole('button', { name: translations.learn.exam['finish-yes'] }) .getByRole('button', { name: translations.learn.exam['finish-yes'] })
.click(); .click();
await expect( await expect(
page.getByText(translations.learn.exam['finish-header']) page.getByRole('dialog', {
name: translations.learn.exam['finish-header']
})
).not.toBeVisible(); ).not.toBeVisible();
}); });
@@ -86,8 +98,11 @@ test.describe('Finish Exit exam Modal E2E Test Suite', () => {
await page await page
.getByRole('button', { name: translations.buttons.close }) .getByRole('button', { name: translations.buttons.close })
.click(); .click();
await expect( await expect(
page.getByText(translations.learn.exam['finish-header']) page.getByRole('dialog', {
name: translations.learn.exam['finish-header']
})
).not.toBeVisible(); ).not.toBeVisible();
}); });
}); });