diff --git a/client/src/templates/Challenges/fill-in-the-blank/parse-blanks.test.ts b/client/src/templates/Challenges/fill-in-the-blank/parse-blanks.test.ts
new file mode 100644
index 00000000000..a0de29f6814
--- /dev/null
+++ b/client/src/templates/Challenges/fill-in-the-blank/parse-blanks.test.ts
@@ -0,0 +1,130 @@
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+import { parseBlanks } from './parse-blanks';
+
+describe('parseBlanks', () => {
+ it('handles strings without blanks', () => {
+ expect(parseBlanks('
hello world
')).toEqual([
+ [{ type: 'text', value: 'hello world' }]
+ ]);
+ });
+
+ it('handles strings without blanks and with multiple paragraphs', () => {
+ expect(parseBlanks('hello world
dlrow olleh
')).toEqual([
+ [{ type: 'text', value: 'hello world' }],
+ [{ type: 'text', value: 'dlrow olleh' }]
+ ]);
+ });
+
+ it('handles strings with blanks', () => {
+ expect(parseBlanks('hello _!
')).toEqual([
+ [
+ { type: 'text', value: 'hello ' },
+ { type: 'blank', value: 0 },
+ { type: 'text', value: '!' }
+ ]
+ ]);
+
+ expect(parseBlanks('hello _! Nice _ to meet you.
')).toEqual([
+ [
+ { type: 'text', value: 'hello ' },
+ { type: 'blank', value: 0 },
+ { type: 'text', value: '! Nice ' },
+ { type: 'blank', value: 1 },
+ { type: 'text', value: ' to meet you.' }
+ ]
+ ]);
+ });
+
+ it('handles paragraphs with a leading blank', () => {
+ expect(parseBlanks('_ hello
')).toEqual([
+ [
+ { type: 'blank', value: 0 },
+ { type: 'text', value: ' hello' }
+ ]
+ ]);
+ });
+
+ it('removes trailing empty strings', () => {
+ expect(parseBlanks('hello _
')).toEqual([
+ [
+ { type: 'text', value: 'hello ' },
+ { type: 'blank', value: 0 }
+ ]
+ ]);
+ });
+
+ it('handles strings with blanks and multiple paragraphs', () => {
+ expect(parseBlanks(`hello _!
dlrow _
`)).toEqual([
+ [
+ { type: 'text', value: 'hello ' },
+ { type: 'blank', value: 0 },
+ { type: 'text', value: '!' }
+ ],
+ [
+ { type: 'text', value: 'dlrow ' },
+ { type: 'blank', value: 1 }
+ ]
+ ]);
+ });
+
+ it('ignores newlines between paragraphs', () => {
+ expect(
+ parseBlanks(`hello _!
+
+dlrow _
`)
+ ).toEqual([
+ [
+ { type: 'text', value: 'hello ' },
+ { type: 'blank', value: 0 },
+ { type: 'text', value: '!' }
+ ],
+ [
+ { type: 'text', value: 'dlrow ' },
+ { type: 'blank', value: 1 }
+ ]
+ ]);
+ });
+
+ it('ignores whitespace surrounding the input', () => {
+ expect(
+ parseBlanks(` hello _!
+ `)
+ ).toEqual([
+ [
+ { type: 'text', value: 'hello ' },
+ { type: 'blank', value: 0 },
+ { type: 'text', value: '!' }
+ ]
+ ]);
+ });
+
+ it('counts blanks across multiple paragraphs', () => {
+ expect(
+ parseBlanks(`hello _!
dlrow _ _
even _ more _
`)
+ ).toEqual([
+ [
+ { type: 'text', value: 'hello ' },
+ { type: 'blank', value: 0 },
+ { type: 'text', value: '!' }
+ ],
+ [
+ { type: 'text', value: 'dlrow ' },
+ { type: 'blank', value: 1 },
+ { type: 'text', value: ' ' },
+ { type: 'blank', value: 2 }
+ ],
+ [
+ { type: 'text', value: ' even ' },
+ { type: 'blank', value: 3 },
+ { type: 'text', value: ' more ' },
+ { type: 'blank', value: 4 }
+ ]
+ ]);
+ });
+
+ it('throws if the string is not wrapped in p tags', () => {
+ expect(() => parseBlanks('hello _!')).toThrow();
+ expect(() => parseBlanks('hello _!
hello _!')).toThrow();
+ expect(() => parseBlanks('hello _!hello
')).toThrow();
+ });
+});
diff --git a/client/src/templates/Challenges/fill-in-the-blank/parse-blanks.ts b/client/src/templates/Challenges/fill-in-the-blank/parse-blanks.ts
new file mode 100644
index 00000000000..84a018df0aa
--- /dev/null
+++ b/client/src/templates/Challenges/fill-in-the-blank/parse-blanks.ts
@@ -0,0 +1,52 @@
+type TextNode = { type: 'text'; value: string };
+type BlankNode = { type: 'blank'; value: number };
+type ParagraphElement = TextNode | BlankNode;
+
+export const parseBlanks = (text: string) => {
+ const trimmed = text.trim();
+ if (!trimmed.startsWith('') || !trimmed.endsWith('
')) {
+ throw new Error(`Expected
+${text}
+to be wrapped in tags`);
+ }
+ // We're expecting only paragraphs, so text after
and before can be
+ // ignored.
+ const rawParagraphs = trimmed
+ .split('
')
+ .map(s => s.replace(/<\/p>\s*/, ''));
+ // There is always a leading empty string, so we remove it.
+ rawParagraphs.shift();
+
+ const { paragraphs } = rawParagraphs.reduce(
+ (acc, p) => {
+ const splitByBlank = p.split('_');
+
+ const parsedParagraph = splitByBlank
+ .map((text, i) => [
+ { type: 'text', value: text },
+ { type: 'blank', value: acc.count + i }
+ ])
+ .flat();
+ parsedParagraph.pop(); // remove last blank
+
+ const paragraph = parsedParagraph.filter(p => {
+ // remove empty strings
+ if (p.type === 'text') {
+ return p.value;
+ } else {
+ return true;
+ }
+ });
+ return {
+ count: acc.count + splitByBlank.length - 1,
+ paragraphs: [...acc.paragraphs, paragraph]
+ };
+ },
+ { count: 0, paragraphs: [] } as {
+ count: number;
+ paragraphs: ParagraphElement[][];
+ }
+ );
+
+ return paragraphs;
+};
diff --git a/client/src/templates/Challenges/fill-in-the-blank/show.tsx b/client/src/templates/Challenges/fill-in-the-blank/show.tsx
index 2b839114d1d..de07301cc56 100644
--- a/client/src/templates/Challenges/fill-in-the-blank/show.tsx
+++ b/client/src/templates/Challenges/fill-in-the-blank/show.tsx
@@ -27,12 +27,13 @@ import {
openModal,
updateSolutionFormValues
} from '../redux/actions';
+import Scene from '../components/scene/scene';
import { isChallengeCompletedSelector } from '../redux/selectors';
+import { parseBlanks } from './parse-blanks';
// Styles
import '../video.css';
import './show.css';
-import Scene from '../components/scene/scene';
// Redux Setup
const mapStateToProps = createSelector(
@@ -273,8 +274,8 @@ class ShowFillInTheBlank extends Component<
)} - ${title}`;
const { allBlanksFilled, feedback, showFeedback, showWrong } = this.state;
+ const paragraphs = parseBlanks(sentence);
- const splitSentence = sentence.replace(/^|<\/p>$/g, '').split('_');
const blankAnswers = blanks.map(b => b.answer);
return (
@@ -328,37 +329,37 @@ class ShowFillInTheBlank extends Component<
if it encounters a key combination, so we have to pass in the individual keys to observe */}
diff --git a/tools/challenge-parser/parser/__fixtures__/ast-fill-in-the-blank-bad-paragraph.json b/tools/challenge-parser/parser/__fixtures__/ast-fill-in-the-blank-bad-paragraph.json
new file mode 100644
index 00000000000..9ad13e9d4c3
--- /dev/null
+++ b/tools/challenge-parser/parser/__fixtures__/ast-fill-in-the-blank-bad-paragraph.json
@@ -0,0 +1,291 @@
+{
+ "type": "root",
+ "children": [
+ {
+ "type": "heading",
+ "depth": 1,
+ "children": [
+ {
+ "type": "text",
+ "value": "--description--",
+ "position": {
+ "start": { "line": 1, "column": 3, "offset": 2 },
+ "end": { "line": 1, "column": 18, "offset": 17 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 1, "column": 1, "offset": 0 },
+ "end": { "line": 1, "column": 18, "offset": 17 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "In English, to check or confirm something people sometimes use tag questions. For example, ",
+ "position": {
+ "start": { "line": 3, "column": 1, "offset": 19 },
+ "end": { "line": 3, "column": 92, "offset": 110 }
+ }
+ },
+ {
+ "type": "inlineCode",
+ "value": "You are a programmer, right?",
+ "position": {
+ "start": { "line": 3, "column": 92, "offset": 110 },
+ "end": { "line": 3, "column": 122, "offset": 140 }
+ }
+ },
+ {
+ "type": "text",
+ "value": " Here, ",
+ "position": {
+ "start": { "line": 3, "column": 122, "offset": 140 },
+ "end": { "line": 3, "column": 129, "offset": 147 }
+ }
+ },
+ {
+ "type": "inlineCode",
+ "value": "right?",
+ "position": {
+ "start": { "line": 3, "column": 129, "offset": 147 },
+ "end": { "line": 3, "column": 137, "offset": 155 }
+ }
+ },
+ {
+ "type": "text",
+ "value": " is used as a tag to check or confirm the previous statement.",
+ "position": {
+ "start": { "line": 3, "column": 137, "offset": 155 },
+ "end": { "line": 3, "column": 198, "offset": 216 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 3, "column": 1, "offset": 19 },
+ "end": { "line": 3, "column": 198, "offset": 216 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 1,
+ "children": [
+ {
+ "type": "text",
+ "value": "--fillInTheBlank--",
+ "position": {
+ "start": { "line": 5, "column": 3, "offset": 220 },
+ "end": { "line": 5, "column": 21, "offset": 238 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 5, "column": 1, "offset": 218 },
+ "end": { "line": 5, "column": 21, "offset": 238 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 2,
+ "children": [
+ {
+ "type": "text",
+ "value": "--sentence--",
+ "position": {
+ "start": { "line": 7, "column": 4, "offset": 243 },
+ "end": { "line": 7, "column": 16, "offset": 255 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 7, "column": 1, "offset": 240 },
+ "end": { "line": 7, "column": 16, "offset": 255 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "inlineCode",
+ "value": "A sentence _ paragraph 1",
+ "position": {
+ "start": { "line": 9, "column": 1, "offset": 257 },
+ "end": { "line": 9, "column": 27, "offset": 283 }
+ }
+ },
+ {
+ "type": "text",
+ "value": "\n",
+ "position": {
+ "start": { "line": 9, "column": 27, "offset": 283 },
+ "end": { "line": 10, "column": 1, "offset": 284 }
+ }
+ },
+ {
+ "type": "inlineCode",
+ "value": "not enough newlines, so no paragraph 2",
+ "position": {
+ "start": { "line": 10, "column": 1, "offset": 284 },
+ "end": { "line": 10, "column": 41, "offset": 324 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 9, "column": 1, "offset": 257 },
+ "end": { "line": 10, "column": 41, "offset": 324 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "Sentence in _ 2",
+ "position": {
+ "start": { "line": 12, "column": 1, "offset": 326 },
+ "end": { "line": 12, "column": 16, "offset": 341 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 12, "column": 1, "offset": 326 },
+ "end": { "line": 12, "column": 16, "offset": 341 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 2,
+ "children": [
+ {
+ "type": "text",
+ "value": "--blanks--",
+ "position": {
+ "start": { "line": 14, "column": 4, "offset": 346 },
+ "end": { "line": 14, "column": 14, "offset": 356 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 14, "column": 1, "offset": 343 },
+ "end": { "line": 14, "column": 14, "offset": 356 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "inlineCode",
+ "value": "are",
+ "position": {
+ "start": { "line": 16, "column": 1, "offset": 358 },
+ "end": { "line": 16, "column": 6, "offset": 363 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 16, "column": 1, "offset": 358 },
+ "end": { "line": 16, "column": 6, "offset": 363 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 3,
+ "children": [
+ {
+ "type": "text",
+ "value": "--feedback--",
+ "position": {
+ "start": { "line": 18, "column": 5, "offset": 369 },
+ "end": { "line": 18, "column": 17, "offset": 381 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 18, "column": 1, "offset": 365 },
+ "end": { "line": 18, "column": 17, "offset": 381 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "Pay attention to the verb in the sentence.",
+ "position": {
+ "start": { "line": 20, "column": 1, "offset": 383 },
+ "end": { "line": 20, "column": 43, "offset": 425 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 20, "column": 1, "offset": 383 },
+ "end": { "line": 20, "column": 43, "offset": 425 }
+ }
+ },
+ {
+ "type": "thematicBreak",
+ "position": {
+ "start": { "line": 22, "column": 1, "offset": 427 },
+ "end": { "line": 22, "column": 4, "offset": 430 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "inlineCode",
+ "value": "right",
+ "position": {
+ "start": { "line": 24, "column": 1, "offset": 432 },
+ "end": { "line": 24, "column": 8, "offset": 439 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 24, "column": 1, "offset": 432 },
+ "end": { "line": 24, "column": 8, "offset": 439 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 3,
+ "children": [
+ {
+ "type": "text",
+ "value": "--feedback--",
+ "position": {
+ "start": { "line": 26, "column": 5, "offset": 445 },
+ "end": { "line": 26, "column": 17, "offset": 457 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 26, "column": 1, "offset": 441 },
+ "end": { "line": 26, "column": 17, "offset": 457 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "Pay attention to the verb in the sentence.",
+ "position": {
+ "start": { "line": 28, "column": 1, "offset": 459 },
+ "end": { "line": 28, "column": 43, "offset": 501 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 28, "column": 1, "offset": 459 },
+ "end": { "line": 28, "column": 43, "offset": 501 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 1, "column": 1, "offset": 0 },
+ "end": { "line": 29, "column": 1, "offset": 502 }
+ }
+}
diff --git a/tools/challenge-parser/parser/__fixtures__/ast-fill-in-the-blank-bad-sentence.json b/tools/challenge-parser/parser/__fixtures__/ast-fill-in-the-blank-bad-sentence.json
new file mode 100644
index 00000000000..07837f4d70b
--- /dev/null
+++ b/tools/challenge-parser/parser/__fixtures__/ast-fill-in-the-blank-bad-sentence.json
@@ -0,0 +1,275 @@
+{
+ "type": "root",
+ "children": [
+ {
+ "type": "heading",
+ "depth": 1,
+ "children": [
+ {
+ "type": "text",
+ "value": "--description--",
+ "position": {
+ "start": { "line": 1, "column": 3, "offset": 2 },
+ "end": { "line": 1, "column": 18, "offset": 17 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 1, "column": 1, "offset": 0 },
+ "end": { "line": 1, "column": 18, "offset": 17 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "In English, to check or confirm something people sometimes use tag questions. For example, ",
+ "position": {
+ "start": { "line": 3, "column": 1, "offset": 19 },
+ "end": { "line": 3, "column": 92, "offset": 110 }
+ }
+ },
+ {
+ "type": "inlineCode",
+ "value": "You are a programmer, right?",
+ "position": {
+ "start": { "line": 3, "column": 92, "offset": 110 },
+ "end": { "line": 3, "column": 122, "offset": 140 }
+ }
+ },
+ {
+ "type": "text",
+ "value": " Here, ",
+ "position": {
+ "start": { "line": 3, "column": 122, "offset": 140 },
+ "end": { "line": 3, "column": 129, "offset": 147 }
+ }
+ },
+ {
+ "type": "inlineCode",
+ "value": "right?",
+ "position": {
+ "start": { "line": 3, "column": 129, "offset": 147 },
+ "end": { "line": 3, "column": 137, "offset": 155 }
+ }
+ },
+ {
+ "type": "text",
+ "value": " is used as a tag to check or confirm the previous statement.",
+ "position": {
+ "start": { "line": 3, "column": 137, "offset": 155 },
+ "end": { "line": 3, "column": 198, "offset": 216 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 3, "column": 1, "offset": 19 },
+ "end": { "line": 3, "column": 198, "offset": 216 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 1,
+ "children": [
+ {
+ "type": "text",
+ "value": "--fillInTheBlank--",
+ "position": {
+ "start": { "line": 5, "column": 3, "offset": 220 },
+ "end": { "line": 5, "column": 21, "offset": 238 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 5, "column": 1, "offset": 218 },
+ "end": { "line": 5, "column": 21, "offset": 238 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 2,
+ "children": [
+ {
+ "type": "text",
+ "value": "--sentence--",
+ "position": {
+ "start": { "line": 7, "column": 4, "offset": 243 },
+ "end": { "line": 7, "column": 16, "offset": 255 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 7, "column": 1, "offset": 240 },
+ "end": { "line": 7, "column": 16, "offset": 255 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "inlineCode",
+ "value": "A sentence _ paragraph 1",
+ "position": {
+ "start": { "line": 9, "column": 1, "offset": 257 },
+ "end": { "line": 9, "column": 27, "offset": 283 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 9, "column": 1, "offset": 257 },
+ "end": { "line": 9, "column": 27, "offset": 283 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "Sentence in _ 2",
+ "position": {
+ "start": { "line": 11, "column": 1, "offset": 285 },
+ "end": { "line": 11, "column": 16, "offset": 300 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 11, "column": 1, "offset": 285 },
+ "end": { "line": 11, "column": 16, "offset": 300 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 2,
+ "children": [
+ {
+ "type": "text",
+ "value": "--blanks--",
+ "position": {
+ "start": { "line": 13, "column": 4, "offset": 305 },
+ "end": { "line": 13, "column": 14, "offset": 315 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 13, "column": 1, "offset": 302 },
+ "end": { "line": 13, "column": 14, "offset": 315 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "inlineCode",
+ "value": "are",
+ "position": {
+ "start": { "line": 15, "column": 1, "offset": 317 },
+ "end": { "line": 15, "column": 6, "offset": 322 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 15, "column": 1, "offset": 317 },
+ "end": { "line": 15, "column": 6, "offset": 322 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 3,
+ "children": [
+ {
+ "type": "text",
+ "value": "--feedback--",
+ "position": {
+ "start": { "line": 17, "column": 5, "offset": 328 },
+ "end": { "line": 17, "column": 17, "offset": 340 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 17, "column": 1, "offset": 324 },
+ "end": { "line": 17, "column": 17, "offset": 340 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "Pay attention to the verb in the sentence.",
+ "position": {
+ "start": { "line": 19, "column": 1, "offset": 342 },
+ "end": { "line": 19, "column": 43, "offset": 384 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 19, "column": 1, "offset": 342 },
+ "end": { "line": 19, "column": 43, "offset": 384 }
+ }
+ },
+ {
+ "type": "thematicBreak",
+ "position": {
+ "start": { "line": 21, "column": 1, "offset": 386 },
+ "end": { "line": 21, "column": 4, "offset": 389 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "inlineCode",
+ "value": "right",
+ "position": {
+ "start": { "line": 23, "column": 1, "offset": 391 },
+ "end": { "line": 23, "column": 8, "offset": 398 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 23, "column": 1, "offset": 391 },
+ "end": { "line": 23, "column": 8, "offset": 398 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 3,
+ "children": [
+ {
+ "type": "text",
+ "value": "--feedback--",
+ "position": {
+ "start": { "line": 25, "column": 5, "offset": 404 },
+ "end": { "line": 25, "column": 17, "offset": 416 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 25, "column": 1, "offset": 400 },
+ "end": { "line": 25, "column": 17, "offset": 416 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "Pay attention to the verb in the sentence.",
+ "position": {
+ "start": { "line": 27, "column": 1, "offset": 418 },
+ "end": { "line": 27, "column": 43, "offset": 460 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 27, "column": 1, "offset": 418 },
+ "end": { "line": 27, "column": 43, "offset": 460 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 1, "column": 1, "offset": 0 },
+ "end": { "line": 28, "column": 1, "offset": 461 }
+ }
+}
diff --git a/tools/challenge-parser/parser/__fixtures__/ast-fill-in-the-blank-two-sentences.json b/tools/challenge-parser/parser/__fixtures__/ast-fill-in-the-blank-two-sentences.json
new file mode 100644
index 00000000000..78438850820
--- /dev/null
+++ b/tools/challenge-parser/parser/__fixtures__/ast-fill-in-the-blank-two-sentences.json
@@ -0,0 +1,275 @@
+{
+ "type": "root",
+ "children": [
+ {
+ "type": "heading",
+ "depth": 1,
+ "children": [
+ {
+ "type": "text",
+ "value": "--description--",
+ "position": {
+ "start": { "line": 1, "column": 3, "offset": 2 },
+ "end": { "line": 1, "column": 18, "offset": 17 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 1, "column": 1, "offset": 0 },
+ "end": { "line": 1, "column": 18, "offset": 17 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "In English, to check or confirm something people sometimes use tag questions. For example, ",
+ "position": {
+ "start": { "line": 3, "column": 1, "offset": 19 },
+ "end": { "line": 3, "column": 92, "offset": 110 }
+ }
+ },
+ {
+ "type": "inlineCode",
+ "value": "You are a programmer, right?",
+ "position": {
+ "start": { "line": 3, "column": 92, "offset": 110 },
+ "end": { "line": 3, "column": 122, "offset": 140 }
+ }
+ },
+ {
+ "type": "text",
+ "value": " Here, ",
+ "position": {
+ "start": { "line": 3, "column": 122, "offset": 140 },
+ "end": { "line": 3, "column": 129, "offset": 147 }
+ }
+ },
+ {
+ "type": "inlineCode",
+ "value": "right?",
+ "position": {
+ "start": { "line": 3, "column": 129, "offset": 147 },
+ "end": { "line": 3, "column": 137, "offset": 155 }
+ }
+ },
+ {
+ "type": "text",
+ "value": " is used as a tag to check or confirm the previous statement.",
+ "position": {
+ "start": { "line": 3, "column": 137, "offset": 155 },
+ "end": { "line": 3, "column": 198, "offset": 216 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 3, "column": 1, "offset": 19 },
+ "end": { "line": 3, "column": 198, "offset": 216 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 1,
+ "children": [
+ {
+ "type": "text",
+ "value": "--fillInTheBlank--",
+ "position": {
+ "start": { "line": 5, "column": 3, "offset": 220 },
+ "end": { "line": 5, "column": 21, "offset": 238 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 5, "column": 1, "offset": 218 },
+ "end": { "line": 5, "column": 21, "offset": 238 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 2,
+ "children": [
+ {
+ "type": "text",
+ "value": "--sentence--",
+ "position": {
+ "start": { "line": 7, "column": 4, "offset": 243 },
+ "end": { "line": 7, "column": 16, "offset": 255 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 7, "column": 1, "offset": 240 },
+ "end": { "line": 7, "column": 16, "offset": 255 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "inlineCode",
+ "value": "A sentence _ paragraph 1",
+ "position": {
+ "start": { "line": 9, "column": 1, "offset": 257 },
+ "end": { "line": 9, "column": 27, "offset": 283 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 9, "column": 1, "offset": 257 },
+ "end": { "line": 9, "column": 27, "offset": 283 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "inlineCode",
+ "value": "Sentence in _ 2",
+ "position": {
+ "start": { "line": 11, "column": 1, "offset": 285 },
+ "end": { "line": 11, "column": 18, "offset": 302 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 11, "column": 1, "offset": 285 },
+ "end": { "line": 11, "column": 18, "offset": 302 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 2,
+ "children": [
+ {
+ "type": "text",
+ "value": "--blanks--",
+ "position": {
+ "start": { "line": 13, "column": 4, "offset": 307 },
+ "end": { "line": 13, "column": 14, "offset": 317 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 13, "column": 1, "offset": 304 },
+ "end": { "line": 13, "column": 14, "offset": 317 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "inlineCode",
+ "value": "are",
+ "position": {
+ "start": { "line": 15, "column": 1, "offset": 319 },
+ "end": { "line": 15, "column": 6, "offset": 324 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 15, "column": 1, "offset": 319 },
+ "end": { "line": 15, "column": 6, "offset": 324 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 3,
+ "children": [
+ {
+ "type": "text",
+ "value": "--feedback--",
+ "position": {
+ "start": { "line": 17, "column": 5, "offset": 330 },
+ "end": { "line": 17, "column": 17, "offset": 342 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 17, "column": 1, "offset": 326 },
+ "end": { "line": 17, "column": 17, "offset": 342 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "Pay attention to the verb in the sentence.",
+ "position": {
+ "start": { "line": 19, "column": 1, "offset": 344 },
+ "end": { "line": 19, "column": 43, "offset": 386 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 19, "column": 1, "offset": 344 },
+ "end": { "line": 19, "column": 43, "offset": 386 }
+ }
+ },
+ {
+ "type": "thematicBreak",
+ "position": {
+ "start": { "line": 21, "column": 1, "offset": 388 },
+ "end": { "line": 21, "column": 4, "offset": 391 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "inlineCode",
+ "value": "right",
+ "position": {
+ "start": { "line": 23, "column": 1, "offset": 393 },
+ "end": { "line": 23, "column": 8, "offset": 400 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 23, "column": 1, "offset": 393 },
+ "end": { "line": 23, "column": 8, "offset": 400 }
+ }
+ },
+ {
+ "type": "heading",
+ "depth": 3,
+ "children": [
+ {
+ "type": "text",
+ "value": "--feedback--",
+ "position": {
+ "start": { "line": 25, "column": 5, "offset": 406 },
+ "end": { "line": 25, "column": 17, "offset": 418 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 25, "column": 1, "offset": 402 },
+ "end": { "line": 25, "column": 17, "offset": 418 }
+ }
+ },
+ {
+ "type": "paragraph",
+ "children": [
+ {
+ "type": "text",
+ "value": "Pay attention to the verb in the sentence.",
+ "position": {
+ "start": { "line": 27, "column": 1, "offset": 420 },
+ "end": { "line": 27, "column": 43, "offset": 462 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 27, "column": 1, "offset": 420 },
+ "end": { "line": 27, "column": 43, "offset": 462 }
+ }
+ }
+ ],
+ "position": {
+ "start": { "line": 1, "column": 1, "offset": 0 },
+ "end": { "line": 28, "column": 1, "offset": 463 }
+ }
+}
diff --git a/tools/challenge-parser/parser/__fixtures__/with-fill-in-the-blank-bad-paragraph.md b/tools/challenge-parser/parser/__fixtures__/with-fill-in-the-blank-bad-paragraph.md
new file mode 100644
index 00000000000..141878a9b61
--- /dev/null
+++ b/tools/challenge-parser/parser/__fixtures__/with-fill-in-the-blank-bad-paragraph.md
@@ -0,0 +1,28 @@
+# --description--
+
+In English, to check or confirm something people sometimes use tag questions. For example, `You are a programmer, right?` Here, `right?` is used as a tag to check or confirm the previous statement.
+
+# --fillInTheBlank--
+
+## --sentence--
+
+`A sentence _ paragraph 1`
+`not enough newlines, so no paragraph 2`
+
+Sentence in _ 2
+
+## --blanks--
+
+`are`
+
+### --feedback--
+
+Pay attention to the verb in the sentence.
+
+---
+
+`right`
+
+### --feedback--
+
+Pay attention to the verb in the sentence.
diff --git a/tools/challenge-parser/parser/__fixtures__/with-fill-in-the-blank-bad-sentence.md b/tools/challenge-parser/parser/__fixtures__/with-fill-in-the-blank-bad-sentence.md
new file mode 100644
index 00000000000..db12c2e27b3
--- /dev/null
+++ b/tools/challenge-parser/parser/__fixtures__/with-fill-in-the-blank-bad-sentence.md
@@ -0,0 +1,27 @@
+# --description--
+
+In English, to check or confirm something people sometimes use tag questions. For example, `You are a programmer, right?` Here, `right?` is used as a tag to check or confirm the previous statement.
+
+# --fillInTheBlank--
+
+## --sentence--
+
+`A sentence _ paragraph 1`
+
+Sentence in _ 2
+
+## --blanks--
+
+`are`
+
+### --feedback--
+
+Pay attention to the verb in the sentence.
+
+---
+
+`right`
+
+### --feedback--
+
+Pay attention to the verb in the sentence.
diff --git a/tools/challenge-parser/parser/__fixtures__/with-fill-in-the-blank-two-sentences.md b/tools/challenge-parser/parser/__fixtures__/with-fill-in-the-blank-two-sentences.md
new file mode 100644
index 00000000000..8732b5ab9ab
--- /dev/null
+++ b/tools/challenge-parser/parser/__fixtures__/with-fill-in-the-blank-two-sentences.md
@@ -0,0 +1,27 @@
+# --description--
+
+In English, to check or confirm something people sometimes use tag questions. For example, `You are a programmer, right?` Here, `right?` is used as a tag to check or confirm the previous statement.
+
+# --fillInTheBlank--
+
+## --sentence--
+
+`A sentence _ paragraph 1`
+
+`Sentence in _ 2`
+
+## --blanks--
+
+`are`
+
+### --feedback--
+
+Pay attention to the verb in the sentence.
+
+---
+
+`right`
+
+### --feedback--
+
+Pay attention to the verb in the sentence.
diff --git a/tools/challenge-parser/parser/plugins/add-fill-in-the-blank.js b/tools/challenge-parser/parser/plugins/add-fill-in-the-blank.js
index b15cdaa055a..e3be3f0e9cd 100644
--- a/tools/challenge-parser/parser/plugins/add-fill-in-the-blank.js
+++ b/tools/challenge-parser/parser/plugins/add-fill-in-the-blank.js
@@ -6,6 +6,32 @@ const mdastToHtml = require('./utils/mdast-to-html');
const { splitOnThematicBreak } = require('./utils/split-on-thematic-break');
+const NOT_IN_PARAGRAPHS = `Each inline code block in the fillInTheBlank sentence section must in its own paragraph
+If you have more than one code block, check that they're separated by a blank line
+Example of bad formatting:
+\`too close\`
+\`to each other\`
+
+Example of good formatting:
+\`separated\`
+
+\`by a blank line\`
+
+`;
+
+const NOT_IN_CODE_BLOCK = `Each paragraph in the fillInTheBlank sentence section must be inside an inline code block
+Example of bad formatting:
+## --sentence--
+
+This is a sentence
+
+Example of good formatting:
+## --sentence--
+
+\`This is a sentence\`
+
+`;
+
function plugin() {
return transformer;
function transformer(tree, file) {
@@ -24,7 +50,17 @@ function plugin() {
}
function getfillInTheBlank(sentenceNodes, blanksNodes) {
- const sentence = mdastToHtml(sentenceNodes);
+ const sentenceWithoutCodeBlocks = sentenceNodes.map(node => {
+ node.children.forEach(child => {
+ if (child.type === 'text' && child.value.trim() === '')
+ throw Error(NOT_IN_PARAGRAPHS);
+ if (child.type !== 'inlineCode') throw Error(NOT_IN_CODE_BLOCK);
+ });
+
+ const children = node.children.map(child => ({ ...child, type: 'text' }));
+ return { ...node, children };
+ });
+ const sentence = mdastToHtml(sentenceWithoutCodeBlocks);
const blanks = getBlanks(blanksNodes);
if (!sentence) throw Error('sentence is missing from fill in the blank');
diff --git a/tools/challenge-parser/parser/plugins/add-fill-in-the-blank.test.js b/tools/challenge-parser/parser/plugins/add-fill-in-the-blank.test.js
index f7a62496015..3a362cf6a34 100644
--- a/tools/challenge-parser/parser/plugins/add-fill-in-the-blank.test.js
+++ b/tools/challenge-parser/parser/plugins/add-fill-in-the-blank.test.js
@@ -1,5 +1,8 @@
const mockFillInTheBlankAST = require('../__fixtures__/ast-fill-in-the-blank.json');
const mockFillInTheBlankYouAreAST = require('../__fixtures__/ast-fill-in-the-blank-one-blank.json');
+const mockFillInTheBlankTwoSentencesAST = require('../__fixtures__/ast-fill-in-the-blank-two-sentences.json');
+const mockFillInTheBlankBadSentence = require('../__fixtures__/ast-fill-in-the-blank-bad-sentence.json');
+const mockFillInTheBlankBadParagraph = require('../__fixtures__/ast-fill-in-the-blank-bad-paragraph.json');
const addFillInTheBlankQuestion = require('./add-fill-in-the-blank');
describe('fill-in-the-blanks plugin', () => {
@@ -66,6 +69,63 @@ describe('fill-in-the-blanks plugin', () => {
});
});
+ it('should extract the sentence from the surrounding inline code block', () => {
+ plugin(mockFillInTheBlankAST, file);
+ const testObject = file.data.fillInTheBlank;
+
+ expect(testObject.sentence).toBe(
+ '
Hello, You _ the new graphic designer, _? _ to meet you!
'
+ );
+ });
+
+ it('should extract sentences from multiple inline code blocks', () => {
+ plugin(mockFillInTheBlankTwoSentencesAST, file);
+ const testObject = file.data.fillInTheBlank;
+
+ expect(testObject.sentence).toBe(
+ `A sentence _ paragraph 1
+Sentence in _ 2
`
+ );
+ });
+
+ it('should throw if a sentence is not inside an inline code block', () => {
+ expect(() => {
+ plugin(mockFillInTheBlankBadSentence, file);
+ }).toThrow(
+ `Each paragraph in the fillInTheBlank sentence section must be inside an inline code block
+Example of bad formatting:
+## --sentence--
+
+This is a sentence
+
+Example of good formatting:
+## --sentence--
+
+\`This is a sentence\`
+
+`
+ );
+ });
+
+ it('should throw if there are multiple inline code blocks in the same paragraph', () => {
+ expect(() => {
+ plugin(mockFillInTheBlankBadParagraph, file);
+ }).toThrow(
+ `Each inline code block in the fillInTheBlank sentence section must in its own paragraph
+If you have more than one code block, check that they're separated by a blank line
+Example of bad formatting:
+\`too close\`
+\`to each other\`
+
+Example of good formatting:
+\`separated\`
+
+\`by a blank line\`
+
+`
+ );
+ });
+
it('should handle one blank', () => {
plugin(mockFillInTheBlankYouAreAST, file);
const testObject = file.data.fillInTheBlank;