chore(deps): upgrade eslint, prettier & related packages

This commit is contained in:
Mrugesh Mohapatra
2021-03-11 00:31:46 +05:30
committed by Mrugesh Mohapatra
parent 0a53a1d7f0
commit 6c91f81b0e
94 changed files with 1462 additions and 1411 deletions

View File

@@ -1,4 +1,3 @@
**/node_modules/**
client/.cache/** client/.cache/**
client/static/** client/static/**
client/public/** client/public/**

View File

@@ -7,8 +7,18 @@
}, },
"parser": "babel-eslint", "parser": "babel-eslint",
"root": true, "root": true,
"extends": ["./.eslintrc-base.json", "prettier", "prettier/react"], "extends": [
"plugins": ["react", "import", "prefer-object-spread", "prettier", "react-hooks", "jsx-a11y"], "./.eslintrc-base.json",
"prettier"
],
"plugins": [
"react",
"import",
"prefer-object-spread",
"prettier",
"react-hooks",
"jsx-a11y"
],
"globals": { "globals": {
"Promise": true, "Promise": true,
"window": true, "window": true,
@@ -20,8 +30,14 @@
"settings": { "settings": {
"react": { "react": {
"version": "16.4.2", "version": "16.4.2",
"import/ignore": ["node_modules", "\\.json$"], "import/ignore": [
"import/extensions": [".js", ".jsx"] "node_modules",
"\\.json$"
],
"import/extensions": [
".js",
".jsx"
]
} }
} }
} }

View File

@@ -4,5 +4,6 @@
"singleQuote": true, "singleQuote": true,
"jsxSingleQuote": true, "jsxSingleQuote": true,
"tabWidth": 2, "tabWidth": 2,
"trailingComma": "none" "trailingComma": "none",
"arrowParens": "avoid"
} }

View File

@@ -41,9 +41,9 @@ export default function donateBoot(app, done) {
metadata: { metadata: {
/* eslint-disable camelcase */ /* eslint-disable camelcase */
sb_service: `freeCodeCamp.org`, sb_service: `freeCodeCamp.org`,
sb_tier: `${ sb_tier: `${donationSubscriptionConfig.duration[duration]} $${
donationSubscriptionConfig.duration[duration] amount / 100
} $${amount / 100} Donation` } Donation`
/* eslint-enable camelcase */ /* eslint-enable camelcase */
} }
}, },

View File

