refactor: simplify challenge.block usage (#41185)

This commit is contained in:
Oliver Eyton-Williams
2021-02-23 05:22:48 +01:00
committed by GitHub
parent 3dd33d87ed
commit f8699a8d55
14 changed files with 32 additions and 47 deletions

View File

@ -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.

View File

@ -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', () => {

View File

@ -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 });

View File

@ -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

View File

@ -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>

View File

@ -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'>

View File

@ -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/>', () => {

View File

@ -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} />}

View File

@ -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",
}
}
/>

View File

@ -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

View File

@ -68,7 +68,7 @@ exports.createChallengePages = createPage => ({ node }, index, thisArray) => {
context: {
challengeMeta: {
superBlock,
block: block,
block,
template,
required,
nextChallengePath: getNextChallengePath(node, index, thisArray),

View File

@ -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;
}

View File

@ -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(

View File

@ -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);
});