mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-07-05 00:14:02 -04:00
feat: [UEPR-57] migrate to react v18
This commit is contained in:
parent
c2c9f4523c
commit
a739682d91
21 changed files with 2538 additions and 3112 deletions
4602
package-lock.json
generated
4602
package-lock.json
generated
File diff suppressed because it is too large
Load diff
31
package.json
31
package.json
|
@ -52,10 +52,10 @@
|
|||
"lodash.defaults": "4.2.0",
|
||||
"lodash.get": "4.4.2",
|
||||
"react-confetti": "6.1.0",
|
||||
"react-helmet": "5.2.1",
|
||||
"react-helmet": "^6.0.0",
|
||||
"react-onclickoutside": "6.13.0",
|
||||
"react-router-dom": "5.3.4",
|
||||
"react-twitter-embed": "3.0.3",
|
||||
"react-router-dom": "6.30.0",
|
||||
"react-twitter-embed": "4.0.4",
|
||||
"react-use": "17.6.0",
|
||||
"scratch-parser": "6.0.0",
|
||||
"scratch-storage": "^4.0.55"
|
||||
|
@ -75,6 +75,9 @@
|
|||
"@formatjs/intl-pluralrules": "5.4.3",
|
||||
"@formatjs/intl-relativetimeformat": "11.4.10",
|
||||
"@scratch/scratch-gui": "^11.0.0-beta.2",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/jest": "29.5.14",
|
||||
"async": "3.2.6",
|
||||
"autoprefixer": "10.4.20",
|
||||
|
@ -90,8 +93,6 @@
|
|||
"css-loader": "5.2.7",
|
||||
"email-validator": "2.0.4",
|
||||
"emit-file-webpack-plugin": "2.0.1",
|
||||
"enzyme": "3.11.0",
|
||||
"enzyme-adapter-react-16": "1.15.8",
|
||||
"eslint": "8.57.1",
|
||||
"eslint-config-scratch": "9.0.9",
|
||||
"eslint-plugin-jest": "27.9.0",
|
||||
|
@ -100,7 +101,7 @@
|
|||
"eslint-plugin-react-hooks": "4.6.2",
|
||||
"fastly": "1.2.1",
|
||||
"file-loader": "6.2.0",
|
||||
"formik": "1.5.8",
|
||||
"formik": "2.4.6",
|
||||
"formsy-react": "1.1.6",
|
||||
"formsy-react-components": "1.1.0",
|
||||
"git-bundle-sha": "0.0.2",
|
||||
|
@ -130,18 +131,18 @@
|
|||
"postcss-simple-vars": "5.0.2",
|
||||
"prop-types": "15.8.1",
|
||||
"query-string": "9.1.1",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
"react-intl": "5.25.1",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-intl": "6.8.9",
|
||||
"react-modal": "3.16.3",
|
||||
"react-plotly.js": "2.6.0",
|
||||
"react-redux": "5.1.2",
|
||||
"react-responsive": "3.0.0",
|
||||
"react-redux": "^8.0.0",
|
||||
"react-responsive": "9.0.0",
|
||||
"react-slick": "0.30.3",
|
||||
"react-string-replace": "0.4.1",
|
||||
"react-telephone-input": "4.75.5",
|
||||
"react-test-renderer": "16.14.0",
|
||||
"redux": "3.7.2",
|
||||
"react-test-renderer": "18.3.1",
|
||||
"redux": "^4.0.0",
|
||||
"redux-mock-store": "1.5.5",
|
||||
"redux-thunk": "2.4.2",
|
||||
"regenerator-runtime": "0.13.9",
|
||||
|
@ -152,7 +153,7 @@
|
|||
"slick-carousel": "1.8.1",
|
||||
"stream-browserify": "3.0.0",
|
||||
"style-loader": "4.0.0",
|
||||
"tap": "14.11.0",
|
||||
"tap": "16.3.10",
|
||||
"url-loader": "3.0.0",
|
||||
"use-onclickoutside": "0.4.1",
|
||||
"webpack": "5.98.0",
|
||||
|
@ -163,9 +164,9 @@
|
|||
},
|
||||
"jest": {
|
||||
"setupFiles": [
|
||||
"<rootDir>/test/helpers/enzyme-setup.js",
|
||||
"jest-canvas-mock"
|
||||
],
|
||||
"testEnvironment": "jsdom",
|
||||
"moduleNameMapper": {
|
||||
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test/__mocks__/fileMock.js",
|
||||
"\\.(css|less|scss)$": "<rootDir>/test/__mocks__/styleMock.js"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// JSX syntax transforms to React.createElement
|
||||
const React = require('react'); // eslint-disable-line
|
||||
const ReactDOM = require('react-dom');
|
||||
const ReactDOM = require('react-dom/client');
|
||||
const StoreProvider = require('react-redux').Provider;
|
||||
const IntlProvider = require('react-intl').IntlProvider;
|
||||
|
||||
|
@ -33,8 +33,10 @@ const render = (jsx, element, reducers, initialState, enhancer) => {
|
|||
intlPolyfill(intlLocale).then(() => {
|
||||
const store = configureStore(reducers, initialState, enhancer);
|
||||
|
||||
const root = ReactDOM.createRoot(element);
|
||||
|
||||
// Render view component
|
||||
ReactDOM.render(
|
||||
root.render(
|
||||
<StoreProvider store={store}>
|
||||
<IntlProvider
|
||||
locale={intlLocale}
|
||||
|
@ -43,8 +45,7 @@ const render = (jsx, element, reducers, initialState, enhancer) => {
|
|||
>
|
||||
{jsx}
|
||||
</IntlProvider>
|
||||
</StoreProvider>,
|
||||
element
|
||||
</StoreProvider>
|
||||
);
|
||||
|
||||
// Get initial session & permissions
|
||||
|
|
|
@ -117,7 +117,7 @@ const Ideas = () => {
|
|||
src="https://scratch.mit.edu/projects/1108790117/embed"
|
||||
width="485"
|
||||
height="402"
|
||||
allowfullscreen
|
||||
allowFullScreen
|
||||
className="ideas-project"
|
||||
/>
|
||||
<div className="banner-description">
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/* eslint-disable react/jsx-no-bind */
|
||||
import React, {useState} from 'react';
|
||||
import React, {useRef, useState} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import onClickOutside from 'react-onclickoutside';
|
||||
import useOnClickOutside from 'use-onclickoutside';
|
||||
|
||||
import {selectStudioDescription, selectIsFetchingInfo} from '../../redux/studio';
|
||||
import {selectCanEditInfo, selectShowEditMuteError} from '../../redux/studio-permissions';
|
||||
|
@ -33,9 +33,11 @@ const StudioDescription = ({
|
|||
const [showMuteMessage, setShowMuteMessage] = useState(false);
|
||||
const [hideValidationMessage, setHideValidationMessage] = useState(false);
|
||||
|
||||
StudioDescription.handleClickOutside = () => {
|
||||
const ref = useRef(null);
|
||||
|
||||
useOnClickOutside(ref, () => {
|
||||
setHideValidationMessage(true);
|
||||
};
|
||||
});
|
||||
|
||||
const fieldClassName = classNames('studio-description', {
|
||||
'mod-fetching': isFetching,
|
||||
|
@ -49,6 +51,7 @@ const StudioDescription = ({
|
|||
className="studio-info-section"
|
||||
onMouseEnter={() => isMutedEditor && setShowMuteMessage(true)}
|
||||
onMouseLeave={() => isMutedEditor && setShowMuteMessage(false)}
|
||||
ref={ref}
|
||||
>
|
||||
{canEditInfo || isMutedEditor ? (
|
||||
<React.Fragment>
|
||||
|
@ -81,10 +84,6 @@ const StudioDescription = ({
|
|||
);
|
||||
};
|
||||
|
||||
const clickOutsideConfig = {
|
||||
handleClickOutside: () => StudioDescription.handleClickOutside
|
||||
};
|
||||
|
||||
StudioDescription.propTypes = {
|
||||
descriptionError: PropTypes.string,
|
||||
canEditInfo: PropTypes.bool,
|
||||
|
@ -95,7 +94,7 @@ StudioDescription.propTypes = {
|
|||
handleUpdate: PropTypes.func
|
||||
};
|
||||
|
||||
const connectedStudioDescription = connect(
|
||||
export default connect(
|
||||
state => ({
|
||||
description: selectStudioDescription(state),
|
||||
canEditInfo: selectCanEditInfo(state),
|
||||
|
@ -108,5 +107,3 @@ const connectedStudioDescription = connect(
|
|||
handleUpdate: mutateStudioDescription
|
||||
}
|
||||
)(StudioDescription);
|
||||
|
||||
export default onClickOutside(connectedStudioDescription, clickOutsideConfig);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-disable react/jsx-no-bind */
|
||||
import React, {useState} from 'react';
|
||||
import React, {useState, useRef} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
@ -32,7 +32,7 @@ const StudioFollow = ({
|
|||
});
|
||||
const [hideValidationMessage, setHideValidationMessage] = useState(false);
|
||||
|
||||
const ref = React.useRef(null);
|
||||
const ref = useRef(null);
|
||||
|
||||
useOnClickOutside(ref, () => {
|
||||
setHideValidationMessage(true);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/* eslint-disable react/jsx-no-bind */
|
||||
import React, {useState} from 'react';
|
||||
import React, {useRef, useState} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import onClickOutside from 'react-onclickoutside';
|
||||
import useOnClickOutside from 'use-onclickoutside';
|
||||
|
||||
import {selectStudioImage, selectIsFetchingInfo} from '../../redux/studio';
|
||||
import {selectCanEditInfo, selectShowEditMuteError} from '../../redux/studio-permissions';
|
||||
|
@ -46,9 +46,12 @@ const StudioImage = ({
|
|||
const [showMuteMessage, setShowMuteMessage] = useState(false);
|
||||
const [hideValidationMessage, setHideValidationMessage] = useState(false);
|
||||
|
||||
StudioImage.handleClickOutside = () => {
|
||||
const ref = useRef(null);
|
||||
|
||||
useOnClickOutside(ref, () => {
|
||||
setHideValidationMessage(true);
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={fieldClassName}
|
||||
|
@ -99,10 +102,6 @@ const StudioImage = ({
|
|||
);
|
||||
};
|
||||
|
||||
const clickOutsideConfig = {
|
||||
handleClickOutside: () => StudioImage.handleClickOutside
|
||||
};
|
||||
|
||||
StudioImage.propTypes = {
|
||||
imageError: PropTypes.string,
|
||||
canEditInfo: PropTypes.bool,
|
||||
|
@ -113,7 +112,7 @@ StudioImage.propTypes = {
|
|||
handleUpdate: PropTypes.func
|
||||
};
|
||||
|
||||
const connectedStudioImage = connect(
|
||||
export default connect(
|
||||
state => ({
|
||||
image: selectStudioImage(state),
|
||||
canEditInfo: selectCanEditInfo(state),
|
||||
|
@ -126,5 +125,3 @@ const connectedStudioImage = connect(
|
|||
handleUpdate: mutateStudioImage
|
||||
}
|
||||
)(StudioImage);
|
||||
|
||||
export default onClickOutside(connectedStudioImage, clickOutsideConfig);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import React, {useCallback} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import {useRouteMatch, NavLink} from 'react-router-dom';
|
||||
import {useParams, NavLink} from 'react-router-dom';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import SubNavigation from '../../components/subnavigation/subnavigation.jsx';
|
||||
|
@ -40,67 +40,67 @@ const countLimits = {
|
|||
};
|
||||
|
||||
const StudioTabNav = ({isFetchingInfo, commentCount, projectCount}) => {
|
||||
const {params: {studioPath, studioId}} = useRouteMatch();
|
||||
const {studioPath, studioId} = useParams();
|
||||
const base = `/${studioPath}/${studioId}`;
|
||||
const classes = useCallback(({isActive}) => `nav-link ${isActive ? 'activated' : ''}`);
|
||||
return (
|
||||
<SubNavigation
|
||||
align="left"
|
||||
className="studio-tab-nav"
|
||||
>
|
||||
<NavLink
|
||||
activeClassName="active"
|
||||
className="nav_link"
|
||||
className={classes}
|
||||
to={base}
|
||||
exact
|
||||
>
|
||||
<li><img
|
||||
src={projectsIcon}
|
||||
/><FormattedMessage
|
||||
id={isFetchingInfo ? 'studio.tabNavProjects' : 'studio.tabNavProjectsWithCount'}
|
||||
values={{
|
||||
projectCount: (
|
||||
<span className="tab-count">
|
||||
({limitCount(projectCount, countLimits.projects)})
|
||||
</span>
|
||||
)
|
||||
}}
|
||||
/></li>
|
||||
<li>
|
||||
<img src={projectsIcon} />
|
||||
<FormattedMessage
|
||||
id={isFetchingInfo ? 'studio.tabNavProjects' : 'studio.tabNavProjectsWithCount'}
|
||||
values={{
|
||||
projectCount: (
|
||||
<span className="tab-count">
|
||||
({limitCount(projectCount, countLimits.projects)})
|
||||
</span>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
activeClassName="active"
|
||||
className="nav_link"
|
||||
className={classes}
|
||||
to={`${base}/comments`}
|
||||
>
|
||||
<li><img
|
||||
src={commentsIcon}
|
||||
/><FormattedMessage
|
||||
id={isFetchingInfo ? 'studio.tabNavComments' : 'studio.tabNavCommentsWithCount'}
|
||||
values={{
|
||||
commentCount: (
|
||||
<span className="tab-count">
|
||||
({limitCount(commentCount, countLimits.comments)})
|
||||
</span>
|
||||
)
|
||||
}}
|
||||
/></li>
|
||||
<li>
|
||||
<img src={commentsIcon} />
|
||||
<FormattedMessage
|
||||
id={isFetchingInfo ? 'studio.tabNavComments' : 'studio.tabNavCommentsWithCount'}
|
||||
values={{
|
||||
commentCount: (
|
||||
<span className="tab-count">
|
||||
({limitCount(commentCount, countLimits.comments)})
|
||||
</span>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
activeClassName="active"
|
||||
className="nav_link"
|
||||
className={classes}
|
||||
to={`${base}/curators`}
|
||||
>
|
||||
<li><img
|
||||
src={curatorsIcon}
|
||||
/><FormattedMessage id="studio.tabNavCurators" /></li>
|
||||
<li>
|
||||
<img src={curatorsIcon} />
|
||||
<FormattedMessage id="studio.tabNavCurators" />
|
||||
</li>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
activeClassName="active"
|
||||
className="nav_link"
|
||||
className={classes}
|
||||
to={`${base}/activity`}
|
||||
>
|
||||
<li><img
|
||||
src={activityIcon}
|
||||
/><FormattedMessage id="studio.tabNavActivity" /></li>
|
||||
<li>
|
||||
<img src={activityIcon} />
|
||||
<FormattedMessage id="studio.tabNavActivity" />
|
||||
</li>
|
||||
</NavLink>
|
||||
</SubNavigation>
|
||||
);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
/* eslint-disable react/jsx-no-bind */
|
||||
import React, {useState} from 'react';
|
||||
import React, {useRef, useState} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import onClickOutside from 'react-onclickoutside';
|
||||
import useOnClickOutside from 'use-onclickoutside';
|
||||
|
||||
import '../../components/forms/inplace-input.scss';
|
||||
import {selectStudioTitle, selectIsFetchingInfo} from '../../redux/studio';
|
||||
|
@ -38,9 +38,11 @@ const StudioTitle = ({
|
|||
const [showMuteMessage, setShowMuteMessage] = useState(false);
|
||||
const [hideValidationMessage, setHideValidationMessage] = useState(false);
|
||||
|
||||
StudioTitle.handleClickOutside = () => {
|
||||
const ref = useRef(null);
|
||||
|
||||
useOnClickOutside(ref, () => {
|
||||
setHideValidationMessage(true);
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -74,10 +76,6 @@ const StudioTitle = ({
|
|||
);
|
||||
};
|
||||
|
||||
const clickOutsideConfig = {
|
||||
handleClickOutside: () => StudioTitle.handleClickOutside
|
||||
};
|
||||
|
||||
StudioTitle.propTypes = {
|
||||
titleError: PropTypes.string,
|
||||
canEditInfo: PropTypes.bool,
|
||||
|
@ -88,7 +86,7 @@ StudioTitle.propTypes = {
|
|||
handleUpdate: PropTypes.func
|
||||
};
|
||||
|
||||
const connectedStudioTitle = connect(
|
||||
export default connect(
|
||||
state => ({
|
||||
title: selectStudioTitle(state),
|
||||
canEditInfo: selectCanEditInfo(state),
|
||||
|
@ -101,5 +99,3 @@ const connectedStudioTitle = connect(
|
|||
handleUpdate: mutateStudioTitle
|
||||
}
|
||||
)(StudioTitle);
|
||||
|
||||
export default onClickOutside(connectedStudioTitle, clickOutsideConfig);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React, {useEffect} from 'react';
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Switch,
|
||||
Routes,
|
||||
Route,
|
||||
Redirect,
|
||||
useRouteMatch
|
||||
Navigate,
|
||||
useParams
|
||||
} from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import {connect} from 'react-redux';
|
||||
|
@ -38,7 +38,7 @@ import {FormattedMessage} from 'react-intl';
|
|||
import {selectShowCuratorMuteError} from '../../redux/studio-permissions.js';
|
||||
|
||||
const StudioShell = ({isAdmin, showCuratorMuteError, muteExpiresAtMs, studioLoadFailed, onLoadInfo}) => {
|
||||
const match = useRouteMatch();
|
||||
const {studioPath, studioId} = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
onLoadInfo();
|
||||
|
@ -56,41 +56,52 @@ const StudioShell = ({isAdmin, showCuratorMuteError, muteExpiresAtMs, studioLoad
|
|||
<div className="studio-tabs">
|
||||
<StudioTabNav />
|
||||
<div>
|
||||
<Switch>
|
||||
<Route path={`${match.path}/curators`}>
|
||||
<StudioCuratorInvite />
|
||||
{showCuratorMuteError &&
|
||||
<CommentingStatus>
|
||||
<p>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="studio.mutedCurators"
|
||||
values={{
|
||||
inDuration: formatRelativeTime(muteExpiresAtMs, window._locale)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div><FormattedMessage id="studio.mutedPaused" /></div>
|
||||
</p>
|
||||
</CommentingStatus>
|
||||
<Routes>
|
||||
<Route
|
||||
path="/curators"
|
||||
element={
|
||||
<>
|
||||
<StudioCuratorInvite />
|
||||
{showCuratorMuteError &&
|
||||
<CommentingStatus>
|
||||
<p>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="studio.mutedCurators"
|
||||
values={{
|
||||
inDuration: formatRelativeTime(
|
||||
muteExpiresAtMs,
|
||||
window._locale
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div><FormattedMessage id="studio.mutedPaused" /></div>
|
||||
</p>
|
||||
</CommentingStatus>
|
||||
}
|
||||
<StudioManagers />
|
||||
<StudioCurators />
|
||||
</>
|
||||
}
|
||||
<StudioManagers />
|
||||
<StudioCurators />
|
||||
</Route>
|
||||
<Route path={`${match.path}/comments`}>
|
||||
<StudioComments />
|
||||
</Route>
|
||||
<Route path={`${match.path}/activity`}>
|
||||
<StudioActivity />
|
||||
</Route>
|
||||
<Route path={`${match.path}/projects`}>
|
||||
{/* We can force /projects back to / this way */}
|
||||
<Redirect to={match.url} />
|
||||
</Route>
|
||||
<Route path={match.path}>
|
||||
<StudioProjects />
|
||||
</Route>
|
||||
</Switch>
|
||||
/>
|
||||
<Route
|
||||
path="/comments"
|
||||
element={<StudioComments />}
|
||||
/>
|
||||
<Route
|
||||
path="/activity"
|
||||
element={<StudioActivity />}
|
||||
/>
|
||||
<Route
|
||||
path="/projects"
|
||||
element={<Navigate to={`/${studioPath}/${studioId}`} />}
|
||||
/>
|
||||
<Route
|
||||
path="/"
|
||||
element={<StudioProjects />}
|
||||
/>
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -121,12 +132,13 @@ render(
|
|||
<Page className="studio-page">
|
||||
<StudioAdminPanel />
|
||||
<Router>
|
||||
<Switch>
|
||||
<Routes>
|
||||
{/* Use variable studioPath to support /studio-playground/ or future route */}
|
||||
<Route path="/:studioPath/:studioId">
|
||||
<ConnectedStudioShell />
|
||||
</Route>
|
||||
</Switch>
|
||||
<Route
|
||||
path="/:studioPath/:studioId/*"
|
||||
element={<ConnectedStudioShell />}
|
||||
/>
|
||||
</Routes>
|
||||
</Router>
|
||||
</Page>,
|
||||
document.getElementById('app'),
|
||||
|
|
|
@ -1,52 +1,49 @@
|
|||
/*
|
||||
* Helpers for using enzyme and react-test-renderer with react-intl
|
||||
*/
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import {createIntl, IntlProvider} from 'react-intl';
|
||||
import {mount, shallow} from 'enzyme';
|
||||
import intlShape from '../../src/lib/intl-shape';
|
||||
import {render} from '@testing-library/react';
|
||||
import {IntlProvider} from 'react-intl';
|
||||
import routes from '../../src/routes.json';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import merge from 'lodash.merge';
|
||||
|
||||
const shallowWithIntl = (node, {context} = {}) => shallow(
|
||||
node,
|
||||
{
|
||||
context: Object.assign({}, context),
|
||||
wrappingComponent: IntlProvider,
|
||||
wrappingComponentProps: {
|
||||
locale: 'en',
|
||||
messages: {}
|
||||
// TBD: Move code to script that executes before running all tests,
|
||||
// fix issue where texts for views don't load
|
||||
|
||||
const globalTemplateFile = path.resolve(__dirname, '../../src/l10n.json');
|
||||
const generalLocales = {en: JSON.parse(fs.readFileSync(globalTemplateFile, 'utf8'))};
|
||||
const defaultLocales = {};
|
||||
const views = [];
|
||||
|
||||
for (const route in routes) {
|
||||
if (typeof routes[route].redirect !== 'undefined') {
|
||||
continue;
|
||||
}
|
||||
|
||||
views.push(routes[route].name);
|
||||
try {
|
||||
const subdir = routes[route].view.split('/');
|
||||
subdir.pop();
|
||||
const l10n = path.resolve(__dirname, `../../src/views/${subdir.join('/')}/l10n.json`);
|
||||
const viewIds = JSON.parse(fs.readFileSync(l10n, 'utf8'));
|
||||
defaultLocales[routes[route].name] = viewIds;
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
).dive();
|
||||
}
|
||||
|
||||
const mountWithIntl = (node, {context, childContextTypes} = {}) => {
|
||||
const intl = createIntl({locale: 'en', messages: {}});
|
||||
return mount(
|
||||
node,
|
||||
{
|
||||
context: Object.assign({}, context, {intl}),
|
||||
childContextTypes: Object.assign({}, {intl: intlShape}, childContextTypes),
|
||||
wrappingComponent: IntlProvider,
|
||||
wrappingComponentProps: {
|
||||
locale: 'en',
|
||||
messages: {}
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
views.map(view => defaultLocales[view]).reduce((acc, curr) => merge(acc, curr), generalLocales);
|
||||
|
||||
// react-test-renderer component for use with snapshot testing
|
||||
const componentWithIntl = (children, props = {locale: 'en'}) => renderer.create(
|
||||
<IntlProvider
|
||||
textComponent="span"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</IntlProvider>
|
||||
);
|
||||
const renderWithIntl = ui => ({
|
||||
...render(
|
||||
<IntlProvider
|
||||
locale="en"
|
||||
messages={generalLocales.en}
|
||||
>
|
||||
{ui}
|
||||
</IntlProvider>
|
||||
)
|
||||
});
|
||||
|
||||
export {
|
||||
componentWithIntl,
|
||||
shallowWithIntl,
|
||||
mountWithIntl
|
||||
};
|
||||
export {renderWithIntl};
|
||||
|
|
68
test/helpers/react-testing-library-wrapper.js
vendored
Normal file
68
test/helpers/react-testing-library-wrapper.js
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
import {render} from '@testing-library/react';
|
||||
import {renderWithIntl} from './intl-helpers';
|
||||
|
||||
const findNode = (fiberNode, selector, comparator) => {
|
||||
if (fiberNode &&
|
||||
fiberNode.stateNode &&
|
||||
fiberNode.stateNode.state &&
|
||||
comparator(selector, fiberNode)) {
|
||||
return fiberNode;
|
||||
}
|
||||
|
||||
let currentNode;
|
||||
|
||||
if (!currentNode && fiberNode && fiberNode.child) {
|
||||
currentNode = findNode(fiberNode.child, selector, comparator);
|
||||
}
|
||||
|
||||
if (!currentNode && fiberNode && fiberNode.sibling) {
|
||||
currentNode = findNode(fiberNode.sibling, selector, comparator);
|
||||
}
|
||||
|
||||
return currentNode;
|
||||
};
|
||||
|
||||
const getInstance = (container, selector, comparator) => {
|
||||
const rootFiberKey = Object.keys(container).find(key =>
|
||||
key.startsWith('__reactContainer')
|
||||
);
|
||||
const rootFiber = container[rootFiberKey];
|
||||
|
||||
return findNode(rootFiber.stateNode.current, selector, comparator) || null;
|
||||
};
|
||||
|
||||
const compareComponentName = (componentName, fiberNode) => fiberNode.elementType?.name.startsWith(componentName);
|
||||
|
||||
const renderWithInstance = (ux, componentName) => {
|
||||
const component = render(ux);
|
||||
|
||||
return {
|
||||
instance: () => getInstance(
|
||||
component.container,
|
||||
componentName,
|
||||
compareComponentName)?.stateNode,
|
||||
findByComponentName: selector => getInstance(
|
||||
component.container,
|
||||
selector,
|
||||
compareComponentName)?.stateNode,
|
||||
...component
|
||||
};
|
||||
};
|
||||
|
||||
const renderWithInstanceAndIntl = (ux, componentName) => {
|
||||
const component = renderWithIntl(ux);
|
||||
|
||||
return {
|
||||
instance: () => getInstance(
|
||||
component.container,
|
||||
componentName,
|
||||
compareComponentName)?.stateNode,
|
||||
findByComponentName: selector => getInstance(
|
||||
component.container,
|
||||
selector,
|
||||
compareComponentName)?.stateNode,
|
||||
...component
|
||||
};
|
||||
};
|
||||
|
||||
export {renderWithInstance as render, renderWithInstanceAndIntl as renderWithIntl};
|
9
test/unit/components/__snapshots__/captcha.test.jsx.snap
Normal file
9
test/unit/components/__snapshots__/captcha.test.jsx.snap
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Captcha test Captcha renders the div google wants 1`] = `
|
||||
<div
|
||||
class="g-recaptcha"
|
||||
data-badge="bottomright"
|
||||
data-size="invisible"
|
||||
/>
|
||||
`;
|
|
@ -0,0 +1,71 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CommentingStatus Basic render 1`] = `
|
||||
<div
|
||||
class="commenting-status"
|
||||
>
|
||||
<div
|
||||
class="commenting-status-inner-content"
|
||||
>
|
||||
<div
|
||||
class="flex-row comment-status-img"
|
||||
>
|
||||
<img
|
||||
class="comment-status-icon"
|
||||
src="/svgs/project/comment-status.svg"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="flex-row"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CommentingStatus Children added 1`] = `
|
||||
<div
|
||||
class="commenting-status"
|
||||
>
|
||||
<div
|
||||
class="commenting-status-inner-content"
|
||||
>
|
||||
<div
|
||||
class="flex-row comment-status-img"
|
||||
>
|
||||
<img
|
||||
class="comment-status-icon"
|
||||
src="/svgs/project/comment-status.svg"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="flex-row"
|
||||
>
|
||||
<img
|
||||
class="myChildDiv"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CommentingStatus ClassNames added 1`] = `
|
||||
<div
|
||||
class="commenting-status class1"
|
||||
>
|
||||
<div
|
||||
class="commenting-status-inner-content class2"
|
||||
>
|
||||
<div
|
||||
class="flex-row comment-status-img"
|
||||
>
|
||||
<img
|
||||
class="comment-status-icon"
|
||||
src="/svgs/project/comment-status.svg"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="flex-row"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -1,9 +1,10 @@
|
|||
/* eslint-disable max-len */
|
||||
const React = require('react');
|
||||
const {mountWithIntl} = require('../../helpers/intl-helpers.jsx');
|
||||
const {renderWithIntl} = require('../../helpers/intl-helpers.jsx');
|
||||
import {ConnectedBecomeAScratcher as BecomeAScratcherPage} from '../../../src/views/become-a-scratcher/become-a-scratcher.jsx';
|
||||
import sessionActions from '../../../src/redux/session.js';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
jest.mock('react-dom', () => ({
|
||||
render: jest.fn()
|
||||
|
@ -24,10 +25,10 @@ describe('BecomeAScratcherPage', () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
const component = mountWithIntl(
|
||||
const {container} = renderWithIntl(
|
||||
<BecomeAScratcherPage />, {context: {store: NotLoggedInUserStore}}
|
||||
);
|
||||
expect(component.find('div.not-available-outer').exists()).toBeTruthy();
|
||||
expect(container.querySelector('div.not-available-outer')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Display No Invitation when user is not invited', () => {
|
||||
|
@ -45,10 +46,10 @@ describe('BecomeAScratcherPage', () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
const component = mountWithIntl(
|
||||
const {container} = renderWithIntl(
|
||||
<BecomeAScratcherPage />, {context: {store: NotInvitedUserStore}}
|
||||
);
|
||||
expect(component.find('div.no-invitation').exists()).toBeTruthy();
|
||||
expect(container.querySelector('div.no-invitation').exists()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Display Onboarding when user is invited', () => {
|
||||
|
@ -67,10 +68,10 @@ describe('BecomeAScratcherPage', () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
const component = mountWithIntl(
|
||||
const {container} = renderWithIntl(
|
||||
<BecomeAScratcherPage />, {context: {store: InvitedUserStore}}
|
||||
);
|
||||
expect(component.find('div.congratulations-page').exists()).toBeTruthy();
|
||||
expect(container.querySelector('div.congratulations-page').exists()).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Display celebration page when user is already a scratcher', () => {
|
||||
|
@ -89,9 +90,9 @@ describe('BecomeAScratcherPage', () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
const component = mountWithIntl(
|
||||
const {container} = renderWithIntl(
|
||||
<BecomeAScratcherPage />, {context: {store: AlreadyScratcherStore}}
|
||||
);
|
||||
expect(component.find('div.hooray-screen').exists()).toBeTruthy();
|
||||
expect(container.querySelector('div.hooray-screen').exists()).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
const React = require('react');
|
||||
const enzyme = require('enzyme');
|
||||
|
||||
|
||||
const Captcha = require('../../../src/components/captcha/captcha.jsx');
|
||||
const {render} = require('@testing-library/react');
|
||||
|
||||
describe('Captcha test', () => {
|
||||
global.grecaptcha = {
|
||||
|
@ -14,10 +13,10 @@ describe('Captcha test', () => {
|
|||
const props = {
|
||||
onCaptchaLoad: jest.fn()
|
||||
};
|
||||
const wrapper = enzyme.shallow(<Captcha
|
||||
render(<Captcha
|
||||
{...props}
|
||||
/>);
|
||||
wrapper.instance().onCaptchaLoad();
|
||||
|
||||
expect(global.grecaptcha.render).toHaveBeenCalled();
|
||||
expect(props.onCaptchaLoad).toHaveBeenCalled();
|
||||
});
|
||||
|
@ -26,10 +25,10 @@ describe('Captcha test', () => {
|
|||
const props = {
|
||||
onCaptchaLoad: jest.fn()
|
||||
};
|
||||
const wrapper = enzyme.shallow(<Captcha
|
||||
const {container} = (<Captcha
|
||||
{...props}
|
||||
/>);
|
||||
wrapper.instance().executeCaptcha();
|
||||
container.executeCaptcha();
|
||||
expect(global.grecaptcha.execute).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
@ -37,9 +36,9 @@ describe('Captcha test', () => {
|
|||
const props = {
|
||||
onCaptchaLoad: jest.fn()
|
||||
};
|
||||
const wrapper = enzyme.mount(<Captcha
|
||||
const {container} = render(<Captcha
|
||||
{...props}
|
||||
/>);
|
||||
expect(wrapper.find('div.g-recaptcha')).toHaveLength(1);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,33 +1,35 @@
|
|||
const React = require('react');
|
||||
const {shallow} = require('enzyme');
|
||||
const {render} = require('@testing-library/react');
|
||||
require('@testing-library/jest-dom');
|
||||
const CommentingStatus = require('../../../src/components/commenting-status/commenting-status.jsx');
|
||||
|
||||
describe('CommentingStatus', () => {
|
||||
test('Basic render', () => {
|
||||
const component = shallow(
|
||||
const {container} = render(
|
||||
<CommentingStatus />
|
||||
);
|
||||
expect(component.find('div.commenting-status').exists()).toBe(true);
|
||||
expect(component.find('img.comment-status-icon').exists()).toBe(true);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('ClassNames added', () => {
|
||||
const component = shallow(
|
||||
const {container} = render(
|
||||
<CommentingStatus
|
||||
className="class1"
|
||||
innerClassName="class2"
|
||||
/>
|
||||
);
|
||||
expect(component.find('div.class1').exists()).toBe(true);
|
||||
expect(component.find('div.class2').exists()).toBe(true);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Children added', () => {
|
||||
const component = shallow(
|
||||
const {container} = render(
|
||||
<CommentingStatus>
|
||||
<img className="myChildDiv" />
|
||||
</CommentingStatus>
|
||||
);
|
||||
expect(component.find('img.myChildDiv').exists()).toBe(true);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
const React = require('react');
|
||||
const {shallowWithIntl} = require('../../helpers/intl-helpers.jsx');
|
||||
import {mountWithIntl} from '../../helpers/intl-helpers.jsx';
|
||||
import React, {act} from 'react';
|
||||
import {Provider} from 'react-redux';
|
||||
const ComposeComment = require('../../../src/views/preview/comment/compose-comment.jsx');
|
||||
import configureStore from 'redux-mock-store';
|
||||
import '@testing-library/jest-dom';
|
||||
import {renderWithIntl} from '../../helpers/react-testing-library-wrapper.js';
|
||||
import {screen} from '@testing-library/react';
|
||||
|
||||
describe('Compose Comment test', () => {
|
||||
const mockStore = configureStore();
|
||||
|
@ -40,13 +42,14 @@ describe('Compose Comment test', () => {
|
|||
if (!store) {
|
||||
store = defaultStore;
|
||||
}
|
||||
const wrapper = shallowWithIntl(
|
||||
<ComposeComment
|
||||
{...defaultProps()}
|
||||
{...props}
|
||||
|
||||
/>
|
||||
, {context: {store}}
|
||||
const wrapper = renderWithIntl(
|
||||
<Provider store={store}>
|
||||
<ComposeComment
|
||||
{...defaultProps()}
|
||||
{...props}
|
||||
/>
|
||||
</Provider>,
|
||||
'ComposeComment'
|
||||
);
|
||||
return wrapper;
|
||||
};
|
||||
|
@ -82,57 +85,65 @@ describe('Compose Comment test', () => {
|
|||
});
|
||||
|
||||
test('Modal & Comment status do not show', () => {
|
||||
const component = getComposeCommentWrapper({});
|
||||
const {container} = getComposeCommentWrapper({});
|
||||
// Comment compsoe box is there
|
||||
expect(component.find('FlexRow.compose-comment').exists()).toEqual(true);
|
||||
expect(container.querySelector('.compose-comment')).toBeInTheDocument();
|
||||
// No error message
|
||||
expect(component.find('FlexRow.compose-error-row').exists()).toEqual(false);
|
||||
expect(component.find('MuteModal').exists()).toEqual(false);
|
||||
expect(component.find('CommentingStatus').exists()).toEqual(false);
|
||||
expect(container.querySelector('.compose-error-row')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.modal-mute')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.commenting-status')).not.toBeInTheDocument();
|
||||
// Buttons start enabled
|
||||
expect(component.find('Button.compose-post').props().disabled).toBe(false);
|
||||
expect(component.find('Button.compose-cancel').props().disabled).toBe(false);
|
||||
expect(container.querySelector('.compose-post')).not.toBeDisabled();
|
||||
expect(container.querySelector('.compose-cancel')).not.toBeDisabled();
|
||||
|
||||
});
|
||||
|
||||
test('Error messages shows when comment rejected', () => {
|
||||
const component = getComposeCommentWrapper({});
|
||||
const commentInstance = component.instance();
|
||||
commentInstance.setState({
|
||||
error: 'isFlood',
|
||||
status: 'REJECTED'
|
||||
test('Error messages shows when comment rejected', async () => {
|
||||
const {container, instance} = getComposeCommentWrapper({});
|
||||
const commentInstance = instance();
|
||||
await act(() => {
|
||||
commentInstance.setState({
|
||||
error: 'isFlood',
|
||||
status: 'REJECTED'
|
||||
});
|
||||
});
|
||||
component.update();
|
||||
expect(component.find('FlexRow.compose-error-row').exists()).toEqual(true);
|
||||
|
||||
|
||||
expect(container.querySelector('.compose-error-row')).toBeInTheDocument();
|
||||
// Buttons stay enabled when comment rejected for non-mute reasons
|
||||
expect(component.find('Button.compose-post').props().disabled).toBe(false);
|
||||
expect(component.find('Button.compose-cancel').props().disabled).toBe(false);
|
||||
expect(container.querySelector('.compose-post')).not.toBeDisabled();
|
||||
expect(container.querySelector('.compose-cancel')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
test('No error message shows when comment rejected because user is already muted', () => {
|
||||
const component = getComposeCommentWrapper({});
|
||||
const commentInstance = component.instance();
|
||||
commentInstance.setState({
|
||||
error: 'isMuted',
|
||||
status: 'COMPOSE_DISALLOWED'
|
||||
test('No error message shows when comment rejected because user is already muted', async () => {
|
||||
const {container, instance} = getComposeCommentWrapper({});
|
||||
const commentInstance = instance();
|
||||
await act(() => {
|
||||
commentInstance.setState({
|
||||
error: 'isMuted',
|
||||
status: 'COMPOSE_DISALLOWED'
|
||||
});
|
||||
});
|
||||
component.update();
|
||||
expect(component.find('FlexRow.compose-error-row').exists()).toEqual(false);
|
||||
expect(container.querySelector('.compose-error-row')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Comment Status shows but compose box does not when you load the page and you are already muted', () => {
|
||||
const realDateNow = Date.now.bind(global.Date);
|
||||
global.Date.now = () => 0;
|
||||
const component = getComposeCommentWrapper({});
|
||||
const commentInstance = component.instance();
|
||||
commentInstance.setState({muteExpiresAtMs: 100, status: 'COMPOSE_DISALLOWED'});
|
||||
component.update();
|
||||
const {container, instance} = getComposeCommentWrapper({});
|
||||
const commentInstance = instance();
|
||||
act(() => {
|
||||
commentInstance.setState({muteExpiresAtMs: 100, status: 'COMPOSE_DISALLOWED'});
|
||||
});
|
||||
|
||||
// Compose box should be hidden if muted unless they got muted due to a comment they just posted.
|
||||
expect(component.find('FlexRow.compose-comment').exists()).toEqual(false);
|
||||
expect(component.find('MuteModal').exists()).toEqual(false);
|
||||
expect(component.find('CommentingStatus').exists()).toEqual(true);
|
||||
global.Date.now = realDateNow;
|
||||
expect(container.querySelector('.compose-comment')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.modal-mute')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.commenting-status')).toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
global.Date.now = realDateNow;
|
||||
});
|
||||
});
|
||||
|
||||
test('Comment Status and compose box do not show on replies when muted, but mute modal does', () => {
|
||||
|
@ -152,26 +163,20 @@ describe('Compose Comment test', () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
const component = mountWithIntl(
|
||||
<ComposeComment
|
||||
{...defaultProps()}
|
||||
isReply
|
||||
/>
|
||||
, {context: {store}}
|
||||
);
|
||||
expect(component.find('FlexRow.compose-comment').exists()).toEqual(false);
|
||||
expect(component.find('MuteModal').exists()).toBe(true);
|
||||
expect(component.find('MuteModal').props().startStep).toBe(1);
|
||||
expect(component.find('CommentingStatus').exists()).toEqual(false);
|
||||
const {container, findByComponentName} = getComposeCommentWrapper({isReply: true}, store);
|
||||
expect(container.querySelector('.compose-comment')).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
||||
expect(findByComponentName('MuteModal').props.startStep).toBe(1);
|
||||
expect(container.querySelector('.commenting-status')).not.toBeInTheDocument();
|
||||
global.Date.now = realDateNow;
|
||||
});
|
||||
|
||||
test('Comment Status and compose box show on replies when not muted', () => {
|
||||
const realDateNow = Date.now.bind(global.Date);
|
||||
global.Date.now = () => 0;
|
||||
const component = getComposeCommentWrapper({isReply: true});
|
||||
expect(component.find('FlexRow.compose-comment').exists()).toEqual(true);
|
||||
expect(component.find('CommentingStatus').exists()).toEqual(false);
|
||||
const {container} = getComposeCommentWrapper({isReply: true});
|
||||
expect(container.querySelector('.compose-comment')).toBeInTheDocument();
|
||||
expect(container.querySelector('.commenting-status')).not.toBeInTheDocument();
|
||||
global.Date.now = realDateNow;
|
||||
});
|
||||
|
||||
|
@ -194,89 +199,93 @@ describe('Compose Comment test', () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
const component = getComposeCommentWrapper({}, mutedStore);
|
||||
const commentInstance = component.instance();
|
||||
const {container, instance} = getComposeCommentWrapper({}, mutedStore);
|
||||
const commentInstance = instance();
|
||||
// Check conversion to ms from seconds is done at init time.
|
||||
expect(commentInstance.state.muteExpiresAtMs).toEqual(5 * 1000);
|
||||
// Check we setup a timeout to expire the widget when timeout reached.
|
||||
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 5 * 1000);
|
||||
expect(commentInstance.state.showWarning).toBe(true);
|
||||
// Compose box should be hidden if muted unless they got muted due to a comment they just posted.
|
||||
expect(component.find('FlexRow.compose-comment').exists()).toEqual(false);
|
||||
expect(component.find('MuteModal').exists()).toEqual(false);
|
||||
expect(component.find('CommentingStatus').exists()).toEqual(true);
|
||||
expect(container.querySelector('FlexRow.compose-comment')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.modal-mute')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.commenting-status')).toBeInTheDocument();
|
||||
global.Date.now = realDateNow;
|
||||
});
|
||||
|
||||
test('Comment Status shows when user just submitted a reply comment that got them muted', () => {
|
||||
const realDateNow = Date.now.bind(global.Date);
|
||||
global.Date.now = () => 0;
|
||||
const component = getComposeCommentWrapper({isReply: true});
|
||||
const commentInstance = component.instance();
|
||||
commentInstance.setState({
|
||||
status: 'REJECTED_MUTE',
|
||||
muteExpiresAtMs: 100
|
||||
const {container, instance} = getComposeCommentWrapper({isReply: true});
|
||||
const commentInstance = instance();
|
||||
act(() => {
|
||||
commentInstance.setState({
|
||||
status: 'REJECTED_MUTE',
|
||||
muteExpiresAtMs: 100
|
||||
});
|
||||
});
|
||||
component.update();
|
||||
expect(component.find('FlexRow.compose-comment').exists()).toEqual(true);
|
||||
expect(component.find('MuteModal').exists()).toEqual(false);
|
||||
expect(component.find('CommentingStatus').exists()).toEqual(true);
|
||||
expect(container.querySelector('.compose-comment')).toBeInTheDocument();
|
||||
expect(container.querySelector('.modal-mute')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.commenting-status')).toBeInTheDocument();
|
||||
// Compose box exists but is disabled
|
||||
expect(component.find('InplaceInput.compose-input').exists()).toEqual(true);
|
||||
expect(component.find('InplaceInput.compose-input').props().disabled).toBe(true);
|
||||
expect(component.find('Button.compose-post').props().disabled).toBe(true);
|
||||
expect(component.find('Button.compose-cancel').props().disabled).toBe(true);
|
||||
expect(container.querySelector('.compose-input')).toBeInTheDocument();
|
||||
expect(container.querySelector('.inplace-textarea')).toBeDisabled();
|
||||
expect(container.querySelector('.compose-post')).toBeDisabled();
|
||||
expect(container.querySelector('.compose-cancel')).toBeDisabled();
|
||||
global.Date.now = realDateNow;
|
||||
});
|
||||
|
||||
test('Comment Status shows when user just submitted a comment that got them muted', () => {
|
||||
const realDateNow = Date.now.bind(global.Date);
|
||||
global.Date.now = () => 0;
|
||||
const component = getComposeCommentWrapper({});
|
||||
const commentInstance = component.instance();
|
||||
commentInstance.setState({
|
||||
status: 'REJECTED_MUTE',
|
||||
muteExpiresAtMs: 100
|
||||
const {container, instance} = getComposeCommentWrapper({});
|
||||
const commentInstance = instance();
|
||||
act(() => {
|
||||
commentInstance.setState({
|
||||
status: 'REJECTED_MUTE',
|
||||
muteExpiresAtMs: 100
|
||||
});
|
||||
});
|
||||
component.update();
|
||||
expect(component.find('FlexRow.compose-comment').exists()).toEqual(true);
|
||||
expect(component.find('MuteModal').exists()).toEqual(false);
|
||||
expect(component.find('CommentingStatus').exists()).toEqual(true);
|
||||
expect(container.querySelector('.compose-comment')).toBeInTheDocument();
|
||||
expect(container.querySelector('.modal-mute')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.commenting-status')).toBeInTheDocument();
|
||||
// Compose box exists but is disabled
|
||||
expect(component.find('InplaceInput.compose-input').exists()).toEqual(true);
|
||||
expect(component.find('InplaceInput.compose-input').props().disabled).toBe(true);
|
||||
expect(component.find('Button.compose-post').props().disabled).toBe(true);
|
||||
expect(component.find('Button.compose-cancel').props().disabled).toBe(true);
|
||||
expect(container.querySelector('.compose-input')).toBeInTheDocument();
|
||||
expect(container.querySelector('.inplace-textarea')).toBeDisabled();
|
||||
expect(container.querySelector('.compose-post')).toBeDisabled();
|
||||
expect(container.querySelector('.compose-cancel')).toBeDisabled();
|
||||
global.Date.now = realDateNow;
|
||||
});
|
||||
test('Comment Error does not show for mutes', () => {
|
||||
const realDateNow = Date.now.bind(global.Date);
|
||||
global.Date.now = () => 0;
|
||||
const component = getComposeCommentWrapper({});
|
||||
const commentInstance = component.instance();
|
||||
commentInstance.setState({
|
||||
status: 'REJECTED_MUTE',
|
||||
error: 'a mute error'
|
||||
const {container, instance} = getComposeCommentWrapper({});
|
||||
const commentInstance = instance();
|
||||
act(() => {
|
||||
commentInstance.setState({
|
||||
status: 'REJECTED_MUTE',
|
||||
error: 'a mute error'
|
||||
});
|
||||
});
|
||||
component.update();
|
||||
expect(component.find('FlexRow.compose-error-row').exists()).toEqual(false);
|
||||
expect(component.find('FlexRow.compose-comment').exists()).toEqual(true);
|
||||
expect(container.querySelector('.compose-error-row')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.compose-comment')).toBeInTheDocument();
|
||||
global.Date.now = realDateNow;
|
||||
});
|
||||
test('Comment Error does show for non-mute errors', () => {
|
||||
const component = getComposeCommentWrapper({});
|
||||
const commentInstance = component.instance();
|
||||
commentInstance.setState({
|
||||
error: 'some error',
|
||||
status: 'REJECTED'
|
||||
const {container, instance} = getComposeCommentWrapper({});
|
||||
const commentInstance = instance();
|
||||
act(() => {
|
||||
commentInstance.setState({
|
||||
error: 'some error',
|
||||
status: 'REJECTED'
|
||||
});
|
||||
});
|
||||
component.update();
|
||||
expect(component.find('FlexRow.compose-error-row').exists()).toEqual(true);
|
||||
expect(component.find('FlexRow.compose-comment').exists()).toEqual(true);
|
||||
expect(component.find('InplaceInput.compose-input').exists()).toEqual(true);
|
||||
expect(component.find('InplaceInput.compose-input').props().disabled).toBe(false);
|
||||
expect(component.find('Button.compose-post').props().disabled).toBe(false);
|
||||
expect(component.find('Button.compose-cancel').props().disabled).toBe(false);
|
||||
expect(container.querySelector('.compose-error-row')).toBeInTheDocument();
|
||||
expect(container.querySelector('.compose-comment')).toBeInTheDocument();
|
||||
expect(container.querySelector('.compose-input')).toBeInTheDocument();
|
||||
expect(container.querySelector('.inplace-textarea')).not.toBeDisabled();
|
||||
expect(container.querySelector('.compose-post')).not.toBeDisabled();
|
||||
expect(container.querySelector('.compose-cancel')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
test('Mute Modal shows when muteOpen is true', () => {
|
||||
|
@ -292,19 +301,15 @@ describe('Compose Comment test', () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
const component = mountWithIntl(
|
||||
<ComposeComment
|
||||
{...defaultProps()}
|
||||
/>
|
||||
, {context: {store}}
|
||||
);
|
||||
const {instance, findByComponentName} = getComposeCommentWrapper({}, store);
|
||||
// set state on the ComposeComment component, not the wrapper
|
||||
const commentInstance = component.find('ComposeComment').instance();
|
||||
commentInstance.setState({muteOpen: true});
|
||||
component.update();
|
||||
expect(component.find('MuteModal').exists()).toEqual(true);
|
||||
expect(component.find('MuteModal').props().startStep).toEqual(0);
|
||||
expect(component.find('MuteModal').props().showWarning).toBe(false);
|
||||
const commentInstance = instance();
|
||||
act(() => {
|
||||
commentInstance.setState({muteOpen: true});
|
||||
});
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
||||
expect(findByComponentName('MuteModal').props.startStep).toEqual(0);
|
||||
expect(findByComponentName('MuteModal').props.showWarning).toBe(false);
|
||||
global.Date.now = realDateNow;
|
||||
});
|
||||
|
||||
|
@ -319,24 +324,21 @@ describe('Compose Comment test', () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
const component = mountWithIntl(
|
||||
<ComposeComment
|
||||
{...defaultProps()}
|
||||
/>
|
||||
, {context: {store}}
|
||||
);
|
||||
const {findByComponentName, instance} = getComposeCommentWrapper({}, store);
|
||||
// set state on the ComposeComment component, not the wrapper
|
||||
const commentInstance = component.find('ComposeComment').instance();
|
||||
commentInstance.setState({muteOpen: true});
|
||||
component.update();
|
||||
expect(component.find('MuteModal').exists()).toEqual(true);
|
||||
expect(component.find('MuteModal').props().showWarning).toBe(false);
|
||||
commentInstance.setState({
|
||||
muteOpen: true,
|
||||
showWarning: true
|
||||
const commentInstance = instance();
|
||||
act(() => {
|
||||
commentInstance.setState({muteOpen: true});
|
||||
});
|
||||
component.update();
|
||||
expect(component.find('MuteModal').props().showWarning).toBe(true);
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
||||
expect(findByComponentName('MuteModal').props.showWarning).toBe(false);
|
||||
act(() => {
|
||||
commentInstance.setState({
|
||||
muteOpen: true,
|
||||
showWarning: true
|
||||
});
|
||||
});
|
||||
expect(findByComponentName('MuteModal').props.showWarning).toBe(true);
|
||||
});
|
||||
|
||||
test('Mute Modal gets showFeedback props from state', () => {
|
||||
|
@ -351,46 +353,45 @@ describe('Compose Comment test', () => {
|
|||
}
|
||||
});
|
||||
|
||||
const component = mountWithIntl(
|
||||
<ComposeComment
|
||||
{...defaultProps()}
|
||||
/>
|
||||
, {context: {store}}
|
||||
);
|
||||
const {instance, findByComponentName} = getComposeCommentWrapper({}, store);
|
||||
|
||||
const commentInstance = component.find('ComposeComment').instance();
|
||||
commentInstance.setState({
|
||||
status: 'REJECTED_MUTE',
|
||||
error: 'isBad',
|
||||
muteOpen: true
|
||||
const commentInstance = instance();
|
||||
act(() => {
|
||||
commentInstance.setState({
|
||||
status: 'REJECTED_MUTE',
|
||||
error: 'isBad',
|
||||
muteOpen: true
|
||||
});
|
||||
});
|
||||
|
||||
component.update();
|
||||
expect(component.find('MuteModal').exists()).toEqual(true);
|
||||
expect(component.find('MuteModal').props().showFeedback).toBe(true);
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
||||
expect(findByComponentName('MuteModal').props.showFeedback).toBe(true);
|
||||
|
||||
commentInstance.setState({
|
||||
status: 'COMPOSE_DISALLOWED',
|
||||
error: 'isMute',
|
||||
showWarning: true,
|
||||
muteOpen: true
|
||||
act(() => {
|
||||
commentInstance.setState({
|
||||
status: 'COMPOSE_DISALLOWED',
|
||||
error: 'isMute',
|
||||
showWarning: true,
|
||||
muteOpen: true
|
||||
});
|
||||
});
|
||||
|
||||
component.update();
|
||||
expect(component.find('MuteModal').exists()).toEqual(true);
|
||||
expect(component.find('MuteModal').props().showFeedback).toBe(false);
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
||||
expect(findByComponentName('MuteModal').props.showFeedback).toBe(false);
|
||||
|
||||
commentInstance.setState({
|
||||
status: 'REJECTED',
|
||||
error: 'isBad',
|
||||
showWarning: true,
|
||||
muteOpen: true
|
||||
act(() => {
|
||||
commentInstance.setState({
|
||||
status: 'REJECTED',
|
||||
error: 'isBad',
|
||||
showWarning: true,
|
||||
muteOpen: true
|
||||
});
|
||||
});
|
||||
|
||||
component.update();
|
||||
expect(component.find('MuteModal').exists()).toEqual(true);
|
||||
expect(component.find('MuteModal').props().showFeedback).toBe(false);
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
||||
expect(findByComponentName('MuteModal').props.showFeedback).toBe(false);
|
||||
});
|
||||
|
||||
test('shouldShowMuteModal is false when muteStatus is undefined', () => {
|
||||
const commentInstance = getComposeCommentWrapper({}).instance();
|
||||
expect(commentInstance.shouldShowMuteModal()).toBe(false);
|
||||
|
@ -513,16 +514,20 @@ describe('Compose Comment test', () => {
|
|||
|
||||
test('getMuteModalStartStep: A reply that got them muted', () => {
|
||||
const commentInstance = getComposeCommentWrapper({isReply: true}).instance();
|
||||
commentInstance.setState({
|
||||
status: 'REJECTED_MUTE'
|
||||
act(() => {
|
||||
commentInstance.setState({
|
||||
status: 'REJECTED_MUTE'
|
||||
});
|
||||
});
|
||||
expect(commentInstance.getMuteModalStartStep()).toBe(0);
|
||||
});
|
||||
|
||||
test('getMuteModalStartStep: A reply click when already muted', () => {
|
||||
const commentInstance = getComposeCommentWrapper({isReply: true}).instance();
|
||||
commentInstance.setState({
|
||||
status: 'COMPOSE_DISALLOWED'
|
||||
act(() => {
|
||||
commentInstance.setState({
|
||||
status: 'COMPOSE_DISALLOWED'
|
||||
});
|
||||
});
|
||||
expect(commentInstance.getMuteModalStartStep()).toBe(1);
|
||||
});
|
||||
|
@ -532,7 +537,9 @@ describe('Compose Comment test', () => {
|
|||
global.Date.now = () => 0; // Set "now" to 0 for easier testing.
|
||||
|
||||
const commentInstance = getComposeCommentWrapper({}).instance();
|
||||
commentInstance.setState({muteExpiresAtMs: 100});
|
||||
act(() => {
|
||||
commentInstance.setState({muteExpiresAtMs: 100});
|
||||
});
|
||||
expect(commentInstance.isMuted()).toBe(true);
|
||||
global.Date.now = realDateNow;
|
||||
});
|
||||
|
@ -542,7 +549,9 @@ describe('Compose Comment test', () => {
|
|||
global.Date.now = () => 0;
|
||||
|
||||
const commentInstance = getComposeCommentWrapper({}).instance();
|
||||
commentInstance.setState({muteExpiresAtMs: -100});
|
||||
act(() => {
|
||||
commentInstance.setState({muteExpiresAtMs: -100});
|
||||
});
|
||||
expect(commentInstance.isMuted()).toBe(false);
|
||||
global.Date.now = realDateNow;
|
||||
});
|
||||
|
@ -559,7 +568,9 @@ describe('Compose Comment test', () => {
|
|||
test('getMuteMessageInfo: muteType set and just got muted', () => {
|
||||
const justMuted = true;
|
||||
const commentInstance = getComposeCommentWrapper({}).instance();
|
||||
commentInstance.setState({muteType: 'unconstructive'});
|
||||
act(() => {
|
||||
commentInstance.setState({muteType: 'unconstructive'});
|
||||
});
|
||||
expect(commentInstance.getMuteMessageInfo(justMuted).commentType).toBe('comment.type.unconstructive');
|
||||
expect(commentInstance.getMuteMessageInfo(justMuted)
|
||||
.muteStepContent[0]).toBe('comment.unconstructive.content1');
|
||||
|
@ -568,12 +579,16 @@ describe('Compose Comment test', () => {
|
|||
test('getMuteMessageInfo: muteType set and already muted', () => {
|
||||
const justMuted = false;
|
||||
const commentInstance = getComposeCommentWrapper({}).instance();
|
||||
commentInstance.setState({muteType: 'pii'});
|
||||
act(() => {
|
||||
commentInstance.setState({muteType: 'pii'});
|
||||
});
|
||||
expect(commentInstance.getMuteMessageInfo(justMuted).commentType).toBe('comment.type.pii.past');
|
||||
// PII has the same content1 regardless of whether you were just muted
|
||||
expect(commentInstance.getMuteMessageInfo(justMuted).muteStepContent[0]).toBe('comment.pii.content1');
|
||||
|
||||
commentInstance.setState({muteType: 'vulgarity'});
|
||||
act(() => {
|
||||
commentInstance.setState({muteType: 'vulgarity'});
|
||||
});
|
||||
expect(commentInstance.getMuteMessageInfo(justMuted).commentType).toBe('comment.type.vulgarity.past');
|
||||
expect(commentInstance.getMuteMessageInfo(justMuted).muteStepContent[0]).toBe('comment.type.vulgarity.past');
|
||||
});
|
||||
|
@ -595,14 +610,18 @@ describe('Compose Comment test', () => {
|
|||
test('getMuteMessageInfo: muteType set to something we don\'t have messages for and just got muted', () => {
|
||||
const justMuted = true;
|
||||
const commentInstance = getComposeCommentWrapper({}).instance();
|
||||
commentInstance.setState({muteType: 'spaghetti'});
|
||||
act(() => {
|
||||
commentInstance.setState({muteType: 'spaghetti'});
|
||||
});
|
||||
expect(commentInstance.getMuteMessageInfo(justMuted).commentType).toBe('comment.type.general');
|
||||
});
|
||||
|
||||
test('getMuteMessageInfo: muteType set to something we don\'t have messages for and already muted', () => {
|
||||
const justMuted = false;
|
||||
const commentInstance = getComposeCommentWrapper({}).instance();
|
||||
commentInstance.setState({muteType: 'spaghetti'});
|
||||
act(() => {
|
||||
commentInstance.setState({muteType: 'spaghetti'});
|
||||
});
|
||||
expect(commentInstance.getMuteMessageInfo(justMuted).commentType).toBe('comment.type.general.past');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import {mountWithIntl} from '../../helpers/intl-helpers.jsx';
|
||||
import {renderWithIntl} from '../../helpers/intl-helpers.jsx';
|
||||
import DonateTopBanner from '../../../src/views/splash/donate/donate-banner';
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
describe('DonateBannerTest', () => {
|
||||
let realDateNow;
|
||||
|
@ -13,25 +14,27 @@ describe('DonateBannerTest', () => {
|
|||
});
|
||||
test('Testing 2024 EOY campaign message', () => {
|
||||
global.Date.now = () => new Date(2024, 11, 16).getTime();
|
||||
const component = mountWithIntl(
|
||||
const {container} = renderWithIntl(
|
||||
<DonateTopBanner />
|
||||
);
|
||||
|
||||
expect(component.find('div.donate-banner').exists()).toEqual(true);
|
||||
expect(component.find('p.donate-text').exists()).toEqual(true);
|
||||
expect(component.find('FormattedMessage[id="donatebanner.eoyCampaign"]').exists()).toEqual(true);
|
||||
expect(component.find('FormattedMessage[id="donatebanner.askSupport"]').exists()).toEqual(false);
|
||||
expect(container.querySelector('div.donate-banner')).toBeInTheDocument();
|
||||
expect(container.querySelector('p.donate-text')).toBeInTheDocument();
|
||||
|
||||
const donateText = container.querySelector('p.donate-text');
|
||||
expect(donateText.innerHTML).toEqual('donatebanner.eoyCampaign');
|
||||
|
||||
});
|
||||
test('testing default message comes back after January 9, 2025', () => {
|
||||
// Date after Scratch week
|
||||
global.Date.now = () => new Date(2025, 0, 10).getTime();
|
||||
const component = mountWithIntl(
|
||||
const {container} = renderWithIntl(
|
||||
<DonateTopBanner />
|
||||
);
|
||||
expect(component.find('div.donate-banner').exists()).toEqual(true);
|
||||
expect(component.find('p.donate-text').exists()).toEqual(true);
|
||||
expect(component.find('FormattedMessage[id="donatebanner.askSupport"]').exists()).toEqual(true);
|
||||
expect(component.find('FormattedMessage[id="donatebanner.eoyCampaign"]').exists()).toEqual(false);
|
||||
expect(container.querySelector('div.donate-banner')).toBeInTheDocument();
|
||||
expect(container.querySelector('p.donate-text')).toBeInTheDocument();
|
||||
|
||||
const donateText = container.querySelector('p.donate-text');
|
||||
expect(donateText.innerHTML).toEqual('donatebanner.askSupport');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,35 +1,37 @@
|
|||
const React = require('react');
|
||||
const {mountWithIntl} = require('../../helpers/intl-helpers.jsx');
|
||||
const {renderWithIntl} = require('../../helpers/intl-helpers.jsx');
|
||||
const EmailConfirmationBanner = require('../../../src/components/dropdown-banner/email-confirmation/banner.jsx');
|
||||
const {fireEvent} = require('@testing-library/react');
|
||||
require('@testing-library/jest-dom');
|
||||
|
||||
jest.mock('../../../src/components/modal/email-confirmation/modal.jsx', () => () => 'MockEmailConfirmationModal');
|
||||
|
||||
|
||||
describe('EmailConfirmationBanner', () => {
|
||||
test('Clicking "Confirm your email" opens the email confirmation modal', () => {
|
||||
const component = mountWithIntl(
|
||||
const {container} = renderWithIntl(
|
||||
<EmailConfirmationBanner />
|
||||
);
|
||||
|
||||
expect(component.text()).not.toContain('MockEmailConfirmationModal');
|
||||
|
||||
const confirmWrapper = component.find({id: 'emailConfirmationBanner.confirm'});
|
||||
const confirmLink = mountWithIntl(confirmWrapper.props().values.confirmLink);
|
||||
confirmLink.simulate('click');
|
||||
component.update();
|
||||
expect(container).not.toHaveTextContent('MockEmailConfirmationModal');
|
||||
|
||||
expect(component.text()).toContain('MockEmailConfirmationModal');
|
||||
const confirmLink = container.querySelector('a.showEmailConfirmationModalLink');
|
||||
fireEvent.click(confirmLink);
|
||||
|
||||
expect(container).toHaveTextContent('MockEmailConfirmationModal');
|
||||
});
|
||||
|
||||
test('Clicking X calls onRequestDismiss', () => {
|
||||
|
||||
const requestDismissMock = jest.fn();
|
||||
|
||||
const component = mountWithIntl(
|
||||
const {container} = renderWithIntl(
|
||||
<EmailConfirmationBanner onRequestDismiss={requestDismissMock} />
|
||||
);
|
||||
|
||||
component.find('a.close').simulate('click', {preventDefault () {}});
|
||||
|
||||
const closeButton = container.querySelector('a.close');
|
||||
fireEvent.click(closeButton);
|
||||
|
||||
expect(requestDismissMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
const React = require('react');
|
||||
const {mountWithIntl} = require('../../helpers/intl-helpers.jsx');
|
||||
const {renderWithIntl} = require('../../helpers/intl-helpers.jsx');
|
||||
const EmailConfirmationModal = require('../../../src/components/modal/email-confirmation/modal.jsx');
|
||||
import {Provider} from 'react-redux';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import '@testing-library/jest-dom';
|
||||
import {fireEvent, screen} from '@testing-library/react';
|
||||
|
||||
|
||||
describe('Modal', () => {
|
||||
|
@ -23,33 +26,43 @@ describe('Modal', () => {
|
|||
});
|
||||
|
||||
test('Display email prop correctly', () => {
|
||||
const component = mountWithIntl(
|
||||
<EmailConfirmationModal
|
||||
isOpen
|
||||
/>, {context: {store: defaultStore}}
|
||||
renderWithIntl(
|
||||
<Provider store={defaultStore}>
|
||||
<EmailConfirmationModal
|
||||
isOpen
|
||||
/>
|
||||
</Provider>
|
||||
);
|
||||
expect(component.find('div.modal-text-content').text()).toContain(testEmail);
|
||||
|
||||
expect(screen.getByText(testEmail)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Clicking on Text changes to tips page', () => {
|
||||
const component = mountWithIntl(
|
||||
<EmailConfirmationModal
|
||||
isOpen
|
||||
/>, {email: testEmail, context: {store: defaultStore}}
|
||||
renderWithIntl(
|
||||
<Provider
|
||||
store={defaultStore}
|
||||
>
|
||||
<EmailConfirmationModal
|
||||
isOpen
|
||||
/>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
const tipsLinkWrapper = component.find({id: 'emailConfirmationModal.havingTrouble'});
|
||||
const tipsLink = mountWithIntl(tipsLinkWrapper.props().values.tipsLink);
|
||||
tipsLink.simulate('click');
|
||||
expect(component.text()).toContain('emailConfirmationModal.confirmingTips');
|
||||
const tipsLink = screen.getByText('Check out these tips');
|
||||
fireEvent.click(tipsLink);
|
||||
expect(screen.getByText('Tips for confirming your email address')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Close button shows correctly', () => {
|
||||
const component = mountWithIntl(
|
||||
<EmailConfirmationModal isOpen />, {context: {store: defaultStore}}
|
||||
test('Close button shows correctly', async () => {
|
||||
renderWithIntl(
|
||||
<Provider store={defaultStore}>
|
||||
<EmailConfirmationModal
|
||||
isOpen
|
||||
/>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
expect(component.find('div.modal-content-close').exists()).toBe(true);
|
||||
expect(component.find('img.modal-content-close-img').exists()).toBe(true);
|
||||
expect(await screen.findByRole('dialog')).toBeInTheDocument();
|
||||
expect(screen.getByRole('img', {name: /close-icon/i})).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue