Skip to content

test: add snapshot test for profile #1221

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

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
776cd12
feat: added country disabling feature (#1084)
awais-ansari Sep 17, 2024
2cbdc5e
Merge branch 'master' of github.com:openedx/frontend-app-profile into…
awais-ansari Oct 10, 2024
ca2a79d
Merge pull request #1102 from openedx/aansari/rebase-with-master
awais-ansari Oct 11, 2024
2a99330
Merge branch 'master' of github.com:openedx/frontend-app-profile into…
muhammadadeeltajamul Nov 7, 2024
a03cdc8
Merge pull request #1121 from openedx/inf-renovate-reselect-5
muhammadadeeltajamul Nov 8, 2024
cd73616
Merge branch 'master' of github.com:openedx/frontend-app-profile into…
muhammadadeeltajamul Nov 11, 2024
f3a328c
Merge pull request #1122 from openedx/inf-rebase-dep-0001
muhammadadeeltajamul Nov 11, 2024
c025310
feat: reskin of Profile MFE main page (#1114)
eemaanamir Nov 18, 2024
b92efcd
feat: fixed reloading issue
sundasnoreen12 Nov 25, 2024
c9b3fb9
fix: fixed responsiveness issues on mobile view (#1133)
eemaanamir Nov 26, 2024
e8be5b5
test: added not found test case
sundasnoreen12 Nov 26, 2024
1d0059e
Merge pull request #1137 from openedx/sundasNew/INF-1594
sundasnoreen12 Nov 26, 2024
acfcfb4
Merge branch 'master' of github.com:openedx/frontend-app-profile into…
awais-ansari Mar 18, 2025
2216c1b
test: updated test cases
awais-ansari Mar 18, 2025
adb12b4
Merge pull request #1184 from openedx/aansari/sync-with-master
awais-ansari Mar 18, 2025
29edfa9
feat: added restricted country functionality
sundasnoreen12 Mar 18, 2025
7e3571e
fix: fixed test cases
sundasnoreen12 Mar 18, 2025
3fffbfe
test: updated snapshot
sundasnoreen12 Mar 18, 2025
896f2ce
test: updated test cases
awais-ansari Mar 18, 2025
bf5dd5b
Merge pull request #1185 from openedx/sundas/restricted-countries-cha…
sundasnoreen12 Mar 19, 2025
82ce644
feat: made profile editable (#1212)
eemaanamir May 30, 2025
8d2bd72
feat: made fullname uneditable and added redirect link (#1215)
eemaanamir Jun 10, 2025
f249c7f
test: add test cases for NotFoundPage
awais-ansari Jun 18, 2025
02681b7
test: add form fields test cases
awais-ansari Jun 18, 2025
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
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ APP_ID=''
MFE_CONFIG_API_URL=''
SEARCH_CATALOG_URL=''
ENABLE_SKILLS_BUILDER_PROFILE=''
ENABLE_NEW_PROFILE_VIEW=''
DISABLE_VISIBILITY_EDITING=''
2 changes: 2 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ APP_ID=''
MFE_CONFIG_API_URL=''
SEARCH_CATALOG_URL='http://localhost:18000/courses'
ENABLE_SKILLS_BUILDER_PROFILE=''
ENABLE_NEW_PROFILE_VIEW=''
DISABLE_VISIBILITY_EDITING=''
2 changes: 2 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ LEARNER_RECORD_MFE_BASE_URL='http://localhost:1990'
COLLECT_YEAR_OF_BIRTH=true
APP_ID=''
MFE_CONFIG_API_URL=''
ENABLE_NEW_PROFILE_VIEW=''
DISABLE_VISIBILITY_EDITING=''
10 changes: 3 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions src/data/reducers.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { combineReducers } from 'redux';

import { reducer as profilePage } from '../profile';
import { getConfig } from '@edx/frontend-platform';

import { reducer as profilePageReducer } from '../profile';
import { reducer as newProfilePageReducer } from '../profile-v2';

const isNewProfileEnabled = getConfig().ENABLE_NEW_PROFILE_VIEW;

const createRootReducer = () => combineReducers({
profilePage,
profilePage: isNewProfileEnabled ? newProfilePageReducer : profilePageReducer,
});

export default createRootReducer;
7 changes: 5 additions & 2 deletions src/data/sagas.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { all } from 'redux-saga/effects';

import { getConfig } from '@edx/frontend-platform';
import { saga as profileSaga } from '../profile';
import { saga as newProfileSaga } from '../profile-v2';

const isNewProfileEnabled = getConfig().ENABLE_NEW_PROFILE_VIEW;

export default function* rootSaga() {
yield all([
profileSaga(),
isNewProfileEnabled ? newProfileSaga() : profileSaga(),
]);
}
8 changes: 8 additions & 0 deletions src/index-v2.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@import "~@edx/brand/paragon/fonts";
@import "~@edx/brand/paragon/variables";
@import "~@openedx/paragon/scss/core/core";
@import "~@edx/brand/paragon/overrides";
@import "~@edx/frontend-component-header/dist/index";
@import "~@edx/frontend-component-footer/dist/footer";

@import './profile-v2/index';
13 changes: 10 additions & 3 deletions src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
initialize,
mergeConfig,
subscribe,
getConfig,
} from '@edx/frontend-platform';
import {
AppProvider,
Expand All @@ -22,18 +23,23 @@ import FooterSlot from '@openedx/frontend-slot-footer';
import messages from './i18n';
import configureStore from './data/configureStore';

import './index.scss';
import Head from './head/Head';

import AppRoutes from './routes/AppRoutes';

subscribe(APP_READY, () => {
subscribe(APP_READY, async () => {
const isNewProfileEnabled = getConfig().ENABLE_NEW_PROFILE_VIEW === 'true';
if (isNewProfileEnabled) {
await import('./index-v2.scss');
} else {
await import('./index.scss');
}
ReactDOM.render(
<AppProvider store={configureStore()}>
<Head />
<Header />
<main id="main">
<AppRoutes />
<AppRoutes isNewProfileEnabled={isNewProfileEnabled} />
</main>
<FooterSlot />
</AppProvider>,
Expand All @@ -53,6 +59,7 @@ initialize({
mergeConfig({
COLLECT_YEAR_OF_BIRTH: process.env.COLLECT_YEAR_OF_BIRTH,
ENABLE_SKILLS_BUILDER_PROFILE: process.env.ENABLE_SKILLS_BUILDER_PROFILE,
ENABLE_NEW_PROFILE_VIEW: process.env.ENABLE_NEW_PROFILE_VIEW || null,
}, 'App loadConfig override handler');
},
},
Expand Down
4 changes: 4 additions & 0 deletions src/pacts/frontend-app-profile-edx-platform.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@
"dateJoined": "2017-06-07T00:44:23Z",
"email": "[email protected]",
"isActive": true,
"languageProficiencies": [],
"levelOfEducation": null,
"name": "Lemon Seltzer",
"profileImage": {},
"socialLinks": [],
"username": "staff",
"yearOfBirth": 1901
},
Expand Down
146 changes: 146 additions & 0 deletions src/profile-v2/CertificateCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedDate, FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import { Hyperlink } from '@openedx/paragon';
import get from 'lodash.get';

import classNames from 'classnames';
import professionalCertificateSVG from './assets/professional-certificate.svg';
import verifiedCertificateSVG from './assets/verified-certificate.svg';
import messages from './Certificates.messages';
import { useIsOnMobileScreen } from './data/hooks';

const CertificateCard = ({
certificateType,
courseDisplayName,
courseOrganization,
modifiedDate,
downloadUrl,
courseId,
uuid,
}) => {
const intl = useIntl();

const certificateIllustration = {
professional: professionalCertificateSVG,
'no-id-professional': professionalCertificateSVG,
verified: verifiedCertificateSVG,
honor: null,
audit: null,
}[certificateType] || null;

const isMobileView = useIsOnMobileScreen();

return (
<div
key={`${modifiedDate}-${courseId}`}
className="col-auto d-flex align-items-center p-0"
>
<div className="col certificate p-4 border-light-400 bg-light-200 w-100 h-100">
<div
className="certificate-type-illustration"
style={{ backgroundImage: `url(${certificateIllustration})` }}
/>
<div className={classNames(
'd-flex flex-column position-relative p-0',
{ 'max-width-304px': isMobileView },
{ 'width-314px': !isMobileView },
)}
>
<div className="w-100 color-black">
<p className={classNames([
'mb-0 font-weight-normal',
isMobileView ? 'x-small' : 'small',
])}
>
{intl.formatMessage(get(
messages,
`profile.certificates.types.${certificateType}`,
messages['profile.certificates.types.unknown'],
))}
</p>
<p className={classNames([
'm-0 color-black',
isMobileView ? 'h5' : 'h4',
])}
>
{courseDisplayName}
</p>
<p className={classNames([
'mb-0',
isMobileView ? 'x-small' : 'small',
])}
>
<FormattedMessage
id="profile.certificate.organization.label"
defaultMessage="From"
/>
</p>
<h5 className="mb-0 color-black">{courseOrganization}</h5>
<p className={classNames([
'mb-0',
isMobileView ? 'x-small' : 'small',
])}
>
<FormattedMessage
id="profile.certificate.completion.date.label"
defaultMessage="Completed on {date}"
values={{
date: <FormattedDate value={new Date(modifiedDate)} />,
}}
/>
</p>
</div>
<div className="pt-3">
<Hyperlink
destination={downloadUrl}
target="_blank"
showLaunchIcon={false}
className={classNames(
'btn btn-primary btn-rounded font-weight-normal px-4 py-10px',
{ 'btn-sm': isMobileView },
)}
>
{intl.formatMessage(messages['profile.certificates.view.certificate'])}
</Hyperlink>
</div>
<p
className={classNames([
'mb-0 pt-3',
isMobileView ? 'x-small' : 'small',
])}
>
<FormattedMessage
id="profile.certificate.uuid"
defaultMessage="Credential ID {certificate_uuid}"
values={{
certificate_uuid: uuid,
}}
/>
</p>
</div>
</div>
</div>
);
};

CertificateCard.propTypes = {
certificateType: PropTypes.string,
courseDisplayName: PropTypes.string,
courseOrganization: PropTypes.string,
modifiedDate: PropTypes.string,
downloadUrl: PropTypes.string,
courseId: PropTypes.string.isRequired,
uuid: PropTypes.string,
};

CertificateCard.defaultProps = {
certificateType: 'unknown',
courseDisplayName: '',
courseOrganization: '',
modifiedDate: '',
downloadUrl: '',
uuid: '',
};

export default CertificateCard;
92 changes: 92 additions & 0 deletions src/profile-v2/Certificates.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { connect } from 'react-redux';
import { getConfig } from '@edx/frontend-platform';

import classNames from 'classnames';
import CertificateCard from './CertificateCard';
import { certificatesSelector } from './data/selectors';
import { useIsOnTabletScreen } from './data/hooks';

const Certificates = ({ certificates }) => {
const isTabletView = useIsOnTabletScreen();
return (
<div>
<div className="col justify-content-start align-items-start g-5rem p-0">
<div className="col align-self-stretch height-42px justify-content-start align-items-start p-0">
<p className="font-weight-bold text-primary-500 m-0 h2">
<FormattedMessage
id="profile.your.certificates"
defaultMessage="Your certificates"
description="heading for the certificates section"
/>
</p>
</div>
<div className="col justify-content-start align-items-start pt-2 p-0">
<p className="font-weight-normal text-gray-800 m-0 p-0 p">
<FormattedMessage
id="profile.certificates.description"
defaultMessage="Your learner records information is only visible to you. Only your username and profile image are visible to others on {siteName}."
description="description of the certificates section"
values={{
siteName: getConfig().SITE_NAME,
}}
/>
</p>
</div>
</div>
{certificates?.length > 0 ? (
<div className="col">
<div className={classNames(
'row align-items-center pt-5 g-3rem',
{ 'justify-content-center': isTabletView },
)}
>
{certificates.map(certificate => (
<CertificateCard
key={certificate.courseId}
certificateType={certificate.certificateType}
courseDisplayName={certificate.courseDisplayName}
courseOrganization={certificate.courseOrganization}
modifiedDate={certificate.modifiedDate}
downloadUrl={certificate.downloadUrl}
courseId={certificate.courseId}
uuid={certificate.uuid}
/>
))}
</div>
</div>
) : (
<div className="pt-5">
<FormattedMessage
id="profile.no.certificates"
defaultMessage="You don't have any certificates yet."
description="displays when user has no course completion certificates"
/>
</div>
)}
</div>
);
};

Certificates.propTypes = {
certificates: PropTypes.arrayOf(PropTypes.shape({
certificateType: PropTypes.string,
courseDisplayName: PropTypes.string,
courseOrganization: PropTypes.string,
modifiedDate: PropTypes.string,
downloadUrl: PropTypes.string,
courseId: PropTypes.string.isRequired,
uuid: PropTypes.string,
})),
};

Certificates.defaultProps = {
certificates: [],
};

export default connect(
certificatesSelector,
{},
)(Certificates);
Loading