@@ -77,9 +77,7 @@ function refetchCompletedChallenges(req, res, next) {
} }
const updateMyEmailValidators = [ const updateMyEmailValidators = [
check('email') check('email').isEmail().withMessage('Email format is invalid.')
.isEmail()
.withMessage('Email format is invalid.')
]; ];
function updateMyEmail(req, res, next) { function updateMyEmail(req, res, next) {
@@ -117,9 +115,7 @@ function updateMyCurrentChallenge(req, res, next) {
} }
const updateMyThemeValidators = [ const updateMyThemeValidators = [
check('theme') check('theme').isIn(Object.keys(themes)).withMessage('Theme is invalid.')
.isIn(Object.keys(themes))
.withMessage('Theme is invalid.')
]; ];
function updateMyTheme(req, res, next) { function updateMyTheme(req, res, next) {

View File

@@ -64,7 +64,10 @@ boot(app, __dirname, err => {
setupPassport(app); setupPassport(app);
const { db } = app.datasources; const { db } = app.datasources;
db.on('connected', _.once(() => log('db connected'))); db.on(
'connected',
_.once(() => log('db connected'))
);
app.start = _.once(function () { app.start = _.once(function () {
const server = app.listen(app.get('port'), function () { const server = app.listen(app.get('port'), function () {
app.emit('started'); app.emit('started');

View File

@@ -3,14 +3,8 @@ import moment from 'moment-timezone';
// day count between two epochs (inclusive) // day count between two epochs (inclusive)
export function dayCount([head, tail], timezone = 'UTC') { export function dayCount([head, tail], timezone = 'UTC') {
return Math.ceil( return Math.ceil(
moment( moment(moment(head).tz(timezone).endOf('day')).diff(
moment(head) moment(tail).tz(timezone).startOf('day'),
.tz(timezone)
.endOf('day')
).diff(
moment(tail)
.tz(timezone)
.startOf('day'),
'days', 'days',
true true
) )

View File

@@ -49,9 +49,7 @@ export function timeCache(time, unit) {
// set new expire time in MS and create new subscription to source // set new expire time in MS and create new subscription to source
if (!expireCacheAt || expireCacheAt < Date.now()) { if (!expireCacheAt || expireCacheAt < Date.now()) {
// set expire in ms; // set expire in ms;
expireCacheAt = moment() expireCacheAt = moment().add(time, unit).valueOf();
.add(time, unit)
.valueOf();
cache = new AsyncSubject(); cache = new AsyncSubject();
source.subscribe(cache); source.subscribe(cache);
} }

View File

@@ -25,36 +25,23 @@ export function prepUniqueDaysByHours(cals, tz = 'UTC') {
data.push(cur); data.push(cur);
prev = cur; prev = cur;
} else if ( } else if (
moment(cur) moment(cur).tz(tz).diff(moment(prev).tz(tz).startOf('day'), 'hours') >=
.tz(tz) hoursDay
.diff(
moment(prev)
.tz(tz)
.startOf('day'),
'hours'
) >= hoursDay
) { ) {
data.push(cur); data.push(cur);
prev = cur; prev = cur;
} }
}, []), }, []),
sortBy(e => e), sortBy(e => e),
map(ts => map(ts => moment(ts).tz(tz).startOf('hours').valueOf())
moment(ts)
.tz(tz)
.startOf('hours')
.valueOf()
)
)(cals); )(cals);
} }
export function calcCurrentStreak(cals, tz = 'UTC') { export function calcCurrentStreak(cals, tz = 'UTC') {
let prev = last(cals); let prev = last(cals);
if ( if (
moment() moment().tz(tz).startOf('day').diff(moment(prev).tz(tz), 'hours') >
.tz(tz) hoursBetween
.startOf('day')
.diff(moment(prev).tz(tz), 'hours') > hoursBetween
) { ) {
return 0; return 0;
} }
@@ -62,10 +49,8 @@ export function calcCurrentStreak(cals, tz = 'UTC') {
let streakContinues = true; let streakContinues = true;
forEachRight(cur => { forEachRight(cur => {
if ( if (
moment(prev) moment(prev).tz(tz).startOf('day').diff(moment(cur).tz(tz), 'hours') <=
.tz(tz) hoursBetween
.startOf('day')
.diff(moment(cur).tz(tz), 'hours') <= hoursBetween
) { ) {
prev = cur; prev = cur;
currentStreak++; currentStreak++;
@@ -86,10 +71,8 @@ export function calcLongestStreak(cals, tz = 'UTC') {
const last = cals[index === 0 ? 0 : index - 1]; const last = cals[index === 0 ? 0 : index - 1];
// is streak broken // is streak broken
if ( if (
moment(head) moment(head).tz(tz).startOf('day').diff(moment(last).tz(tz), 'hours') >
.tz(tz) hoursBetween
.startOf('day')
.diff(moment(last).tz(tz), 'hours') > hoursBetween
) { ) {
tail = head; tail = head;
} }

View File

@@ -11,7 +11,8 @@ function createChallengeNode(challenge, reporter) {
// sections. // sections.
if ( if (
typeof challenge.description !== 'string' && typeof challenge.description !== 'string' &&
(challenge.challengeType !== 11 && challenge.challengeType !== 7) challenge.challengeType !== 11 &&
challenge.challengeType !== 7
) { ) {
reporter.warn(` reporter.warn(`

View File

@@ -298,8 +298,9 @@ const ShowCertification = props => {
bsSize='lg' bsSize='lg'
bsStyle='primary' bsStyle='primary'
target='_blank' target='_blank'
href={`https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&name=${certTitle}&organizationId=4831032&issueYear=${certYear}&issueMonth=${certMonth + href={`https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&name=${certTitle}&organizationId=4831032&issueYear=${certYear}&issueMonth=${
1}&certUrl=${certURL}`} certMonth + 1
}&certUrl=${certURL}`}
> >
{t('profile.add-linkedin')} {t('profile.add-linkedin')}
</Button> </Button>
@@ -399,7 +400,4 @@ const ShowCertification = props => {
ShowCertification.displayName = 'ShowCertification'; ShowCertification.displayName = 'ShowCertification';
ShowCertification.propTypes = propTypes; ShowCertification.propTypes = propTypes;
export default connect( export default connect(mapStateToProps, mapDispatchToProps)(ShowCertification);
mapStateToProps,
mapDispatchToProps
)(ShowCertification);

View File

@@ -211,7 +211,4 @@ export function ShowSettings(props) {
ShowSettings.displayName = 'ShowSettings'; ShowSettings.displayName = 'ShowSettings';
ShowSettings.propTypes = propTypes; ShowSettings.propTypes = propTypes;
export default connect( export default connect(mapStateToProps, mapDispatchToProps)(ShowSettings);
mapStateToProps,
mapDispatchToProps
)(ShowSettings);

View File

@@ -155,8 +155,5 @@ ShowUser.displayName = 'ShowUser';
ShowUser.propTypes = propTypes; ShowUser.propTypes = propTypes;
export default withTranslation()( export default withTranslation()(
connect( connect(mapStateToProps, mapDispatchToProps)(ShowUser)
mapStateToProps,
mapDispatchToProps
)(ShowUser)
); );

View File

@@ -24,7 +24,4 @@ AppMountNotifier.propTypes = {
render: PropTypes.func.isRequired render: PropTypes.func.isRequired
}; };
export default connect( export default connect(mapStateToProps, mapDispatchToProps)(AppMountNotifier);
mapStateToProps,
mapDispatchToProps
)(AppMountNotifier);

View File

@@ -163,7 +163,4 @@ function DonateModal({
DonateModal.displayName = 'DonateModal'; DonateModal.displayName = 'DonateModal';
DonateModal.propTypes = propTypes; DonateModal.propTypes = propTypes;
export default connect( export default connect(mapStateToProps, mapDispatchToProps)(DonateModal);
mapStateToProps,
mapDispatchToProps
)(DonateModal);

View File

@@ -10,12 +10,9 @@ import { apiLocation, homeLocation } from '../../../../../config/env.json';
import './login.css'; import './login.css';
const mapStateToProps = createSelector( const mapStateToProps = createSelector(isSignedInSelector, isSignedIn => ({
isSignedInSelector,
isSignedIn => ({
isSignedIn isSignedIn
}) }));
);
function Login(props) { function Login(props) {
const { t } = useTranslation(); const { t } = useTranslation();

View File

@@ -207,7 +207,4 @@ export class NavLinks extends Component {
NavLinks.propTypes = propTypes; NavLinks.propTypes = propTypes;
NavLinks.displayName = 'NavLinks'; NavLinks.displayName = 'NavLinks';
export default connect( export default connect(null, mapDispatchToProps)(withTranslation()(NavLinks));
null,
mapDispatchToProps
)(withTranslation()(NavLinks));

View File

@@ -7,10 +7,7 @@ const createLanguageRedirect = ({ clientLocale, lang }) => {
.filter(item => (item !== clientLocale && item !== lang ? item : '')) .filter(item => (item !== clientLocale && item !== lang ? item : ''))
.join('/'); .join('/');
const hostTail = window?.location?.host const hostTail = window?.location?.host.split('.').slice(1).join('.');
.split('.')
.slice(1)
.join('.');
const nextClient = lang !== 'chinese' ? 'www' : 'chinese'; const nextClient = lang !== 'chinese' ? 'www' : 'chinese';
const nextLocation = `${window?.location?.protocol}//${nextClient}.${hostTail}`; const nextLocation = `${window?.location?.protocol}//${nextClient}.${hostTail}`;

View File

@@ -6,12 +6,9 @@ import { fetchUser, isSignedInSelector, executeGA } from '../../redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
const mapStateToProps = createSelector( const mapStateToProps = createSelector(isSignedInSelector, isSignedIn => ({
isSignedInSelector,
isSignedIn => ({
isSignedIn isSignedIn
}) }));
);
const mapDispatchToProps = { fetchUser, executeGA }; const mapDispatchToProps = { fetchUser, executeGA };

View File

@@ -90,7 +90,4 @@ LearnLayout.propTypes = {
}) })
}; };
export default connect( export default connect(mapStateToProps, mapDispatchToProps)(LearnLayout);
mapStateToProps,
mapDispatchToProps
)(LearnLayout);

View File

@@ -336,9 +336,17 @@ function useIdToNameMap() {
certPath: getPathFromID(id) certPath: getPathFromID(id)
}); });
} }
edges.forEach(({ node: { id, title, fields: { slug } } }) => { edges.forEach(
({
node: {
id,
title,
fields: { slug }
}
}) => {
idToNameMap.set(id, { challengeTitle: title, challengePath: slug }); idToNameMap.set(id, { challengeTitle: title, challengePath: slug });
}); }
);
return idToNameMap; return idToNameMap;
} }

View File

@@ -386,9 +386,7 @@ export class CertificationSettings extends Component {
newChallengeFound = true; newChallengeFound = true;
} }
const valuesSaved = values(formChalObj) const valuesSaved = values(formChalObj).filter(Boolean).filter(isString);
.filter(Boolean)
.filter(isString);
const isProjectSectionComplete = valuesSaved.length === oldSubmissions; const isProjectSectionComplete = valuesSaved.length === oldSubmissions;

View File

@@ -14,12 +14,9 @@ import Spacer from '../helpers/Spacer';
import ToggleSetting from './ToggleSetting'; import ToggleSetting from './ToggleSetting';
import SectionHeader from './SectionHeader'; import SectionHeader from './SectionHeader';
const mapStateToProps = createSelector( const mapStateToProps = createSelector(userSelector, user => ({
userSelector,
user => ({
user user
}) }));
);
const mapDispatchToProps = dispatch => const mapDispatchToProps = dispatch =>
bindActionCreators({ submitProfileUI }, dispatch); bindActionCreators({ submitProfileUI }, dispatch);

View File

@@ -45,7 +45,4 @@ class SearchPage extends Component {
SearchPage.displayName = 'SearchPage'; SearchPage.displayName = 'SearchPage';
SearchPage.propTypes = propTypes; SearchPage.propTypes = propTypes;
export default connect( export default connect(null, mapDispatchToProps)(withTranslation()(SearchPage));
null,
mapDispatchToProps
)(withTranslation()(SearchPage));

View File

@@ -971,9 +971,6 @@ Editor.propTypes = propTypes;
// NOTE: withRef gets replaced by forwardRef in react-redux 6, // NOTE: withRef gets replaced by forwardRef in react-redux 6,
// https://github.com/reduxjs/react-redux/releases/tag/v6.0.0 // https://github.com/reduxjs/react-redux/releases/tag/v6.0.0
export default connect( export default connect(mapStateToProps, mapDispatchToProps, null, {
mapStateToProps, withRef: true
mapDispatchToProps, })(Editor);
null,
{ withRef: true }
)(Editor);

View File

@@ -86,7 +86,4 @@ class EditorTabs extends Component {
EditorTabs.displayName = 'EditorTabs'; EditorTabs.displayName = 'EditorTabs';
EditorTabs.propTypes = propTypes; EditorTabs.propTypes = propTypes;
export default connect( export default connect(mapStateToProps, mapDispatchToProps)(EditorTabs);
mapStateToProps,
mapDispatchToProps
)(EditorTabs);

View File

@@ -101,7 +101,4 @@ class MobileLayout extends Component {
MobileLayout.displayName = 'MobileLayout'; MobileLayout.displayName = 'MobileLayout';
MobileLayout.propTypes = propTypes; MobileLayout.propTypes = propTypes;
export default connect( export default connect(mapStateToProps, mapDispatchToProps)(MobileLayout);
mapStateToProps,
mapDispatchToProps
)(MobileLayout);

View File

@@ -274,9 +274,6 @@ MultifileEditor.propTypes = propTypes;
// NOTE: withRef gets replaced by forwardRef in react-redux 6, // NOTE: withRef gets replaced by forwardRef in react-redux 6,
// https://github.com/reduxjs/react-redux/releases/tag/v6.0.0 // https://github.com/reduxjs/react-redux/releases/tag/v6.0.0
export default connect( export default connect(mapStateToProps, mapDispatchToProps, null, {
mapStateToProps, withRef: true
mapDispatchToProps, })(MultifileEditor);
null,
{ withRef: true }
)(MultifileEditor);

View File

@@ -90,7 +90,4 @@ function Hotkeys({
Hotkeys.displayName = 'Hotkeys'; Hotkeys.displayName = 'Hotkeys';
Hotkeys.propTypes = propTypes; Hotkeys.propTypes = propTypes;
export default connect( export default connect(mapStateToProps, mapDispatchToProps)(Hotkeys);
mapStateToProps,
mapDispatchToProps
)(Hotkeys);

View File

@@ -63,7 +63,4 @@ class Preview extends Component {
Preview.displayName = 'Preview'; Preview.displayName = 'Preview';
Preview.propTypes = propTypes; Preview.propTypes = propTypes;
export default connect( export default connect(null, mapDispatchToProps)(withTranslation()(Preview));
null,
mapDispatchToProps
)(withTranslation()(Preview));

View File

@@ -18,12 +18,9 @@ const propTypes = {
reset: PropTypes.func.isRequired reset: PropTypes.func.isRequired
}; };
const mapStateToProps = createSelector( const mapStateToProps = createSelector(isResetModalOpenSelector, isOpen => ({
isResetModalOpenSelector,
isOpen => ({
isOpen isOpen
}) }));
);
const mapDispatchToProps = dispatch => const mapDispatchToProps = dispatch =>
bindActionCreators( bindActionCreators(
@@ -80,7 +77,4 @@ function ResetModal({ reset, close, isOpen }) {
ResetModal.displayName = 'ResetModal'; ResetModal.displayName = 'ResetModal';
ResetModal.propTypes = propTypes; ResetModal.propTypes = propTypes;
export default connect( export default connect(mapStateToProps, mapDispatchToProps)(ResetModal);
mapStateToProps,
mapDispatchToProps
)(ResetModal);

View File

@@ -47,7 +47,10 @@ export class SidePanel extends Component {
// users navigate from another challenge // users navigate from another challenge
MathJax.Hub.Config({ MathJax.Hub.Config({
tex2jax: { tex2jax: {
inlineMath: [['$', '$'], ['\\(', '\\)']], inlineMath: [
['$', '$'],
['\\(', '\\)']
],
processEscapes: true, processEscapes: true,
processClass: 'rosetta-code|project-euler' processClass: 'rosetta-code|project-euler'
} }

View File

@@ -105,7 +105,4 @@ function ToolPanel({
ToolPanel.displayName = 'ToolPanel'; ToolPanel.displayName = 'ToolPanel';
ToolPanel.propTypes = propTypes; ToolPanel.propTypes = propTypes;
export default connect( export default connect(mapStateToProps, mapDispatchToProps)(ToolPanel);
mapStateToProps,
mapDispatchToProps
)(ToolPanel);

View File

@@ -43,11 +43,7 @@ const padContentWithCssCatch = partial(compileHeadTail, cssCatch);
export const jsToHtml = cond([ export const jsToHtml = cond([
[ [
matchesProperty('ext', 'js'), matchesProperty('ext', 'js'),
flow( flow(padContentWithJsCatch, wrapInScript, setExtToHTML)
padContentWithJsCatch,
wrapInScript,
setExtToHTML
)
], ],
[stubTrue, identity] [stubTrue, identity]
]); ]);
@@ -55,11 +51,7 @@ export const jsToHtml = cond([
export const cssToHtml = cond([ export const cssToHtml = cond([
[ [
matchesProperty('ext', 'css'), matchesProperty('ext', 'css'),
flow( flow(padContentWithCssCatch, wrapInStyle, setExtToHTML)
padContentWithCssCatch,
wrapInStyle,
setExtToHTML
)
], ],
[stubTrue, identity] [stubTrue, identity]
]); ]);

View File

@@ -3,10 +3,7 @@ import { ofType } from 'redux-observable';
import { types, unlockCode } from './'; import { types, unlockCode } from './';
function codeLockEpic(action$) { function codeLockEpic(action$) {
return action$.pipe( return action$.pipe(ofType(types.updateFile), map(unlockCode));
ofType(types.updateFile),
map(unlockCode)
);
} }
export default codeLockEpic; export default codeLockEpic;

View File

@@ -58,9 +58,11 @@ function createQuestionEpic(action$, state$, { window }) {
? `**Your project link(s)**\n` ? `**Your project link(s)**\n`
: `**Your code so far**` : `**Your code so far**`
} }
${projectFormValues ${
projectFormValues
?.map(([key, val]) => `${key}: ${val}\n`) ?.map(([key, val]) => `${key}: ${val}\n`)
?.join('') || filesToMarkdown(files)} ?.join('') || filesToMarkdown(files)
}
${endingText}` ${endingText}`
); );

View File

@@ -74,9 +74,9 @@ export function* executeChallengeSaga({
yield put(initLogs()); yield put(initLogs());
yield put(initConsole(i18next.t('learn.running-tests'))); yield put(initConsole(i18next.t('learn.running-tests')));
// reset tests to initial state // reset tests to initial state
const tests = (yield select(challengeTestsSelector)).map( const tests = (yield select(
({ text, testString }) => ({ text, testString }) challengeTestsSelector
); )).map(({ text, testString }) => ({ text, testString }));
yield put(updateTests(tests)); yield put(updateTests(tests));
yield fork(takeEveryLog, consoleProxy); yield fork(takeEveryLog, consoleProxy);

View File

@@ -18,12 +18,9 @@ const propTypes = {
}; };
const mapStateToProps = state => { const mapStateToProps = state => {
return createSelector( return createSelector(userSelector, user => ({
userSelector,
user => ({
user user
}) }))(state);
)(state);
}; };
export class CertChallenge extends Component { export class CertChallenge extends Component {

View File

@@ -11,12 +11,9 @@ import GreenPass from '../../../assets/icons/GreenPass';
import GreenNotCompleted from '../../../assets/icons/GreenNotCompleted'; import GreenNotCompleted from '../../../assets/icons/GreenNotCompleted';
const mapStateToProps = state => { const mapStateToProps = state => {
return createSelector( return createSelector(completedChallengesSelector, completedChallenges => ({
completedChallengesSelector,
completedChallenges => ({
completedChallenges: completedChallenges.map(({ id }) => id) completedChallenges: completedChallenges.map(({ id }) => id)
}) }))(state);
)(state);
}; };
const mapDispatchToProps = dispatch => const mapDispatchToProps = dispatch =>

View File

@@ -16,12 +16,7 @@ function prototyper(str) {
.map(str => { .map(str => {
if (prototypeRE.test(str)) { if (prototypeRE.test(str)) {
if (str.length > 9) { if (str.length > 9) {
return prototyper( return prototyper(str.trim().split('prototype').join('-prototype-'));
str
.trim()
.split('prototype')
.join('-prototype-')
);
} }
return str; return str;
} }
@@ -39,12 +34,7 @@ function prototyper(str) {
return `${titleify(first)}.prototype.${finalSecond}`; return `${titleify(first)}.prototype.${finalSecond}`;
} }
if (noProto.length === 1) { if (noProto.length === 1) {
return prototyper( return prototyper(noProto[0].toLowerCase().split('.').join('-'));
noProto[0]
.toLowerCase()
.split('.')
.join('-')
);
} }
return titleify(str, true); return titleify(str, true);
} }

View File

@@ -53,22 +53,22 @@ const donationSubscriptionConfig = {
const paypalConfigTypes = { const paypalConfigTypes = {
live: { live: {
month: { month: {
'500': { planId: 'P-1L11422374370240ULZKX3PA' }, 500: { planId: 'P-1L11422374370240ULZKX3PA' },
'1000': { planId: 'P-61K21421WY874920PL6E36YI' }, 1000: { planId: 'P-61K21421WY874920PL6E36YI' },
'2000': { planId: 'P-31999436LF709112VL6E374A' }, 2000: { planId: 'P-31999436LF709112VL6E374A' },
'3000': { planId: 'P-1KY930839N8045117L6E4BKY' }, 3000: { planId: 'P-1KY930839N8045117L6E4BKY' },
'4000': { planId: 'P-0JW4843250567551AL6E4CAI' }, 4000: { planId: 'P-0JW4843250567551AL6E4CAI' },
'5000': { planId: 'P-0WR49877YD949401BL6E4CTA' } 5000: { planId: 'P-0WR49877YD949401BL6E4CTA' }
} }
}, },
staging: { staging: {
month: { month: {
'500': { planId: 'P-37N14480BW163382FLZYPVMA' }, 500: { planId: 'P-37N14480BW163382FLZYPVMA' },
'1000': { planId: 'P-28B62039J8092810UL6E3FXA' }, 1000: { planId: 'P-28B62039J8092810UL6E3FXA' },
'2000': { planId: 'P-7HR706961M9170433L6HI5VI' }, 2000: { planId: 'P-7HR706961M9170433L6HI5VI' },
'3000': { planId: 'P-35V33574BU596924JL6HI6XY' }, 3000: { planId: 'P-35V33574BU596924JL6HI6XY' },
'4000': { planId: 'P-45M45060289267734L6HJSXA' }, 4000: { planId: 'P-45M45060289267734L6HJSXA' },
'5000': { planId: 'P-0MD70861FY4172444L6HJTUQ' } 5000: { planId: 'P-0MD70861FY4172444L6HJTUQ' }
} }
} }
}; };

View File

@@ -25,10 +25,7 @@ const schema = Joi.object()
block: Joi.string().regex(slugRE), block: Joi.string().regex(slugRE),
blockId: Joi.objectId(), blockId: Joi.objectId(),
challengeOrder: Joi.number(), challengeOrder: Joi.number(),
challengeType: Joi.number() challengeType: Joi.number().min(0).max(11).required(),
.min(0)
.max(11)
.required(),
checksum: Joi.number(), checksum: Joi.number(),
// TODO: require this only for normal challenges, not certs // TODO: require this only for normal challenges, not certs
dashedName: Joi.string().regex(slugRE), dashedName: Joi.string().regex(slugRE),
@@ -62,9 +59,7 @@ const schema = Joi.object()
}), }),
question: Joi.object().keys({ question: Joi.object().keys({
text: Joi.string().required(), text: Joi.string().required(),
answers: Joi.array() answers: Joi.array().items(Joi.string()).required(),
.items(Joi.string())
.required(),
solution: Joi.number().required() solution: Joi.number().required()
}), }),
required: Joi.array().items( required: Joi.array().items(
@@ -92,9 +87,7 @@ const schema = Joi.object()
Joi.object().keys({ Joi.object().keys({
id: Joi.string().allow(''), id: Joi.string().allow(''),
text: Joi.string().required(), text: Joi.string().required(),
testString: Joi.string() testString: Joi.string().allow('').required()
.allow('')
.required()
}), }),
// our tests used in certification verification // our tests used in certification verification
Joi.object().keys({ Joi.object().keys({

View File

@@ -198,9 +198,9 @@ async function setup() {
for (const challenge of challenges) { for (const challenge of challenges) {
const dashedBlockName = challenge.block; const dashedBlockName = challenge.block;
if (!meta[dashedBlockName]) { if (!meta[dashedBlockName]) {
meta[dashedBlockName] = (await getMetaForBlock( meta[dashedBlockName] = (
dashedBlockName await getMetaForBlock(dashedBlockName)
)).challengeOrder; ).challengeOrder;
} }
} }
return { return {
@@ -475,7 +475,9 @@ function populateTestsForLang({ lang, challenges, meta }) {
describe('Check tests against solutions', function () { describe('Check tests against solutions', function () {
solutions.forEach((solution, index) => { solutions.forEach((solution, index) => {
it(`Solution ${index + 1} must pass the tests`, async function() { it(`Solution ${
index + 1
} must pass the tests`, async function () {
this.timeout(5000 * tests.length + 2000); this.timeout(5000 * tests.length + 2000);
const testRunner = await createTestRunner( const testRunner = await createTestRunner(
challenge, challenge,

View File

@@ -43,43 +43,28 @@ describe('Landing page', () => {
}); });
it('Has 5 brand logos', () => { it('Has 5 brand logos', () => {
cy.get('.logo-row') cy.get('.logo-row').children().its('length').should('eq', 5);
.children()
.its('length')
.should('eq', 5);
}); });
it('Has `as seens as` section', () => { it('Has `as seens as` section', () => {
cy.contains('Build projects.').should('be.visible'); cy.contains('Build projects.').should('be.visible');
cy.get('.big-heading') cy.get('.big-heading').siblings().get('svg');
.siblings()
.get('svg');
}); });
it('Has a visible large image on large viewports', function () { it('Has a visible large image on large viewports', function () {
cy.viewport(1200, 660) cy.viewport(1200, 660).get(selectors.landingPageImage).should('be.visible');
.get(selectors.landingPageImage)
.should('be.visible');
cy.viewport(1199, 660) cy.viewport(1199, 660).get(selectors.landingPageImage).should('not.exist');
.get(selectors.landingPageImage)
.should('not.exist');
}); });
it('Has links to all the certifications', function () { it('Has links to all the certifications', function () {
cy.get(selectors.certifications) cy.get(selectors.certifications).children().its('length').should('eq', 10);
.children()
.its('length')
.should('eq', 10);
cy.wrap(certifications).each(cert => { cy.wrap(certifications).each(cert => {
cy.get(selectors.certifications).contains(cert); cy.get(selectors.certifications).contains(cert);
}); });
}); });
it('Has 3 testimonial cards', function () { it('Has 3 testimonial cards', function () {
cy.get(selectors.testimonials) cy.get(selectors.testimonials).children().its('length').should('eq', 3);
.children()
.its('length')
.should('eq', 3);
}); });
}); });

View File

@@ -68,9 +68,7 @@ describe('Navbar', () => {
() => { () => {
cy.get(selectors.menuButton).click(); cy.get(selectors.menuButton).click();
cy.get(selectors.navigationLinks).contains('Forum'); cy.get(selectors.navigationLinks).contains('Forum');
cy.get(selectors.navigationLinks) cy.get(selectors.navigationLinks).contains('Curriculum').click();
.contains('Curriculum')
.click();
cy.url().should('include', '/learn'); cy.url().should('include', '/learn');
cy.get(selectors.navigationLinks).contains('Curriculum'); cy.get(selectors.navigationLinks).contains('Curriculum');
cy.get(selectors.navigationLinks).contains('Forum'); cy.get(selectors.navigationLinks).contains('Forum');
@@ -84,9 +82,7 @@ describe('Navbar', () => {
() => { () => {
cy.contains(selectors.smallCallToAction, 'Sign in'); cy.contains(selectors.smallCallToAction, 'Sign in');
cy.get(selectors.menuButton).click(); cy.get(selectors.menuButton).click();
cy.get(selectors.navigationLinks) cy.get(selectors.navigationLinks).contains('Curriculum').click();
.contains('Curriculum')
.click();
cy.contains(selectors.smallCallToAction, 'Sign in'); cy.contains(selectors.smallCallToAction, 'Sign in');
} }
); );
@@ -95,9 +91,7 @@ describe('Navbar', () => {
cy.login(); cy.login();
cy.get('a[href*="/settings"]').should('be.visible'); cy.get('a[href*="/settings"]').should('be.visible');
cy.get(selectors.menuButton).click(); cy.get(selectors.menuButton).click();
cy.get(selectors.navigationLinks) cy.get(selectors.navigationLinks).contains('Profile').click();
.contains('Profile')
.click();
cy.url().should('include', '/developmentuser'); cy.url().should('include', '/developmentuser');
}); });

View File

@@ -30,17 +30,13 @@ describe('Search bar', () => {
it('Should accept input and display hits', () => { it('Should accept input and display hits', () => {
search('freeCodeCamp'); search('freeCodeCamp');
cy.get('.ais-Hits-list') cy.get('.ais-Hits-list').children().should('to.have.length.of.at.least', 1);
.children()
.should('to.have.length.of.at.least', 1);
}); });
it('Should clear hits when input is cleared', () => { it('Should clear hits when input is cleared', () => {
search('freeCodeCamp'); search('freeCodeCamp');
cy.get('.ais-Hits-list') cy.get('.ais-Hits-list').children().should('to.have.length.of.at.least', 1);
.children()
.should('to.have.length.of.at.least', 1);
clear(); clear();
@@ -52,9 +48,7 @@ describe('Search bar', () => {
search('freeCodeCamp'); search('freeCodeCamp');
cy.get('.ais-Hits-list') cy.get('.ais-Hits-list').children().should('to.have.length.of', 8);
.children()
.should('to.have.length.of', 8);
}); });
it('Should show up to 5 hits when height < 768px', () => { it('Should show up to 5 hits when height < 768px', () => {
@@ -62,17 +56,13 @@ describe('Search bar', () => {
search('freeCodeCamp'); search('freeCodeCamp');
cy.get('.ais-Hits-list') cy.get('.ais-Hits-list').children().should('to.have.length.of', 5);
.children()
.should('to.have.length.of', 5);
}); });
it('Should show no hits for queries that do not exist in the Algolia index', () => { it('Should show no hits for queries that do not exist in the Algolia index', () => {
search('testtttt'); search('testtttt');
cy.get('.ais-Hits-list') cy.get('.ais-Hits-list').children().should('to.have.length.of', 0);
.children()
.should('to.have.length.of', 0);
cy.contains('No tutorials found'); cy.contains('No tutorials found');
}); });

View File

@@ -96,9 +96,7 @@ describe('Username input field', () => {
'the URL to your profile and your certifications.' 'the URL to your profile and your certifications.'
).should('not.exist'); ).should('not.exist');
cy.get('@usernameForm') cy.get('@usernameForm').contains('Save').should('be.disabled');
.contains('Save')
.should('be.disabled');
}); });
it('Should not show anything if user types their current name', () => { it('Should not show anything if user types their current name', () => {
@@ -106,9 +104,7 @@ describe('Username input field', () => {
.clear({ force: true }) .clear({ force: true })
.type('developmentuser', { force: true }); .type('developmentuser', { force: true });
cy.get('@usernameForm') cy.get('@usernameForm').contains('Save').should('be.disabled');
.contains('Save')
.should('be.disabled');
}); });
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
@@ -117,9 +113,7 @@ describe('Username input field', () => {
.clear({ force: true }) .clear({ force: true })
.type('developmentuser', { force: true }); .type('developmentuser', { force: true });
cy.get('@usernameForm') cy.get('@usernameForm').contains('Save').should('be.disabled');
.contains('Save')
.should('be.disabled');
}); });
it('Should show warning if username includes invalid character', () => { it('Should show warning if username includes invalid character', () => {
@@ -141,9 +135,7 @@ describe('Username input field', () => {
.clear({ force: true }) .clear({ force: true })
.type('Quincy Larson', { force: true }); .type('Quincy Larson', { force: true });
cy.get('@usernameForm') cy.get('@usernameForm').contains('Save').should('be.disabled');
.contains('Save')
.should('be.disabled');
}); });
it('Should change username if `Save` button is clicked', () => { it('Should change username if `Save` button is clicked', () => {
@@ -153,9 +145,7 @@ describe('Username input field', () => {
cy.contains('Username is available'); cy.contains('Username is available');
cy.get('@usernameForm') cy.get('@usernameForm').contains('Save').click({ force: true });
.contains('Save')
.click({ force: true });
cy.contains('Account Settings for quincy').should('be.visible'); cy.contains('Account Settings for quincy').should('be.visible');
cy.resetUsername(); cy.resetUsername();

1746
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -71,8 +71,8 @@
"cypress": "^6.6.0", "cypress": "^6.6.0",
"debug": "^4.3.1", "debug": "^4.3.1",
"docsify-cli": "^4.4.3", "docsify-cli": "^4.4.3",
"eslint": "^5.16.0", "eslint": "^7.21.0",
"eslint-config-prettier": "^6.12.0", "eslint-config-prettier": "^8.1.0",
"eslint-plugin-babel": "^5.3.1", "eslint-plugin-babel": "^5.3.1",
"eslint-plugin-import": "^2.22.1", "eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-jsx-a11y": "^6.4.1",
@@ -92,7 +92,7 @@
"markdownlint": "^0.21.0", "markdownlint": "^0.21.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"ora": "^3.4.0", "ora": "^3.4.0",
"prettier": "^1.18.2", "prettier": "^2.2.1",
"prismjs": "^1.23.0", "prismjs": "^1.23.0",
"readdirp-walk": "^1.7.0", "readdirp-walk": "^1.7.0",
"shx": "^0.3.3", "shx": "^0.3.3",

View File

@@ -24,8 +24,10 @@ if (
start < 1 || start < 1 ||
start !== end - 1 start !== end - 1
) { ) {
throw 'Step not created. Steps specified must be' + throw (
' consecutive numbers and start step must be greater than 0.'; 'Step not created. Steps specified must be' +
' consecutive numbers and start step must be greater than 0.'
);
} }
const existingSteps = getExistingStepNums(projectPath); const existingSteps = getExistingStepNums(projectPath);

View File

@@ -73,14 +73,8 @@ const reorderSteps = () => {
const projectPath = getProjectPath(); const projectPath = getProjectPath();
const projectName = process.env.CALLING_DIR const projectName = process.env.CALLING_DIR
? process.env.CALLING_DIR.split(path.sep) ? process.env.CALLING_DIR.split(path.sep).slice(-1).toString()
.slice(-1) : process.cwd().split(path.sep).slice(-1).toString();
.toString()
: process
.cwd()
.split(path.sep)
.slice(-1)
.toString();
const curriculumPath = process.env.CALLING_DIR const curriculumPath = process.env.CALLING_DIR
? '' ? ''
@@ -187,8 +181,10 @@ const getExistingStepNums = projectPath => {
) { ) {
let stepNum = fileName.split('.')[0].split('-')[1]; let stepNum = fileName.split('.')[0].split('-')[1];
if (!/^\d{3}$/.test(stepNum)) { if (!/^\d{3}$/.test(stepNum)) {
throw `Step not created. File ${fileName} has a step number containing non-digits.` + throw (
' Please run reorder-steps script first.'; `Step not created. File ${fileName} has a step number containing non-digits.` +
' Please run reorder-steps script first.'
);
} }
stepNum = parseInt(stepNum, 10); stepNum = parseInt(stepNum, 10);
stepNums.push(stepNum); stepNums.push(stepNum);

View File

@@ -90,14 +90,16 @@ function validateEditableMarkers({ value, position }) {
lines.forEach((line, index) => { lines.forEach((line, index) => {
if (line.match(twoMarkersRE)) { if (line.match(twoMarkersRE)) {
throw Error( throw Error(
`Line ${baseLineNumber + `Line ${
index} has two markers. Each line should only have one.` baseLineNumber + index
} has two markers. Each line should only have one.`
); );
} }
if (line.match(formattedMarkerRE)) { if (line.match(formattedMarkerRE)) {
throw Error( throw Error(
`Line ${baseLineNumber + `Line ${
index} has a malformed marker. It should be --fcc-editable-region--` baseLineNumber + index
} has a malformed marker. It should be --fcc-editable-region--`
); );
} }
}); });

View File

@@ -11,9 +11,7 @@ const { editableRegionMarker } = require('./add-seed');
const tableAndStrikeThrough = require('./table-and-strikethrough'); const tableAndStrikeThrough = require('./table-and-strikethrough');
async function parse(file) { async function parse(file) {
return await remark() return await remark().use(tableAndStrikeThrough).parse(file);
.use(tableAndStrikeThrough)
.parse(file);
} }
function plugin() { function plugin() {

View File

@@ -12,7 +12,7 @@ const usernameIsHttpStatusCode = {
const isNumeric = num => !isNaN(num); const isNumeric = num => !isNaN(num);
const validCharsRE = /^[a-zA-Z0-9\-_+]*$/; const validCharsRE = /^[a-zA-Z0-9\-_+]*$/;
const isHttpStatusCode = str => const isHttpStatusCode = str =>
isNumeric(str) && (parseInt(str, 10) >= 100 && parseInt(str, 10) <= 599); isNumeric(str) && parseInt(str, 10) >= 100 && parseInt(str, 10) <= 599;
const isValidUsername = str => { const isValidUsername = str => {
if (!validCharsRE.test(str)) return invalidCharError; if (!validCharsRE.test(str)) return invalidCharError;
if (str.length < 3) return usernameTooShort; if (str.length < 3) return usernameTooShort;