refactor: simplify challenge.block usage (#41185)
This commit is contained in:
committed by
GitHub
parent
3dd33d87ed
commit
f8699a8d55
@ -13,7 +13,6 @@ import isNumeric from 'validator/lib/isNumeric';
|
||||
import isURL from 'validator/lib/isURL';
|
||||
|
||||
import { ifNoUserSend } from '../utils/middleware';
|
||||
import { dasherize } from '../../../../utils/slugs';
|
||||
import { fixCompletedChallengeItem } from '../../common/utils';
|
||||
import { getChallenges } from '../utils/get-curriculum';
|
||||
import {
|
||||
@ -144,7 +143,7 @@ export function buildUserUpdate(
|
||||
|
||||
export function buildChallengeUrl(challenge) {
|
||||
const { superBlock, block, dashedName } = challenge;
|
||||
return `/learn/${dasherize(superBlock)}/${dasherize(block)}/${dashedName}`;
|
||||
return `/learn/${superBlock}/${block}/${dashedName}`;
|
||||
}
|
||||
|
||||
// this is only called once during boot, so it can be slow.
|
||||
|
@ -159,14 +159,6 @@ describe('boot/challenge', () => {
|
||||
|
||||
expect(result).toEqual(requestedChallengeUrl);
|
||||
});
|
||||
|
||||
it('can handle non-url-compliant challenge names', () => {
|
||||
const challenge = { ...mockChallenge, superBlock: 'my awesome' };
|
||||
const expected = '/learn/my-awesome/actual/challenge';
|
||||
const result = buildChallengeUrl(challenge);
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('challengeUrlResolver', () => {
|
||||
|
@ -3,7 +3,6 @@ const env = require('../config/env');
|
||||
const { createFilePath } = require('gatsby-source-filesystem');
|
||||
const uniq = require('lodash/uniq');
|
||||
|
||||
const { dasherize } = require('../utils/slugs');
|
||||
const { blockNameify } = require('../utils/block-nameify');
|
||||
const {
|
||||
createChallengePages,
|
||||
@ -20,7 +19,7 @@ exports.onCreateNode = function onCreateNode({ node, actions, getNode }) {
|
||||
const { createNodeField } = actions;
|
||||
if (node.internal.type === 'ChallengeNode') {
|
||||
const { tests = [], block, dashedName, superBlock } = node;
|
||||
const slug = `/learn/${superBlock}/${dasherize(block)}/${dashedName}`;
|
||||
const slug = `/learn/${superBlock}/${block}/${dashedName}`;
|
||||
createNodeField({ node, name: 'slug', value: slug });
|
||||
createNodeField({ node, name: 'blockName', value: blockNameify(block) });
|
||||
createNodeField({ node, name: 'tests', value: tests });
|
||||
|
@ -24,7 +24,6 @@ import Hotkeys from '../components/Hotkeys';
|
||||
import { getGuideUrl } from '../utils';
|
||||
import { challengeTypes } from '../../../../utils/challengeTypes';
|
||||
import { ChallengeNode } from '../../../redux/propTypes';
|
||||
import { dasherize } from '../../../../../utils/slugs';
|
||||
import {
|
||||
createFiles,
|
||||
challengeFilesSelector,
|
||||
@ -206,7 +205,7 @@ class ShowClassic extends Component {
|
||||
|
||||
renderInstructionsPanel({ showToolPanel }) {
|
||||
const {
|
||||
fields: { blockName },
|
||||
block,
|
||||
description,
|
||||
instructions,
|
||||
superBlock,
|
||||
@ -216,12 +215,11 @@ class ShowClassic extends Component {
|
||||
const { forumTopicId, title } = this.getChallenge();
|
||||
return (
|
||||
<SidePanel
|
||||
block={blockName}
|
||||
block={block}
|
||||
className='full-height'
|
||||
description={description}
|
||||
guideUrl={getGuideUrl({ forumTopicId, title })}
|
||||
instructions={instructions}
|
||||
section={dasherize(blockName)}
|
||||
showToolPanel={showToolPanel}
|
||||
superBlock={superBlock}
|
||||
title={title}
|
||||
@ -357,6 +355,7 @@ export default connect(
|
||||
export const query = graphql`
|
||||
query ClassicChallenge($slug: String!) {
|
||||
challengeNode(fields: { slug: { eq: $slug } }) {
|
||||
block
|
||||
title
|
||||
description
|
||||
instructions
|
||||
@ -367,8 +366,8 @@ export const query = graphql`
|
||||
translationPending
|
||||
forumTopicId
|
||||
fields {
|
||||
slug
|
||||
blockName
|
||||
slug
|
||||
tests {
|
||||
text
|
||||
testString
|
||||
|
@ -5,14 +5,14 @@ import PrismFormatted from './PrismFormatted';
|
||||
import './challenge-description.css';
|
||||
|
||||
const propTypes = {
|
||||
block: PropTypes.string,
|
||||
description: PropTypes.string,
|
||||
instructions: PropTypes.string,
|
||||
section: PropTypes.string
|
||||
instructions: PropTypes.string
|
||||
};
|
||||
|
||||
function ChallengeDescription({ description, instructions, section }) {
|
||||
function ChallengeDescription({ description, instructions, block }) {
|
||||
return (
|
||||
<div className={`challenge-instructions${section ? ' ' + section : ''}`}>
|
||||
<div className={`challenge-instructions${block ? ' ' + block : ''}`}>
|
||||
{description && <PrismFormatted text={description} />}
|
||||
{instructions && (
|
||||
<Fragment>
|
||||
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from '../../../components/helpers/index';
|
||||
|
||||
import { dasherize } from '../../../../../utils/slugs';
|
||||
import './challenge-title.css';
|
||||
import GreenPass from '../../../assets/icons/GreenPass';
|
||||
import i18next from 'i18next';
|
||||
@ -46,9 +45,9 @@ function ChallengeTitle({
|
||||
<Link
|
||||
className='breadcrumb-right'
|
||||
state={{ breadcrumbBlockClick: block }}
|
||||
to={`/learn/${superBlock}/#${dasherize(block)}`}
|
||||
to={`/learn/${superBlock}/#${block}`}
|
||||
>
|
||||
{i18next.t(`intro:${superBlock}.blocks.${dasherize(block)}.title`)}
|
||||
{i18next.t(`intro:${superBlock}.blocks.${block}.title`)}
|
||||
</Link>
|
||||
</div>
|
||||
<div className='challenge-title'>
|
||||
|
@ -6,10 +6,11 @@ import renderer from 'react-test-renderer';
|
||||
import ChallengeTitle from './Challenge-Title';
|
||||
|
||||
const baseProps = {
|
||||
block: 'fake block',
|
||||
block: 'fake-block',
|
||||
children: 'title text',
|
||||
isCompleted: true,
|
||||
superBlock: 'fake-superblock'
|
||||
superBlock: 'fake-superblock',
|
||||
translationPending: false
|
||||
};
|
||||
|
||||
describe('<ChallengeTitle/>', () => {
|
||||
|
@ -27,7 +27,6 @@ const propTypes = {
|
||||
guideUrl: PropTypes.string,
|
||||
instructions: PropTypes.string,
|
||||
isChallengeCompleted: PropTypes.bool,
|
||||
section: PropTypes.string,
|
||||
showToolPanel: PropTypes.bool,
|
||||
superBlock: PropTypes.string,
|
||||
tests: PropTypes.arrayOf(PropTypes.object),
|
||||
@ -41,8 +40,8 @@ export class SidePanel extends Component {
|
||||
const MathJax = global.MathJax;
|
||||
const mathJaxMountPoint = document.querySelector('#mathjax');
|
||||
const mathJaxChallenge =
|
||||
this.props.section === 'rosetta-code' ||
|
||||
this.props.section === 'project-euler';
|
||||
this.props.block === 'rosetta-code' ||
|
||||
this.props.block === 'project-euler';
|
||||
if (MathJax) {
|
||||
// Configure MathJax when it's loaded and
|
||||
// users navigate from another challenge
|
||||
@ -73,7 +72,6 @@ export class SidePanel extends Component {
|
||||
isChallengeCompleted,
|
||||
guideUrl,
|
||||
tests,
|
||||
section,
|
||||
showToolPanel,
|
||||
superBlock,
|
||||
translationPending,
|
||||
@ -91,9 +89,9 @@ export class SidePanel extends Component {
|
||||
{title}
|
||||
</ChallengeTitle>
|
||||
<ChallengeDescription
|
||||
block={block}
|
||||
description={description}
|
||||
instructions={instructions}
|
||||
section={section}
|
||||
/>
|
||||
</div>
|
||||
{showToolPanel && <ToolPanel guideUrl={guideUrl} videoUrl={videoUrl} />}
|
||||
|
@ -12,7 +12,7 @@ exports[`<ChallengeTitle/> renders correctly 1`] = `
|
||||
href="/learn/fake-superblock"
|
||||
state={
|
||||
Object {
|
||||
"breadcrumbBlockClick": "fake block",
|
||||
"breadcrumbBlockClick": "fake-block",
|
||||
}
|
||||
}
|
||||
>
|
||||
@ -28,7 +28,7 @@ exports[`<ChallengeTitle/> renders correctly 1`] = `
|
||||
href="/learn/fake-superblock/#fake-block"
|
||||
state={
|
||||
Object {
|
||||
"breadcrumbBlockClick": "fake block",
|
||||
"breadcrumbBlockClick": "fake-block",
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
@ -14,7 +14,6 @@ import Login from '../../components/Header/components/Login';
|
||||
import Map from '../../components/Map';
|
||||
import CertChallenge from './components/CertChallenge';
|
||||
import SuperBlockIntro from './components/SuperBlockIntro';
|
||||
import { dasherize } from '../../../../utils/slugs';
|
||||
import Block from './components/Block';
|
||||
import { Spacer } from '../../components/helpers';
|
||||
import {
|
||||
@ -97,7 +96,7 @@ export class SuperBlockIntroductionPage extends Component {
|
||||
|
||||
// if coming from breadcrumb click
|
||||
if (location.state && location.state.breadcrumbBlockClick) {
|
||||
return dasherize(location.state.breadcrumbBlockClick);
|
||||
return location.state.breadcrumbBlockClick;
|
||||
}
|
||||
|
||||
// if the URL includes a hash
|
||||
|
@ -68,7 +68,7 @@ exports.createChallengePages = createPage => ({ node }, index, thisArray) => {
|
||||
context: {
|
||||
challengeMeta: {
|
||||
superBlock,
|
||||
block: block,
|
||||
block,
|
||||
template,
|
||||
required,
|
||||
nextChallengePath: getNextChallengePath(node, index, thisArray),
|
||||
|
@ -288,7 +288,7 @@ ${getFullPath('english')}
|
||||
template,
|
||||
time
|
||||
} = meta;
|
||||
challenge.block = blockName;
|
||||
challenge.block = dasherize(blockName);
|
||||
challenge.order = order;
|
||||
challenge.superOrder = superOrder;
|
||||
challenge.superBlock = superBlock;
|
||||
@ -298,7 +298,7 @@ ${getFullPath('english')}
|
||||
challenge.template = template;
|
||||
challenge.time = time;
|
||||
challenge.helpCategory =
|
||||
challenge.helpCategory || helpCategoryMap[dasherize(blockName)];
|
||||
challenge.helpCategory || helpCategoryMap[challenge.block];
|
||||
challenge.translationPending =
|
||||
lang !== 'english' && !isAuditedCert(lang, superBlock);
|
||||
|
||||
@ -344,7 +344,6 @@ function prepareChallenge(challenge) {
|
||||
if (challenge.solutionFiles) {
|
||||
challenge.solutionFiles = filesToObject(challenge.solutionFiles);
|
||||
}
|
||||
challenge.block = dasherize(challenge.block);
|
||||
return challenge;
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,8 @@ Joi.objectId = require('joi-objectid')(Joi);
|
||||
|
||||
const { challengeTypes } = require('../../client/utils/challengeTypes');
|
||||
|
||||
const slugRE = new RegExp('^[a-z0-9-]+$');
|
||||
|
||||
const fileJoi = Joi.object().keys({
|
||||
key: Joi.string(),
|
||||
ext: Joi.string(),
|
||||
@ -20,7 +22,7 @@ const fileJoi = Joi.object().keys({
|
||||
|
||||
const schema = Joi.object()
|
||||
.keys({
|
||||
block: Joi.string(),
|
||||
block: Joi.string().regex(slugRE),
|
||||
blockId: Joi.objectId(),
|
||||
challengeOrder: Joi.number(),
|
||||
challengeType: Joi.number()
|
||||
@ -29,7 +31,7 @@ const schema = Joi.object()
|
||||
.required(),
|
||||
checksum: Joi.number(),
|
||||
// TODO: require this only for normal challenges, not certs
|
||||
dashedName: Joi.string(),
|
||||
dashedName: Joi.string().regex(slugRE),
|
||||
description: Joi.when('challengeType', {
|
||||
is: Joi.only([challengeTypes.step, challengeTypes.video]),
|
||||
then: Joi.string().allow(''),
|
||||
@ -82,7 +84,7 @@ const schema = Joi.object()
|
||||
indexpy: fileJoi
|
||||
})
|
||||
),
|
||||
superBlock: Joi.string(),
|
||||
superBlock: Joi.string().regex(slugRE),
|
||||
superOrder: Joi.number(),
|
||||
suborder: Joi.number(),
|
||||
tests: Joi.array().items(
|
||||
|
@ -44,7 +44,6 @@ const ChallengeTitles = require('./utils/challengeTitles');
|
||||
const { challengeSchemaValidator } = require('../schema/challengeSchema');
|
||||
const { challengeTypes } = require('../../client/utils/challengeTypes');
|
||||
|
||||
const { dasherize } = require('../../utils/slugs');
|
||||
const { toSortedArray } = require('../../utils/sort-files');
|
||||
|
||||
const { testedLang } = require('../utils');
|
||||
@ -197,7 +196,7 @@ async function setup() {
|
||||
|
||||
const meta = {};
|
||||
for (const challenge of challenges) {
|
||||
const dashedBlockName = dasherize(challenge.block);
|
||||
const dashedBlockName = challenge.block;
|
||||
if (!meta[dashedBlockName]) {
|
||||
meta[dashedBlockName] = (await getMetaForBlock(
|
||||
dashedBlockName
|
||||
@ -256,7 +255,7 @@ function populateTestsForLang({ lang, challenges, meta }) {
|
||||
describe(`Check challenges (${lang})`, function() {
|
||||
this.timeout(5000);
|
||||
challenges.forEach((challenge, id) => {
|
||||
const dashedBlockName = dasherize(challenge.block);
|
||||
const dashedBlockName = challenge.block;
|
||||
describe(challenge.block || 'No block', function() {
|
||||
describe(challenge.title || 'No title', function() {
|
||||
// Note: the title in meta.json are purely for human readability and
|
||||
@ -280,8 +279,7 @@ function populateTestsForLang({ lang, challenges, meta }) {
|
||||
throw new AssertionError(result.error);
|
||||
}
|
||||
const { id, title, block, dashedName } = challenge;
|
||||
const dashedBlock = dasherize(block);
|
||||
const pathAndTitle = `${dashedBlock}/${dashedName}`;
|
||||
const pathAndTitle = `${block}/${dashedName}`;
|
||||
mongoIds.check(id, title);
|
||||
challengeTitles.check(title, pathAndTitle);
|
||||
});
|
||||
|
Reference in New Issue
Block a user