mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-26 23:58:31 -06:00
starting quiz tests
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { extractLabelValue } from "../assignmnet/utils/markdownUtils";
|
import { extractLabelValue } from "../assignmnet/utils/markdownUtils";
|
||||||
import { LocalCoursePage } from "./localCoursePage";
|
import { LocalCoursePage } from "./localCoursePage";
|
||||||
|
|
||||||
export const pageMarkdown = {
|
export const pageMarkdownUtils = {
|
||||||
toMarkdown: (page: LocalCoursePage) => {
|
toMarkdown: (page: LocalCoursePage) => {
|
||||||
const printableDueDate = new Date(page.dueAt)
|
const printableDueDate = new Date(page.dueAt)
|
||||||
.toISOString()
|
.toISOString()
|
||||||
@@ -8,9 +8,10 @@ export interface LocalQuizQuestion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum QuestionType {
|
export enum QuestionType {
|
||||||
MultipleAnswers = "multiple_answers",
|
MULTIPLE_ANSWERS = "multiple_answers",
|
||||||
MultipleChoice = "multiple_choice",
|
MULTIPLE_CHOICE = "multiple_choice",
|
||||||
Essay = "essay",
|
ESSAY = "essay",
|
||||||
ShortAnswer = "short_answer",
|
SHORT_ANSWER = "short_answer",
|
||||||
Matching = "matching"
|
MATCHING = "matching",
|
||||||
|
NONE = "",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,4 @@ export interface LocalQuizQuestionAnswer {
|
|||||||
correct: boolean;
|
correct: boolean;
|
||||||
text: string;
|
text: string;
|
||||||
matchedText?: string;
|
matchedText?: string;
|
||||||
htmlText: string;
|
|
||||||
}
|
}
|
||||||
|
|||||||
154
nextjs/src/models/local/quiz/utils/quizMarkdownUtils.ts
Normal file
154
nextjs/src/models/local/quiz/utils/quizMarkdownUtils.ts
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
import { LocalQuiz } from "../localQuiz";
|
||||||
|
import { quizQuestionMarkdownUtils } from "./quizQuestionMarkdownUtils";
|
||||||
|
|
||||||
|
const extractLabelValue = (input: string, label: string): string => {
|
||||||
|
const pattern = new RegExp(`${label}: (.*?)\n`);
|
||||||
|
const match = pattern.exec(input);
|
||||||
|
return match ? match[1].trim() : "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractDescription = (input: string): string => {
|
||||||
|
const pattern = new RegExp("Description: (.*?)$", "s");
|
||||||
|
const match = pattern.exec(input);
|
||||||
|
return match ? match[1].trim() : "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseBooleanOrThrow = (value: string, label: string): boolean => {
|
||||||
|
if (value.toLowerCase() === "true") return true;
|
||||||
|
if (value.toLowerCase() === "false") return false;
|
||||||
|
throw new Error(`Error with ${label}: ${value}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseBooleanOrDefault = (
|
||||||
|
value: string,
|
||||||
|
label: string,
|
||||||
|
defaultValue: boolean
|
||||||
|
): boolean => {
|
||||||
|
if (value.toLowerCase() === "true") return true;
|
||||||
|
if (value.toLowerCase() === "false") return false;
|
||||||
|
return defaultValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseNumberOrThrow = (value: string, label: string): number => {
|
||||||
|
const parsed = parseInt(value, 10);
|
||||||
|
if (isNaN(parsed)) {
|
||||||
|
throw new Error(`Error with ${label}: ${value}`);
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseDateOrThrow = (value: string, label: string): string => {
|
||||||
|
const date = new Date(value);
|
||||||
|
if (isNaN(date.getTime())) {
|
||||||
|
throw new Error(`Error with ${label}: ${value}`);
|
||||||
|
}
|
||||||
|
return date.toISOString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseDateOrNull = (value: string): string | undefined => {
|
||||||
|
const date = new Date(value);
|
||||||
|
return isNaN(date.getTime()) ? undefined : date.toISOString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getQuizWithOnlySettings = (settings: string): LocalQuiz => {
|
||||||
|
const name = extractLabelValue(settings, "Name");
|
||||||
|
|
||||||
|
const rawShuffleAnswers = extractLabelValue(settings, "ShuffleAnswers");
|
||||||
|
const shuffleAnswers = parseBooleanOrThrow(
|
||||||
|
rawShuffleAnswers,
|
||||||
|
"ShuffleAnswers"
|
||||||
|
);
|
||||||
|
|
||||||
|
const password = extractLabelValue(settings, "Password") || undefined;
|
||||||
|
|
||||||
|
const rawShowCorrectAnswers = extractLabelValue(
|
||||||
|
settings,
|
||||||
|
"ShowCorrectAnswers"
|
||||||
|
);
|
||||||
|
const showCorrectAnswers = parseBooleanOrDefault(
|
||||||
|
rawShowCorrectAnswers,
|
||||||
|
"ShowCorrectAnswers",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
const rawOneQuestionAtATime = extractLabelValue(
|
||||||
|
settings,
|
||||||
|
"OneQuestionAtATime"
|
||||||
|
);
|
||||||
|
const oneQuestionAtATime = parseBooleanOrThrow(
|
||||||
|
rawOneQuestionAtATime,
|
||||||
|
"OneQuestionAtATime"
|
||||||
|
);
|
||||||
|
|
||||||
|
const rawAllowedAttempts = extractLabelValue(settings, "AllowedAttempts");
|
||||||
|
const allowedAttempts = parseNumberOrThrow(
|
||||||
|
rawAllowedAttempts,
|
||||||
|
"AllowedAttempts"
|
||||||
|
);
|
||||||
|
|
||||||
|
const rawDueAt = extractLabelValue(settings, "DueAt");
|
||||||
|
const dueAt = parseDateOrThrow(rawDueAt, "DueAt");
|
||||||
|
|
||||||
|
const rawLockAt = extractLabelValue(settings, "LockAt");
|
||||||
|
const lockAt = parseDateOrNull(rawLockAt);
|
||||||
|
|
||||||
|
const description = extractDescription(settings);
|
||||||
|
const localAssignmentGroupName = extractLabelValue(
|
||||||
|
settings,
|
||||||
|
"AssignmentGroup"
|
||||||
|
);
|
||||||
|
|
||||||
|
const quiz: LocalQuiz = {
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
password,
|
||||||
|
lockAt,
|
||||||
|
dueAt,
|
||||||
|
shuffleAnswers,
|
||||||
|
showCorrectAnswers,
|
||||||
|
oneQuestionAtATime,
|
||||||
|
localAssignmentGroupName,
|
||||||
|
allowedAttempts,
|
||||||
|
questions: [],
|
||||||
|
};
|
||||||
|
return quiz;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const quizMarkdownUtils = {
|
||||||
|
toMarkdown(quiz: LocalQuiz): string {
|
||||||
|
const questionMarkdownArray = quiz.questions.map((q) =>
|
||||||
|
quizQuestionMarkdownUtils.toMarkdown(q)
|
||||||
|
);
|
||||||
|
const questionDelimiter = "\n\n---\n\n";
|
||||||
|
const questionMarkdown = questionMarkdownArray.join(questionDelimiter);
|
||||||
|
|
||||||
|
return `Name: ${quiz.name}
|
||||||
|
LockAt: ${quiz.lockAt ?? ""}
|
||||||
|
DueAt: ${quiz.dueAt}
|
||||||
|
Password: ${quiz.password ?? ""}
|
||||||
|
ShuffleAnswers: ${quiz.shuffleAnswers.toString().toLowerCase()}
|
||||||
|
ShowCorrectAnswers: ${quiz.showCorrectAnswers.toString().toLowerCase()}
|
||||||
|
OneQuestionAtATime: ${quiz.oneQuestionAtATime.toString().toLowerCase()}
|
||||||
|
AssignmentGroup: ${quiz.localAssignmentGroupName}
|
||||||
|
AllowedAttempts: ${quiz.allowedAttempts}
|
||||||
|
Description: ${quiz.description}
|
||||||
|
---
|
||||||
|
${questionMarkdown}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
parseMarkdown(input: string): LocalQuiz {
|
||||||
|
const splitInput = input.split("---\n");
|
||||||
|
const settings = splitInput[0];
|
||||||
|
const quizWithoutQuestions = getQuizWithOnlySettings(settings);
|
||||||
|
|
||||||
|
const rawQuestions = splitInput.slice(1);
|
||||||
|
const questions = rawQuestions
|
||||||
|
.filter((str) => str.trim().length > 0)
|
||||||
|
.map((q, i) => quizQuestionMarkdownUtils.parseMarkdown(q, i));
|
||||||
|
|
||||||
|
return {
|
||||||
|
...quizWithoutQuestions,
|
||||||
|
questions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { QuestionType } from "../localQuizQuestion";
|
||||||
|
import { LocalQuizQuestionAnswer } from "../localQuizQuestionAnswer";
|
||||||
|
|
||||||
|
export const quizQuestionAnswerMarkdownUtils = {
|
||||||
|
// getHtmlText(): string {
|
||||||
|
// return MarkdownService.render(this.text);
|
||||||
|
// }
|
||||||
|
|
||||||
|
parseMarkdown(input: string, questionType: string): LocalQuizQuestionAnswer {
|
||||||
|
const isCorrect = input.startsWith("*") || input[1] === "*";
|
||||||
|
|
||||||
|
if (questionType === QuestionType.MATCHING) {
|
||||||
|
const matchingPattern = /^\^ ?/;
|
||||||
|
const textWithoutMatchDelimiter = input
|
||||||
|
.replace(matchingPattern, "")
|
||||||
|
.trim();
|
||||||
|
const [text, ...matchedParts] = textWithoutMatchDelimiter.split("-");
|
||||||
|
const answer: LocalQuizQuestionAnswer = {
|
||||||
|
correct: true,
|
||||||
|
text: text.trim(),
|
||||||
|
matchedText: matchedParts.join("-").trim(),
|
||||||
|
};
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startingQuestionPattern = /^(\*?[a-z]?\))|\[\s*\]|\[\*\]|\^ /;
|
||||||
|
|
||||||
|
let replaceCount = 0;
|
||||||
|
const text = input
|
||||||
|
.replace(startingQuestionPattern, (m) => (replaceCount++ === 0 ? "" : m))
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
const answer: LocalQuizQuestionAnswer = {
|
||||||
|
correct: isCorrect,
|
||||||
|
text: text,
|
||||||
|
};
|
||||||
|
return answer;
|
||||||
|
},
|
||||||
|
};
|
||||||
201
nextjs/src/models/local/quiz/utils/quizQuestionMarkdownUtils.ts
Normal file
201
nextjs/src/models/local/quiz/utils/quizQuestionMarkdownUtils.ts
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
import { LocalQuiz } from "../localQuiz";
|
||||||
|
import { LocalQuizQuestion, QuestionType } from "../localQuizQuestion";
|
||||||
|
import { LocalQuizQuestionAnswer } from "../localQuizQuestionAnswer";
|
||||||
|
import { quizQuestionAnswerMarkdownUtils } from "./quizQuestionAnswerMarkdownUtils";
|
||||||
|
|
||||||
|
const _validFirstAnswerDelimiters = ["*a)", "a)", "*)", ")", "[ ]", "[*]", "^"];
|
||||||
|
|
||||||
|
const getAnswerStringsWithMultilineSupport = (
|
||||||
|
linesWithoutPoints: string[],
|
||||||
|
questionIndex: number
|
||||||
|
) => {
|
||||||
|
const indexOfAnswerStart = linesWithoutPoints.findIndex((l) =>
|
||||||
|
_validFirstAnswerDelimiters.some((prefix) =>
|
||||||
|
l.trimStart().startsWith(prefix)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (indexOfAnswerStart === -1) {
|
||||||
|
const debugLine = linesWithoutPoints.find((l) => l.trim().length > 0);
|
||||||
|
throw Error(
|
||||||
|
`question ${
|
||||||
|
questionIndex + 1
|
||||||
|
}: no answers when detecting question type on ${debugLine}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const answerLinesRaw = linesWithoutPoints.slice(indexOfAnswerStart);
|
||||||
|
|
||||||
|
const answerStartPattern = /^(\*?[a-z]?\))|(?<!\S)\[\s*\]|\[\*\]|\^/;
|
||||||
|
const answerLines = answerLinesRaw.reduce((acc: string[], line: string) => {
|
||||||
|
const isNewAnswer = answerStartPattern.test(line);
|
||||||
|
if (isNewAnswer) {
|
||||||
|
acc.push(line);
|
||||||
|
} else if (acc.length !== 0) {
|
||||||
|
acc[acc.length - 1] += "\n" + line;
|
||||||
|
} else {
|
||||||
|
acc.push(line);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
return answerLines;
|
||||||
|
};
|
||||||
|
const getQuestionType = (
|
||||||
|
linesWithoutPoints: string[],
|
||||||
|
questionIndex: number
|
||||||
|
): QuestionType => {
|
||||||
|
if (linesWithoutPoints.length === 0) return QuestionType.NONE;
|
||||||
|
if (
|
||||||
|
linesWithoutPoints[linesWithoutPoints.length - 1].toLowerCase() === "essay"
|
||||||
|
)
|
||||||
|
return QuestionType.ESSAY;
|
||||||
|
if (
|
||||||
|
linesWithoutPoints[linesWithoutPoints.length - 1].toLowerCase() ===
|
||||||
|
"short answer"
|
||||||
|
)
|
||||||
|
return QuestionType.SHORT_ANSWER;
|
||||||
|
if (
|
||||||
|
linesWithoutPoints[linesWithoutPoints.length - 1].toLowerCase() ===
|
||||||
|
"short_answer"
|
||||||
|
)
|
||||||
|
return QuestionType.SHORT_ANSWER;
|
||||||
|
|
||||||
|
const answerLines = getAnswerStringsWithMultilineSupport(
|
||||||
|
linesWithoutPoints,
|
||||||
|
questionIndex
|
||||||
|
);
|
||||||
|
const firstAnswerLine = answerLines[0];
|
||||||
|
const isMultipleChoice = ["a)", "*a)", "*)", ")"].some((prefix) =>
|
||||||
|
firstAnswerLine.startsWith(prefix)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isMultipleChoice) return QuestionType.MULTIPLE_CHOICE;
|
||||||
|
|
||||||
|
const isMultipleAnswer = ["[ ]", "[*]"].some((prefix) =>
|
||||||
|
firstAnswerLine.startsWith(prefix)
|
||||||
|
);
|
||||||
|
if (isMultipleAnswer) return QuestionType.MULTIPLE_ANSWERS;
|
||||||
|
|
||||||
|
const isMatching = firstAnswerLine.startsWith("^");
|
||||||
|
if (isMatching) return QuestionType.MATCHING;
|
||||||
|
|
||||||
|
return QuestionType.NONE;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAnswers = (
|
||||||
|
linesWithoutPoints: string[],
|
||||||
|
questionIndex: number,
|
||||||
|
questionType: string
|
||||||
|
): LocalQuizQuestionAnswer[] => {
|
||||||
|
const answerLines = getAnswerStringsWithMultilineSupport(
|
||||||
|
linesWithoutPoints,
|
||||||
|
questionIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
const answers = answerLines.map((a, i) =>
|
||||||
|
quizQuestionAnswerMarkdownUtils.parseMarkdown(a, questionType)
|
||||||
|
);
|
||||||
|
return answers;
|
||||||
|
};
|
||||||
|
const getAnswerMarkdown = (
|
||||||
|
question: LocalQuizQuestion,
|
||||||
|
answer: LocalQuizQuestionAnswer,
|
||||||
|
index: number
|
||||||
|
): string => {
|
||||||
|
const multilineMarkdownCompatibleText = answer.text.startsWith("```")
|
||||||
|
? "\n" + answer.text
|
||||||
|
: answer.text;
|
||||||
|
|
||||||
|
if (question.questionType === "multiple_answers") {
|
||||||
|
const correctIndicator = answer.correct ? "*" : " ";
|
||||||
|
const questionTypeIndicator = `[${correctIndicator}] `;
|
||||||
|
|
||||||
|
return `${questionTypeIndicator}${multilineMarkdownCompatibleText}`;
|
||||||
|
} else if (question.questionType === "matching") {
|
||||||
|
return `^ ${answer.text} - ${answer.matchedText}`;
|
||||||
|
} else {
|
||||||
|
const questionLetter = String.fromCharCode(97 + index);
|
||||||
|
const correctIndicator = answer.correct ? "*" : "";
|
||||||
|
const questionTypeIndicator = `${correctIndicator}${questionLetter}) `;
|
||||||
|
|
||||||
|
return `${questionTypeIndicator}${multilineMarkdownCompatibleText}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const quizQuestionMarkdownUtils = {
|
||||||
|
toMarkdown(question: LocalQuizQuestion): string {
|
||||||
|
const answerArray = question.answers.map((a, i) =>
|
||||||
|
getAnswerMarkdown(question, a, i)
|
||||||
|
);
|
||||||
|
const answersText = answerArray.join("\n");
|
||||||
|
const questionTypeIndicator =
|
||||||
|
question.questionType === "essay" ||
|
||||||
|
question.questionType === "short_answer"
|
||||||
|
? question.questionType
|
||||||
|
: "";
|
||||||
|
|
||||||
|
return `Points: ${question.points}\n${question.text}\n${answersText}${questionTypeIndicator}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
parseMarkdown(input: string, questionIndex: number): LocalQuizQuestion {
|
||||||
|
const lines = input.trim().split("\n");
|
||||||
|
const firstLineIsPoints = lines[0].toLowerCase().includes("points: ");
|
||||||
|
|
||||||
|
const textHasPoints =
|
||||||
|
lines.length > 0 &&
|
||||||
|
lines[0].includes(": ") &&
|
||||||
|
lines[0].split(": ").length > 1 &&
|
||||||
|
!isNaN(parseFloat(lines[0].split(": ")[1]));
|
||||||
|
|
||||||
|
const points =
|
||||||
|
firstLineIsPoints && textHasPoints
|
||||||
|
? parseFloat(lines[0].split(": ")[1])
|
||||||
|
: 1;
|
||||||
|
|
||||||
|
const linesWithoutPoints = firstLineIsPoints ? lines.slice(1) : lines;
|
||||||
|
|
||||||
|
const linesWithoutAnswers = linesWithoutPoints.filter(
|
||||||
|
(line, index) =>
|
||||||
|
!_validFirstAnswerDelimiters.some((prefix) =>
|
||||||
|
line.trimStart().startsWith(prefix)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const questionType = getQuestionType(linesWithoutPoints, questionIndex);
|
||||||
|
|
||||||
|
const questionTypesWithoutAnswers = [
|
||||||
|
"essay",
|
||||||
|
"short answer",
|
||||||
|
"short_answer",
|
||||||
|
];
|
||||||
|
|
||||||
|
const descriptionLines = questionTypesWithoutAnswers.includes(
|
||||||
|
questionType.toLowerCase()
|
||||||
|
)
|
||||||
|
? linesWithoutAnswers
|
||||||
|
.slice(0, linesWithoutPoints.length)
|
||||||
|
.filter(
|
||||||
|
(line, index) =>
|
||||||
|
!questionTypesWithoutAnswers.includes(line.toLowerCase())
|
||||||
|
)
|
||||||
|
: linesWithoutAnswers;
|
||||||
|
|
||||||
|
const description = descriptionLines.join("\n");
|
||||||
|
|
||||||
|
const typesWithAnswers = [
|
||||||
|
"multiple_choice",
|
||||||
|
"multiple_answers",
|
||||||
|
"matching",
|
||||||
|
];
|
||||||
|
const answers = typesWithAnswers.includes(questionType)
|
||||||
|
? getAnswers(linesWithoutPoints, questionIndex, questionType)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const question: LocalQuizQuestion = {
|
||||||
|
text: description,
|
||||||
|
questionType,
|
||||||
|
points,
|
||||||
|
answers,
|
||||||
|
};
|
||||||
|
return question;
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import { LocalCoursePage } from "../../page/localCoursePage";
|
import { LocalCoursePage } from "../../page/localCoursePage";
|
||||||
import { pageMarkdown } from "../../page/pageMarkdown";
|
import { pageMarkdownUtils } from "../../page/pageMarkdownUtils";
|
||||||
|
|
||||||
describe("PageMarkdownTests", () => {
|
describe("PageMarkdownTests", () => {
|
||||||
it("can parse page", () => {
|
it("can parse page", () => {
|
||||||
@@ -10,9 +10,9 @@ describe("PageMarkdownTests", () => {
|
|||||||
dueAt: new Date().toISOString(),
|
dueAt: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const pageMarkdownString = pageMarkdown.toMarkdown(page);
|
const pageMarkdownString = pageMarkdownUtils.toMarkdown(page);
|
||||||
|
|
||||||
const parsedPage = pageMarkdown.parseMarkdown(pageMarkdownString);
|
const parsedPage = pageMarkdownUtils.parseMarkdown(pageMarkdownString);
|
||||||
|
|
||||||
expect(parsedPage).toEqual(page);
|
expect(parsedPage).toEqual(page);
|
||||||
});
|
});
|
||||||
|
|||||||
110
nextjs/src/models/local/tests/markdown/quiz/testAnswer.test.ts
Normal file
110
nextjs/src/models/local/tests/markdown/quiz/testAnswer.test.ts
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import { QuestionType } from '../../../../../models/local/quiz/localQuizQuestion';
|
||||||
|
import { quizMarkdownUtils } from '../../../../../models/local/quiz/utils/quizMarkdownUtils';
|
||||||
|
import { quizQuestionMarkdownUtils } from '../../../../../models/local/quiz/utils/quizQuestionMarkdownUtils';
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
|
||||||
|
describe('TextAnswerTests', () => {
|
||||||
|
it('can parse essay', () => {
|
||||||
|
const rawMarkdownQuiz = `
|
||||||
|
Name: Test Quiz
|
||||||
|
ShuffleAnswers: true
|
||||||
|
OneQuestionAtATime: false
|
||||||
|
DueAt: 2023-08-21T23:59:00
|
||||||
|
LockAt: 2023-08-21T23:59:00
|
||||||
|
AssignmentGroup: Assignments
|
||||||
|
AllowedAttempts: -1
|
||||||
|
Description: this is the
|
||||||
|
multi line
|
||||||
|
description
|
||||||
|
---
|
||||||
|
Which events are triggered when the user clicks on an input field?
|
||||||
|
essay
|
||||||
|
`;
|
||||||
|
|
||||||
|
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz);
|
||||||
|
const firstQuestion = quiz.questions[0];
|
||||||
|
|
||||||
|
expect(firstQuestion.points).toBe(1);
|
||||||
|
expect(firstQuestion.questionType).toBe(QuestionType.ESSAY);
|
||||||
|
expect(firstQuestion.text).not.toContain('essay');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can parse short answer', () => {
|
||||||
|
const rawMarkdownQuiz = `
|
||||||
|
Name: Test Quiz
|
||||||
|
ShuffleAnswers: true
|
||||||
|
OneQuestionAtATime: false
|
||||||
|
DueAt: 2023-08-21T23:59:00
|
||||||
|
LockAt: 2023-08-21T23:59:00
|
||||||
|
AssignmentGroup: Assignments
|
||||||
|
AllowedAttempts: -1
|
||||||
|
Description: this is the
|
||||||
|
multi line
|
||||||
|
description
|
||||||
|
---
|
||||||
|
Which events are triggered when the user clicks on an input field?
|
||||||
|
short answer
|
||||||
|
`;
|
||||||
|
|
||||||
|
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz);
|
||||||
|
const firstQuestion = quiz.questions[0];
|
||||||
|
|
||||||
|
expect(firstQuestion.points).toBe(1);
|
||||||
|
expect(firstQuestion.questionType).toBe(QuestionType.SHORT_ANSWER);
|
||||||
|
expect(firstQuestion.text).not.toContain('short answer');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('short answer to markdown is correct', () => {
|
||||||
|
const rawMarkdownQuiz = `
|
||||||
|
Name: Test Quiz
|
||||||
|
ShuffleAnswers: true
|
||||||
|
OneQuestionAtATime: false
|
||||||
|
DueAt: 2023-08-21T23:59:00
|
||||||
|
LockAt: 2023-08-21T23:59:00
|
||||||
|
AssignmentGroup: Assignments
|
||||||
|
AllowedAttempts: -1
|
||||||
|
Description: this is the
|
||||||
|
multi line
|
||||||
|
description
|
||||||
|
---
|
||||||
|
Which events are triggered when the user clicks on an input field?
|
||||||
|
short answer
|
||||||
|
`;
|
||||||
|
|
||||||
|
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz);
|
||||||
|
const firstQuestion = quiz.questions[0];
|
||||||
|
|
||||||
|
const questionMarkdown = quizQuestionMarkdownUtils.toMarkdown(firstQuestion);
|
||||||
|
const expectedMarkdown = `Points: 1
|
||||||
|
Which events are triggered when the user clicks on an input field?
|
||||||
|
short_answer`;
|
||||||
|
expect(questionMarkdown).toContain(expectedMarkdown);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('essay question to markdown is correct', () => {
|
||||||
|
const rawMarkdownQuiz = `
|
||||||
|
Name: Test Quiz
|
||||||
|
ShuffleAnswers: true
|
||||||
|
OneQuestionAtATime: false
|
||||||
|
DueAt: 2023-08-21T23:59:00
|
||||||
|
LockAt: 2023-08-21T23:59:00
|
||||||
|
AssignmentGroup: Assignments
|
||||||
|
AllowedAttempts: -1
|
||||||
|
Description: this is the
|
||||||
|
multi line
|
||||||
|
description
|
||||||
|
---
|
||||||
|
Which events are triggered when the user clicks on an input field?
|
||||||
|
essay
|
||||||
|
`;
|
||||||
|
|
||||||
|
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz);
|
||||||
|
const firstQuestion = quiz.questions[0];
|
||||||
|
|
||||||
|
const questionMarkdown = quizQuestionMarkdownUtils.toMarkdown(firstQuestion);
|
||||||
|
const expectedMarkdown = `Points: 1
|
||||||
|
Which events are triggered when the user clicks on an input field?
|
||||||
|
essay`;
|
||||||
|
expect(questionMarkdown).toContain(expectedMarkdown);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user