From 2b5f14cf6979706d394a7699aa00f2b5e9d02bbb Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 31 Jan 2022 16:37:54 -0500 Subject: [PATCH 001/104] Require Hello World in the document --- src/text.Test.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/text.Test.tsx diff --git a/src/text.Test.tsx b/src/text.Test.tsx new file mode 100644 index 0000000000..b32c330d3f --- /dev/null +++ b/src/text.Test.tsx @@ -0,0 +1,9 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import App from "./App"; + +test("renders the text 'Hello World' somewhere", () => { + render(); + const text = screen.getByText(/Hello World/); + expect(text).toBeInTheDocument(); +}); From a7dee05e0bee0379110c6189433d12482280146a Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 31 Jan 2022 16:41:17 -0500 Subject: [PATCH 002/104] Rename text.Test.tsx to text.test.tsx --- src/{text.Test.tsx => text.test.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{text.Test.tsx => text.test.tsx} (100%) diff --git a/src/text.Test.tsx b/src/text.test.tsx similarity index 100% rename from src/text.Test.tsx rename to src/text.test.tsx From 3e381f38b1d44afd102eb053a8ba9a48a069434e Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 31 Jan 2022 16:56:42 -0500 Subject: [PATCH 003/104] Include the task info --- public/tasks/task-first-branch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 public/tasks/task-first-branch.md diff --git a/public/tasks/task-first-branch.md b/public/tasks/task-first-branch.md new file mode 100644 index 0000000000..94333338a0 --- /dev/null +++ b/public/tasks/task-first-branch.md @@ -0,0 +1,5 @@ +# Task - First Branch + +Version: 0.0.1 + +Pass a short test to have certain text on the page. From 986b28ac0afb41e603602013f71e6ef6e257c722 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 2 Feb 2022 13:12:40 -0500 Subject: [PATCH 004/104] First stab at questions --- public/tasks/task-objects.md | 5 + src/data/questions.json | 79 ++++++++++ src/objects.test.ts | 295 +++++++++++++++++++++++++++++++++++ src/objects.ts | 141 +++++++++++++++++ 4 files changed, 520 insertions(+) create mode 100644 public/tasks/task-objects.md create mode 100644 src/data/questions.json create mode 100644 src/objects.test.ts create mode 100644 src/objects.ts diff --git a/public/tasks/task-objects.md b/public/tasks/task-objects.md new file mode 100644 index 0000000000..480889da0d --- /dev/null +++ b/public/tasks/task-objects.md @@ -0,0 +1,5 @@ +# Task - Objects + +Version: 0.0.1 + +Implement functions that work with objects immutably. diff --git a/src/data/questions.json b/src/data/questions.json new file mode 100644 index 0000000000..3b19537526 --- /dev/null +++ b/src/data/questions.json @@ -0,0 +1,79 @@ +{ + "BLANK_QUESTIONS": [ + { + "id": 1, + "name": "Question 1", + "body": "", + "type": "multiple_choice_question", + "options": [], + "expected": "", + "points": 1, + "published": false + }, + { + "id": 47, + "name": "My New Question", + "body": "", + "type": "multiple_choice_question", + "options": [], + "expected": "", + "points": 1, + "published": false + }, + { + "id": 2, + "name": "Question 2", + "body": "", + "type": "short_answer_question", + "options": [], + "expected": "", + "points": 1, + "published": false + } + ], + "SIMPLE_QUESTIONS": [ + { + "id": 1, + "name": "Addition", + "body": "What is 2+2?", + "type": "short_answer_question", + "options": [], + "expected": "4", + "points": 1, + "published": true + }, + { + "id": 2, + "name": "Letters", + "body": "What is the last letter of the English alphabet?", + "type": "short_answer_question", + "options": [], + "expected": "Z", + "points": 1, + "published": false + }, + { + "id": 5, + "name": "Colors", + "body": "Which of these is a color?", + "type": "multiple_choice_question", + "options": ["red", "apple", "firetruck"], + "expected": "red", + "points": 1, + "published": true + }, + { + "id": 9, + "name": "Shapes", + "body": "What shape can you make with one line?", + "type": "multiple_choice_question", + "options": ["square", "triangle", "circle"], + "expected": "circle", + "points": 2, + "published": false + } + ], + "SIMPLE_QUESTIONS_2": [], + "EMPTY_QUESTIONS": [], + "TRIVIA_QUESTIONS": [] +} diff --git a/src/objects.test.ts b/src/objects.test.ts new file mode 100644 index 0000000000..bcff7ab176 --- /dev/null +++ b/src/objects.test.ts @@ -0,0 +1,295 @@ +import { + makeBlankQuestion, + isCorrect, + Question, + isValid, + toShortForm, + toMarkdown, + duplicateQuestion, + renameQuestion, + publishQuestion, + addOption, + mergeQuestion +} from "./objects"; +import testQuestionData from "./data/questions.json"; +import backupQuestionData from "./data/questions.json"; + +//////////////////////////////////////////// +// Setting up the test data + +const { BLANK_QUESTIONS, SIMPLE_QUESTIONS }: Record = + // Typecast the test data that we imported to be a record matching + // strings to the question list + testQuestionData as Record; + +// We have backup versions of the data to make sure all changes are immutable +const { + BLANK_QUESTIONS: BACKUP_BLANK_QUESTIONS, + SIMPLE_QUESTIONS: BACKUP_SIMPLE_QUESTIONS +}: Record = backupQuestionData as Record< + string, + Question[] +>; + +// Unpack the list of simple questions into convenient constants +const [ADDITION_QUESTION, LETTER_QUESTION, COLOR_QUESTION, SHAPE_QUESTION] = + SIMPLE_QUESTIONS; +const [ + BACKUP_ADDITION_QUESTION, + BACKUP_LETTER_QUESTION, + BACKUP_COLOR_QUESTION, + BACKUP_SHAPE_QUESTION +] = BACKUP_SIMPLE_QUESTIONS; + +//////////////////////////////////////////// +// Actual tests + +describe("Testing the object functions", () => { + ////////////////////////////////// + // makeBlankQuestion + + test("Testing the makeBlankQuestion function", () => { + expect( + makeBlankQuestion(1, "Question 1", "multiple_choice_question") + ).toEqual(BLANK_QUESTIONS[0]); + expect( + makeBlankQuestion(47, "My New Question", "multiple_choice_question") + ).toEqual(BLANK_QUESTIONS[1]); + expect( + makeBlankQuestion(2, "Question 2", "short_answer_question") + ).toEqual(BLANK_QUESTIONS[2]); + }); + + /////////////////////////////////// + // isCorrect + test("Testing the isCorrect function", () => { + expect(isCorrect(ADDITION_QUESTION, "4")).toEqual(true); + expect(isCorrect(ADDITION_QUESTION, "2")).toEqual(false); + expect(isCorrect(ADDITION_QUESTION, " 4\n")).toEqual(true); + expect(isCorrect(LETTER_QUESTION, "Z")).toEqual(true); + expect(isCorrect(LETTER_QUESTION, "z")).toEqual(true); + expect(isCorrect(LETTER_QUESTION, "4")).toEqual(false); + expect(isCorrect(LETTER_QUESTION, "0")).toEqual(false); + expect(isCorrect(LETTER_QUESTION, "zed")).toEqual(false); + expect(isCorrect(COLOR_QUESTION, "red")).toEqual(true); + expect(isCorrect(COLOR_QUESTION, "apple")).toEqual(false); + expect(isCorrect(COLOR_QUESTION, "firetruck")).toEqual(false); + expect(isCorrect(SHAPE_QUESTION, "square")).toEqual(false); + expect(isCorrect(SHAPE_QUESTION, "triangle")).toEqual(false); + expect(isCorrect(SHAPE_QUESTION, "circle")).toEqual(true); + }); + + /////////////////////////////////// + // isValid + test("Testing the isValid function", () => { + expect(isValid(ADDITION_QUESTION, "4")).toEqual(true); + expect(isValid(ADDITION_QUESTION, "2")).toEqual(true); + expect(isValid(ADDITION_QUESTION, " 4\n")).toEqual(true); + expect(isValid(LETTER_QUESTION, "Z")).toEqual(true); + expect(isValid(LETTER_QUESTION, "z")).toEqual(true); + expect(isValid(LETTER_QUESTION, "4")).toEqual(true); + expect(isValid(LETTER_QUESTION, "0")).toEqual(true); + expect(isValid(LETTER_QUESTION, "zed")).toEqual(true); + expect(isValid(COLOR_QUESTION, "red")).toEqual(true); + expect(isValid(COLOR_QUESTION, "apple")).toEqual(true); + expect(isValid(COLOR_QUESTION, "firetruck")).toEqual(true); + expect(isValid(COLOR_QUESTION, "RED")).toEqual(false); + expect(isValid(COLOR_QUESTION, "orange")).toEqual(false); + expect(isValid(SHAPE_QUESTION, "square")).toEqual(true); + expect(isValid(SHAPE_QUESTION, "triangle")).toEqual(true); + expect(isValid(SHAPE_QUESTION, "circle")).toEqual(true); + expect(isValid(SHAPE_QUESTION, "circle ")).toEqual(false); + expect(isValid(SHAPE_QUESTION, "rhombus")).toEqual(false); + }); + + /////////////////////////////////// + // toShortForm + test("Testing the toShortForm function", () => { + expect(toShortForm(ADDITION_QUESTION)).toEqual("1: Addition"); + expect(toShortForm(LETTER_QUESTION)).toEqual("2: Letters"); + expect(toShortForm(COLOR_QUESTION)).toEqual("5: Colors"); + expect(toShortForm(SHAPE_QUESTION)).toEqual("9: Shapes"); + expect(toShortForm(BLANK_QUESTIONS[1])).toEqual("47: My New Que"); + }); + + /////////////////////////////////// + // toMarkdown + test("Testing the toMarkdown function", () => { + expect(toMarkdown(ADDITION_QUESTION)).toEqual(`# Addition +What is 2+2?`); + expect(toMarkdown(LETTER_QUESTION)).toEqual(`# Letters +What is the last letter of the English alphabet?`); + expect(toMarkdown(COLOR_QUESTION)).toEqual(`# Colors +Which of these is a color? +- red +- apple +- firetruck`); + expect(toMarkdown(SHAPE_QUESTION)).toEqual(`# Shapes +What shape can you make with one line? +- square +- triangle +- circle`); + }); + + afterEach(() => { + expect(ADDITION_QUESTION).toEqual(BACKUP_ADDITION_QUESTION); + expect(LETTER_QUESTION).toEqual(BACKUP_LETTER_QUESTION); + expect(SHAPE_QUESTION).toEqual(BACKUP_SHAPE_QUESTION); + expect(COLOR_QUESTION).toEqual(BACKUP_COLOR_QUESTION); + expect(BLANK_QUESTIONS).toEqual(BACKUP_BLANK_QUESTIONS); + }); + + /////////////////////////////////// + // renameQuestion + test("Testing the renameQuestion function", () => { + expect( + renameQuestion(ADDITION_QUESTION, "My Addition Question") + ).toEqual({ + id: 1, + name: "My Addition Question", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }); + expect( + renameQuestion(SHAPE_QUESTION, "I COMPLETELY CHANGED THIS NAME") + ).toEqual({ + id: 9, + name: "I COMPLETELY CHANGED THIS NAME", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + }); + }); + + /////////////////////////////////// + // publishQuestion + test("Testing the publishQuestion function", () => { + expect(publishQuestion(ADDITION_QUESTION)).toEqual({ + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: false + }); + expect(publishQuestion(LETTER_QUESTION)).toEqual({ + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: true + }); + expect(publishQuestion(publishQuestion(ADDITION_QUESTION))).toEqual({ + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }); + }); + + /////////////////////////////////// + // duplicateQuestion + test("Testing the duplicateQuestion function", () => { + expect(duplicateQuestion(9, ADDITION_QUESTION)).toEqual({ + id: 9, + name: "Copy of Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: false + }); + expect(duplicateQuestion(55, LETTER_QUESTION)).toEqual({ + id: 55, + name: "Copy of Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }); + }); + + /////////////////////////////////// + // addOption + test("Testing the addOption function", () => { + expect(addOption(SHAPE_QUESTION, "heptagon")).toEqual({ + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle", "heptagon"], + expected: "circle", + points: 2, + published: false + }); + expect(addOption(COLOR_QUESTION, "squiggles")).toEqual({ + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck", "squiggles"], + expected: "red", + points: 1, + published: true + }); + }); + + /////////////////////////////////// + // mergeQuestion + test("Testing the mergeQuestion function", () => { + expect( + mergeQuestion( + 192, + "More Points Addition", + ADDITION_QUESTION, + SHAPE_QUESTION + ) + ).toEqual({ + id: 192, + name: "More Points Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 2, + published: false + }); + + expect( + mergeQuestion( + 99, + "Less Points Shape", + SHAPE_QUESTION, + ADDITION_QUESTION + ) + ).toEqual({ + id: 99, + name: "Less Points Shape", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 1, + published: false + }); + }); +}); diff --git a/src/objects.ts b/src/objects.ts new file mode 100644 index 0000000000..d03dd473e3 --- /dev/null +++ b/src/objects.ts @@ -0,0 +1,141 @@ +/** QuestionType influences how a question is asked and what kinds of answers are possible */ +export type QuestionType = "multiple_choice_question" | "short_answer_question"; + +export interface Question { + /** A unique identifier for the question */ + id: number; + /** The human-friendly title of the question */ + name: string; + /** The instructions and content of the Question */ + body: string; + /** The kind of Question; influences how the user answers and what options are displayed */ + type: QuestionType; + /** The possible answers for a Question (for Multiple Choice questions) */ + options: string[]; + /** The actually correct answer expected */ + expected: string; + /** How many points this question is worth, roughly indicating its importance and difficulty */ + points: number; + /** Whether or not this question is ready to display to students */ + published: boolean; +} + +/** + * Create a new blank question with the given `id`, `name`, and `type. The `body` and + * `expected` should be empty strings, the `options` should be an empty list, the `points` + * should default to 1, and `published` should default to false. + */ +export function makeBlankQuestion( + id: number, + name: string, + type: QuestionType +): Question { + return {}; +} + +/** + * Consumes a question and a potential `answer`, and returns whether or not + * the `answer` is correct. You should check that the `answer` is equal to + * the `expected`, ignoring capitalization and trimming any whitespace. + * + * HINT: Look up the `trim` and `toLowerCase` functions. + */ +export function isCorrect(question: Question, answer: string): boolean { + return false; +} + +/** + * Consumes a question and a potential `answer`, and returns whether or not + * the `answer` is valid (but not necessarily correct). For a `short_answer_question`, + * any answer is valid. But for a `multiple_choice_question`, the `answer` must + * be exactly one of the options. + */ +export function isValid(question: Question, answer: string): boolean { + return false; +} + +/** + * Consumes a question and produces a string representation combining the + * `id` and first 10 characters of the `name`. The two strings should be + * separated by ": ". So for example, the question with id 9 and the + * name "My First Question" would become "9: My First Q". + */ +export function toShortForm(question: Question): string { + return ""; +} + +/** + * Consumes a question and returns a formatted string representation as follows: + * - The first line should be a hash sign, a space, and then the `name` + * - The second line should be the `body` + * - If the question is a `multiple_choice_question`, then the following lines + * need to show each option on its line, preceded by a dash and space. + * + * The example below might help, but don't include the border! + * ----------Example------------- + * |# Name | + * |The body goes here! | + * |- Option 1 | + * |- Option 2 | + * |- Option 3 | + * ------------------------------ + * Check the unit tests for more examples of what this looks like! + */ +export function toMarkdown(question: Question): string { + return ""; +} + +/** + * Return a new version of the given question, except the name should now be + * `newName`. + */ +export function renameQuestion(question: Question, newName: string): Question { + return question; +} + +/** + * Return a new version of the given question, except the `published` field + * should be inverted. If the question was not published, now it should be + * published; if it was published, now it should be not published. + */ +export function publishQuestion(question: Question): Question { + return question; +} + +/** + * Create a new question based on the old question, copying over its `body`, `type`, + * `options`, `expected`, and `points` without changes. The `name` should be copied + * over as "Copy of ORIGINAL NAME" (e.g., so "Question 1" would become "Copy of Question 1"). + * The `published` field should be reset to false. + */ +export function duplicateQuestion(id: number, oldQuestion: Question): Question { + return oldQuestion; +} + +/** + * Return a new version of the given question, with the `newOption` added to + * the list of existing `options`. Remember that the new Question MUST have + * its own separate copy of the `options` list, rather than the same reference + * to the original question's list! + * Check out the subsection about "Nested Fields" for more information. + */ +export function addOption(question: Question, newOption: string): Question { + return question; +} + +/** + * Consumes an id, name, and two questions, and produces a new question. + * The new question will use the `body`, `type`, `options`, and `expected` of the + * `contentQuestion`. The second question will provide the `points`. + * The `published` status should be set to false. + * Notice that the second Question is provided as just an object with a `points` + * field; but the function call would be the same as if it were a `Question` type! + */ +export function mergeQuestion( + id: number, + name: string, + contentQuestion: Question, + { points }: { points: number } +): Question { + return contentQuestion; +} From e6b1dab1961daf6f03459789cef974bf043501f2 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 3 Feb 2022 14:10:55 -0500 Subject: [PATCH 005/104] Allow one or more instances of the Hello World text --- src/text.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text.test.tsx b/src/text.test.tsx index b32c330d3f..f99a063e76 100644 --- a/src/text.test.tsx +++ b/src/text.test.tsx @@ -4,6 +4,6 @@ import App from "./App"; test("renders the text 'Hello World' somewhere", () => { render(); - const text = screen.getByText(/Hello World/); - expect(text).toBeInTheDocument(); + const texts = screen.getAllByText(/Hello World/); + expect(texts.length).toBeGreaterThanOrEqual(1); }); From 2c852d620be705187b5ade2a68df632c6d6d4256 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 6 Feb 2022 18:33:46 -0500 Subject: [PATCH 006/104] Move Question interface to separate file --- src/interfaces/question.ts | 21 +++++++++++++++++++++ src/objects.test.ts | 2 +- src/objects.ts | 22 +--------------------- 3 files changed, 23 insertions(+), 22 deletions(-) create mode 100644 src/interfaces/question.ts diff --git a/src/interfaces/question.ts b/src/interfaces/question.ts new file mode 100644 index 0000000000..a39431565e --- /dev/null +++ b/src/interfaces/question.ts @@ -0,0 +1,21 @@ +/** QuestionType influences how a question is asked and what kinds of answers are possible */ +export type QuestionType = "multiple_choice_question" | "short_answer_question"; + +export interface Question { + /** A unique identifier for the question */ + id: number; + /** The human-friendly title of the question */ + name: string; + /** The instructions and content of the Question */ + body: string; + /** The kind of Question; influences how the user answers and what options are displayed */ + type: QuestionType; + /** The possible answers for a Question (for Multiple Choice questions) */ + options: string[]; + /** The actually correct answer expected */ + expected: string; + /** How many points this question is worth, roughly indicating its importance and difficulty */ + points: number; + /** Whether or not this question is ready to display to students */ + published: boolean; +} diff --git a/src/objects.test.ts b/src/objects.test.ts index bcff7ab176..a9c76a334e 100644 --- a/src/objects.test.ts +++ b/src/objects.test.ts @@ -1,7 +1,7 @@ +import { Question } from "./interfaces/question"; import { makeBlankQuestion, isCorrect, - Question, isValid, toShortForm, toMarkdown, diff --git a/src/objects.ts b/src/objects.ts index d03dd473e3..3fd2072e5e 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -1,24 +1,4 @@ -/** QuestionType influences how a question is asked and what kinds of answers are possible */ -export type QuestionType = "multiple_choice_question" | "short_answer_question"; - -export interface Question { - /** A unique identifier for the question */ - id: number; - /** The human-friendly title of the question */ - name: string; - /** The instructions and content of the Question */ - body: string; - /** The kind of Question; influences how the user answers and what options are displayed */ - type: QuestionType; - /** The possible answers for a Question (for Multiple Choice questions) */ - options: string[]; - /** The actually correct answer expected */ - expected: string; - /** How many points this question is worth, roughly indicating its importance and difficulty */ - points: number; - /** Whether or not this question is ready to display to students */ - published: boolean; -} +import { Question, QuestionType } from "./interfaces/question"; /** * Create a new blank question with the given `id`, `name`, and `type. The `body` and From dc3662af02ffe003b2044d4262bddf02ad6c7333 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 8 Feb 2022 00:36:21 -0500 Subject: [PATCH 007/104] Create answer interface --- src/interfaces/answer.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/interfaces/answer.ts diff --git a/src/interfaces/answer.ts b/src/interfaces/answer.ts new file mode 100644 index 0000000000..743ee8dff9 --- /dev/null +++ b/src/interfaces/answer.ts @@ -0,0 +1,13 @@ +/*** + * A representation of a students' answer in a quizzing game + */ +export interface Answer { + /** The ID of the question being answered. */ + questionId: number; + /** The text that the student entered for their answer. */ + text: string; + /** Whether or not the student has submitted this answer. */ + submitted: boolean; + /** Whether or not the students' answer matched the expected. */ + correct: boolean; +} From 51221ee3f303c4927f4efd8f4e286c754cb7b006 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 8 Feb 2022 00:36:37 -0500 Subject: [PATCH 008/104] First stab at nested tasks --- src/nested.test.ts | 57 +++++++++++++++ src/nested.ts | 178 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 src/nested.test.ts create mode 100644 src/nested.ts diff --git a/src/nested.test.ts b/src/nested.test.ts new file mode 100644 index 0000000000..1e3ff24b5c --- /dev/null +++ b/src/nested.test.ts @@ -0,0 +1,57 @@ +import { Question } from "./interfaces/question"; +import { getPublishedQuestions } from "./nested"; +import testQuestionData from "./data/questions.json"; +import backupQuestionData from "./data/questions.json"; + +const { BLANK_QUESTIONS, SIMPLE_QUESTIONS }: Record = + // Typecast the test data that we imported to be a record matching + // strings to the question list + testQuestionData as Record; + +// We have backup versions of the data to make sure all changes are immutable +const { + BLANK_QUESTIONS: BACKUP_BLANK_QUESTIONS, + SIMPLE_QUESTIONS: BACKUP_SIMPLE_QUESTIONS +}: Record = backupQuestionData as Record< + string, + Question[] +>; + +//////////////////////////////////////////// +// Actual tests + +describe("Testing the Question[] functions", () => { + ////////////////////////////////// + // getPublishedQuestions + + test("Testing the getPublishedQuestions function", () => { + expect(getPublishedQuestions(BLANK_QUESTIONS)).toEqual([]); + expect(getPublishedQuestions(SIMPLE_QUESTIONS)).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck"], + expected: "red", + points: 1, + published: true + } + ]); + }); + + afterEach(() => { + expect(BLANK_QUESTIONS).toEqual(BACKUP_BLANK_QUESTIONS); + expect(SIMPLE_QUESTIONS).toEqual(BACKUP_SIMPLE_QUESTIONS); + }); +}); diff --git a/src/nested.ts b/src/nested.ts new file mode 100644 index 0000000000..b9fb13f3cf --- /dev/null +++ b/src/nested.ts @@ -0,0 +1,178 @@ +import { Answer } from "./interfaces/answer"; +import { Question, QuestionType } from "./interfaces/question"; + +/** + * Consumes an array of questions and returns a new array with only the questions + * that are `published`. + */ +export function getPublishedQuestions(questions: Question[]): Question[] { + return []; +} + +/** + * Consumes an array of questions and returns a new array of only the questions that are + * considered "non-empty". An empty question has an empty string for its `body` and + * `expected`, and an empty array for its `options`. + */ +export function getNonEmptyQuestions(questions: Question[]): Question[] { + return []; +} + +/*** + * Consumes an array of questions and returns the question with the given `id`. If the + * question is not found, return `null` instead. + */ +export function findQuestion( + questions: Question[], + id: number +): Question | null { + return null; +} + +/** + * Consumes an array of questions and returns a new array that does not contain the question + * with the given `id`. + */ +export function removeQuestion(questions: Question[], id: number): Question[] { + return []; +} + +/*** + * Consumes an array of questions and returns a new array containing just the names of the + * questions, as an array. + */ +export function getNames(questions: Question[]): string[] { + return []; +} + +/*** + * Consumes an array of questions and returns the sum total of all their points added together. + */ +export function sumPoints(questions: Question[]): number { + return 0; +} + +/*** + * Consumes an array of questions and returns the sum total of the PUBLISHED questions. + */ +export function sumPublishedPoints(questions: Question[]): number { + return 0; +} + +/*** + * Consumes an array of questions, and produces a Comma-Separated Value (CSV) string representation. + * A CSV is a type of file frequently used to share tabular data; we will use a single string + * to represent the entire file. The first line of the file is the headers "id", "name", "options", + * "points", and "published". The following line contains the value for each question, separated by + * commas. For the `options` field, use the NUMBER of options. + * + * Here is an example of what this will look like (do not include the border). + *` +id,name,options,points,published +1,Addition,0,1,true +2,Letters,0,1,false +5,Colors,3,1,true +9,Shapes,3,2,false +` * + * Check the unit tests for more examples! + */ +export function toCSV(questions: Question[]): string { + return ""; +} + +/** + * Consumes an array of Questions and produces a corresponding array of + * Answers. Each Question gets its own Answer, copying over the `id` as the `questionId`, + * making the `text` an empty string, and using false for both `submitted` and `correct`. + */ +export function makeAnswers(questions: Question[]): Answer[] { + return []; +} + +/*** + * Consumes an array of Questions and produces a new array of questions, where + * each question is now published, regardless of its previous published status. + */ +export function publishAll(questions: Question[]): Question[] { + return []; +} + +/*** + * Consumes an array of Questions and produces whether or not all the questions + * are the same type. They can be any type, as long as they are all the SAME type. + */ +export function sameType(questions: Question[]): boolean { + return false; +} + +/*** + * Consumes an array of Questions and produces a new array of the same Questions, + * except that a blank question has been added onto the end. Reuse the `makeBlankQuestion` + * you defined in the `objects.ts` file. + */ +export function addNewQuestion( + questions: Question[], + id: number, + name: string, + type: QuestionType +): Question[] { + return []; +} + +/*** + * Consumes an array of Questions and produces a new array of Questions, where all + * the Questions are the same EXCEPT for the one with the given `targetId`. That + * Question should be the same EXCEPT that its name should now be `newName`. + */ +export function renameQuestionById( + questions: Question[], + targetId: number, + newName: string +): Question[] { + return []; +} + +/*** + * Consumes an array of Questions and produces a new array of Questions, where all + * the Questions are the same EXCEPT for the one with the given `targetId`. That + * Question should be the same EXCEPT that its `type` should now be the `newQuestionType` + * AND if the `newQuestionType` is no longer "multiple_choice_question" than the `options` + * must be set to an empty list. + */ +export function changeQuestionTypeById( + questions: Question[], + targetId: number, + newQuestionType: QuestionType +): Question[] { + return []; +} + +/** + * Consumes an array of Questions and produces a new array of Questions, where all + * the Questions are the same EXCEPT for the one with the given `targetId`. That + * Question should be the same EXCEPT that its `option` array should have a new element. + * If the `targetOptionIndex` is -1, the `newOption` should be added to the end of the list. + * Otherwise, it should *replace* the existing element at the `targetOptionIndex`. + */ +export function editOption( + questions: Question[], + targetId: number, + targetOptionIndex: number, + newOption: string +) { + return []; +} + +/*** + * Consumes an array of questions, and produces a new array based on the original array. + * The only difference is that the question with id `targetId` should now be duplicated, with + * the duplicate inserted directly after the original question. Use the `duplicateQuestion` + * function you defined previously; the `newId` is the parameter to use for the duplicate's ID. + */ +export function duplicateQuestionInArray( + questions: Question[], + targetId: number, + newId: number +): Question[] { + return []; +} From 3a793cc12152d73df161d3a61691f72d1dc6dde8 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 9 Feb 2022 13:20:35 -0500 Subject: [PATCH 009/104] Document Question interface --- src/interfaces/question.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/interfaces/question.ts b/src/interfaces/question.ts index a39431565e..5def48f2f7 100644 --- a/src/interfaces/question.ts +++ b/src/interfaces/question.ts @@ -1,6 +1,7 @@ /** QuestionType influences how a question is asked and what kinds of answers are possible */ export type QuestionType = "multiple_choice_question" | "short_answer_question"; +/** A representation of a Question in a quizzing application */ export interface Question { /** A unique identifier for the question */ id: number; From 5c39a97a647cd7e5d686beda8208a81e5f339478 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 9 Feb 2022 13:20:46 -0500 Subject: [PATCH 010/104] Expand questions test data --- src/data/questions.json | 147 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 3 deletions(-) diff --git a/src/data/questions.json b/src/data/questions.json index 3b19537526..0411f30afe 100644 --- a/src/data/questions.json +++ b/src/data/questions.json @@ -73,7 +73,148 @@ "published": false } ], - "SIMPLE_QUESTIONS_2": [], - "EMPTY_QUESTIONS": [], - "TRIVIA_QUESTIONS": [] + "TRIVIA_QUESTIONS": [ + { + "id": 1, + "name": "Mascot", + "body": "What is the name of the UD Mascot?", + "type": "multiple_choice_question", + "options": ["Bluey", "YoUDee", "Charles the Wonder Dog"], + "expected": "YoUDee", + "points": 7, + "published": false + }, + { + "id": 2, + "name": "Motto", + "body": "What is the University of Delaware's motto?", + "type": "multiple_choice_question", + "options": [ + "Knowledge is the light of the mind", + "Just U Do it", + "Nothing, what's the motto with you?" + ], + "expected": "Knowledge is the light of the mind", + "points": 3, + "published": false + }, + { + "id": 3, + "name": "Goats", + "body": "How many goats are there usually on the Green?", + "type": "multiple_choice_question", + "options": [ + "Zero, why would there be goats on the green?", + "18420", + "Two" + ], + "expected": "Two", + "points": 10, + "published": false + } + ], + "EMPTY_QUESTIONS": [ + { + "id": 1, + "name": "Empty 1", + "body": "This question is not empty, right?", + "type": "multiple_choice_question", + "options": ["correct", "it is", "not"], + "expected": "correct", + "points": 5, + "published": true + }, + { + "id": 2, + "name": "Empty 2", + "body": "", + "type": "multiple_choice_question", + "options": ["this", "one", "is", "not", "empty", "either"], + "expected": "one", + "points": 5, + "published": true + }, + { + "id": 3, + "name": "Empty 3", + "body": "This questions is not empty either!", + "type": "short_answer_question", + "options": [], + "expected": "", + "points": 5, + "published": true + }, + { + "id": 4, + "name": "Empty 4", + "body": "", + "type": "short_answer_question", + "options": [], + "expected": "Even this one is not empty", + "points": 5, + "published": true + }, + { + "id": 5, + "name": "Empty 5 (Actual)", + "body": "", + "type": "short_answer_question", + "options": [], + "expected": "", + "points": 5, + "published": false + } + ], + "SIMPLE_QUESTIONS_2": [ + { + "id": 478, + "name": "Students", + "body": "How many students are taking CISC275 this semester?", + "type": "short_answer_question", + "options": [], + "expected": "90", + "points": 53, + "published": true + }, + { + "id": 1937, + "name": "Importance", + "body": "On a scale of 1 to 10, how important is this quiz for them?", + "type": "short_answer_question", + "options": [], + "expected": "10", + "points": 47, + "published": true + }, + { + "id": 479, + "name": "Sentience", + "body": "Is it technically possible for this quiz to become sentient?", + "type": "short_answer_question", + "options": [], + "expected": "Yes", + "points": 40, + "published": true + }, + { + "id": 777, + "name": "Danger", + "body": "If this quiz became sentient, would it pose a danger to others?", + "type": "short_answer_question", + "options": [], + "expected": "Yes", + "points": 60, + "published": true + }, + { + "id": 1937, + "name": "Listening", + "body": "Is this quiz listening to us right now?", + "type": "short_answer_question", + "options": [], + "expected": "Yes", + "points": 100, + "published": true + } + ] } From 6ae0b6f210c37a37dace7b94ef0fc5c0b8fbcbfb Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 9 Feb 2022 13:21:43 -0500 Subject: [PATCH 011/104] Add a little hint for a tough one --- src/nested.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/nested.ts b/src/nested.ts index b9fb13f3cf..7934ec1741 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -153,6 +153,9 @@ export function changeQuestionTypeById( * Question should be the same EXCEPT that its `option` array should have a new element. * If the `targetOptionIndex` is -1, the `newOption` should be added to the end of the list. * Otherwise, it should *replace* the existing element at the `targetOptionIndex`. + * + * Remember, if a function starts getting too complicated, think about how a helper function + * can make it simpler! Break down complicated tasks into little pieces. */ export function editOption( questions: Question[], From b1bbbc869d8093ca9e286df1330ab150e7d4901d Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 9 Feb 2022 13:22:01 -0500 Subject: [PATCH 012/104] Nested tests (phew) --- src/nested.test.ts | 1187 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1184 insertions(+), 3 deletions(-) diff --git a/src/nested.test.ts b/src/nested.test.ts index 1e3ff24b5c..3d2b75406d 100644 --- a/src/nested.test.ts +++ b/src/nested.test.ts @@ -1,9 +1,32 @@ import { Question } from "./interfaces/question"; -import { getPublishedQuestions } from "./nested"; +import { + getPublishedQuestions, + getNonEmptyQuestions, + findQuestion, + removeQuestion, + getNames, + sumPoints, + sumPublishedPoints, + toCSV, + makeAnswers, + publishAll, + sameType, + addNewQuestion, + renameQuestionById, + changeQuestionTypeById, + editOption, + duplicateQuestionInArray +} from "./nested"; import testQuestionData from "./data/questions.json"; import backupQuestionData from "./data/questions.json"; -const { BLANK_QUESTIONS, SIMPLE_QUESTIONS }: Record = +const { + BLANK_QUESTIONS, + SIMPLE_QUESTIONS, + TRIVIA_QUESTIONS, + EMPTY_QUESTIONS, + SIMPLE_QUESTIONS_2 +}: Record = // Typecast the test data that we imported to be a record matching // strings to the question list testQuestionData as Record; @@ -11,12 +34,41 @@ const { BLANK_QUESTIONS, SIMPLE_QUESTIONS }: Record = // We have backup versions of the data to make sure all changes are immutable const { BLANK_QUESTIONS: BACKUP_BLANK_QUESTIONS, - SIMPLE_QUESTIONS: BACKUP_SIMPLE_QUESTIONS + SIMPLE_QUESTIONS: BACKUP_SIMPLE_QUESTIONS, + TRIVIA_QUESTIONS: BACKUP_TRIVIA_QUESTIONS, + EMPTY_QUESTIONS: BACKUP_EMPTY_QUESTIONS, + SIMPLE_QUESTIONS_2: BACKUP_SIMPLE_QUESTIONS_2 }: Record = backupQuestionData as Record< string, Question[] >; +const NEW_BLANK_QUESTION = { + id: 142, + name: "A new question", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false +}; + +const NEW_TRIVIA_QUESTION = { + id: 449, + name: "Colors", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + /*body: "The official colors of UD are Blue and ...?", + type: "multiple_choice_question", + options: ["Black, like my soul", "Blue again, we're tricky.", "#FFD200"], + expected: "#FFD200",*/ + points: 1, + published: false +}; + //////////////////////////////////////////// // Actual tests @@ -48,10 +100,1139 @@ describe("Testing the Question[] functions", () => { published: true } ]); + expect(getPublishedQuestions(TRIVIA_QUESTIONS)).toEqual([]); + expect(getPublishedQuestions(SIMPLE_QUESTIONS_2)).toEqual( + BACKUP_SIMPLE_QUESTIONS_2 + ); + expect(getPublishedQuestions(EMPTY_QUESTIONS)).toEqual([ + { + id: 1, + name: "Empty 1", + body: "This question is not empty, right?", + type: "multiple_choice_question", + options: ["correct", "it is", "not"], + expected: "correct", + points: 5, + published: true + }, + { + id: 2, + name: "Empty 2", + body: "", + type: "multiple_choice_question", + options: ["this", "one", "is", "not", "empty", "either"], + expected: "one", + points: 5, + published: true + }, + { + id: 3, + name: "Empty 3", + body: "This questions is not empty either!", + type: "short_answer_question", + options: [], + expected: "", + points: 5, + published: true + }, + { + id: 4, + name: "Empty 4", + body: "", + type: "short_answer_question", + options: [], + expected: "Even this one is not empty", + points: 5, + published: true + } + ]); + }); + + test("Testing the getNonEmptyQuestions functions", () => { + expect(getNonEmptyQuestions(BLANK_QUESTIONS)).toEqual([]); + expect(getNonEmptyQuestions(SIMPLE_QUESTIONS)).toEqual( + BACKUP_SIMPLE_QUESTIONS + ); + expect(getNonEmptyQuestions(TRIVIA_QUESTIONS)).toEqual( + BACKUP_TRIVIA_QUESTIONS + ); + expect(getNonEmptyQuestions(SIMPLE_QUESTIONS_2)).toEqual( + BACKUP_SIMPLE_QUESTIONS_2 + ); + expect(getNonEmptyQuestions(EMPTY_QUESTIONS)).toEqual([ + { + id: 1, + name: "Empty 1", + body: "This question is not empty, right?", + type: "multiple_choice_question", + options: ["correct", "it is", "not"], + expected: "correct", + points: 5, + published: true + }, + { + id: 2, + name: "Empty 2", + body: "", + type: "multiple_choice_question", + options: ["this", "one", "is", "not", "empty", "either"], + expected: "one", + points: 5, + published: true + }, + { + id: 3, + name: "Empty 3", + body: "This questions is not empty either!", + type: "short_answer_question", + options: [], + expected: "", + points: 5, + published: true + }, + { + id: 4, + name: "Empty 4", + body: "", + type: "short_answer_question", + options: [], + expected: "Even this one is not empty", + points: 5, + published: true + } + ]); + }); + + test("Testing the findQuestion function", () => { + expect(findQuestion(BLANK_QUESTIONS, 1)).toEqual(BLANK_QUESTIONS[0]); + expect(findQuestion(BLANK_QUESTIONS, 47)).toEqual(BLANK_QUESTIONS[1]); + expect(findQuestion(BLANK_QUESTIONS, 2)).toEqual(BLANK_QUESTIONS[2]); + expect(findQuestion(BLANK_QUESTIONS, 3)).toEqual(null); + expect(findQuestion(SIMPLE_QUESTIONS, 1)).toEqual(SIMPLE_QUESTIONS[0]); + expect(findQuestion(SIMPLE_QUESTIONS, 2)).toEqual(SIMPLE_QUESTIONS[1]); + expect(findQuestion(SIMPLE_QUESTIONS, 5)).toEqual(SIMPLE_QUESTIONS[2]); + expect(findQuestion(SIMPLE_QUESTIONS, 9)).toEqual(SIMPLE_QUESTIONS[3]); + expect(findQuestion(SIMPLE_QUESTIONS, 6)).toEqual(null); + expect(findQuestion(SIMPLE_QUESTIONS_2, 478)).toEqual( + SIMPLE_QUESTIONS_2[0] + ); + expect(findQuestion([], 0)).toEqual(null); + }); + + test("Testing the removeQuestion", () => { + expect(removeQuestion(BLANK_QUESTIONS, 1)).toEqual([ + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(removeQuestion(BLANK_QUESTIONS, 47)).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(removeQuestion(BLANK_QUESTIONS, 2)).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(removeQuestion(SIMPLE_QUESTIONS, 9)).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }, + { + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck"], + expected: "red", + points: 1, + published: true + } + ]); + expect(removeQuestion(SIMPLE_QUESTIONS, 5)).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }, + { + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + } + ]); + }); + + test("Testing the getNames function", () => { + expect(getNames(BLANK_QUESTIONS)).toEqual([ + "Question 1", + "My New Question", + "Question 2" + ]); + expect(getNames(SIMPLE_QUESTIONS)).toEqual([ + "Addition", + "Letters", + "Colors", + "Shapes" + ]); + expect(getNames(TRIVIA_QUESTIONS)).toEqual([ + "Mascot", + "Motto", + "Goats" + ]); + expect(getNames(SIMPLE_QUESTIONS_2)).toEqual([ + "Students", + "Importance", + "Sentience", + "Danger", + "Listening" + ]); + expect(getNames(EMPTY_QUESTIONS)).toEqual([ + "Empty 1", + "Empty 2", + "Empty 3", + "Empty 4", + "Empty 5 (Actual)" + ]); + }); + + test("Testing the sumPoints function", () => { + expect(sumPoints(BLANK_QUESTIONS)).toEqual(3); + expect(sumPoints(SIMPLE_QUESTIONS)).toEqual(5); + expect(sumPoints(TRIVIA_QUESTIONS)).toEqual(20); + expect(sumPoints(EMPTY_QUESTIONS)).toEqual(25); + expect(sumPoints(SIMPLE_QUESTIONS_2)).toEqual(300); + }); + + test("Testing the sumPublishedPoints function", () => { + expect(sumPublishedPoints(BLANK_QUESTIONS)).toEqual(0); + expect(sumPublishedPoints(SIMPLE_QUESTIONS)).toEqual(2); + expect(sumPublishedPoints(TRIVIA_QUESTIONS)).toEqual(0); + expect(sumPublishedPoints(EMPTY_QUESTIONS)).toEqual(20); + expect(sumPublishedPoints(SIMPLE_QUESTIONS_2)).toEqual(300); + }); + + test("Testing the toCSV function", () => { + expect(toCSV(BLANK_QUESTIONS)).toEqual(`id,name,options,points,published +1,Question 1,0,1,false +47,My New Question,0,1,false +2,Question 2,0,1,false`); + expect(toCSV(SIMPLE_QUESTIONS)) + .toEqual(`id,name,options,points,published +1,Addition,0,1,true +2,Letters,0,1,false +5,Colors,3,1,true +9,Shapes,3,2,false`); + expect(toCSV(TRIVIA_QUESTIONS)) + .toEqual(`id,name,options,points,published +1,Mascot,3,7,false +2,Motto,3,3,false +3,Goats,3,10,false`); + expect(toCSV(EMPTY_QUESTIONS)).toEqual(`id,name,options,points,published +1,Empty 1,3,5,true +2,Empty 2,6,5,true +3,Empty 3,0,5,true +4,Empty 4,0,5,true +5,Empty 5 (Actual),0,5,false`); + expect(toCSV(SIMPLE_QUESTIONS_2)) + .toEqual(`id,name,options,points,published +478,Students,0,53,true +1937,Importance,0,47,true +479,Sentience,0,40,true +777,Danger,0,60,true +1937,Listening,0,100,true`); + }); + + test("Testing the makeAnswers function", () => { + expect(makeAnswers(BLANK_QUESTIONS)).toEqual([ + { questionId: 1, correct: false, text: "", submitted: false }, + { questionId: 47, correct: false, text: "", submitted: false }, + { questionId: 2, correct: false, text: "", submitted: false } + ]); + expect(makeAnswers(SIMPLE_QUESTIONS)).toEqual([ + { questionId: 1, correct: false, text: "", submitted: false }, + { questionId: 2, correct: false, text: "", submitted: false }, + { questionId: 5, correct: false, text: "", submitted: false }, + { questionId: 9, correct: false, text: "", submitted: false } + ]); + expect(makeAnswers(TRIVIA_QUESTIONS)).toEqual([ + { questionId: 1, correct: false, text: "", submitted: false }, + { questionId: 2, correct: false, text: "", submitted: false }, + { questionId: 3, correct: false, text: "", submitted: false } + ]); + expect(makeAnswers(SIMPLE_QUESTIONS_2)).toEqual([ + { questionId: 478, correct: false, text: "", submitted: false }, + { questionId: 1937, correct: false, text: "", submitted: false }, + { questionId: 479, correct: false, text: "", submitted: false }, + { questionId: 777, correct: false, text: "", submitted: false }, + { questionId: 1937, correct: false, text: "", submitted: false } + ]); + expect(makeAnswers(EMPTY_QUESTIONS)).toEqual([ + { questionId: 1, correct: false, text: "", submitted: false }, + { questionId: 2, correct: false, text: "", submitted: false }, + { questionId: 3, correct: false, text: "", submitted: false }, + { questionId: 4, correct: false, text: "", submitted: false }, + { questionId: 5, correct: false, text: "", submitted: false } + ]); + }); + + test("Testing the publishAll function", () => { + expect(publishAll(BLANK_QUESTIONS)).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: true + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: true + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: true + } + ]); + expect(publishAll(SIMPLE_QUESTIONS)).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: true + }, + { + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck"], + expected: "red", + points: 1, + published: true + }, + { + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: true + } + ]); + expect(publishAll(TRIVIA_QUESTIONS)).toEqual([ + { + id: 1, + name: "Mascot", + body: "What is the name of the UD Mascot?", + type: "multiple_choice_question", + options: ["Bluey", "YoUDee", "Charles the Wonder Dog"], + expected: "YoUDee", + points: 7, + published: true + }, + { + id: 2, + name: "Motto", + body: "What is the University of Delaware's motto?", + type: "multiple_choice_question", + options: [ + "Knowledge is the light of the mind", + "Just U Do it", + "Nothing, what's the motto with you?" + ], + expected: "Knowledge is the light of the mind", + points: 3, + published: true + }, + { + id: 3, + name: "Goats", + body: "How many goats are there usually on the Green?", + type: "multiple_choice_question", + options: [ + "Zero, why would there be goats on the green?", + "18420", + "Two" + ], + expected: "Two", + points: 10, + published: true + } + ]); + expect(publishAll(EMPTY_QUESTIONS)).toEqual([ + { + id: 1, + name: "Empty 1", + body: "This question is not empty, right?", + type: "multiple_choice_question", + options: ["correct", "it is", "not"], + expected: "correct", + points: 5, + published: true + }, + { + id: 2, + name: "Empty 2", + body: "", + type: "multiple_choice_question", + options: ["this", "one", "is", "not", "empty", "either"], + expected: "one", + points: 5, + published: true + }, + { + id: 3, + name: "Empty 3", + body: "This questions is not empty either!", + type: "short_answer_question", + options: [], + expected: "", + points: 5, + published: true + }, + { + id: 4, + name: "Empty 4", + body: "", + type: "short_answer_question", + options: [], + expected: "Even this one is not empty", + points: 5, + published: true + }, + { + id: 5, + name: "Empty 5 (Actual)", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 5, + published: true + } + ]); + expect(publishAll(SIMPLE_QUESTIONS_2)).toEqual(SIMPLE_QUESTIONS_2); + }); + + test("Testing the sameType function", () => { + expect(sameType([])).toEqual(true); + expect(sameType(BLANK_QUESTIONS)).toEqual(false); + expect(sameType(SIMPLE_QUESTIONS)).toEqual(false); + expect(sameType(TRIVIA_QUESTIONS)).toEqual(true); + expect(sameType(EMPTY_QUESTIONS)).toEqual(false); + expect(sameType(SIMPLE_QUESTIONS_2)).toEqual(true); + }); + + test("Testing the addNewQuestion function", () => { + expect( + addNewQuestion([], 142, "A new question", "short_answer_question") + ).toEqual([NEW_BLANK_QUESTION]); + expect( + addNewQuestion( + BLANK_QUESTIONS, + 142, + "A new question", + "short_answer_question" + ) + ).toEqual([...BLANK_QUESTIONS, NEW_BLANK_QUESTION]); + expect( + addNewQuestion( + TRIVIA_QUESTIONS, + 449, + "Colors", + "multiple_choice_question" + ) + ).toEqual([...TRIVIA_QUESTIONS, NEW_TRIVIA_QUESTION]); + }); + + test("Testing the renameQuestionById function", () => { + expect(renameQuestionById(BLANK_QUESTIONS, 1, "New Name")).toEqual([ + { + id: 1, + name: "New Name", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(renameQuestionById(BLANK_QUESTIONS, 47, "Another Name")).toEqual( + [ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "Another Name", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ] + ); + expect(renameQuestionById(SIMPLE_QUESTIONS, 5, "Colours")).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }, + { + id: 5, + name: "Colours", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck"], + expected: "red", + points: 1, + published: true + }, + { + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + } + ]); + }); + + test("Test the changeQuestionTypeById function", () => { + expect( + changeQuestionTypeById( + BLANK_QUESTIONS, + 1, + "multiple_choice_question" + ) + ).toEqual(BLANK_QUESTIONS); + expect( + changeQuestionTypeById(BLANK_QUESTIONS, 1, "short_answer_question") + ).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect( + changeQuestionTypeById(BLANK_QUESTIONS, 47, "short_answer_question") + ).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect( + changeQuestionTypeById(TRIVIA_QUESTIONS, 3, "short_answer_question") + ).toEqual([ + { + id: 1, + name: "Mascot", + body: "What is the name of the UD Mascot?", + type: "multiple_choice_question", + options: ["Bluey", "YoUDee", "Charles the Wonder Dog"], + expected: "YoUDee", + points: 7, + published: false + }, + { + id: 2, + name: "Motto", + body: "What is the University of Delaware's motto?", + type: "multiple_choice_question", + options: [ + "Knowledge is the light of the mind", + "Just U Do it", + "Nothing, what's the motto with you?" + ], + expected: "Knowledge is the light of the mind", + points: 3, + published: false + }, + { + id: 3, + name: "Goats", + body: "How many goats are there usually on the Green?", + type: "short_answer_question", + options: [], + expected: "Two", + points: 10, + published: false + } + ]); + }); + + test("Testing the addEditQuestionOption function", () => { + expect(editOption(BLANK_QUESTIONS, 1, -1, "NEW OPTION")).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: ["NEW OPTION"], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(editOption(BLANK_QUESTIONS, 47, -1, "Another option")).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: ["Another option"], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(editOption(SIMPLE_QUESTIONS, 5, -1, "newspaper")).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }, + { + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck", "newspaper"], + expected: "red", + points: 1, + published: true + }, + { + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + } + ]); + expect(editOption(SIMPLE_QUESTIONS, 5, 0, "newspaper")).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }, + { + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["newspaper", "apple", "firetruck"], + expected: "red", + points: 1, + published: true + }, + { + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + } + ]); + + expect(editOption(SIMPLE_QUESTIONS, 5, 2, "newspaper")).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }, + { + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "newspaper"], + expected: "red", + points: 1, + published: true + }, + { + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + } + ]); + }); + + test("Testing the duplicateQuestionInArray function", () => { + expect(duplicateQuestionInArray(BLANK_QUESTIONS, 1, 27)).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 27, + name: "Copy of Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(duplicateQuestionInArray(BLANK_QUESTIONS, 47, 19)).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 19, + name: "Copy of My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(duplicateQuestionInArray(TRIVIA_QUESTIONS, 3, 111)).toEqual([ + { + id: 1, + name: "Mascot", + body: "What is the name of the UD Mascot?", + type: "multiple_choice_question", + options: ["Bluey", "YoUDee", "Charles the Wonder Dog"], + expected: "YoUDee", + points: 7, + published: false + }, + { + id: 2, + name: "Motto", + body: "What is the University of Delaware's motto?", + type: "multiple_choice_question", + options: [ + "Knowledge is the light of the mind", + "Just U Do it", + "Nothing, what's the motto with you?" + ], + expected: "Knowledge is the light of the mind", + points: 3, + published: false + }, + { + id: 3, + name: "Goats", + body: "How many goats are there usually on the Green?", + type: "multiple_choice_question", + options: [ + "Zero, why would there be goats on the green?", + "18420", + "Two" + ], + expected: "Two", + points: 10, + published: false + }, + { + id: 111, + name: "Copy of Goats", + body: "How many goats are there usually on the Green?", + type: "multiple_choice_question", + options: [ + "Zero, why would there be goats on the green?", + "18420", + "Two" + ], + expected: "Two", + points: 10, + published: false + } + ]); }); afterEach(() => { expect(BLANK_QUESTIONS).toEqual(BACKUP_BLANK_QUESTIONS); expect(SIMPLE_QUESTIONS).toEqual(BACKUP_SIMPLE_QUESTIONS); + expect(TRIVIA_QUESTIONS).toEqual(BACKUP_TRIVIA_QUESTIONS); + expect(SIMPLE_QUESTIONS_2).toEqual(BACKUP_SIMPLE_QUESTIONS_2); + expect(EMPTY_QUESTIONS).toEqual(BACKUP_EMPTY_QUESTIONS); }); }); From ab9bfb53c8eb4842f3149957337db26e621eff2c Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 9 Feb 2022 18:10:22 -0500 Subject: [PATCH 013/104] Basic starter files for components --- src/App.tsx | 18 +++++++++++++++--- src/components/ChangeType.tsx | 5 +++++ src/components/CycleHoliday.tsx | 5 +++++ src/components/RevealAnswer.tsx | 5 +++++ src/components/StartAttempt.tsx | 5 +++++ src/components/TwoDice.tsx | 5 +++++ 6 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 src/components/ChangeType.tsx create mode 100644 src/components/CycleHoliday.tsx create mode 100644 src/components/RevealAnswer.tsx create mode 100644 src/components/StartAttempt.tsx create mode 100644 src/components/TwoDice.tsx diff --git a/src/App.tsx b/src/App.tsx index e4f028fd06..0c973b1754 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,10 @@ import React from "react"; import "./App.css"; +import { ChangeType } from "./components/ChangeType"; +import { RevealAnswer } from "./components/RevealAnswer"; +import { StartAttempt } from "./components/StartAttempt"; +import { TwoDice } from "./components/TwoDice"; +import { CycleHoliday } from "./components/CycleHoliday"; function App(): JSX.Element { return ( @@ -7,9 +12,16 @@ function App(): JSX.Element {
UD CISC275 with React Hooks and TypeScript
-

- Edit src/App.tsx and save to reload. -

+
+ +
+ +
+ +
+ +
+ ); } diff --git a/src/components/ChangeType.tsx b/src/components/ChangeType.tsx new file mode 100644 index 0000000000..9a856820c4 --- /dev/null +++ b/src/components/ChangeType.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export function ChangeType(): JSX.Element { + return
Change Type
; +} diff --git a/src/components/CycleHoliday.tsx b/src/components/CycleHoliday.tsx new file mode 100644 index 0000000000..b3d85fa55a --- /dev/null +++ b/src/components/CycleHoliday.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export function CycleHoliday(): JSX.Element { + return
Cycle Holiday
; +} diff --git a/src/components/RevealAnswer.tsx b/src/components/RevealAnswer.tsx new file mode 100644 index 0000000000..6d724c4ccf --- /dev/null +++ b/src/components/RevealAnswer.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export function RevealAnswer(): JSX.Element { + return
Reveal Answer
; +} diff --git a/src/components/StartAttempt.tsx b/src/components/StartAttempt.tsx new file mode 100644 index 0000000000..12786ec0cd --- /dev/null +++ b/src/components/StartAttempt.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export function StartAttempt(): JSX.Element { + return
Start Attempt
; +} diff --git a/src/components/TwoDice.tsx b/src/components/TwoDice.tsx new file mode 100644 index 0000000000..b9a5260966 --- /dev/null +++ b/src/components/TwoDice.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export function TwoDice(): JSX.Element { + return
Two Dice
; +} From 97658638bc1a69bfa9e7a84a90a0307145db7db2 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 9 Feb 2022 18:30:51 -0500 Subject: [PATCH 014/104] Another extra paren error --- .eslintrc.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index d5986e4354..36f0947989 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -38,7 +38,8 @@ { "nestedBinaryExpressions": false, "returnAssign": false, - "enforceForArrowConditionals": false + "enforceForArrowConditionals": false, + "ignoreJSX": "all" } ], "brace-style": ["error", "1tbs"], From c0bbc391d8eb1994783a8207e386f45578813333 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 13 Feb 2022 14:38:40 -0500 Subject: [PATCH 015/104] Updated, complete tests for all state components --- src/App.tsx | 3 + src/components/ChangeType.test.tsx | 53 ++++++++ src/components/ChangeType.tsx | 4 +- src/components/Counter.test.tsx | 39 ++++++ src/components/Counter.tsx | 12 ++ src/components/CycleHoliday.test.tsx | 55 ++++++++ src/components/CycleHoliday.tsx | 3 +- src/components/RevealAnswer.test.tsx | 36 +++++ src/components/RevealAnswer.tsx | 3 +- src/components/StartAttempt.test.tsx | 196 +++++++++++++++++++++++++++ src/components/StartAttempt.tsx | 3 +- src/components/TwoDice.test.tsx | 140 +++++++++++++++++++ src/components/TwoDice.tsx | 13 +- 13 files changed, 555 insertions(+), 5 deletions(-) create mode 100644 src/components/ChangeType.test.tsx create mode 100644 src/components/Counter.test.tsx create mode 100644 src/components/Counter.tsx create mode 100644 src/components/CycleHoliday.test.tsx create mode 100644 src/components/RevealAnswer.test.tsx create mode 100644 src/components/StartAttempt.test.tsx create mode 100644 src/components/TwoDice.test.tsx diff --git a/src/App.tsx b/src/App.tsx index 0c973b1754..504138f1c3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,6 +5,7 @@ import { RevealAnswer } from "./components/RevealAnswer"; import { StartAttempt } from "./components/StartAttempt"; import { TwoDice } from "./components/TwoDice"; import { CycleHoliday } from "./components/CycleHoliday"; +import { Counter } from "./components/Counter"; function App(): JSX.Element { return ( @@ -12,6 +13,8 @@ function App(): JSX.Element {
UD CISC275 with React Hooks and TypeScript
+
+

diff --git a/src/components/ChangeType.test.tsx b/src/components/ChangeType.test.tsx new file mode 100644 index 0000000000..10b4f0dc3c --- /dev/null +++ b/src/components/ChangeType.test.tsx @@ -0,0 +1,53 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { ChangeType } from "./ChangeType"; + +describe("ChangeType Component tests", () => { + beforeEach(() => { + render(); + }); + test("The initial type is Short Answer", () => { + // We use `getByText` because the text MUST be there + const typeText = screen.getByText(/Short Answer/i); + expect(typeText).toBeInTheDocument(); + }); + test("The initial type is not Multiple Choice", () => { + // We use `queryByText` because the text might not be there + const typeText = screen.queryByText(/Multiple Choice/i); + expect(typeText).toBeNull(); + }); + + test("There is a button labeled Change Type", () => { + const changeTypeButton = screen.getByRole("button", { + name: /Change Type/i + }); + expect(changeTypeButton).toBeInTheDocument(); + }); + + test("Clicking the button changes the type.", () => { + const changeTypeButton = screen.getByRole("button", { + name: /Change Type/i + }); + changeTypeButton.click(); + // Should be Multiple Choice + const typeTextMC = screen.getByText(/Multiple Choice/i); + expect(typeTextMC).toBeInTheDocument(); + // Should NOT be Short Answer + const typeTextSA = screen.queryByText(/Short Answer/i); + expect(typeTextSA).toBeNull(); + }); + + test("Clicking the button twice keeps the type the same.", () => { + const changeTypeButton = screen.getByRole("button", { + name: /Change Type/i + }); + changeTypeButton.click(); + changeTypeButton.click(); + // Should be Short Answer + const typeTextSA = screen.getByText(/Short Answer/i); + expect(typeTextSA).toBeInTheDocument(); + // Should NOT be Multiple Choice + const typeTextMC = screen.queryByText(/Multiple Choice/i); + expect(typeTextMC).toBeNull(); + }); +}); diff --git a/src/components/ChangeType.tsx b/src/components/ChangeType.tsx index 9a856820c4..5608076f64 100644 --- a/src/components/ChangeType.tsx +++ b/src/components/ChangeType.tsx @@ -1,4 +1,6 @@ -import React from "react"; +import React, { useState } from "react"; +import { Button } from "react-bootstrap"; +import { QuestionType } from "../interfaces/question"; export function ChangeType(): JSX.Element { return
Change Type
; diff --git a/src/components/Counter.test.tsx b/src/components/Counter.test.tsx new file mode 100644 index 0000000000..7a37c46e38 --- /dev/null +++ b/src/components/Counter.test.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { Counter } from "./Counter"; + +describe("Counter Component tests", () => { + beforeEach(() => { + render(); + }); + test("The initial value is 0", () => { + // We use `getByText` because the text MUST be there + const valueText = screen.getByText(/0/i); + expect(valueText).toBeInTheDocument(); + }); + test("The initial value is not 1", () => { + // We use `queryByText` because the text might not be there + const valueText = screen.queryByText(/1/i); + expect(valueText).toBeNull(); + }); + + test("There is a button named Add One", () => { + const addOneButton = screen.getByRole("button", { name: /Add One/i }); + expect(addOneButton).toBeInTheDocument(); + }); + + test("Clicking the button once adds one", () => { + const addOneButton = screen.getByRole("button", { name: /Add One/i }); + addOneButton.click(); + const valueText = screen.getByText(/1/i); + expect(valueText).toBeInTheDocument(); + }); + + test("Clicking the button twice adds two", () => { + const addOneButton = screen.getByRole("button", { name: /Add One/i }); + addOneButton.click(); + addOneButton.click(); + const valueText = screen.getByText(/2/i); + expect(valueText).toBeInTheDocument(); + }); +}); diff --git a/src/components/Counter.tsx b/src/components/Counter.tsx new file mode 100644 index 0000000000..1987698ed1 --- /dev/null +++ b/src/components/Counter.tsx @@ -0,0 +1,12 @@ +import React, { useState } from "react"; +import { Button } from "react-bootstrap"; + +export function Counter(): JSX.Element { + const [value, setValue] = useState(0); + return ( + + + to {value}. + + ); +} diff --git a/src/components/CycleHoliday.test.tsx b/src/components/CycleHoliday.test.tsx new file mode 100644 index 0000000000..145e2cb3c8 --- /dev/null +++ b/src/components/CycleHoliday.test.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { CycleHoliday } from "./CycleHoliday"; + +describe("CycleHoliday Component tests", () => { + beforeEach(() => { + render(); + }); + + test("An initial holiday is displayed", () => { + const initialHoliday = screen.getByText(/Holiday: (.*)/i); + expect(initialHoliday).toBeInTheDocument(); + }); + + test("There are two buttons", () => { + const alphabetButton = screen.getByRole("button", { + name: /Alphabet/i + }); + const yearButton = screen.getByRole("button", { + name: /Year/i + }); + expect(alphabetButton).toBeInTheDocument(); + expect(yearButton).toBeInTheDocument(); + }); + + test("Can cycle through 5 distinct holidays alphabetically", () => { + const alphabetButton = screen.getByRole("button", { + name: /Alphabet/i + }); + const initialHoliday = screen.getByText(/Holiday ?[:)-](.*)/i); + const states: string[] = []; + for (let i = 0; i < 6; i++) { + states.push(initialHoliday.textContent || ""); + alphabetButton.click(); + } + const uniqueStates = states.filter((x, y) => states.indexOf(x) == y); + expect(uniqueStates).toHaveLength(5); + expect(states[0]).toEqual(states[5]); + }); + + test("Can cycle through 5 distinct holidays by year", () => { + const yearButton = screen.getByRole("button", { + name: /Year/i + }); + const initialHoliday = screen.getByText(/Holiday ?[:)-](.*)/i); + const states: string[] = []; + for (let i = 0; i < 6; i++) { + states.push(initialHoliday.textContent || ""); + yearButton.click(); + } + const uniqueStates = states.filter((x, y) => states.indexOf(x) == y); + expect(uniqueStates).toHaveLength(5); + expect(states[0]).toEqual(states[5]); + }); +}); diff --git a/src/components/CycleHoliday.tsx b/src/components/CycleHoliday.tsx index b3d85fa55a..7c671f889f 100644 --- a/src/components/CycleHoliday.tsx +++ b/src/components/CycleHoliday.tsx @@ -1,4 +1,5 @@ -import React from "react"; +import React, { useState } from "react"; +import { Button } from "react-bootstrap"; export function CycleHoliday(): JSX.Element { return
Cycle Holiday
; diff --git a/src/components/RevealAnswer.test.tsx b/src/components/RevealAnswer.test.tsx new file mode 100644 index 0000000000..aa7996e964 --- /dev/null +++ b/src/components/RevealAnswer.test.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { RevealAnswer } from "./RevealAnswer"; + +describe("RevealAnswer Component tests", () => { + beforeEach(() => { + render(); + }); + test("The answer '42' is not visible initially", () => { + const answerText = screen.queryByText(/42/); + expect(answerText).toBeNull(); + }); + test("There is a Reveal Answer button", () => { + const revealButton = screen.getByRole("button", { + name: /Reveal Answer/i + }); + expect(revealButton).toBeInTheDocument(); + }); + test("Clicking Reveal Answer button reveals the '42'", () => { + const revealButton = screen.getByRole("button", { + name: /Reveal Answer/i + }); + revealButton.click(); + const answerText = screen.getByText(/42/); + expect(answerText).toBeInTheDocument(); + }); + test("Clicking Reveal Answer button twice hides the '42'", () => { + const revealButton = screen.getByRole("button", { + name: /Reveal Answer/i + }); + revealButton.click(); + revealButton.click(); + const answerText = screen.queryByText(/42/); + expect(answerText).toBeNull(); + }); +}); diff --git a/src/components/RevealAnswer.tsx b/src/components/RevealAnswer.tsx index 6d724c4ccf..07db6f62d2 100644 --- a/src/components/RevealAnswer.tsx +++ b/src/components/RevealAnswer.tsx @@ -1,4 +1,5 @@ -import React from "react"; +import React, { useState } from "react"; +import { Button } from "react-bootstrap"; export function RevealAnswer(): JSX.Element { return
Reveal Answer
; diff --git a/src/components/StartAttempt.test.tsx b/src/components/StartAttempt.test.tsx new file mode 100644 index 0000000000..3d41c953cf --- /dev/null +++ b/src/components/StartAttempt.test.tsx @@ -0,0 +1,196 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { StartAttempt } from "./StartAttempt"; + +/*** + * Helper function to extract a number from an HTMLElement's textContent. + * + * If you aren't familiar with Regular Expressions: + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions + */ +export function extractDigits(element: HTMLElement): number | null { + const attemptNumberText = element.textContent || ""; + // We use a "regular expression" to find digits and extract them as text + const attemptNumberDigitsMatched = attemptNumberText.match(/\d+/); + // Provides a Matched Regular Expression or null + if (attemptNumberDigitsMatched === null) { + // Should never be possible, since then there was no number to have found. + // But TypeScript is cautious and demands we provide SOMETHING. + return null; + } else { + // Not null, get the first matched value and convert to number + return parseInt(attemptNumberDigitsMatched[0]); + } +} + +describe("StartAttempt Component tests", () => { + beforeEach(() => { + render(); + }); + test("The Number of attempts is displayed initially, without other numbers", () => { + const attemptNumber = screen.getByText(/(\d+)/); + expect(attemptNumber).toBeInTheDocument(); + }); + test("The Number of attempts is more than 0", () => { + const attemptNumber = extractDigits(screen.getByText(/(\d+)/)); + expect(attemptNumber).toBeGreaterThan(0); + }); + test("The Number of attempts is less than 10", () => { + const attemptNumber = extractDigits(screen.getByText(/(\d+)/)); + expect(attemptNumber).toBeLessThan(10); + }); + test("There is an initially enabled Start Quiz button", () => { + const startButton = screen.getByRole("button", { name: /Start Quiz/i }); + expect(startButton).toBeInTheDocument(); + expect(startButton).toBeEnabled(); + }); + test("There is an initially disabled Stop Quiz button", () => { + const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); + expect(stopButton).toBeInTheDocument(); + expect(stopButton).toBeDisabled(); + }); + test("There is an initially enabled Mulligan button", () => { + const mulliganButton = screen.getByRole("button", { + name: /Mulligan/i + }); + expect(mulliganButton).toBeInTheDocument(); + expect(mulliganButton).toBeEnabled(); + }); + test("Clicking Mulligan increases attempts", () => { + const attemptNumber: number = + extractDigits(screen.getByText(/(\d+)/)) || 0; + const mulliganButton = screen.getByRole("button", { + name: /Mulligan/i + }); + mulliganButton.click(); + const attemptNumberLater = extractDigits(screen.getByText(/(\d+)/)); + expect(attemptNumber + 1).toEqual(attemptNumberLater); + }); + test("Clicking Mulligan twice increases attempts by two", () => { + const attemptNumber: number = + extractDigits(screen.getByText(/(\d+)/)) || 0; + const mulliganButton = screen.getByRole("button", { + name: /Mulligan/i + }); + mulliganButton.click(); + mulliganButton.click(); + const attemptNumberLater = extractDigits(screen.getByText(/(\d+)/)); + expect(attemptNumber + 2).toEqual(attemptNumberLater); + }); + test("Clicking Start Quiz decreases attempts", () => { + const attemptNumber: number = + extractDigits(screen.getByText(/(\d+)/)) || 0; + const startButton = screen.getByRole("button", { + name: /Start Quiz/i + }); + startButton.click(); + const attemptNumberLater = + extractDigits(screen.getByText(/(\d+)/)) || 0; + expect(attemptNumber - 1).toEqual(attemptNumberLater); + }); + test("Clicking Start Quiz changes enabled buttons", () => { + // Given the buttons... + const startButton = screen.getByRole("button", { + name: /Start Quiz/i + }); + const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); + const mulliganButton = screen.getByRole("button", { + name: /Mulligan/i + }); + // When the start button is clicked + startButton.click(); + // Then the start is disabled, stop is enabled, and mulligan is disabled + expect(startButton).toBeDisabled(); + expect(stopButton).toBeEnabled(); + expect(mulliganButton).toBeDisabled(); + }); + test("Clicking Start and Stop Quiz changes enabled buttons", () => { + // Given the buttons and initial attempt number... + const startButton = screen.getByRole("button", { + name: /Start Quiz/i + }); + const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); + const mulliganButton = screen.getByRole("button", { + name: /Mulligan/i + }); + // When we click the start button and then the stop button + startButton.click(); + stopButton.click(); + // Then the start is enabled, stop is disabled, and mulligan is enabled + expect(startButton).toBeEnabled(); + expect(stopButton).toBeDisabled(); + expect(mulliganButton).toBeEnabled(); + }); + test("Clicking Start, Stop, Mulligan sets attempts to original", () => { + // Given the buttons and initial attempt number... + const startButton = screen.getByRole("button", { + name: /Start Quiz/i + }); + const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); + const mulliganButton = screen.getByRole("button", { + name: /Mulligan/i + }); + const attemptNumber: number = + extractDigits(screen.getByText(/(\d+)/)) || 0; + // When we click the start button and then the stop button + startButton.click(); + stopButton.click(); + // Then the attempt is decreased + const attemptNumberLater: number = + extractDigits(screen.getByText(/(\d+)/)) || 0; + expect(attemptNumber - 1).toEqual(attemptNumberLater); + // And when we click the mulligan button + mulliganButton.click(); + // Then the attempt is increased back to starting value + const attemptNumberLatest: number = + extractDigits(screen.getByText(/(\d+)/)) || 0; + expect(attemptNumber).toEqual(attemptNumberLatest); + }); + test("Cannot click start quiz when out of attempts", () => { + // Given the buttons and initial attempt number... + const startButton = screen.getByRole("button", { + name: /Start Quiz/i + }); + const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); + const mulliganButton = screen.getByRole("button", { + name: /Mulligan/i + }); + let attemptNumber = extractDigits(screen.getByText(/(\d+)/)) || 0; + const initialAttempt = attemptNumber; + // Arbitrary number of times to try clicking; assume we do not have more than that number of attempts. + let maxAttempts = 10; + // While there are still attempts apparently available... + while (attemptNumber > 0) { + // Then the buttons + expect(startButton).toBeEnabled(); + expect(stopButton).toBeDisabled(); + expect(mulliganButton).toBeEnabled(); + // And when we Start and then immediately stop the quiz... + startButton.click(); + stopButton.click(); + // Then the number is going down, and doesn't go past 0 somehow + attemptNumber = extractDigits(screen.getByText(/(\d+)/)) || 0; + expect(attemptNumber).toBeGreaterThanOrEqual(0); + expect(attemptNumber).not.toEqual(initialAttempt); + // And then the maximum number of attempts does not exceed 10 + maxAttempts -= 1; + expect(maxAttempts).toBeGreaterThanOrEqual(0); + } + // Then the attempt is at zero + expect(attemptNumber).toEqual(0); + // And then the stop button is disabled, the start button is disabled, and mulligan is enabled + expect(startButton).toBeDisabled(); + expect(stopButton).toBeDisabled(); + expect(mulliganButton).toBeEnabled(); + // And when we click the mulligan button + mulliganButton.click(); + // Then the attempt is increased back to 1 + const attemptNumberLatest: number = + extractDigits(screen.getByText(/(\d+)/)) || 0; + expect(attemptNumberLatest).toEqual(1); + // And the start button is reenabled + expect(startButton).toBeEnabled(); + expect(stopButton).toBeDisabled(); + expect(mulliganButton).toBeEnabled(); + }); +}); diff --git a/src/components/StartAttempt.tsx b/src/components/StartAttempt.tsx index 12786ec0cd..0c0a85fdb6 100644 --- a/src/components/StartAttempt.tsx +++ b/src/components/StartAttempt.tsx @@ -1,4 +1,5 @@ -import React from "react"; +import React, { useState } from "react"; +import { Button } from "react-bootstrap"; export function StartAttempt(): JSX.Element { return
Start Attempt
; diff --git a/src/components/TwoDice.test.tsx b/src/components/TwoDice.test.tsx new file mode 100644 index 0000000000..7996051096 --- /dev/null +++ b/src/components/TwoDice.test.tsx @@ -0,0 +1,140 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { TwoDice } from "./TwoDice"; +import { extractDigits } from "./StartAttempt.test"; + +describe("TwoDice Component tests", () => { + let mathRandomFunction: jest.SpyInstance; + beforeEach(() => { + mathRandomFunction = jest + .spyOn(global.Math, "random") + .mockReturnValue(0.5) // 4 + .mockReturnValueOnce(0.0) // 1 + .mockReturnValueOnce(0.99) // 6 + .mockReturnValueOnce(0.75) // 5 + .mockReturnValueOnce(0.75) // 5 + .mockReturnValueOnce(0.1) // 1 + .mockReturnValueOnce(0.1); // 1 + }); + afterEach(() => { + jest.spyOn(global.Math, "random").mockRestore(); + }); + beforeEach(() => { + render(); + }); + test("There is a `left-die` and `right-die` testid", () => { + const leftDie = screen.getByTestId("left-die"); + const rightDie = screen.getByTestId("right-die"); + expect(leftDie).toBeInTheDocument(); + expect(rightDie).toBeInTheDocument(); + }); + test("The `left-die` and `right-die` are two different numbers", () => { + const leftDie = screen.getByTestId("left-die"); + const rightDie = screen.getByTestId("right-die"); + const leftNumber = extractDigits(leftDie); + const rightNumber = extractDigits(rightDie); + // Then they are two numbers + expect(leftNumber).not.toBeNull(); + expect(rightNumber).not.toBeNull(); + // Then they are two different numbers + expect(leftNumber).not.toEqual(rightNumber); + }); + test("There are two buttons present", () => { + const leftButton = screen.getByRole("button", { name: /Roll Left/i }); + const rightButton = screen.getByRole("button", { name: /Roll Right/i }); + expect(leftButton).toBeInTheDocument(); + expect(rightButton).toBeInTheDocument(); + }); + test("Clicking left button changes first number", () => { + const leftButton = screen.getByRole("button", { name: /Roll Left/i }); + leftButton.click(); + leftButton.click(); + leftButton.click(); + // Then the random function should be called 3 times + expect(mathRandomFunction).toBeCalledTimes(3); + // And the number to be 5 + const leftNumber = extractDigits(screen.getByTestId("left-die")); + expect(leftNumber).toEqual(5); + }); + // Clicking right button changes second number + test("Clicking right button changes second number", () => { + const rightButton = screen.getByRole("button", { name: /Roll Right/i }); + rightButton.click(); + rightButton.click(); + rightButton.click(); + // Then the random function should be called 3 times + expect(mathRandomFunction).toBeCalledTimes(3); + // And the number to be 5 + const rightNumber = extractDigits(screen.getByTestId("right-die")); + expect(rightNumber).toEqual(5); + }); + // Rolling two different numbers does not win or lose the game + test("Rolling two different numbers does not win or lose the game", () => { + // Given + const leftButton = screen.getByRole("button", { name: /Roll Left/i }); + const rightButton = screen.getByRole("button", { name: /Roll Right/i }); + const leftDie = screen.getByTestId("left-die"); + const rightDie = screen.getByTestId("right-die"); + // When the left and right buttons are rolled once each + leftButton.click(); + rightButton.click(); + // Then the numbers are not equal + const leftNumber = extractDigits(leftDie); + const rightNumber = extractDigits(rightDie); + expect(leftNumber).toEqual(1); + expect(rightNumber).toEqual(6); + // And then the game is not won + const winText = screen.queryByText(/Win/i); + expect(winText).toBeNull(); + // And then nor is the game lost + const loseText = screen.queryByText(/Lose/i); + expect(loseText).toBeNull(); + }); + test("Getting snake eyes loses the game", () => { + // Given + const leftButton = screen.getByRole("button", { name: /Roll Left/i }); + const rightButton = screen.getByRole("button", { name: /Roll Right/i }); + const leftDie = screen.getByTestId("left-die"); + const rightDie = screen.getByTestId("right-die"); + // When the left and right buttons are rolled once each + leftButton.click(); + rightButton.click(); + rightButton.click(); + rightButton.click(); + rightButton.click(); + // Then the numbers are not equal + const leftNumber = extractDigits(leftDie); + const rightNumber = extractDigits(rightDie); + expect(leftNumber).toEqual(1); + expect(rightNumber).toEqual(1); + // And then the game is not won + const winText = screen.queryByText(/Win/i); + expect(winText).toBeNull(); + // And then the game is lost + const loseText = screen.getByText(/Lose/i); + expect(loseText).toBeInTheDocument(); + }); + test("Getting matching numbers wins the game", () => { + // Given + const leftButton = screen.getByRole("button", { name: /Roll Left/i }); + const rightButton = screen.getByRole("button", { name: /Roll Right/i }); + const leftDie = screen.getByTestId("left-die"); + const rightDie = screen.getByTestId("right-die"); + // When the left and right buttons are rolled once each + leftButton.click(); + leftButton.click(); + leftButton.click(); + rightButton.click(); + // Then the numbers are not equal + const leftNumber = extractDigits(leftDie); + const rightNumber = extractDigits(rightDie); + expect(leftNumber).toEqual(5); + expect(rightNumber).toEqual(5); + // And then the game is not lost + const loseText = screen.queryByText(/Lose/i); + expect(loseText).toBeNull(); + // And then the game is won + const winText = screen.getByText(/Win/i); + expect(winText).toBeInTheDocument(); + }); +}); diff --git a/src/components/TwoDice.tsx b/src/components/TwoDice.tsx index b9a5260966..372502fe64 100644 --- a/src/components/TwoDice.tsx +++ b/src/components/TwoDice.tsx @@ -1,4 +1,15 @@ -import React from "react"; +import React, { useState } from "react"; +import { Button } from "react-bootstrap"; + +/** + * Here is a helper function you *must* use to "roll" your die. + * The function uses the builtin `random` function of the `Math` + * module (which returns a random decimal between 0 up until 1) in order + * to produce a random integer between 1 and 6 (inclusive). + */ +export function d6(): number { + return 1 + Math.floor(Math.random() * 6); +} export function TwoDice(): JSX.Element { return
Two Dice
; From eb40f3eb8827e668c1949ca0c9c13db2f52fe4b4 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 19 Feb 2022 13:53:22 -0500 Subject: [PATCH 016/104] Forgot task record for state --- public/tasks/task-state.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 public/tasks/task-state.md diff --git a/public/tasks/task-state.md b/public/tasks/task-state.md new file mode 100644 index 0000000000..ef8197ffcb --- /dev/null +++ b/public/tasks/task-state.md @@ -0,0 +1,5 @@ +# Task - State + +Version: 0.0.1 + +Create some new components that have React State. From 6669ffad92d4bd98218097868d381d9280eb9fca Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 19 Feb 2022 15:23:47 -0500 Subject: [PATCH 017/104] First draft of components subtasks --- public/tasks/task-components.md | 5 +++ src/App.tsx | 12 +++++++ src/bad-components/ColoredBox.tsx | 41 ++++++++++++++++++++++ src/bad-components/DoubleHalf.tsx | 21 +++++++++++ src/bad-components/DoubleHalfState.tsx | 3 ++ src/bad-components/ShoveBox.tsx | 48 ++++++++++++++++++++++++++ src/bad-components/TrackingNumbers.tsx | 38 ++++++++++++++++++++ 7 files changed, 168 insertions(+) create mode 100644 public/tasks/task-components.md create mode 100644 src/bad-components/ColoredBox.tsx create mode 100644 src/bad-components/DoubleHalf.tsx create mode 100644 src/bad-components/DoubleHalfState.tsx create mode 100644 src/bad-components/ShoveBox.tsx create mode 100644 src/bad-components/TrackingNumbers.tsx diff --git a/public/tasks/task-components.md b/public/tasks/task-components.md new file mode 100644 index 0000000000..a6ff48694d --- /dev/null +++ b/public/tasks/task-components.md @@ -0,0 +1,5 @@ +# Task - Components + +Version: 0.0.1 + +Fix some components that are using state incorrectly. diff --git a/src/App.tsx b/src/App.tsx index 504138f1c3..98499aa554 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,6 +6,10 @@ import { StartAttempt } from "./components/StartAttempt"; import { TwoDice } from "./components/TwoDice"; import { CycleHoliday } from "./components/CycleHoliday"; import { Counter } from "./components/Counter"; +import { DoubleHalf } from "./bad-components/DoubleHalf"; +import { ColoredBox } from "./bad-components/ColoredBox"; +import { ShoveBox } from "./bad-components/ShoveBox"; +import { TrackingNumbers } from "./bad-components/TrackingNumbers"; function App(): JSX.Element { return ( @@ -14,6 +18,14 @@ function App(): JSX.Element { UD CISC275 with React Hooks and TypeScript
+ {/* */} +
+ +
+ +
+ +

diff --git a/src/bad-components/ColoredBox.tsx b/src/bad-components/ColoredBox.tsx new file mode 100644 index 0000000000..622f77fd31 --- /dev/null +++ b/src/bad-components/ColoredBox.tsx @@ -0,0 +1,41 @@ +import React, { useState } from "react"; +import { Button } from "react-bootstrap"; + +export const COLORS = ["red", "blue", "green"]; +const DEFAULT_COLOR_INDEX = 0; + +function ChangeColor(): JSX.Element { + const [colorIndex, setColorIndex] = useState(DEFAULT_COLOR_INDEX); + return ( + + ); +} + +function ColorPreview(): JSX.Element { + return ( +
+ ); +} + +export function ColoredBox(): JSX.Element { + return ( +
+ The current color is: {COLORS[DEFAULT_COLOR_INDEX]} +
+ + +
+
+ ); +} diff --git a/src/bad-components/DoubleHalf.tsx b/src/bad-components/DoubleHalf.tsx new file mode 100644 index 0000000000..a8376dbea2 --- /dev/null +++ b/src/bad-components/DoubleHalf.tsx @@ -0,0 +1,21 @@ +import React, { useState } from "react"; +import { Button } from "react-bootstrap"; +import { dhValue, setDhValue } from "./DoubleHalfState"; + +function Doubler(): JSX.Element { + return ; +} + +function Halver(): JSX.Element { + return ; +} + +export function DoubleHalf(): JSX.Element { + return ( +
+ The current value is: {dhValue} + + +
+ ); +} diff --git a/src/bad-components/DoubleHalfState.tsx b/src/bad-components/DoubleHalfState.tsx new file mode 100644 index 0000000000..2b4569a37a --- /dev/null +++ b/src/bad-components/DoubleHalfState.tsx @@ -0,0 +1,3 @@ +import { useState } from "react"; + +export const [dhValue, setDhValue] = useState(10); diff --git a/src/bad-components/ShoveBox.tsx b/src/bad-components/ShoveBox.tsx new file mode 100644 index 0000000000..d0b4ec0561 --- /dev/null +++ b/src/bad-components/ShoveBox.tsx @@ -0,0 +1,48 @@ +import React, { useState } from "react"; +import { Button } from "react-bootstrap"; + +function ShoveBoxButton({ + position, + setPosition +}: { + position: number; + setPosition: (newPosition: number) => void; +}) { + return ( + + ); +} + +function MoveableBox(): JSX.Element { + const [position, setPosition] = useState(10); + return ( +
+ ); +} + +export function ShoveBox(): JSX.Element { + const box = MoveableBox(); + + return ( +
+ {/* The box is at: {box.position} +
+ + {box} +
*/} +
+ ); +} diff --git a/src/bad-components/TrackingNumbers.tsx b/src/bad-components/TrackingNumbers.tsx new file mode 100644 index 0000000000..910e273ed1 --- /dev/null +++ b/src/bad-components/TrackingNumbers.tsx @@ -0,0 +1,38 @@ +import React, { useState } from "react"; +import { Button } from "react-bootstrap"; + +export function TrackingNumbers(): JSX.Element { + const [latestNumber, setLatestNumber] = useState(0); + const [trackedNumbers, setTrackedNumbers] = useState([]); + + function stopTracking(targetNumber: number) { + const withoutNumber = trackedNumbers.filter( + (aNumber: number) => aNumber !== targetNumber + ); + setTrackedNumbers(withoutNumber); + } + + function trackNumber() { + setLatestNumber(1 + latestNumber); + const withNumber = [...trackedNumbers, latestNumber]; + setTrackedNumbers(withNumber); + } + + // Uncomment the below and fix the error! + return ( +
+ {/*
    + {trackedNumbers.map((trackedNumber: number) => ( +
  1. + Tracking {trackedNumber} ( + + ) +
  2. + ))} +
*/} + +
+ ); +} From 562f3067fd1fce48bd3ed2cb4cb6bfe1e24d65f5 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 24 Feb 2022 10:48:53 -0500 Subject: [PATCH 018/104] Another subtask, ChooseTeam --- src/App.tsx | 3 ++ src/bad-components/ChooseTeam.tsx | 54 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/bad-components/ChooseTeam.tsx diff --git a/src/App.tsx b/src/App.tsx index 98499aa554..d4ec176137 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,7 @@ import { DoubleHalf } from "./bad-components/DoubleHalf"; import { ColoredBox } from "./bad-components/ColoredBox"; import { ShoveBox } from "./bad-components/ShoveBox"; import { TrackingNumbers } from "./bad-components/TrackingNumbers"; +import { ChooseTeam } from "./bad-components/ChooseTeam"; function App(): JSX.Element { return ( @@ -20,6 +21,8 @@ function App(): JSX.Element {
{/* */}
+ +

diff --git a/src/bad-components/ChooseTeam.tsx b/src/bad-components/ChooseTeam.tsx new file mode 100644 index 0000000000..f2c3cd49f0 --- /dev/null +++ b/src/bad-components/ChooseTeam.tsx @@ -0,0 +1,54 @@ +import React, { useState } from "react"; +import { Button, Row, Col } from "react-bootstrap"; + +const PEOPLE = [ + "Alan Turing", + "Grace Hopper", + "Ada Lovelace", + "Charles Babbage", + "Barbara Liskov", + "Margaret Hamilton" +]; + +export function ChooseTeam(): JSX.Element { + const [allOptions, setAllOptions] = useState(PEOPLE); + const [team, setTeam] = useState([]); + + function chooseMember() { + /* + if (!team.includes(newMember)) { + team.push(newMember); + } + */ + } + + function clearTeam() { + /* + team = []; + */ + } + + return ( +
+ + + {allOptions.map((option: string) => ( +
+ Add{" "} + +
+ ))} + + + Team: + {team.map((member: string) => ( +
  • {member}
  • + ))} + + +
    +
    + ); +} From 4a34f5fa59524db440c37c83f2edcbb1b640096c Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 24 Feb 2022 11:33:41 -0500 Subject: [PATCH 019/104] Oops order out of operations --- src/bad-components/ColoredBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bad-components/ColoredBox.tsx b/src/bad-components/ColoredBox.tsx index 622f77fd31..5e2a9f7ac8 100644 --- a/src/bad-components/ColoredBox.tsx +++ b/src/bad-components/ColoredBox.tsx @@ -7,7 +7,7 @@ const DEFAULT_COLOR_INDEX = 0; function ChangeColor(): JSX.Element { const [colorIndex, setColorIndex] = useState(DEFAULT_COLOR_INDEX); return ( - ); From 7327f4c2f93d210353eba03e002770d1d6503174 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 24 Feb 2022 11:41:03 -0500 Subject: [PATCH 020/104] Add headers to each subtask --- src/bad-components/ChooseTeam.tsx | 1 + src/bad-components/ColoredBox.tsx | 1 + src/bad-components/DoubleHalf.tsx | 1 + src/bad-components/ShoveBox.tsx | 1 + src/bad-components/TrackingNumbers.tsx | 1 + 5 files changed, 5 insertions(+) diff --git a/src/bad-components/ChooseTeam.tsx b/src/bad-components/ChooseTeam.tsx index f2c3cd49f0..c02334e661 100644 --- a/src/bad-components/ChooseTeam.tsx +++ b/src/bad-components/ChooseTeam.tsx @@ -30,6 +30,7 @@ export function ChooseTeam(): JSX.Element { return (
    +

    Choose Team

    {allOptions.map((option: string) => ( diff --git a/src/bad-components/ColoredBox.tsx b/src/bad-components/ColoredBox.tsx index 5e2a9f7ac8..0a5bb83d96 100644 --- a/src/bad-components/ColoredBox.tsx +++ b/src/bad-components/ColoredBox.tsx @@ -31,6 +31,7 @@ function ColorPreview(): JSX.Element { export function ColoredBox(): JSX.Element { return (
    +

    Colored Box

    The current color is: {COLORS[DEFAULT_COLOR_INDEX]}
    diff --git a/src/bad-components/DoubleHalf.tsx b/src/bad-components/DoubleHalf.tsx index a8376dbea2..f3871eeec6 100644 --- a/src/bad-components/DoubleHalf.tsx +++ b/src/bad-components/DoubleHalf.tsx @@ -13,6 +13,7 @@ function Halver(): JSX.Element { export function DoubleHalf(): JSX.Element { return (
    +

    Double Half

    The current value is: {dhValue} diff --git a/src/bad-components/ShoveBox.tsx b/src/bad-components/ShoveBox.tsx index d0b4ec0561..910aca490b 100644 --- a/src/bad-components/ShoveBox.tsx +++ b/src/bad-components/ShoveBox.tsx @@ -35,6 +35,7 @@ export function ShoveBox(): JSX.Element { return (
    +

    Shove Box

    {/* The box is at: {box.position}
    +

    Tracking Numbers

    {/*
      {trackedNumbers.map((trackedNumber: number) => (
    1. From cf7c212a6631e9ef7c9f9c4b4dfd5d3cac72acb5 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 24 Feb 2022 13:00:45 -0500 Subject: [PATCH 021/104] Make testing easier for these components --- src/bad-components/ColoredBox.tsx | 1 + src/bad-components/DoubleHalf.tsx | 4 +++- src/bad-components/ShoveBox.tsx | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bad-components/ColoredBox.tsx b/src/bad-components/ColoredBox.tsx index 0a5bb83d96..a3c3c3f077 100644 --- a/src/bad-components/ColoredBox.tsx +++ b/src/bad-components/ColoredBox.tsx @@ -16,6 +16,7 @@ function ChangeColor(): JSX.Element { function ColorPreview(): JSX.Element { return (

      Double Half

      - The current value is: {dhValue} +
      + The current value is: {dhValue} +
      diff --git a/src/bad-components/ShoveBox.tsx b/src/bad-components/ShoveBox.tsx index 910aca490b..7c55142636 100644 --- a/src/bad-components/ShoveBox.tsx +++ b/src/bad-components/ShoveBox.tsx @@ -17,6 +17,7 @@ function MoveableBox(): JSX.Element { const [position, setPosition] = useState(10); return (
      Date: Thu, 24 Feb 2022 13:01:04 -0500 Subject: [PATCH 022/104] Ugh this component is stupid, let's just forget about it for now --- src/App.tsx | 3 -- src/bad-components/TrackingNumbers.tsx | 39 -------------------------- 2 files changed, 42 deletions(-) delete mode 100644 src/bad-components/TrackingNumbers.tsx diff --git a/src/App.tsx b/src/App.tsx index d4ec176137..e09c26ec76 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,7 +9,6 @@ import { Counter } from "./components/Counter"; import { DoubleHalf } from "./bad-components/DoubleHalf"; import { ColoredBox } from "./bad-components/ColoredBox"; import { ShoveBox } from "./bad-components/ShoveBox"; -import { TrackingNumbers } from "./bad-components/TrackingNumbers"; import { ChooseTeam } from "./bad-components/ChooseTeam"; function App(): JSX.Element { @@ -27,8 +26,6 @@ function App(): JSX.Element {

      - -

      diff --git a/src/bad-components/TrackingNumbers.tsx b/src/bad-components/TrackingNumbers.tsx deleted file mode 100644 index 70a9a7c7de..0000000000 --- a/src/bad-components/TrackingNumbers.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useState } from "react"; -import { Button } from "react-bootstrap"; - -export function TrackingNumbers(): JSX.Element { - const [latestNumber, setLatestNumber] = useState(0); - const [trackedNumbers, setTrackedNumbers] = useState([]); - - function stopTracking(targetNumber: number) { - const withoutNumber = trackedNumbers.filter( - (aNumber: number) => aNumber !== targetNumber - ); - setTrackedNumbers(withoutNumber); - } - - function trackNumber() { - setLatestNumber(1 + latestNumber); - const withNumber = [...trackedNumbers, latestNumber]; - setTrackedNumbers(withNumber); - } - - // Uncomment the below and fix the error! - return ( -
      -

      Tracking Numbers

      - {/*
        - {trackedNumbers.map((trackedNumber: number) => ( -
      1. - Tracking {trackedNumber} ( - - ) -
      2. - ))} -
      */} - -
      - ); -} From 89053a45855b254fb0363d8f002ac5535524e77f Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 24 Feb 2022 13:03:58 -0500 Subject: [PATCH 023/104] Provide the tests for the bad components --- src/bad-components/ChooseTeam.test.tsx | 61 ++++++++++++++++++++++++++ src/bad-components/ColoredBox.test.tsx | 31 +++++++++++++ src/bad-components/DoubleHalf.test.tsx | 56 +++++++++++++++++++++++ src/bad-components/ShoveBox.test.tsx | 31 +++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 src/bad-components/ChooseTeam.test.tsx create mode 100644 src/bad-components/ColoredBox.test.tsx create mode 100644 src/bad-components/DoubleHalf.test.tsx create mode 100644 src/bad-components/ShoveBox.test.tsx diff --git a/src/bad-components/ChooseTeam.test.tsx b/src/bad-components/ChooseTeam.test.tsx new file mode 100644 index 0000000000..f11465a2d6 --- /dev/null +++ b/src/bad-components/ChooseTeam.test.tsx @@ -0,0 +1,61 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { ChooseTeam } from "./ChooseTeam"; + +describe("ChooseTeam Component tests", () => { + beforeEach(() => { + render(); + }); + test("The initial team is empty", () => { + const currentTeam = screen.queryAllByRole("listitem"); + expect(currentTeam).toHaveLength(0); + }); + test("There are 7 buttons.", () => { + const adders = screen.queryAllByRole("button"); + expect(adders).toHaveLength(7); + }); + test("Clicking first team member works", () => { + const first = screen.queryAllByRole("button")[0]; + first.click(); + const currentTeam = screen.queryAllByRole("listitem"); + expect(currentTeam).toHaveLength(1); + expect(currentTeam[0].textContent).toEqual(first.textContent); + }); + test("Clicking the third team member works", () => { + const third = screen.queryAllByRole("button")[2]; + third.click(); + const currentTeam = screen.queryAllByRole("listitem"); + expect(currentTeam).toHaveLength(1); + expect(currentTeam[0].textContent).toEqual(third.textContent); + }); + test("Clicking three team members works", () => { + const [, second, third, , fifth] = screen.queryAllByRole("button"); + third.click(); + second.click(); + fifth.click(); + const currentTeam = screen.queryAllByRole("listitem"); + expect(currentTeam).toHaveLength(3); + expect(currentTeam[0].textContent).toEqual(third.textContent); + expect(currentTeam[1].textContent).toEqual(second.textContent); + expect(currentTeam[2].textContent).toEqual(fifth.textContent); + }); + test("Clearing the team works", () => { + const [, second, third, fourth, fifth, , clear] = + screen.queryAllByRole("button"); + third.click(); + second.click(); + fifth.click(); + let currentTeam = screen.queryAllByRole("listitem"); + expect(currentTeam).toHaveLength(3); + expect(currentTeam[0].textContent).toEqual(third.textContent); + expect(currentTeam[1].textContent).toEqual(second.textContent); + expect(currentTeam[2].textContent).toEqual(fifth.textContent); + clear.click(); + currentTeam = screen.queryAllByRole("listitem"); + expect(currentTeam).toHaveLength(0); + fourth.click(); + currentTeam = screen.queryAllByRole("listitem"); + expect(currentTeam).toHaveLength(1); + expect(currentTeam[0].textContent).toEqual(fourth.textContent); + }); +}); diff --git a/src/bad-components/ColoredBox.test.tsx b/src/bad-components/ColoredBox.test.tsx new file mode 100644 index 0000000000..c17a13f66c --- /dev/null +++ b/src/bad-components/ColoredBox.test.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { ColoredBox } from "./ColoredBox"; + +describe("ColoredBox Component tests", () => { + beforeEach(() => { + render(); + }); + test("The ColoredBox is initially red.", () => { + const box = screen.getByTestId("colored-box"); + expect(box).toHaveStyle({ backgroundColor: "red" }); + }); + test("There is a button", () => { + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + test("Clicking the button advances the color.", () => { + const nextColor = screen.getByRole("button"); + nextColor.click(); + expect(screen.getByTestId("colored-box")).toHaveStyle({ + backgroundColor: "blue" + }); + nextColor.click(); + expect(screen.getByTestId("colored-box")).toHaveStyle({ + backgroundColor: "green" + }); + nextColor.click(); + expect(screen.getByTestId("colored-box")).toHaveStyle({ + backgroundColor: "red" + }); + }); +}); diff --git a/src/bad-components/DoubleHalf.test.tsx b/src/bad-components/DoubleHalf.test.tsx new file mode 100644 index 0000000000..cbae5f68af --- /dev/null +++ b/src/bad-components/DoubleHalf.test.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { DoubleHalf } from "./DoubleHalf"; + +describe("DoubleHalf Component tests", () => { + beforeEach(() => { + render(); + }); + test("The DoubleHalf value is initially 10", () => { + expect(screen.getByText("10")).toBeInTheDocument(); + expect(screen.queryByText("20")).not.toBeInTheDocument(); + expect(screen.queryByText("5")).not.toBeInTheDocument(); + }); + test("There are Double and Halve buttons", () => { + expect( + screen.getByRole("button", { name: /Double/i }) + ).toBeInTheDocument(); + expect( + screen.getByRole("button", { name: /Halve/i }) + ).toBeInTheDocument(); + }); + test("You can double the number.", () => { + const double = screen.getByRole("button", { name: /Double/i }); + double.click(); + expect(screen.getByText("20")).toBeInTheDocument(); + expect(screen.queryByText("10")).not.toBeInTheDocument(); + }); + test("You can halve the number.", () => { + const halve = screen.getByRole("button", { name: /Halve/i }); + halve.click(); + expect(screen.getByText("5")).toBeInTheDocument(); + expect(screen.queryByText("10")).not.toBeInTheDocument(); + }); + test("You can double AND halve the number.", () => { + const double = screen.getByRole("button", { name: /Double/i }); + const halve = screen.getByRole("button", { name: /Halve/i }); + double.click(); + expect(screen.getByText("20")).toBeInTheDocument(); + expect(screen.queryByText("10")).not.toBeInTheDocument(); + double.click(); + expect(screen.getByText("40")).toBeInTheDocument(); + expect(screen.queryByText("20")).not.toBeInTheDocument(); + halve.click(); + expect(screen.getByText("20")).toBeInTheDocument(); + expect(screen.queryByText("10")).not.toBeInTheDocument(); + halve.click(); + expect(screen.getByText("10")).toBeInTheDocument(); + expect(screen.queryByText("20")).not.toBeInTheDocument(); + halve.click(); + expect(screen.getByText("5")).toBeInTheDocument(); + expect(screen.queryByText("10")).not.toBeInTheDocument(); + halve.click(); + expect(screen.getByText("2.5")).toBeInTheDocument(); + expect(screen.queryByText("5")).not.toBeInTheDocument(); + }); +}); diff --git a/src/bad-components/ShoveBox.test.tsx b/src/bad-components/ShoveBox.test.tsx new file mode 100644 index 0000000000..2adec13d4e --- /dev/null +++ b/src/bad-components/ShoveBox.test.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { ShoveBox } from "./ShoveBox"; + +describe("ShoveBox Component tests", () => { + beforeEach(() => { + render(); + }); + test("The MoveableBox is initially nearby.", () => { + const box = screen.getByTestId("moveable-box"); + expect(box).toHaveStyle({ marginLeft: "10px" }); + }); + test("There is a button", () => { + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + test("Clicking the button moves the box.", () => { + const shoveBox = screen.getByRole("button"); + shoveBox.click(); + expect(screen.getByTestId("moveable-box")).toHaveStyle({ + marginLeft: "14px" + }); + shoveBox.click(); + expect(screen.getByTestId("moveable-box")).toHaveStyle({ + marginLeft: "18px" + }); + shoveBox.click(); + expect(screen.getByTestId("moveable-box")).toHaveStyle({ + marginLeft: "22px" + }); + }); +}); From 41387d36384e8b2a3551fd8772d9f7928ed1275d Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Fri, 25 Feb 2022 17:07:48 -0500 Subject: [PATCH 024/104] First stab at form task components --- src/App.tsx | 15 +++++++++++++++ src/form-components/AddQuestion.tsx | 9 +++++++++ src/form-components/CheckAnswer.tsx | 9 +++++++++ src/form-components/EditMode.tsx | 9 +++++++++ src/form-components/GiveAttempts.tsx | 9 +++++++++ src/form-components/MultipleChoiceQuestion.tsx | 9 +++++++++ 6 files changed, 60 insertions(+) create mode 100644 src/form-components/AddQuestion.tsx create mode 100644 src/form-components/CheckAnswer.tsx create mode 100644 src/form-components/EditMode.tsx create mode 100644 src/form-components/GiveAttempts.tsx create mode 100644 src/form-components/MultipleChoiceQuestion.tsx diff --git a/src/App.tsx b/src/App.tsx index e09c26ec76..84c54a446a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,11 @@ import { DoubleHalf } from "./bad-components/DoubleHalf"; import { ColoredBox } from "./bad-components/ColoredBox"; import { ShoveBox } from "./bad-components/ShoveBox"; import { ChooseTeam } from "./bad-components/ChooseTeam"; +import { CheckAnswer } from "./form-components/CheckAnswer"; +import { GiveAttempts } from "./form-components/GiveAttempts"; +import { EditMode } from "./form-components/EditMode"; +import { MultipleChoiceQuestion } from "./form-components/MultipleChoiceQuestion"; +import { AddQuestion } from "./form-components/AddQuestion"; function App(): JSX.Element { return ( @@ -18,6 +23,16 @@ function App(): JSX.Element { UD CISC275 with React Hooks and TypeScript
      + +
      + +
      + +
      + +
      + +
      {/* */}
      diff --git a/src/form-components/AddQuestion.tsx b/src/form-components/AddQuestion.tsx new file mode 100644 index 0000000000..24210d9a1b --- /dev/null +++ b/src/form-components/AddQuestion.tsx @@ -0,0 +1,9 @@ +import React, { useState } from "react"; + +export function AddQuestion(): JSX.Element { + return ( +
      +

      Add Question

      +
      + ); +} diff --git a/src/form-components/CheckAnswer.tsx b/src/form-components/CheckAnswer.tsx new file mode 100644 index 0000000000..7127833cad --- /dev/null +++ b/src/form-components/CheckAnswer.tsx @@ -0,0 +1,9 @@ +import React, { useState } from "react"; + +export function CheckAnswer(): JSX.Element { + return ( +
      +

      Check Answer

      +
      + ); +} diff --git a/src/form-components/EditMode.tsx b/src/form-components/EditMode.tsx new file mode 100644 index 0000000000..fac8734531 --- /dev/null +++ b/src/form-components/EditMode.tsx @@ -0,0 +1,9 @@ +import React, { useState } from "react"; + +export function EditMode(): JSX.Element { + return ( +
      +

      Edit Mode

      +
      + ); +} diff --git a/src/form-components/GiveAttempts.tsx b/src/form-components/GiveAttempts.tsx new file mode 100644 index 0000000000..2ca61863fc --- /dev/null +++ b/src/form-components/GiveAttempts.tsx @@ -0,0 +1,9 @@ +import React, { useState } from "react"; + +export function GiveAttempts(): JSX.Element { + return ( +
      +

      Give Attempts

      +
      + ); +} diff --git a/src/form-components/MultipleChoiceQuestion.tsx b/src/form-components/MultipleChoiceQuestion.tsx new file mode 100644 index 0000000000..80a482d063 --- /dev/null +++ b/src/form-components/MultipleChoiceQuestion.tsx @@ -0,0 +1,9 @@ +import React, { useState } from "react"; + +export function MultipleChoiceQuestion(): JSX.Element { + return ( +
      +

      Multiple Choice Question

      +
      + ); +} From 1b03faf6c20bb63ee4a6a282772fb0529ac1b0fa Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 27 Feb 2022 14:26:41 -0500 Subject: [PATCH 025/104] Provide tests, change addQuestion to changeColor --- src/App.tsx | 11 ++- src/form-components/ChangeColor.test.tsx | 35 ++++++++ .../{AddQuestion.tsx => ChangeColor.tsx} | 4 +- src/form-components/CheckAnswer.test.tsx | 45 +++++++++++ src/form-components/CheckAnswer.tsx | 6 +- src/form-components/EditMode.test.tsx | 48 +++++++++++ src/form-components/GiveAttempts.test.tsx | 46 +++++++++++ .../MultipleChoiceQuestion.test.tsx | 79 +++++++++++++++++++ .../MultipleChoiceQuestion.tsx | 8 +- 9 files changed, 274 insertions(+), 8 deletions(-) create mode 100644 src/form-components/ChangeColor.test.tsx rename src/form-components/{AddQuestion.tsx => ChangeColor.tsx} (54%) create mode 100644 src/form-components/CheckAnswer.test.tsx create mode 100644 src/form-components/EditMode.test.tsx create mode 100644 src/form-components/GiveAttempts.test.tsx create mode 100644 src/form-components/MultipleChoiceQuestion.test.tsx diff --git a/src/App.tsx b/src/App.tsx index 84c54a446a..097f7e9ed2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,7 +14,7 @@ import { CheckAnswer } from "./form-components/CheckAnswer"; import { GiveAttempts } from "./form-components/GiveAttempts"; import { EditMode } from "./form-components/EditMode"; import { MultipleChoiceQuestion } from "./form-components/MultipleChoiceQuestion"; -import { AddQuestion } from "./form-components/AddQuestion"; +import { ChangeColor } from "./form-components/ChangeColor"; function App(): JSX.Element { return ( @@ -23,15 +23,18 @@ function App(): JSX.Element { UD CISC275 with React Hooks and TypeScript
      - +


      - +
      - +
      {/* */}
      diff --git a/src/form-components/ChangeColor.test.tsx b/src/form-components/ChangeColor.test.tsx new file mode 100644 index 0000000000..d74ba37243 --- /dev/null +++ b/src/form-components/ChangeColor.test.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { ChangeColor } from "./ChangeColor"; + +describe("ChangeColor Component tests", () => { + beforeEach(() => render()); + test("There are at least 8 radio buttons and the colored box", () => { + const radios = screen.getAllByRole("radio"); + expect(radios.length).toBeGreaterThanOrEqual(8); + expect(screen.getByTestId("colored-box")).toBeInTheDocument(); + }); + test("Switching the color switches the displayed color.", () => { + const radios: HTMLInputElement[] = screen.getAllByRole("radio"); + // Switch to first + radios[0].click(); + let coloredBox = screen.getByTestId("colored-box"); + expect(coloredBox).toHaveTextContent(radios[0].value); + expect(coloredBox).toHaveStyle({ backgroundColor: radios[0].value }); + // Switch to third + radios[2].click(); + coloredBox = screen.getByTestId("colored-box"); + expect(coloredBox).toHaveTextContent(radios[2].value); + expect(coloredBox).toHaveStyle({ backgroundColor: radios[2].value }); + // Switch to 8th + radios[7].click(); + coloredBox = screen.getByTestId("colored-box"); + expect(coloredBox).toHaveTextContent(radios[7].value); + expect(coloredBox).toHaveStyle({ backgroundColor: radios[7].value }); + // Switch back to first + radios[0].click(); + coloredBox = screen.getByTestId("colored-box"); + expect(coloredBox).toHaveTextContent(radios[0].value); + expect(coloredBox).toHaveStyle({ backgroundColor: radios[0].value }); + }); +}); diff --git a/src/form-components/AddQuestion.tsx b/src/form-components/ChangeColor.tsx similarity index 54% rename from src/form-components/AddQuestion.tsx rename to src/form-components/ChangeColor.tsx index 24210d9a1b..bcb584fcf9 100644 --- a/src/form-components/AddQuestion.tsx +++ b/src/form-components/ChangeColor.tsx @@ -1,9 +1,9 @@ import React, { useState } from "react"; -export function AddQuestion(): JSX.Element { +export function ChangeColor(): JSX.Element { return (
      -

      Add Question

      +

      Change Color

      ); } diff --git a/src/form-components/CheckAnswer.test.tsx b/src/form-components/CheckAnswer.test.tsx new file mode 100644 index 0000000000..076ab209a7 --- /dev/null +++ b/src/form-components/CheckAnswer.test.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { CheckAnswer } from "./CheckAnswer"; +import userEvent from "@testing-library/user-event"; + +describe("CheckAnswer Component tests", () => { + test("There is an input box", () => { + render(); + const inputBox = screen.getByRole("textbox"); + expect(inputBox).toBeInTheDocument(); + }); + test("The answer is originally incorrect.", () => { + render(); + expect(screen.getByText(/❌/i)).toBeInTheDocument(); + expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); + }); + test("Entering the right answer makes it correct.", () => { + render(); + const inputBox = screen.getByRole("textbox"); + userEvent.type(inputBox, "42"); + expect(screen.getByText(/✔️/i)).toBeInTheDocument(); + expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); + }); + test("Entering the wrong answer makes it incorrect.", () => { + render(); + const inputBox = screen.getByRole("textbox"); + userEvent.type(inputBox, "43"); + expect(screen.getByText(/❌/i)).toBeInTheDocument(); + expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); + }); + test("Entering a different right answer makes it correct.", () => { + render(); + const inputBox = screen.getByRole("textbox"); + userEvent.type(inputBox, "Hello"); + expect(screen.getByText(/✔️/i)).toBeInTheDocument(); + expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); + }); + test("Entering a different wrong answer still makes it incorrect.", () => { + render(); + const inputBox = screen.getByRole("textbox"); + userEvent.type(inputBox, "42"); + expect(screen.getByText(/❌/i)).toBeInTheDocument(); + expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); + }); +}); diff --git a/src/form-components/CheckAnswer.tsx b/src/form-components/CheckAnswer.tsx index 7127833cad..afb3dbf8a4 100644 --- a/src/form-components/CheckAnswer.tsx +++ b/src/form-components/CheckAnswer.tsx @@ -1,6 +1,10 @@ import React, { useState } from "react"; -export function CheckAnswer(): JSX.Element { +export function CheckAnswer({ + expectedAnswer +}: { + expectedAnswer: string; +}): JSX.Element { return (

      Check Answer

      diff --git a/src/form-components/EditMode.test.tsx b/src/form-components/EditMode.test.tsx new file mode 100644 index 0000000000..b2f2a43a36 --- /dev/null +++ b/src/form-components/EditMode.test.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { EditMode } from "./EditMode"; +import userEvent from "@testing-library/user-event"; + +describe("EditMode Component tests", () => { + beforeEach(() => render()); + test("There is one checkbox and no textboxes", () => { + const switchButton = screen.getByRole("checkbox"); + expect(switchButton).toBeInTheDocument(); + expect(switchButton.parentElement).toHaveClass("form-switch"); + expect(screen.queryByRole("textbox")).not.toBeInTheDocument(); + }); + test("Initial text should be 'Your Name is a student'.", () => { + expect(screen.getByText(/Your Name is a student/i)).toBeInTheDocument(); + }); + test("Can switch into Edit Mode", () => { + const switchButton = screen.getByRole("checkbox"); + switchButton.click(); + expect(screen.getByRole("textbox")).toBeInTheDocument(); + expect(screen.getAllByRole("checkbox")).toHaveLength(2); + }); + test("Editing the name and student status changes the text", () => { + const switchButton = screen.getByRole("checkbox"); + switchButton.click(); + const nameBox = screen.getByRole("textbox"); + userEvent.type(nameBox, "Ada Lovelace"); + const studentBox = screen.getByRole("checkbox", { name: /student/i }); + studentBox.click(); + switchButton.click(); + expect( + screen.getByText(/Ada Lovelace is not a student/i) + ).toBeInTheDocument(); + }); + test("Different name, click student box twice changes the text", () => { + const switchButton = screen.getByRole("checkbox"); + switchButton.click(); + const nameBox = screen.getByRole("textbox"); + userEvent.type(nameBox, "Alan Turing"); + const studentBox = screen.getByRole("checkbox", { name: /student/i }); + studentBox.click(); + studentBox.click(); + switchButton.click(); + expect( + screen.getByText(/Alan Turing is a student/i) + ).toBeInTheDocument(); + }); +}); diff --git a/src/form-components/GiveAttempts.test.tsx b/src/form-components/GiveAttempts.test.tsx new file mode 100644 index 0000000000..df6f218a20 --- /dev/null +++ b/src/form-components/GiveAttempts.test.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { GiveAttempts } from "./GiveAttempts"; +import userEvent from "@testing-library/user-event"; + +describe("GiveAttempts Component tests", () => { + beforeEach(() => { + render(); + }); + + test("There is a number entry box and two buttons", () => { + expect(screen.getByRole("spinbutton")).toBeInTheDocument(); + expect(screen.getAllByRole("button")).toHaveLength(2); + }); + + test("There is are initially 3 attempts", () => { + expect(screen.getByText(/3/i)).toBeInTheDocument(); + }); + + test("You can use attempts", () => { + const use = screen.getByRole("button", { name: /use/i }); + use.click(); + expect(screen.getByText(/2/i)).toBeInTheDocument(); + use.click(); + use.click(); + expect(screen.getByText(/0/i)).toBeInTheDocument(); + expect(use).toBeDisabled(); + }); + test("You can gain arbitrary attempts", () => { + const gain = screen.getByRole("button", { name: /gain/i }); + const amountToGive = screen.getByRole("spinbutton"); + userEvent.type(amountToGive, "10"); + gain.click(); + expect(screen.getByText(/13/i)).toBeInTheDocument(); + userEvent.type(amountToGive, "100"); + gain.click(); + expect(screen.getByText(/113/i)).toBeInTheDocument(); + }); + test("Cannot gain blank amounts", () => { + const gain = screen.getByRole("button", { name: /gain/i }); + const amountToGive = screen.getByRole("spinbutton"); + userEvent.type(amountToGive, ""); + gain.click(); + expect(screen.getByText(/3/i)).toBeInTheDocument(); + }); +}); diff --git a/src/form-components/MultipleChoiceQuestion.test.tsx b/src/form-components/MultipleChoiceQuestion.test.tsx new file mode 100644 index 0000000000..03a520a818 --- /dev/null +++ b/src/form-components/MultipleChoiceQuestion.test.tsx @@ -0,0 +1,79 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { MultipleChoiceQuestion } from "./MultipleChoiceQuestion"; +import userEvent from "@testing-library/user-event"; + +describe("MultipleChoiceQuestion Component tests", () => { + test("There is a select box", () => { + render( + + ); + expect(screen.getByRole("combobox")).toBeInTheDocument(); + }); + test("The answer is initially incorrect", () => { + render( + + ); + expect(screen.getByText(/❌/i)).toBeInTheDocument(); + expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); + }); + test("Can choose the correct answer", () => { + render( + + ); + const select = screen.getByRole("combobox"); + userEvent.selectOptions(select, "2"); + expect(screen.getByText(/✔️/i)).toBeInTheDocument(); + expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); + }); + test("Can choose the correct answer and then incorrect", () => { + render( + + ); + const select = screen.getByRole("combobox"); + userEvent.selectOptions(select, "2"); + expect(screen.getByText(/✔️/i)).toBeInTheDocument(); + expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); + userEvent.selectOptions(select, "3"); + expect(screen.getByText(/❌/i)).toBeInTheDocument(); + expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); + }); + test("Can start off initially correct", () => { + render( + + ); + const select = screen.getByRole("combobox"); + userEvent.selectOptions(select, "Alpha"); + expect(screen.getByText(/✔️/i)).toBeInTheDocument(); + expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); + }); + test("One more test of choosing the right answer", () => { + render( + + ); + expect(screen.getByText(/❌/i)).toBeInTheDocument(); + expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); + const select = screen.getByRole("combobox"); + userEvent.selectOptions(select, "World"); + expect(screen.getByText(/✔️/i)).toBeInTheDocument(); + expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); + }); +}); diff --git a/src/form-components/MultipleChoiceQuestion.tsx b/src/form-components/MultipleChoiceQuestion.tsx index 80a482d063..a84759862f 100644 --- a/src/form-components/MultipleChoiceQuestion.tsx +++ b/src/form-components/MultipleChoiceQuestion.tsx @@ -1,6 +1,12 @@ import React, { useState } from "react"; -export function MultipleChoiceQuestion(): JSX.Element { +export function MultipleChoiceQuestion({ + options, + expectedAnswer +}: { + options: string[]; + expectedAnswer: string; +}): JSX.Element { return (

      Multiple Choice Question

      From 43b41ec5873213a8fd0ec104cce219ebc29d2aa5 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 9 Mar 2022 20:21:59 -0500 Subject: [PATCH 026/104] Fix entering blank text for GiveAttempts --- src/form-components/GiveAttempts.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/form-components/GiveAttempts.test.tsx b/src/form-components/GiveAttempts.test.tsx index df6f218a20..eb1c3e4a45 100644 --- a/src/form-components/GiveAttempts.test.tsx +++ b/src/form-components/GiveAttempts.test.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { render, screen } from "@testing-library/react"; +import { fireEvent, render, screen } from "@testing-library/react"; import { GiveAttempts } from "./GiveAttempts"; import userEvent from "@testing-library/user-event"; @@ -39,7 +39,7 @@ describe("GiveAttempts Component tests", () => { test("Cannot gain blank amounts", () => { const gain = screen.getByRole("button", { name: /gain/i }); const amountToGive = screen.getByRole("spinbutton"); - userEvent.type(amountToGive, ""); + fireEvent.change(amountToGive, { target: { value: "" } }); gain.click(); expect(screen.getByText(/3/i)).toBeInTheDocument(); }); From 7a207345d9e404afd04607811b89bb758de02905 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 12:52:12 -0400 Subject: [PATCH 027/104] Include json test command here --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index cf6e1bc772..fc2b66a549 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "build": "react-scripts build", "test": "react-scripts test", "test:cov": "react-scripts test --coverage --watchAll", + "test:json": "react-scripts test --json --watchAll=false --outputFile jest-output.json --coverage", "eject": "react-scripts eject", "lint": "eslint ./src --ext .tsx --ext .ts --max-warnings 0", "eslint-output": "eslint-output ./src --ext .tsx --ext .ts --max-warnings 0", From 7fe9ca316fad2e694586e037fe601b85a2584c56 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 31 Jan 2022 16:37:54 -0500 Subject: [PATCH 028/104] Require Hello World in the document --- src/text.Test.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/text.Test.tsx diff --git a/src/text.Test.tsx b/src/text.Test.tsx new file mode 100644 index 0000000000..b32c330d3f --- /dev/null +++ b/src/text.Test.tsx @@ -0,0 +1,9 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import App from "./App"; + +test("renders the text 'Hello World' somewhere", () => { + render(); + const text = screen.getByText(/Hello World/); + expect(text).toBeInTheDocument(); +}); From b8b8878c873d4faa2fd5f04d656e23d66c7d6cef Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 31 Jan 2022 16:56:42 -0500 Subject: [PATCH 029/104] Include the task info --- public/tasks/task-first-branch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 public/tasks/task-first-branch.md diff --git a/public/tasks/task-first-branch.md b/public/tasks/task-first-branch.md new file mode 100644 index 0000000000..94333338a0 --- /dev/null +++ b/public/tasks/task-first-branch.md @@ -0,0 +1,5 @@ +# Task - First Branch + +Version: 0.0.1 + +Pass a short test to have certain text on the page. From fbdebdec2006b01d3976bd9408037baf82eb5e56 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 31 Jan 2022 16:41:17 -0500 Subject: [PATCH 030/104] Rename text.Test.tsx to text.test.tsx --- src/{text.Test.tsx => text.test.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{text.Test.tsx => text.test.tsx} (100%) diff --git a/src/text.Test.tsx b/src/text.test.tsx similarity index 100% rename from src/text.Test.tsx rename to src/text.test.tsx From 2f0146c22beca5c5ac48603876f0fa8ea2e2e905 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 3 Feb 2022 14:10:55 -0500 Subject: [PATCH 031/104] Allow one or more instances of the Hello World text --- src/text.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/text.test.tsx b/src/text.test.tsx index b32c330d3f..f99a063e76 100644 --- a/src/text.test.tsx +++ b/src/text.test.tsx @@ -4,6 +4,6 @@ import App from "./App"; test("renders the text 'Hello World' somewhere", () => { render(); - const text = screen.getByText(/Hello World/); - expect(text).toBeInTheDocument(); + const texts = screen.getAllByText(/Hello World/); + expect(texts.length).toBeGreaterThanOrEqual(1); }); From ac36b32302a8ea2e66b4b954626c8e396e172075 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 29 Jan 2022 23:59:24 -0500 Subject: [PATCH 032/104] First set of tests --- public/tasks/task-html-css.md | 5 +++++ src/HtmlCss.test.tsx | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 public/tasks/task-html-css.md create mode 100644 src/HtmlCss.test.tsx diff --git a/public/tasks/task-html-css.md b/public/tasks/task-html-css.md new file mode 100644 index 0000000000..ebc0efcba5 --- /dev/null +++ b/public/tasks/task-html-css.md @@ -0,0 +1,5 @@ +# Task - HTML/CSS + +Version: 0.0.1 + +Add in some HTML and CSS, including a fancy looking button. diff --git a/src/HtmlCss.test.tsx b/src/HtmlCss.test.tsx new file mode 100644 index 0000000000..168ce37fde --- /dev/null +++ b/src/HtmlCss.test.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import App from "./App"; + +describe("Some HTML Elements are added.", () => { + test("There is a header", () => { + render(); + const header = screen.getByRole("heading"); + expect(header).toBeInTheDocument(); + }); +}); + +describe("Some basic CSS is added.", () => { + test("There is a floating red box", () => { + render(); + expect(true); + }); +}); + +describe("Some Bootstrap Elements are added", () => { + test("There is a bootstrap button", () => { + render(); + const button = screen.getByRole("button"); + expect(button).toBeInTheDocument(); + expect(button).toHaveClass("btn"); + expect(button).toHaveClass("btn-primary"); + }); +}); From d04739d1d2ec0c934c0ecf1dc05ac1289063627d Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 30 Jan 2022 00:24:38 -0500 Subject: [PATCH 033/104] Some logging tests --- src/HtmlCss.test.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/HtmlCss.test.tsx b/src/HtmlCss.test.tsx index 168ce37fde..bf957616f9 100644 --- a/src/HtmlCss.test.tsx +++ b/src/HtmlCss.test.tsx @@ -1,6 +1,7 @@ import React from "react"; import { render, screen } from "@testing-library/react"; import App from "./App"; +import userEvent from "@testing-library/user-event"; describe("Some HTML Elements are added.", () => { test("There is a header", () => { @@ -18,11 +19,25 @@ describe("Some basic CSS is added.", () => { }); describe("Some Bootstrap Elements are added", () => { - test("There is a bootstrap button", () => { + test("There is one bootstrap button with the text 'Log Hello World'", () => { render(); - const button = screen.getByRole("button"); + const button = screen.getByRole("button", { name: /Log Hello World/i }); expect(button).toBeInTheDocument(); expect(button).toHaveClass("btn"); expect(button).toHaveClass("btn-primary"); }); + + test("Not clicking the bootstrap button does not logs 'Hello World!'", () => { + const consoleSpy = jest.spyOn(console, "log"); + render(); + expect(consoleSpy).not.toHaveBeenCalledWith("Hello World!"); + }); + + test("Clicking the bootstrap button logs 'Hello World!'", () => { + const consoleSpy = jest.spyOn(console, "log"); + render(); + const button = screen.getByRole("button", { name: /Log Hello World/i }); + userEvent.click(button); + expect(consoleSpy).toHaveBeenCalledWith("Hello World!"); + }); }); From b26100f543943eced73fdff33784861243c65386 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 30 Jan 2022 00:47:43 -0500 Subject: [PATCH 034/104] More html tests --- src/HtmlCss.test.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/HtmlCss.test.tsx b/src/HtmlCss.test.tsx index bf957616f9..676c37f903 100644 --- a/src/HtmlCss.test.tsx +++ b/src/HtmlCss.test.tsx @@ -9,6 +9,20 @@ describe("Some HTML Elements are added.", () => { const header = screen.getByRole("heading"); expect(header).toBeInTheDocument(); }); + + test("There is an image with alt text", () => { + render(); + const image = screen.getByRole("image"); + expect(image).toBeInTheDocument(); + expect(image).toHaveAttribute("alt"); + }); + + test("There is a list with at least three elements", () => { + render(); + const list = screen.getByRole("list"); + expect(list).toBeInTheDocument(); + expect(list.children.length).toBeGreaterThanOrEqual(3); + }); }); describe("Some basic CSS is added.", () => { From 3bf4550a8f042dee28a57c06abec05dfce779519 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 30 Jan 2022 00:55:24 -0500 Subject: [PATCH 035/104] Fix the image test --- src/HtmlCss.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HtmlCss.test.tsx b/src/HtmlCss.test.tsx index 676c37f903..79b7db2dda 100644 --- a/src/HtmlCss.test.tsx +++ b/src/HtmlCss.test.tsx @@ -12,7 +12,7 @@ describe("Some HTML Elements are added.", () => { test("There is an image with alt text", () => { render(); - const image = screen.getByRole("image"); + const image = screen.getByRole("img"); expect(image).toBeInTheDocument(); expect(image).toHaveAttribute("alt"); }); From 8dff2b64a2efc0b1b49703077965fe5e334eab1a Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 31 Jan 2022 16:31:58 -0500 Subject: [PATCH 036/104] Updated CSS tests, left a note about additional tests --- src/HtmlCss.test.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/HtmlCss.test.tsx b/src/HtmlCss.test.tsx index 79b7db2dda..379fc8f449 100644 --- a/src/HtmlCss.test.tsx +++ b/src/HtmlCss.test.tsx @@ -30,6 +30,14 @@ describe("Some basic CSS is added.", () => { render(); expect(true); }); + + test("The background color of the header area is different", () => { + render(); + const banner = screen.getByRole("banner"); + expect(banner).not.toHaveStyle({ + "background-color": "rgb(40, 44, 52)" + }); + }); }); describe("Some Bootstrap Elements are added", () => { From b66d4de909f74de4cba160a6fff05b078b9b47cc Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Mon, 31 Jan 2022 16:32:13 -0500 Subject: [PATCH 037/104] See previous commit message --- src/HtmlCss.test.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/HtmlCss.test.tsx b/src/HtmlCss.test.tsx index 379fc8f449..36767ad350 100644 --- a/src/HtmlCss.test.tsx +++ b/src/HtmlCss.test.tsx @@ -26,11 +26,6 @@ describe("Some HTML Elements are added.", () => { }); describe("Some basic CSS is added.", () => { - test("There is a floating red box", () => { - render(); - expect(true); - }); - test("The background color of the header area is different", () => { render(); const banner = screen.getByRole("banner"); @@ -63,3 +58,7 @@ describe("Some Bootstrap Elements are added", () => { expect(consoleSpy).toHaveBeenCalledWith("Hello World!"); }); }); + +/** + * Remember, there are additional tasks described on the page! + */ From 0a24364f67b1ee221ebe175d6c494d5eca6ad7dc Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 13:10:09 -0400 Subject: [PATCH 038/104] Add in new css test --- src/HtmlCss.test.tsx | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/HtmlCss.test.tsx b/src/HtmlCss.test.tsx index 36767ad350..48b0a6df2d 100644 --- a/src/HtmlCss.test.tsx +++ b/src/HtmlCss.test.tsx @@ -30,7 +30,7 @@ describe("Some basic CSS is added.", () => { render(); const banner = screen.getByRole("banner"); expect(banner).not.toHaveStyle({ - "background-color": "rgb(40, 44, 52)" + "background-color": "rgb(40, 44, 52)", }); }); }); @@ -59,6 +59,25 @@ describe("Some Bootstrap Elements are added", () => { }); }); -/** - * Remember, there are additional tasks described on the page! - */ +describe("Some additional CSS was added", () => { + test("checks if any element has a background color of red", () => { + const { container } = render(); + // Get all elements in the rendered container + const elements = container.querySelectorAll("*"); + + // Check if any element has a background color of red + let foundRedBackground = false; + + elements.forEach((element) => { + const style = getComputedStyle(element); + if ( + style.backgroundColor === "red" || + style.backgroundColor === "rgb(255, 0, 0)" + ) { + foundRedBackground = true; + } + }); + + expect(foundRedBackground).toBe(true); + }); +}); From 4d43d7a5d133522b0a8d92e1cb3d7e4053a81992 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 13:12:28 -0400 Subject: [PATCH 039/104] Add in points --- src/HtmlCss.test.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/HtmlCss.test.tsx b/src/HtmlCss.test.tsx index 48b0a6df2d..320cb97524 100644 --- a/src/HtmlCss.test.tsx +++ b/src/HtmlCss.test.tsx @@ -4,20 +4,20 @@ import App from "./App"; import userEvent from "@testing-library/user-event"; describe("Some HTML Elements are added.", () => { - test("There is a header", () => { + test("(2 pts) There is a header", () => { render(); const header = screen.getByRole("heading"); expect(header).toBeInTheDocument(); }); - test("There is an image with alt text", () => { + test("(2 pts) There is an image with alt text", () => { render(); const image = screen.getByRole("img"); expect(image).toBeInTheDocument(); expect(image).toHaveAttribute("alt"); }); - test("There is a list with at least three elements", () => { + test("(2 pts) There is a list with at least three elements", () => { render(); const list = screen.getByRole("list"); expect(list).toBeInTheDocument(); @@ -25,7 +25,7 @@ describe("Some HTML Elements are added.", () => { }); }); -describe("Some basic CSS is added.", () => { +describe("(2 pts) Some basic CSS is added.", () => { test("The background color of the header area is different", () => { render(); const banner = screen.getByRole("banner"); @@ -35,7 +35,7 @@ describe("Some basic CSS is added.", () => { }); }); -describe("Some Bootstrap Elements are added", () => { +describe("(2 pts) Some Bootstrap Elements are added", () => { test("There is one bootstrap button with the text 'Log Hello World'", () => { render(); const button = screen.getByRole("button", { name: /Log Hello World/i }); @@ -44,13 +44,13 @@ describe("Some Bootstrap Elements are added", () => { expect(button).toHaveClass("btn-primary"); }); - test("Not clicking the bootstrap button does not logs 'Hello World!'", () => { + test("(2 pts) Not clicking the bootstrap button does not logs 'Hello World!'", () => { const consoleSpy = jest.spyOn(console, "log"); render(); expect(consoleSpy).not.toHaveBeenCalledWith("Hello World!"); }); - test("Clicking the bootstrap button logs 'Hello World!'", () => { + test("(2 pts) Clicking the bootstrap button logs 'Hello World!'", () => { const consoleSpy = jest.spyOn(console, "log"); render(); const button = screen.getByRole("button", { name: /Log Hello World/i }); @@ -60,7 +60,7 @@ describe("Some Bootstrap Elements are added", () => { }); describe("Some additional CSS was added", () => { - test("checks if any element has a background color of red", () => { + test("(2 pts) checks if any element has a background color of red", () => { const { container } = render(); // Get all elements in the rendered container const elements = container.querySelectorAll("*"); From 83c4461f9dbe7d2a66c09eed18959565a302eea2 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 29 Jan 2022 23:23:45 -0500 Subject: [PATCH 040/104] Basic functions tests and stubs --- public/tasks/task-functions.md | 5 +++ src/functions.test.ts | 59 ++++++++++++++++++++++++++++++++++ src/functions.ts | 19 +++++++++++ 3 files changed, 83 insertions(+) create mode 100644 public/tasks/task-functions.md create mode 100644 src/functions.test.ts create mode 100644 src/functions.ts diff --git a/public/tasks/task-functions.md b/public/tasks/task-functions.md new file mode 100644 index 0000000000..36e7926bb7 --- /dev/null +++ b/public/tasks/task-functions.md @@ -0,0 +1,5 @@ +# Task - Functions + +Version: 0.0.1 + +Implement a bunch of functions that work on primitives. diff --git a/src/functions.test.ts b/src/functions.test.ts new file mode 100644 index 0000000000..e0bef414ea --- /dev/null +++ b/src/functions.test.ts @@ -0,0 +1,59 @@ +import { + add3, + fahrenheitToCelius, + shout, + isQuestion, + convertYesNo +} from "./functions"; + +test("Testing the basic functions", () => { + it("Testing the add3 function", () => { + expect(add3(1, 2, 3)).toBe(6); + expect(add3(9, 7, 4)).toBe(20); + expect(add3(6, -3, 9)).toBe(15); + expect(add3(10, 1, -9)).toBe(11); + expect(add3(-9, -100, -4)).toBe(0); + expect(add3(-1, -1, 1)).toBe(1); + }); + + it("Testing the fahrenheitToCelius function", () => { + expect(fahrenheitToCelius(32)).toBe(0); + expect(fahrenheitToCelius(-40)).toBe(40); + expect(fahrenheitToCelius(-22)).toBe(-30); + expect(fahrenheitToCelius(14)).toBe(-10); + expect(fahrenheitToCelius(68)).toBe(20); + expect(fahrenheitToCelius(86)).toBe(30); + expect(fahrenheitToCelius(212)).toBe(100); + }); + + it("Testing the shout function", () => { + expect(shout("Hello")).toBe("HELLO!"); + expect(shout("What?")).toBe("WHAT?!"); + expect(shout("oHo")).toBe("OHO!"); + expect(shout("AHHHH!!!")).toBe("AHHHH!!!!"); + expect(shout("")).toBe("!"); + expect(shout("Please go outside")).toBe("PLEASE GO OUTSIDE!"); + }); + + it("Testing the isQuestion function", () => { + expect(isQuestion("Is this a question?")).toBe(true); + expect(isQuestion("Who are you?")).toBe(true); + expect(isQuestion("WHAT ARE YOU !?")).toBe(true); + expect(isQuestion("WHAT IS THIS?!")).toBe(false); + expect(isQuestion("OH GOD!")).toBe(false); + expect(isQuestion("Oh nevermind, it's fine.")).toBe(false); + expect(isQuestion("")).toBe(false); + }); + + it("Testing the convertYesNo function", () => { + expect(convertYesNo("yes")).toBe(true); + expect(convertYesNo("YES")).toBe(true); + expect(convertYesNo("NO")).toBe(false); + expect(convertYesNo("no")).toBe(false); + expect(convertYesNo("Apple")).toBe(null); + expect(convertYesNo("Bananas")).toBe(null); + expect(convertYesNo("Nope")).toBe(null); + expect(convertYesNo("Yesterday")).toBe(null); + expect(convertYesNo("Maybe")).toBe(null); + }); +}); diff --git a/src/functions.ts b/src/functions.ts new file mode 100644 index 0000000000..03193e4212 --- /dev/null +++ b/src/functions.ts @@ -0,0 +1,19 @@ +export function fahrenheitToCelius(temperature: number): number { + return 0; +} + +export function add3(first: number, second: number, third: number): number { + return 0; +} + +export function shout(message: string): string { + return ""; +} + +export function isQuestion(message: string): boolean { + return true; +} + +export function convertYesNo(word: string): boolean | null { + return true; +} From a48653022ec3c8b7ce99cf49d98b041e20815420 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 29 Jan 2022 23:30:17 -0500 Subject: [PATCH 041/104] Fix test organization --- src/functions.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/functions.test.ts b/src/functions.test.ts index e0bef414ea..98c926bb6f 100644 --- a/src/functions.test.ts +++ b/src/functions.test.ts @@ -6,8 +6,8 @@ import { convertYesNo } from "./functions"; -test("Testing the basic functions", () => { - it("Testing the add3 function", () => { +describe("Testing the basic functions", () => { + test("Testing the add3 function", () => { expect(add3(1, 2, 3)).toBe(6); expect(add3(9, 7, 4)).toBe(20); expect(add3(6, -3, 9)).toBe(15); @@ -16,7 +16,7 @@ test("Testing the basic functions", () => { expect(add3(-1, -1, 1)).toBe(1); }); - it("Testing the fahrenheitToCelius function", () => { + test("Testing the fahrenheitToCelius function", () => { expect(fahrenheitToCelius(32)).toBe(0); expect(fahrenheitToCelius(-40)).toBe(40); expect(fahrenheitToCelius(-22)).toBe(-30); @@ -26,7 +26,7 @@ test("Testing the basic functions", () => { expect(fahrenheitToCelius(212)).toBe(100); }); - it("Testing the shout function", () => { + test("Testing the shout function", () => { expect(shout("Hello")).toBe("HELLO!"); expect(shout("What?")).toBe("WHAT?!"); expect(shout("oHo")).toBe("OHO!"); @@ -35,7 +35,7 @@ test("Testing the basic functions", () => { expect(shout("Please go outside")).toBe("PLEASE GO OUTSIDE!"); }); - it("Testing the isQuestion function", () => { + test("Testing the isQuestion function", () => { expect(isQuestion("Is this a question?")).toBe(true); expect(isQuestion("Who are you?")).toBe(true); expect(isQuestion("WHAT ARE YOU !?")).toBe(true); @@ -45,7 +45,7 @@ test("Testing the basic functions", () => { expect(isQuestion("")).toBe(false); }); - it("Testing the convertYesNo function", () => { + test("Testing the convertYesNo function", () => { expect(convertYesNo("yes")).toBe(true); expect(convertYesNo("YES")).toBe(true); expect(convertYesNo("NO")).toBe(false); From 9722564e99cecda5d50dd95524c94a76c4cda923 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 29 Jan 2022 23:39:22 -0500 Subject: [PATCH 042/104] Fix issue in fahrenheit conversion --- src/functions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/functions.test.ts b/src/functions.test.ts index 98c926bb6f..3eb9f4f3aa 100644 --- a/src/functions.test.ts +++ b/src/functions.test.ts @@ -18,7 +18,7 @@ describe("Testing the basic functions", () => { test("Testing the fahrenheitToCelius function", () => { expect(fahrenheitToCelius(32)).toBe(0); - expect(fahrenheitToCelius(-40)).toBe(40); + expect(fahrenheitToCelius(-40)).toBe(-40); expect(fahrenheitToCelius(-22)).toBe(-30); expect(fahrenheitToCelius(14)).toBe(-10); expect(fahrenheitToCelius(68)).toBe(20); From bd06d5d0e3ed264f7bffb4e8e4811d0efc170255 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 3 Feb 2022 14:27:08 -0500 Subject: [PATCH 043/104] Move around some of the functions --- src/functions.test.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/functions.test.ts b/src/functions.test.ts index 3eb9f4f3aa..c496ac7e99 100644 --- a/src/functions.test.ts +++ b/src/functions.test.ts @@ -7,15 +7,6 @@ import { } from "./functions"; describe("Testing the basic functions", () => { - test("Testing the add3 function", () => { - expect(add3(1, 2, 3)).toBe(6); - expect(add3(9, 7, 4)).toBe(20); - expect(add3(6, -3, 9)).toBe(15); - expect(add3(10, 1, -9)).toBe(11); - expect(add3(-9, -100, -4)).toBe(0); - expect(add3(-1, -1, 1)).toBe(1); - }); - test("Testing the fahrenheitToCelius function", () => { expect(fahrenheitToCelius(32)).toBe(0); expect(fahrenheitToCelius(-40)).toBe(-40); @@ -26,6 +17,15 @@ describe("Testing the basic functions", () => { expect(fahrenheitToCelius(212)).toBe(100); }); + test("Testing the add3 function", () => { + expect(add3(1, 2, 3)).toBe(6); + expect(add3(9, 7, 4)).toBe(20); + expect(add3(6, -3, 9)).toBe(15); + expect(add3(10, 1, -9)).toBe(11); + expect(add3(-9, -100, -4)).toBe(0); + expect(add3(-1, -1, 1)).toBe(1); + }); + test("Testing the shout function", () => { expect(shout("Hello")).toBe("HELLO!"); expect(shout("What?")).toBe("WHAT?!"); From 4cd1900783f690690229b7c17cf9e81995f52b3a Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Thu, 3 Feb 2022 14:27:18 -0500 Subject: [PATCH 044/104] Explain what the actual functions require you to do --- src/functions.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/functions.ts b/src/functions.ts index 03193e4212..e614c81c0c 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -1,19 +1,41 @@ +/** + * Consumes a single temperature in Fahrenheit (a number) and converts to Celsius + * using this formula: + * C = (F - 32) * 5/9 + */ export function fahrenheitToCelius(temperature: number): number { return 0; } +/** + * Consumes three numbers and produces their sum. BUT you should only add a number + * if the number is greater than zero. + */ export function add3(first: number, second: number, third: number): number { return 0; } +/** + * Consumes a string and produces the same string in UPPERCASE and with an exclamation + * mark added to the end. + */ export function shout(message: string): string { return ""; } +/** + * Consumes a string (a message) and returns a boolean if the string ends in a question + * mark. Do not use an `if` statement in solving this question. + */ export function isQuestion(message: string): boolean { return true; } +/** + * Consumes a word (a string) and returns either `true`, `false`, or `null`. If the string + * is "yes" (upper or lower case), then return `true`. If the string is "no" (again, either + * upper or lower case), then return `false`. Otherwise, return `null`. + */ export function convertYesNo(word: string): boolean | null { return true; } From cf1d21a31d00c2e8dc8bb7c76f372b3e0adebfbe Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 13:15:59 -0400 Subject: [PATCH 045/104] Update formatting --- src/functions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/functions.test.ts b/src/functions.test.ts index c496ac7e99..a082bfd61a 100644 --- a/src/functions.test.ts +++ b/src/functions.test.ts @@ -3,7 +3,7 @@ import { fahrenheitToCelius, shout, isQuestion, - convertYesNo + convertYesNo, } from "./functions"; describe("Testing the basic functions", () => { From e11693a366f61cdb442c6f6f5822bd49e2dd604f Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 13:18:24 -0400 Subject: [PATCH 046/104] Add in points --- src/functions.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/functions.test.ts b/src/functions.test.ts index a082bfd61a..3d921f5d64 100644 --- a/src/functions.test.ts +++ b/src/functions.test.ts @@ -7,7 +7,7 @@ import { } from "./functions"; describe("Testing the basic functions", () => { - test("Testing the fahrenheitToCelius function", () => { + test("(3 pts) Testing the fahrenheitToCelius function", () => { expect(fahrenheitToCelius(32)).toBe(0); expect(fahrenheitToCelius(-40)).toBe(-40); expect(fahrenheitToCelius(-22)).toBe(-30); @@ -17,7 +17,7 @@ describe("Testing the basic functions", () => { expect(fahrenheitToCelius(212)).toBe(100); }); - test("Testing the add3 function", () => { + test("(3 pts) Testing the add3 function", () => { expect(add3(1, 2, 3)).toBe(6); expect(add3(9, 7, 4)).toBe(20); expect(add3(6, -3, 9)).toBe(15); @@ -26,7 +26,7 @@ describe("Testing the basic functions", () => { expect(add3(-1, -1, 1)).toBe(1); }); - test("Testing the shout function", () => { + test("(3 pts) Testing the shout function", () => { expect(shout("Hello")).toBe("HELLO!"); expect(shout("What?")).toBe("WHAT?!"); expect(shout("oHo")).toBe("OHO!"); @@ -35,7 +35,7 @@ describe("Testing the basic functions", () => { expect(shout("Please go outside")).toBe("PLEASE GO OUTSIDE!"); }); - test("Testing the isQuestion function", () => { + test("(3 pts) Testing the isQuestion function", () => { expect(isQuestion("Is this a question?")).toBe(true); expect(isQuestion("Who are you?")).toBe(true); expect(isQuestion("WHAT ARE YOU !?")).toBe(true); @@ -45,7 +45,7 @@ describe("Testing the basic functions", () => { expect(isQuestion("")).toBe(false); }); - test("Testing the convertYesNo function", () => { + test("(3 pts) Testing the convertYesNo function", () => { expect(convertYesNo("yes")).toBe(true); expect(convertYesNo("YES")).toBe(true); expect(convertYesNo("NO")).toBe(false); From 7cc4e3f20e61307e9f22eb466fe21871b3eefbd3 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 1 Feb 2022 14:51:32 -0500 Subject: [PATCH 047/104] First stab at array problems --- public/tasks/task-arrays.md | 5 +++ src/arrays.test.ts | 12 ++++++ src/arrays.ts | 84 +++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 public/tasks/task-arrays.md create mode 100644 src/arrays.test.ts create mode 100644 src/arrays.ts diff --git a/public/tasks/task-arrays.md b/public/tasks/task-arrays.md new file mode 100644 index 0000000000..c2fbf80f8d --- /dev/null +++ b/public/tasks/task-arrays.md @@ -0,0 +1,5 @@ +# Task - Arrays + +Version: 0.0.1 + +Implement functions that work with arrays immutably. diff --git a/src/arrays.test.ts b/src/arrays.test.ts new file mode 100644 index 0000000000..b812349d2f --- /dev/null +++ b/src/arrays.test.ts @@ -0,0 +1,12 @@ +import { bookEndList } from "./arrays"; + +describe("Testing the array functions", () => { + const NUMBERS_1 = [1, 2, 3]; + + test("Testing the bookEndList function", () => { + // Ensure that the original array was not changed + expect(bookEndList(NUMBERS_1)).not.toBe(NUMBERS_1); + // And that a correct new array was returned + expect(bookEndList(NUMBERS_1)).toEqual([1, 3]); + }); +}); diff --git a/src/arrays.ts b/src/arrays.ts new file mode 100644 index 0000000000..7604b40cdb --- /dev/null +++ b/src/arrays.ts @@ -0,0 +1,84 @@ +/** + * Consume an array of numbers, and return a new array containing + * JUST the first and last number. If there are no elements, return + * an empty array. If there is one element, the resulting list should + * the number twice. + */ +export function bookEndList(numbers: number[]): number[] { + return numbers; +} + +/** + * Consume an array of numbers, and return a new array where each + * number has been tripled (multiplied by 3). + */ +export function tripleNumbers(numbers: number[]): number[] { + return numbers; +} + +/** + * Consume an array of strings and convert them to integers. If + * the number cannot be parsed as an integer, convert it to "?" instead. + */ +export function stringsToIntegers(numbers: string[]): number[] { + return []; +} + +/** + * Consume an array of strings and return them as numbers. Note that + * the strings MAY have "$" symbols at the beginning, in which case + * those should be removed. If the result cannot be parsed as an integer, + * convert it to "?" instead. + */ +// Remember, you can write functions as lambdas too! They work exactly the same. +export const removeDollars = (amounts: string[]): number[] => { + return []; +}; + +/** + * Consume an array of messages and return a new list of the messages. However, any + * string that ends in "!" should be made uppercase. + */ +export const shoutIfExclaiming = (messages: string[]): string[] => { + return []; +}; + +/** + * Consumes an array of words and returns the number of words that are LESS THAN + * 4 letters long. + */ +export function countShortWords(words: string[]): number { + return 0; +} + +/** + * Consumes an array of colors (e.g., 'red', 'purple') and returns true if ALL + * the colors are either 'red', 'blue', or 'green'. If an empty list is given, + * then return true. + */ +export function allRGB(colors: string[]): boolean { + return false; +} + +/** + * Consumes an array of numbers, and produces a string representation of the + * numbers being added together along with their actual sum. + * + * For instance, the array [1, 2, 3] would become "6=1+2+3". + */ +export function makeMath(addends: number[]): string { + return ""; +} + +/** + * Consumes an array of numbers and produces a new array of the same numbers, + * with one difference. After the FIRST negative number, insert the sum of all + * previous numbers in the list. If there are no negative numbers, then append + * 0 to the list. + * + * For instance, the array [1, 9, -5, 7] would become [1, 9, -5, 10, 7] + * And the array [1, 9, 7] would become [1, 9, 0] + */ +export function injectPositive(values: number[]): number[] { + return []; +} From f25333778032fc42866a278af6a3ce871f735150 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 1 Feb 2022 16:09:10 -0500 Subject: [PATCH 048/104] Add in the rest of the tests --- src/arrays.test.ts | 269 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 265 insertions(+), 4 deletions(-) diff --git a/src/arrays.test.ts b/src/arrays.test.ts index b812349d2f..0881d9fe8a 100644 --- a/src/arrays.test.ts +++ b/src/arrays.test.ts @@ -1,12 +1,273 @@ -import { bookEndList } from "./arrays"; +import { + allRGB, + bookEndList, + countShortWords, + injectPositive, + makeMath, + removeDollars, + shoutIfExclaiming, + stringsToIntegers, + tripleNumbers +} from "./arrays"; describe("Testing the array functions", () => { + ////////////////////////////////// + // bookEndList and tripleNumbers + const NUMBERS_1 = [1, 2, 3]; + const NUMBERS_2 = [100, 300, 200]; + const NUMBERS_3 = [5]; + const NUMBERS_4: number[] = []; + const NUMBERS_5 = [100, 199, 1, -5, 7, 3]; + const NUMBERS_6 = [-100, -200, 100, 200]; + const NUMBERS_7 = [199, 1, 550, 50, 200]; + + // Ensure that none of the arrays were changed mutably + // If you fail these, you aren't using map/filter/reduce/etc. properly! + afterEach(() => { + expect(NUMBERS_1).toEqual([1, 2, 3]); + expect(NUMBERS_2).toEqual([100, 300, 200]); + expect(NUMBERS_3).toEqual([5]); + expect(NUMBERS_4).toEqual([]); + expect(NUMBERS_5).toEqual([100, 199, 1, -5, 7, 3]); + expect(NUMBERS_6).toEqual([-100, -200, 100, 200]); + expect(NUMBERS_7).toEqual([199, 1, 550, 50, 200]); + }); test("Testing the bookEndList function", () => { - // Ensure that the original array was not changed - expect(bookEndList(NUMBERS_1)).not.toBe(NUMBERS_1); - // And that a correct new array was returned expect(bookEndList(NUMBERS_1)).toEqual([1, 3]); + expect(bookEndList(NUMBERS_2)).toEqual([100, 200]); + expect(bookEndList(NUMBERS_3)).toEqual([5, 5]); + expect(bookEndList(NUMBERS_4)).toEqual([]); + expect(bookEndList(NUMBERS_5)).toEqual([100, 3]); + expect(bookEndList(NUMBERS_6)).toEqual([-100, 200]); + }); + + test("Testing the tripleNumbers function", () => { + expect(tripleNumbers(NUMBERS_1)).toEqual([3, 6, 9]); + expect(tripleNumbers(NUMBERS_2)).toEqual([300, 900, 600]); + expect(tripleNumbers(NUMBERS_3)).toEqual([15]); + expect(tripleNumbers(NUMBERS_4)).toEqual([]); + expect(tripleNumbers(NUMBERS_5)).toEqual([300, 597, 3, -15, 21, 9]); + expect(tripleNumbers(NUMBERS_6)).toEqual([-300, -600, 300, 600]); + }); + + ////////////////////////////////// + // stringsToIntegers + + const VALUES_1 = ["1", "2", "3"]; + const VALUES_2 = ["100", "200", "300"]; + const VALUES_3 = ["5"]; + const VALUES_4: string[] = []; + const VALUES_5 = ["100", "?", "27", "$44"]; + const VALUES_6 = ["-1", "0", "1", "*1"]; + const VALUES_7 = ["apple", "banana", "cactus"]; + + // Ensure that none of the arrays were changed mutably + // If you fail these, you aren't using map/filter/reduce/etc. properly! + afterEach(() => { + expect(VALUES_1).toEqual(["1", "2", "3"]); + expect(VALUES_2).toEqual(["100", "200", "300"]); + expect(VALUES_3).toEqual(["5"]); + expect(VALUES_4).toEqual([]); + expect(VALUES_5).toEqual(["100", "?", "27", "$44"]); + expect(VALUES_6).toEqual(["-1", "0", "1", "*1"]); + expect(VALUES_7).toEqual(["apple", "banana", "cactus"]); + }); + + test("Testing the stringsToIntegers function", () => { + expect(stringsToIntegers(VALUES_1)).toEqual([1, 2, 3]); + expect(stringsToIntegers(VALUES_2)).toEqual([100, 200, 300]); + expect(stringsToIntegers(VALUES_3)).toEqual([5]); + expect(stringsToIntegers(VALUES_4)).toEqual([]); + expect(stringsToIntegers(VALUES_5)).toEqual([100, 0, 27, 0]); + expect(stringsToIntegers(VALUES_6)).toEqual([-1, 0, 1, 0]); + expect(stringsToIntegers(VALUES_7)).toEqual([0, 0, 0]); + }); + + ////////////////////////////////// + // removeDollars + + const AMOUNTS_1 = ["$1", "$2", "$3"]; + const AMOUNTS_2 = ["$100", "$200", "$300", "$400"]; + const AMOUNTS_3 = ["$5"]; + const AMOUNTS_4 = ["$"]; + const AMOUNTS_5 = ["100", "200", "$300", "$400"]; + const AMOUNTS_6: string[] = []; + const AMOUNTS_7 = ["100", "???", "7", "$233", "", "$"]; + const AMOUNTS_8 = ["$one", "two", "$three"]; + + // Ensure that none of the arrays were changed mutably + // If you fail these, you aren't using map/filter/reduce/etc. properly! + afterEach(() => { + expect(AMOUNTS_1).toEqual(["$1", "$2", "$3"]); + expect(AMOUNTS_2).toEqual(["$100", "$200", "$300", "$400"]); + expect(AMOUNTS_3).toEqual(["$5"]); + expect(AMOUNTS_4).toEqual(["$"]); + expect(AMOUNTS_5).toEqual(["100", "200", "$300", "$400"]); + expect(AMOUNTS_6).toEqual([]); + expect(AMOUNTS_7).toEqual(["100", "???", "7", "$233", "", "$"]); + expect(AMOUNTS_8).toEqual(["$one", "two", "$three"]); + }); + + test("Testing the removeDollars function", () => { + expect(removeDollars(AMOUNTS_1)).toEqual([1, 2, 3]); + expect(removeDollars(AMOUNTS_2)).toEqual([100, 200, 300, 400]); + expect(removeDollars(AMOUNTS_3)).toEqual([5]); + expect(removeDollars(AMOUNTS_4)).toEqual([0]); + expect(removeDollars(AMOUNTS_5)).toEqual([100, 200, 300, 400]); + expect(removeDollars(AMOUNTS_6)).toEqual([]); + expect(removeDollars(AMOUNTS_7)).toEqual([100, 0, 7, 233, 0, 0]); + expect(removeDollars(AMOUNTS_8)).toEqual([0, 0, 0]); + }); + + ////////////////////////////////// + // shoutIfExclaiming + + const MESSAGE_1 = ["Hello", "you", "are", "great!"]; + const MESSAGE_2 = ["oho!", "Oho!", "oHo!", "oHO!", "OHO!"]; + const MESSAGE_3 = ["Wait?", "What?", "Lo", "How?", "High!"]; + const MESSAGE_4 = ["??????"]; + const MESSAGE_5: string[] = ["This one is very long!"]; + const MESSAGE_6 = ["No", "Caps", "here.", "Right?"]; + + // Ensure that none of the arrays were changed mutably + // If you fail these, you aren't using map/filter/reduce/etc. properly! + afterEach(() => { + expect(MESSAGE_1).toEqual(["Hello", "you", "are", "great!"]); + expect(MESSAGE_2).toEqual(["oho!", "Oho!", "oHo!", "oHO!", "OHO!"]); + expect(MESSAGE_3).toEqual(["Wait?", "What?", "Lo", "How?", "High!"]); + expect(MESSAGE_4).toEqual(["??????"]); + expect(MESSAGE_5).toEqual(["This one is very long!"]); + expect(MESSAGE_6).toEqual(["No", "Caps", "here.", "Right?"]); + }); + + test("Testing the shoutIfExclaiming function", () => { + expect(shoutIfExclaiming(MESSAGE_1)).toEqual([ + "Hello", + "you", + "are", + "GREAT!" + ]); + expect(shoutIfExclaiming(MESSAGE_2)).toEqual([ + "OHO!", + "OHO!", + "OHO!", + "OHO!", + "OHO!" + ]); + expect(shoutIfExclaiming(MESSAGE_3)).toEqual(["Lo", "HIGH!"]); + expect(shoutIfExclaiming(MESSAGE_4)).toEqual([]); + expect(shoutIfExclaiming(MESSAGE_5)).toEqual([ + "THIS ONE IS VERY LONG!" + ]); + expect(shoutIfExclaiming(MESSAGE_6)).toEqual(["No", "Caps", "here."]); + }); + + ////////////////////////////////// + // countShortWords + + const WORDS_1 = ["the", "cat", "in", "the", "hat"]; + const WORDS_2 = ["one", "two", "three", "four", "five", "six", "seven"]; + const WORDS_3 = ["alpha", "beta", "gamma"]; + const WORDS_4 = ["Longest", "Words", "Possible"]; + const WORDS_5: string[] = []; + const WORDS_6 = ["", "", "", ""]; + + // Ensure that none of the arrays were changed mutably + // If you fail these, you aren't using map/filter/reduce/etc. properly! + afterEach(() => { + expect(WORDS_1).toEqual(["the", "cat", "in", "the", "hat"]); + expect(WORDS_2).toEqual([ + "one", + "two", + "three", + "four", + "five", + "six", + "seven" + ]); + expect(WORDS_3).toEqual(["alpha", "beta", "gamma"]); + expect(WORDS_4).toEqual(["Longest", "Words", "Possible"]); + expect(WORDS_5).toEqual([]); + expect(WORDS_6).toEqual(["", "", "", ""]); + }); + + test("Testing the countShortWords function", () => { + expect(countShortWords(WORDS_1)).toEqual(5); + expect(countShortWords(WORDS_2)).toEqual(3); + expect(countShortWords(WORDS_3)).toEqual(0); + expect(countShortWords(WORDS_4)).toEqual(0); + expect(countShortWords(WORDS_5)).toEqual(0); + expect(countShortWords(WORDS_6)).toEqual(4); + }); + + ////////////////////////////////// + // allRGB + + const COLORS_1 = ["red", "green", "blue"]; + const COLORS_2 = ["red", "red", "red"]; + const COLORS_3 = ["red", "red", "blue", "blue", "green", "red"]; + const COLORS_4 = ["purple", "orange", "violet"]; + const COLORS_5 = ["red", "blue", "yellow"]; + const COLORS_6 = ["green"]; + const COLORS_7 = ["red"]; + const COLORS_8 = ["kabluey"]; + const COLORS_9: string[] = []; + + // Ensure that none of the arrays were changed mutably + // If you fail these, you aren't using map/filter/reduce/etc. properly! + afterEach(() => { + expect(COLORS_1).toEqual(["red", "green", "blue"]); + expect(COLORS_2).toEqual(["red", "red", "red"]); + expect(COLORS_3).toEqual([ + "red", + "red", + "blue", + "blue", + "green", + "red" + ]); + expect(COLORS_4).toEqual(["purple", "orange", "violet"]); + expect(COLORS_5).toEqual(["red", "blue", "yellow"]); + expect(COLORS_6).toEqual(["green"]); + expect(COLORS_7).toEqual(["red"]); + expect(COLORS_8).toEqual(["kabluey"]); + expect(COLORS_9).toEqual([]); + }); + + test("Testing the allRGB function", () => { + expect(allRGB(COLORS_1)).toEqual(true); + expect(allRGB(COLORS_2)).toEqual(true); + expect(allRGB(COLORS_3)).toEqual(true); + expect(allRGB(COLORS_4)).toEqual(false); + expect(allRGB(COLORS_5)).toEqual(false); + expect(allRGB(COLORS_6)).toEqual(true); + expect(allRGB(COLORS_7)).toEqual(true); + expect(allRGB(COLORS_8)).toEqual(false); + expect(allRGB(COLORS_9)).toEqual(true); + }); + + ////////////////////////////////// + // makeMath + + test("Testing the makeMath function", () => { + expect(makeMath(NUMBERS_1)).toEqual("6=1+2+3"); + expect(makeMath(NUMBERS_2)).toEqual("600=100+300+200"); + expect(makeMath(NUMBERS_3)).toEqual("5=5"); + expect(makeMath(NUMBERS_4)).toEqual("0=0"); + expect(makeMath(NUMBERS_7)).toEqual("1000=199+1+550+50+200"); + }); + + ////////////////////////////////// + // injectPositive + test("Testing the tripleNumbers function", () => { + expect(injectPositive(NUMBERS_1)).toEqual([1, 2, 3, 6]); + expect(injectPositive(NUMBERS_2)).toEqual([100, 300, 200, 600]); + expect(injectPositive(NUMBERS_3)).toEqual([5, 5]); + expect(injectPositive(NUMBERS_4)).toEqual([0]); + expect(injectPositive(NUMBERS_5)).toEqual([100, 199, 1, -5, 300, 7, 3]); + expect(injectPositive(NUMBERS_6)).toEqual([-100, 0, -200, 100, 200]); + expect(injectPositive(NUMBERS_7)).toEqual([199, 1, 550, 50, 200, 1000]); }); }); From b8777b1873553a2e2780b67fd504486b9d16bd92 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 1 Feb 2022 16:09:25 -0500 Subject: [PATCH 049/104] Fix question text --- src/arrays.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/arrays.ts b/src/arrays.ts index 7604b40cdb..4a2ffe8e5b 100644 --- a/src/arrays.ts +++ b/src/arrays.ts @@ -18,7 +18,7 @@ export function tripleNumbers(numbers: number[]): number[] { /** * Consume an array of strings and convert them to integers. If - * the number cannot be parsed as an integer, convert it to "?" instead. + * the number cannot be parsed as an integer, convert it to 0 instead. */ export function stringsToIntegers(numbers: string[]): number[] { return []; @@ -28,7 +28,7 @@ export function stringsToIntegers(numbers: string[]): number[] { * Consume an array of strings and return them as numbers. Note that * the strings MAY have "$" symbols at the beginning, in which case * those should be removed. If the result cannot be parsed as an integer, - * convert it to "?" instead. + * convert it to 0 instead. */ // Remember, you can write functions as lambdas too! They work exactly the same. export const removeDollars = (amounts: string[]): number[] => { @@ -37,7 +37,8 @@ export const removeDollars = (amounts: string[]): number[] => { /** * Consume an array of messages and return a new list of the messages. However, any - * string that ends in "!" should be made uppercase. + * string that ends in "!" should be made uppercase. Also, remove any strings that end + * in question marks ("?"). */ export const shoutIfExclaiming = (messages: string[]): string[] => { return []; @@ -65,6 +66,7 @@ export function allRGB(colors: string[]): boolean { * numbers being added together along with their actual sum. * * For instance, the array [1, 2, 3] would become "6=1+2+3". + * And the array [] would become "0=0". */ export function makeMath(addends: number[]): string { return ""; @@ -74,10 +76,10 @@ export function makeMath(addends: number[]): string { * Consumes an array of numbers and produces a new array of the same numbers, * with one difference. After the FIRST negative number, insert the sum of all * previous numbers in the list. If there are no negative numbers, then append - * 0 to the list. + * the sum to the list. * * For instance, the array [1, 9, -5, 7] would become [1, 9, -5, 10, 7] - * And the array [1, 9, 7] would become [1, 9, 0] + * And the array [1, 9, 7] would become [1, 9, 7, 17] */ export function injectPositive(values: number[]): number[] { return []; From f87771e7d8058f6c4fc6d8c6d036953f65b3a775 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Fri, 11 Feb 2022 14:24:17 -0500 Subject: [PATCH 050/104] Update arrays.test.ts --- src/arrays.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arrays.test.ts b/src/arrays.test.ts index 0881d9fe8a..3652078efa 100644 --- a/src/arrays.test.ts +++ b/src/arrays.test.ts @@ -261,7 +261,7 @@ describe("Testing the array functions", () => { ////////////////////////////////// // injectPositive - test("Testing the tripleNumbers function", () => { + test("Testing the injectPositive function", () => { expect(injectPositive(NUMBERS_1)).toEqual([1, 2, 3, 6]); expect(injectPositive(NUMBERS_2)).toEqual([100, 300, 200, 600]); expect(injectPositive(NUMBERS_3)).toEqual([5, 5]); From f0d316b36ae394d502e75849b5532b76ffdf7c68 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 13:21:13 -0400 Subject: [PATCH 051/104] Add in points --- src/arrays.test.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/arrays.test.ts b/src/arrays.test.ts index 3652078efa..c2847517bd 100644 --- a/src/arrays.test.ts +++ b/src/arrays.test.ts @@ -7,7 +7,7 @@ import { removeDollars, shoutIfExclaiming, stringsToIntegers, - tripleNumbers + tripleNumbers, } from "./arrays"; describe("Testing the array functions", () => { @@ -34,7 +34,7 @@ describe("Testing the array functions", () => { expect(NUMBERS_7).toEqual([199, 1, 550, 50, 200]); }); - test("Testing the bookEndList function", () => { + test("(3 pts) Testing the bookEndList function", () => { expect(bookEndList(NUMBERS_1)).toEqual([1, 3]); expect(bookEndList(NUMBERS_2)).toEqual([100, 200]); expect(bookEndList(NUMBERS_3)).toEqual([5, 5]); @@ -43,7 +43,7 @@ describe("Testing the array functions", () => { expect(bookEndList(NUMBERS_6)).toEqual([-100, 200]); }); - test("Testing the tripleNumbers function", () => { + test("(3 pts) Testing the tripleNumbers function", () => { expect(tripleNumbers(NUMBERS_1)).toEqual([3, 6, 9]); expect(tripleNumbers(NUMBERS_2)).toEqual([300, 900, 600]); expect(tripleNumbers(NUMBERS_3)).toEqual([15]); @@ -75,7 +75,7 @@ describe("Testing the array functions", () => { expect(VALUES_7).toEqual(["apple", "banana", "cactus"]); }); - test("Testing the stringsToIntegers function", () => { + test("(3 pts) Testing the stringsToIntegers function", () => { expect(stringsToIntegers(VALUES_1)).toEqual([1, 2, 3]); expect(stringsToIntegers(VALUES_2)).toEqual([100, 200, 300]); expect(stringsToIntegers(VALUES_3)).toEqual([5]); @@ -110,7 +110,7 @@ describe("Testing the array functions", () => { expect(AMOUNTS_8).toEqual(["$one", "two", "$three"]); }); - test("Testing the removeDollars function", () => { + test("(3 pts) Testing the removeDollars function", () => { expect(removeDollars(AMOUNTS_1)).toEqual([1, 2, 3]); expect(removeDollars(AMOUNTS_2)).toEqual([100, 200, 300, 400]); expect(removeDollars(AMOUNTS_3)).toEqual([5]); @@ -142,24 +142,24 @@ describe("Testing the array functions", () => { expect(MESSAGE_6).toEqual(["No", "Caps", "here.", "Right?"]); }); - test("Testing the shoutIfExclaiming function", () => { + test("(3 pts) Testing the shoutIfExclaiming function", () => { expect(shoutIfExclaiming(MESSAGE_1)).toEqual([ "Hello", "you", "are", - "GREAT!" + "GREAT!", ]); expect(shoutIfExclaiming(MESSAGE_2)).toEqual([ "OHO!", "OHO!", "OHO!", "OHO!", - "OHO!" + "OHO!", ]); expect(shoutIfExclaiming(MESSAGE_3)).toEqual(["Lo", "HIGH!"]); expect(shoutIfExclaiming(MESSAGE_4)).toEqual([]); expect(shoutIfExclaiming(MESSAGE_5)).toEqual([ - "THIS ONE IS VERY LONG!" + "THIS ONE IS VERY LONG!", ]); expect(shoutIfExclaiming(MESSAGE_6)).toEqual(["No", "Caps", "here."]); }); @@ -185,7 +185,7 @@ describe("Testing the array functions", () => { "four", "five", "six", - "seven" + "seven", ]); expect(WORDS_3).toEqual(["alpha", "beta", "gamma"]); expect(WORDS_4).toEqual(["Longest", "Words", "Possible"]); @@ -193,7 +193,7 @@ describe("Testing the array functions", () => { expect(WORDS_6).toEqual(["", "", "", ""]); }); - test("Testing the countShortWords function", () => { + test("(3 pts) Testing the countShortWords function", () => { expect(countShortWords(WORDS_1)).toEqual(5); expect(countShortWords(WORDS_2)).toEqual(3); expect(countShortWords(WORDS_3)).toEqual(0); @@ -226,7 +226,7 @@ describe("Testing the array functions", () => { "blue", "blue", "green", - "red" + "red", ]); expect(COLORS_4).toEqual(["purple", "orange", "violet"]); expect(COLORS_5).toEqual(["red", "blue", "yellow"]); @@ -236,7 +236,7 @@ describe("Testing the array functions", () => { expect(COLORS_9).toEqual([]); }); - test("Testing the allRGB function", () => { + test("(3 pts) Testing the allRGB function", () => { expect(allRGB(COLORS_1)).toEqual(true); expect(allRGB(COLORS_2)).toEqual(true); expect(allRGB(COLORS_3)).toEqual(true); @@ -251,7 +251,7 @@ describe("Testing the array functions", () => { ////////////////////////////////// // makeMath - test("Testing the makeMath function", () => { + test("(3 pts) Testing the makeMath function", () => { expect(makeMath(NUMBERS_1)).toEqual("6=1+2+3"); expect(makeMath(NUMBERS_2)).toEqual("600=100+300+200"); expect(makeMath(NUMBERS_3)).toEqual("5=5"); @@ -261,7 +261,7 @@ describe("Testing the array functions", () => { ////////////////////////////////// // injectPositive - test("Testing the injectPositive function", () => { + test("(3 pts) Testing the injectPositive function", () => { expect(injectPositive(NUMBERS_1)).toEqual([1, 2, 3, 6]); expect(injectPositive(NUMBERS_2)).toEqual([100, 300, 200, 600]); expect(injectPositive(NUMBERS_3)).toEqual([5, 5]); From c2e556dece7ea7737c13bdd355ef3ebcee121e70 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 2 Feb 2022 13:12:40 -0500 Subject: [PATCH 052/104] First stab at questions --- public/tasks/task-objects.md | 5 + src/data/questions.json | 79 ++++++++++ src/objects.test.ts | 295 +++++++++++++++++++++++++++++++++++ src/objects.ts | 141 +++++++++++++++++ 4 files changed, 520 insertions(+) create mode 100644 public/tasks/task-objects.md create mode 100644 src/data/questions.json create mode 100644 src/objects.test.ts create mode 100644 src/objects.ts diff --git a/public/tasks/task-objects.md b/public/tasks/task-objects.md new file mode 100644 index 0000000000..480889da0d --- /dev/null +++ b/public/tasks/task-objects.md @@ -0,0 +1,5 @@ +# Task - Objects + +Version: 0.0.1 + +Implement functions that work with objects immutably. diff --git a/src/data/questions.json b/src/data/questions.json new file mode 100644 index 0000000000..3b19537526 --- /dev/null +++ b/src/data/questions.json @@ -0,0 +1,79 @@ +{ + "BLANK_QUESTIONS": [ + { + "id": 1, + "name": "Question 1", + "body": "", + "type": "multiple_choice_question", + "options": [], + "expected": "", + "points": 1, + "published": false + }, + { + "id": 47, + "name": "My New Question", + "body": "", + "type": "multiple_choice_question", + "options": [], + "expected": "", + "points": 1, + "published": false + }, + { + "id": 2, + "name": "Question 2", + "body": "", + "type": "short_answer_question", + "options": [], + "expected": "", + "points": 1, + "published": false + } + ], + "SIMPLE_QUESTIONS": [ + { + "id": 1, + "name": "Addition", + "body": "What is 2+2?", + "type": "short_answer_question", + "options": [], + "expected": "4", + "points": 1, + "published": true + }, + { + "id": 2, + "name": "Letters", + "body": "What is the last letter of the English alphabet?", + "type": "short_answer_question", + "options": [], + "expected": "Z", + "points": 1, + "published": false + }, + { + "id": 5, + "name": "Colors", + "body": "Which of these is a color?", + "type": "multiple_choice_question", + "options": ["red", "apple", "firetruck"], + "expected": "red", + "points": 1, + "published": true + }, + { + "id": 9, + "name": "Shapes", + "body": "What shape can you make with one line?", + "type": "multiple_choice_question", + "options": ["square", "triangle", "circle"], + "expected": "circle", + "points": 2, + "published": false + } + ], + "SIMPLE_QUESTIONS_2": [], + "EMPTY_QUESTIONS": [], + "TRIVIA_QUESTIONS": [] +} diff --git a/src/objects.test.ts b/src/objects.test.ts new file mode 100644 index 0000000000..bcff7ab176 --- /dev/null +++ b/src/objects.test.ts @@ -0,0 +1,295 @@ +import { + makeBlankQuestion, + isCorrect, + Question, + isValid, + toShortForm, + toMarkdown, + duplicateQuestion, + renameQuestion, + publishQuestion, + addOption, + mergeQuestion +} from "./objects"; +import testQuestionData from "./data/questions.json"; +import backupQuestionData from "./data/questions.json"; + +//////////////////////////////////////////// +// Setting up the test data + +const { BLANK_QUESTIONS, SIMPLE_QUESTIONS }: Record = + // Typecast the test data that we imported to be a record matching + // strings to the question list + testQuestionData as Record; + +// We have backup versions of the data to make sure all changes are immutable +const { + BLANK_QUESTIONS: BACKUP_BLANK_QUESTIONS, + SIMPLE_QUESTIONS: BACKUP_SIMPLE_QUESTIONS +}: Record = backupQuestionData as Record< + string, + Question[] +>; + +// Unpack the list of simple questions into convenient constants +const [ADDITION_QUESTION, LETTER_QUESTION, COLOR_QUESTION, SHAPE_QUESTION] = + SIMPLE_QUESTIONS; +const [ + BACKUP_ADDITION_QUESTION, + BACKUP_LETTER_QUESTION, + BACKUP_COLOR_QUESTION, + BACKUP_SHAPE_QUESTION +] = BACKUP_SIMPLE_QUESTIONS; + +//////////////////////////////////////////// +// Actual tests + +describe("Testing the object functions", () => { + ////////////////////////////////// + // makeBlankQuestion + + test("Testing the makeBlankQuestion function", () => { + expect( + makeBlankQuestion(1, "Question 1", "multiple_choice_question") + ).toEqual(BLANK_QUESTIONS[0]); + expect( + makeBlankQuestion(47, "My New Question", "multiple_choice_question") + ).toEqual(BLANK_QUESTIONS[1]); + expect( + makeBlankQuestion(2, "Question 2", "short_answer_question") + ).toEqual(BLANK_QUESTIONS[2]); + }); + + /////////////////////////////////// + // isCorrect + test("Testing the isCorrect function", () => { + expect(isCorrect(ADDITION_QUESTION, "4")).toEqual(true); + expect(isCorrect(ADDITION_QUESTION, "2")).toEqual(false); + expect(isCorrect(ADDITION_QUESTION, " 4\n")).toEqual(true); + expect(isCorrect(LETTER_QUESTION, "Z")).toEqual(true); + expect(isCorrect(LETTER_QUESTION, "z")).toEqual(true); + expect(isCorrect(LETTER_QUESTION, "4")).toEqual(false); + expect(isCorrect(LETTER_QUESTION, "0")).toEqual(false); + expect(isCorrect(LETTER_QUESTION, "zed")).toEqual(false); + expect(isCorrect(COLOR_QUESTION, "red")).toEqual(true); + expect(isCorrect(COLOR_QUESTION, "apple")).toEqual(false); + expect(isCorrect(COLOR_QUESTION, "firetruck")).toEqual(false); + expect(isCorrect(SHAPE_QUESTION, "square")).toEqual(false); + expect(isCorrect(SHAPE_QUESTION, "triangle")).toEqual(false); + expect(isCorrect(SHAPE_QUESTION, "circle")).toEqual(true); + }); + + /////////////////////////////////// + // isValid + test("Testing the isValid function", () => { + expect(isValid(ADDITION_QUESTION, "4")).toEqual(true); + expect(isValid(ADDITION_QUESTION, "2")).toEqual(true); + expect(isValid(ADDITION_QUESTION, " 4\n")).toEqual(true); + expect(isValid(LETTER_QUESTION, "Z")).toEqual(true); + expect(isValid(LETTER_QUESTION, "z")).toEqual(true); + expect(isValid(LETTER_QUESTION, "4")).toEqual(true); + expect(isValid(LETTER_QUESTION, "0")).toEqual(true); + expect(isValid(LETTER_QUESTION, "zed")).toEqual(true); + expect(isValid(COLOR_QUESTION, "red")).toEqual(true); + expect(isValid(COLOR_QUESTION, "apple")).toEqual(true); + expect(isValid(COLOR_QUESTION, "firetruck")).toEqual(true); + expect(isValid(COLOR_QUESTION, "RED")).toEqual(false); + expect(isValid(COLOR_QUESTION, "orange")).toEqual(false); + expect(isValid(SHAPE_QUESTION, "square")).toEqual(true); + expect(isValid(SHAPE_QUESTION, "triangle")).toEqual(true); + expect(isValid(SHAPE_QUESTION, "circle")).toEqual(true); + expect(isValid(SHAPE_QUESTION, "circle ")).toEqual(false); + expect(isValid(SHAPE_QUESTION, "rhombus")).toEqual(false); + }); + + /////////////////////////////////// + // toShortForm + test("Testing the toShortForm function", () => { + expect(toShortForm(ADDITION_QUESTION)).toEqual("1: Addition"); + expect(toShortForm(LETTER_QUESTION)).toEqual("2: Letters"); + expect(toShortForm(COLOR_QUESTION)).toEqual("5: Colors"); + expect(toShortForm(SHAPE_QUESTION)).toEqual("9: Shapes"); + expect(toShortForm(BLANK_QUESTIONS[1])).toEqual("47: My New Que"); + }); + + /////////////////////////////////// + // toMarkdown + test("Testing the toMarkdown function", () => { + expect(toMarkdown(ADDITION_QUESTION)).toEqual(`# Addition +What is 2+2?`); + expect(toMarkdown(LETTER_QUESTION)).toEqual(`# Letters +What is the last letter of the English alphabet?`); + expect(toMarkdown(COLOR_QUESTION)).toEqual(`# Colors +Which of these is a color? +- red +- apple +- firetruck`); + expect(toMarkdown(SHAPE_QUESTION)).toEqual(`# Shapes +What shape can you make with one line? +- square +- triangle +- circle`); + }); + + afterEach(() => { + expect(ADDITION_QUESTION).toEqual(BACKUP_ADDITION_QUESTION); + expect(LETTER_QUESTION).toEqual(BACKUP_LETTER_QUESTION); + expect(SHAPE_QUESTION).toEqual(BACKUP_SHAPE_QUESTION); + expect(COLOR_QUESTION).toEqual(BACKUP_COLOR_QUESTION); + expect(BLANK_QUESTIONS).toEqual(BACKUP_BLANK_QUESTIONS); + }); + + /////////////////////////////////// + // renameQuestion + test("Testing the renameQuestion function", () => { + expect( + renameQuestion(ADDITION_QUESTION, "My Addition Question") + ).toEqual({ + id: 1, + name: "My Addition Question", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }); + expect( + renameQuestion(SHAPE_QUESTION, "I COMPLETELY CHANGED THIS NAME") + ).toEqual({ + id: 9, + name: "I COMPLETELY CHANGED THIS NAME", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + }); + }); + + /////////////////////////////////// + // publishQuestion + test("Testing the publishQuestion function", () => { + expect(publishQuestion(ADDITION_QUESTION)).toEqual({ + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: false + }); + expect(publishQuestion(LETTER_QUESTION)).toEqual({ + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: true + }); + expect(publishQuestion(publishQuestion(ADDITION_QUESTION))).toEqual({ + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }); + }); + + /////////////////////////////////// + // duplicateQuestion + test("Testing the duplicateQuestion function", () => { + expect(duplicateQuestion(9, ADDITION_QUESTION)).toEqual({ + id: 9, + name: "Copy of Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: false + }); + expect(duplicateQuestion(55, LETTER_QUESTION)).toEqual({ + id: 55, + name: "Copy of Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }); + }); + + /////////////////////////////////// + // addOption + test("Testing the addOption function", () => { + expect(addOption(SHAPE_QUESTION, "heptagon")).toEqual({ + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle", "heptagon"], + expected: "circle", + points: 2, + published: false + }); + expect(addOption(COLOR_QUESTION, "squiggles")).toEqual({ + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck", "squiggles"], + expected: "red", + points: 1, + published: true + }); + }); + + /////////////////////////////////// + // mergeQuestion + test("Testing the mergeQuestion function", () => { + expect( + mergeQuestion( + 192, + "More Points Addition", + ADDITION_QUESTION, + SHAPE_QUESTION + ) + ).toEqual({ + id: 192, + name: "More Points Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 2, + published: false + }); + + expect( + mergeQuestion( + 99, + "Less Points Shape", + SHAPE_QUESTION, + ADDITION_QUESTION + ) + ).toEqual({ + id: 99, + name: "Less Points Shape", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 1, + published: false + }); + }); +}); diff --git a/src/objects.ts b/src/objects.ts new file mode 100644 index 0000000000..d03dd473e3 --- /dev/null +++ b/src/objects.ts @@ -0,0 +1,141 @@ +/** QuestionType influences how a question is asked and what kinds of answers are possible */ +export type QuestionType = "multiple_choice_question" | "short_answer_question"; + +export interface Question { + /** A unique identifier for the question */ + id: number; + /** The human-friendly title of the question */ + name: string; + /** The instructions and content of the Question */ + body: string; + /** The kind of Question; influences how the user answers and what options are displayed */ + type: QuestionType; + /** The possible answers for a Question (for Multiple Choice questions) */ + options: string[]; + /** The actually correct answer expected */ + expected: string; + /** How many points this question is worth, roughly indicating its importance and difficulty */ + points: number; + /** Whether or not this question is ready to display to students */ + published: boolean; +} + +/** + * Create a new blank question with the given `id`, `name`, and `type. The `body` and + * `expected` should be empty strings, the `options` should be an empty list, the `points` + * should default to 1, and `published` should default to false. + */ +export function makeBlankQuestion( + id: number, + name: string, + type: QuestionType +): Question { + return {}; +} + +/** + * Consumes a question and a potential `answer`, and returns whether or not + * the `answer` is correct. You should check that the `answer` is equal to + * the `expected`, ignoring capitalization and trimming any whitespace. + * + * HINT: Look up the `trim` and `toLowerCase` functions. + */ +export function isCorrect(question: Question, answer: string): boolean { + return false; +} + +/** + * Consumes a question and a potential `answer`, and returns whether or not + * the `answer` is valid (but not necessarily correct). For a `short_answer_question`, + * any answer is valid. But for a `multiple_choice_question`, the `answer` must + * be exactly one of the options. + */ +export function isValid(question: Question, answer: string): boolean { + return false; +} + +/** + * Consumes a question and produces a string representation combining the + * `id` and first 10 characters of the `name`. The two strings should be + * separated by ": ". So for example, the question with id 9 and the + * name "My First Question" would become "9: My First Q". + */ +export function toShortForm(question: Question): string { + return ""; +} + +/** + * Consumes a question and returns a formatted string representation as follows: + * - The first line should be a hash sign, a space, and then the `name` + * - The second line should be the `body` + * - If the question is a `multiple_choice_question`, then the following lines + * need to show each option on its line, preceded by a dash and space. + * + * The example below might help, but don't include the border! + * ----------Example------------- + * |# Name | + * |The body goes here! | + * |- Option 1 | + * |- Option 2 | + * |- Option 3 | + * ------------------------------ + * Check the unit tests for more examples of what this looks like! + */ +export function toMarkdown(question: Question): string { + return ""; +} + +/** + * Return a new version of the given question, except the name should now be + * `newName`. + */ +export function renameQuestion(question: Question, newName: string): Question { + return question; +} + +/** + * Return a new version of the given question, except the `published` field + * should be inverted. If the question was not published, now it should be + * published; if it was published, now it should be not published. + */ +export function publishQuestion(question: Question): Question { + return question; +} + +/** + * Create a new question based on the old question, copying over its `body`, `type`, + * `options`, `expected`, and `points` without changes. The `name` should be copied + * over as "Copy of ORIGINAL NAME" (e.g., so "Question 1" would become "Copy of Question 1"). + * The `published` field should be reset to false. + */ +export function duplicateQuestion(id: number, oldQuestion: Question): Question { + return oldQuestion; +} + +/** + * Return a new version of the given question, with the `newOption` added to + * the list of existing `options`. Remember that the new Question MUST have + * its own separate copy of the `options` list, rather than the same reference + * to the original question's list! + * Check out the subsection about "Nested Fields" for more information. + */ +export function addOption(question: Question, newOption: string): Question { + return question; +} + +/** + * Consumes an id, name, and two questions, and produces a new question. + * The new question will use the `body`, `type`, `options`, and `expected` of the + * `contentQuestion`. The second question will provide the `points`. + * The `published` status should be set to false. + * Notice that the second Question is provided as just an object with a `points` + * field; but the function call would be the same as if it were a `Question` type! + */ +export function mergeQuestion( + id: number, + name: string, + contentQuestion: Question, + { points }: { points: number } +): Question { + return contentQuestion; +} From 406ffb2b572cb14e885af2a2fddc8e9cc42c97dd Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 6 Feb 2022 18:33:46 -0500 Subject: [PATCH 053/104] Move Question interface to separate file --- src/interfaces/question.ts | 21 +++++++++++++++++++++ src/objects.test.ts | 2 +- src/objects.ts | 22 +--------------------- 3 files changed, 23 insertions(+), 22 deletions(-) create mode 100644 src/interfaces/question.ts diff --git a/src/interfaces/question.ts b/src/interfaces/question.ts new file mode 100644 index 0000000000..a39431565e --- /dev/null +++ b/src/interfaces/question.ts @@ -0,0 +1,21 @@ +/** QuestionType influences how a question is asked and what kinds of answers are possible */ +export type QuestionType = "multiple_choice_question" | "short_answer_question"; + +export interface Question { + /** A unique identifier for the question */ + id: number; + /** The human-friendly title of the question */ + name: string; + /** The instructions and content of the Question */ + body: string; + /** The kind of Question; influences how the user answers and what options are displayed */ + type: QuestionType; + /** The possible answers for a Question (for Multiple Choice questions) */ + options: string[]; + /** The actually correct answer expected */ + expected: string; + /** How many points this question is worth, roughly indicating its importance and difficulty */ + points: number; + /** Whether or not this question is ready to display to students */ + published: boolean; +} diff --git a/src/objects.test.ts b/src/objects.test.ts index bcff7ab176..a9c76a334e 100644 --- a/src/objects.test.ts +++ b/src/objects.test.ts @@ -1,7 +1,7 @@ +import { Question } from "./interfaces/question"; import { makeBlankQuestion, isCorrect, - Question, isValid, toShortForm, toMarkdown, diff --git a/src/objects.ts b/src/objects.ts index d03dd473e3..3fd2072e5e 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -1,24 +1,4 @@ -/** QuestionType influences how a question is asked and what kinds of answers are possible */ -export type QuestionType = "multiple_choice_question" | "short_answer_question"; - -export interface Question { - /** A unique identifier for the question */ - id: number; - /** The human-friendly title of the question */ - name: string; - /** The instructions and content of the Question */ - body: string; - /** The kind of Question; influences how the user answers and what options are displayed */ - type: QuestionType; - /** The possible answers for a Question (for Multiple Choice questions) */ - options: string[]; - /** The actually correct answer expected */ - expected: string; - /** How many points this question is worth, roughly indicating its importance and difficulty */ - points: number; - /** Whether or not this question is ready to display to students */ - published: boolean; -} +import { Question, QuestionType } from "./interfaces/question"; /** * Create a new blank question with the given `id`, `name`, and `type. The `body` and From 9b9adb6f2ccbd1113a09cb8e13186d6d4f829928 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 13:27:44 -0400 Subject: [PATCH 054/104] Fix formatting --- src/objects.test.ts | 70 ++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/src/objects.test.ts b/src/objects.test.ts index a9c76a334e..4d3117405d 100644 --- a/src/objects.test.ts +++ b/src/objects.test.ts @@ -9,7 +9,7 @@ import { renameQuestion, publishQuestion, addOption, - mergeQuestion + mergeQuestion, } from "./objects"; import testQuestionData from "./data/questions.json"; import backupQuestionData from "./data/questions.json"; @@ -25,7 +25,7 @@ const { BLANK_QUESTIONS, SIMPLE_QUESTIONS }: Record = // We have backup versions of the data to make sure all changes are immutable const { BLANK_QUESTIONS: BACKUP_BLANK_QUESTIONS, - SIMPLE_QUESTIONS: BACKUP_SIMPLE_QUESTIONS + SIMPLE_QUESTIONS: BACKUP_SIMPLE_QUESTIONS, }: Record = backupQuestionData as Record< string, Question[] @@ -38,7 +38,7 @@ const [ BACKUP_ADDITION_QUESTION, BACKUP_LETTER_QUESTION, BACKUP_COLOR_QUESTION, - BACKUP_SHAPE_QUESTION + BACKUP_SHAPE_QUESTION, ] = BACKUP_SIMPLE_QUESTIONS; //////////////////////////////////////////// @@ -48,21 +48,25 @@ describe("Testing the object functions", () => { ////////////////////////////////// // makeBlankQuestion - test("Testing the makeBlankQuestion function", () => { + test("(3 pts) Testing the makeBlankQuestion function", () => { expect( - makeBlankQuestion(1, "Question 1", "multiple_choice_question") + makeBlankQuestion(1, "Question 1", "multiple_choice_question"), ).toEqual(BLANK_QUESTIONS[0]); expect( - makeBlankQuestion(47, "My New Question", "multiple_choice_question") + makeBlankQuestion( + 47, + "My New Question", + "multiple_choice_question", + ), ).toEqual(BLANK_QUESTIONS[1]); expect( - makeBlankQuestion(2, "Question 2", "short_answer_question") + makeBlankQuestion(2, "Question 2", "short_answer_question"), ).toEqual(BLANK_QUESTIONS[2]); }); /////////////////////////////////// // isCorrect - test("Testing the isCorrect function", () => { + test("(3 pts) Testing the isCorrect function", () => { expect(isCorrect(ADDITION_QUESTION, "4")).toEqual(true); expect(isCorrect(ADDITION_QUESTION, "2")).toEqual(false); expect(isCorrect(ADDITION_QUESTION, " 4\n")).toEqual(true); @@ -81,7 +85,7 @@ describe("Testing the object functions", () => { /////////////////////////////////// // isValid - test("Testing the isValid function", () => { + test("(3 pts) Testing the isValid function", () => { expect(isValid(ADDITION_QUESTION, "4")).toEqual(true); expect(isValid(ADDITION_QUESTION, "2")).toEqual(true); expect(isValid(ADDITION_QUESTION, " 4\n")).toEqual(true); @@ -104,7 +108,7 @@ describe("Testing the object functions", () => { /////////////////////////////////// // toShortForm - test("Testing the toShortForm function", () => { + test("(3 pts) Testing the toShortForm function", () => { expect(toShortForm(ADDITION_QUESTION)).toEqual("1: Addition"); expect(toShortForm(LETTER_QUESTION)).toEqual("2: Letters"); expect(toShortForm(COLOR_QUESTION)).toEqual("5: Colors"); @@ -114,7 +118,7 @@ describe("Testing the object functions", () => { /////////////////////////////////// // toMarkdown - test("Testing the toMarkdown function", () => { + test("(3 pts) Testing the toMarkdown function", () => { expect(toMarkdown(ADDITION_QUESTION)).toEqual(`# Addition What is 2+2?`); expect(toMarkdown(LETTER_QUESTION)).toEqual(`# Letters @@ -141,9 +145,9 @@ What shape can you make with one line? /////////////////////////////////// // renameQuestion - test("Testing the renameQuestion function", () => { + test("(3 pts) Testing the renameQuestion function", () => { expect( - renameQuestion(ADDITION_QUESTION, "My Addition Question") + renameQuestion(ADDITION_QUESTION, "My Addition Question"), ).toEqual({ id: 1, name: "My Addition Question", @@ -152,10 +156,10 @@ What shape can you make with one line? options: [], expected: "4", points: 1, - published: true + published: true, }); expect( - renameQuestion(SHAPE_QUESTION, "I COMPLETELY CHANGED THIS NAME") + renameQuestion(SHAPE_QUESTION, "I COMPLETELY CHANGED THIS NAME"), ).toEqual({ id: 9, name: "I COMPLETELY CHANGED THIS NAME", @@ -164,13 +168,13 @@ What shape can you make with one line? options: ["square", "triangle", "circle"], expected: "circle", points: 2, - published: false + published: false, }); }); /////////////////////////////////// // publishQuestion - test("Testing the publishQuestion function", () => { + test("(3 pts) Testing the publishQuestion function", () => { expect(publishQuestion(ADDITION_QUESTION)).toEqual({ id: 1, name: "Addition", @@ -179,7 +183,7 @@ What shape can you make with one line? options: [], expected: "4", points: 1, - published: false + published: false, }); expect(publishQuestion(LETTER_QUESTION)).toEqual({ id: 2, @@ -189,7 +193,7 @@ What shape can you make with one line? options: [], expected: "Z", points: 1, - published: true + published: true, }); expect(publishQuestion(publishQuestion(ADDITION_QUESTION))).toEqual({ id: 1, @@ -199,13 +203,13 @@ What shape can you make with one line? options: [], expected: "4", points: 1, - published: true + published: true, }); }); /////////////////////////////////// // duplicateQuestion - test("Testing the duplicateQuestion function", () => { + test("(3 pts) Testing the duplicateQuestion function", () => { expect(duplicateQuestion(9, ADDITION_QUESTION)).toEqual({ id: 9, name: "Copy of Addition", @@ -214,7 +218,7 @@ What shape can you make with one line? options: [], expected: "4", points: 1, - published: false + published: false, }); expect(duplicateQuestion(55, LETTER_QUESTION)).toEqual({ id: 55, @@ -224,13 +228,13 @@ What shape can you make with one line? options: [], expected: "Z", points: 1, - published: false + published: false, }); }); /////////////////////////////////// // addOption - test("Testing the addOption function", () => { + test("(3 pts) Testing the addOption function", () => { expect(addOption(SHAPE_QUESTION, "heptagon")).toEqual({ id: 9, name: "Shapes", @@ -239,7 +243,7 @@ What shape can you make with one line? options: ["square", "triangle", "circle", "heptagon"], expected: "circle", points: 2, - published: false + published: false, }); expect(addOption(COLOR_QUESTION, "squiggles")).toEqual({ id: 5, @@ -249,20 +253,20 @@ What shape can you make with one line? options: ["red", "apple", "firetruck", "squiggles"], expected: "red", points: 1, - published: true + published: true, }); }); /////////////////////////////////// // mergeQuestion - test("Testing the mergeQuestion function", () => { + test("(3 pts) Testing the mergeQuestion function", () => { expect( mergeQuestion( 192, "More Points Addition", ADDITION_QUESTION, - SHAPE_QUESTION - ) + SHAPE_QUESTION, + ), ).toEqual({ id: 192, name: "More Points Addition", @@ -271,7 +275,7 @@ What shape can you make with one line? options: [], expected: "4", points: 2, - published: false + published: false, }); expect( @@ -279,8 +283,8 @@ What shape can you make with one line? 99, "Less Points Shape", SHAPE_QUESTION, - ADDITION_QUESTION - ) + ADDITION_QUESTION, + ), ).toEqual({ id: 99, name: "Less Points Shape", @@ -289,7 +293,7 @@ What shape can you make with one line? options: ["square", "triangle", "circle"], expected: "circle", points: 1, - published: false + published: false, }); }); }); From 3660252c2f3f53f262fadb91e8d14d0eeffa6cd2 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 2 Feb 2022 13:12:40 -0500 Subject: [PATCH 055/104] First stab at questions --- public/tasks/task-objects.md | 5 + src/data/questions.json | 79 ++++++++++ src/objects.test.ts | 295 +++++++++++++++++++++++++++++++++++ src/objects.ts | 141 +++++++++++++++++ 4 files changed, 520 insertions(+) create mode 100644 public/tasks/task-objects.md create mode 100644 src/data/questions.json create mode 100644 src/objects.test.ts create mode 100644 src/objects.ts diff --git a/public/tasks/task-objects.md b/public/tasks/task-objects.md new file mode 100644 index 0000000000..480889da0d --- /dev/null +++ b/public/tasks/task-objects.md @@ -0,0 +1,5 @@ +# Task - Objects + +Version: 0.0.1 + +Implement functions that work with objects immutably. diff --git a/src/data/questions.json b/src/data/questions.json new file mode 100644 index 0000000000..3b19537526 --- /dev/null +++ b/src/data/questions.json @@ -0,0 +1,79 @@ +{ + "BLANK_QUESTIONS": [ + { + "id": 1, + "name": "Question 1", + "body": "", + "type": "multiple_choice_question", + "options": [], + "expected": "", + "points": 1, + "published": false + }, + { + "id": 47, + "name": "My New Question", + "body": "", + "type": "multiple_choice_question", + "options": [], + "expected": "", + "points": 1, + "published": false + }, + { + "id": 2, + "name": "Question 2", + "body": "", + "type": "short_answer_question", + "options": [], + "expected": "", + "points": 1, + "published": false + } + ], + "SIMPLE_QUESTIONS": [ + { + "id": 1, + "name": "Addition", + "body": "What is 2+2?", + "type": "short_answer_question", + "options": [], + "expected": "4", + "points": 1, + "published": true + }, + { + "id": 2, + "name": "Letters", + "body": "What is the last letter of the English alphabet?", + "type": "short_answer_question", + "options": [], + "expected": "Z", + "points": 1, + "published": false + }, + { + "id": 5, + "name": "Colors", + "body": "Which of these is a color?", + "type": "multiple_choice_question", + "options": ["red", "apple", "firetruck"], + "expected": "red", + "points": 1, + "published": true + }, + { + "id": 9, + "name": "Shapes", + "body": "What shape can you make with one line?", + "type": "multiple_choice_question", + "options": ["square", "triangle", "circle"], + "expected": "circle", + "points": 2, + "published": false + } + ], + "SIMPLE_QUESTIONS_2": [], + "EMPTY_QUESTIONS": [], + "TRIVIA_QUESTIONS": [] +} diff --git a/src/objects.test.ts b/src/objects.test.ts new file mode 100644 index 0000000000..bcff7ab176 --- /dev/null +++ b/src/objects.test.ts @@ -0,0 +1,295 @@ +import { + makeBlankQuestion, + isCorrect, + Question, + isValid, + toShortForm, + toMarkdown, + duplicateQuestion, + renameQuestion, + publishQuestion, + addOption, + mergeQuestion +} from "./objects"; +import testQuestionData from "./data/questions.json"; +import backupQuestionData from "./data/questions.json"; + +//////////////////////////////////////////// +// Setting up the test data + +const { BLANK_QUESTIONS, SIMPLE_QUESTIONS }: Record = + // Typecast the test data that we imported to be a record matching + // strings to the question list + testQuestionData as Record; + +// We have backup versions of the data to make sure all changes are immutable +const { + BLANK_QUESTIONS: BACKUP_BLANK_QUESTIONS, + SIMPLE_QUESTIONS: BACKUP_SIMPLE_QUESTIONS +}: Record = backupQuestionData as Record< + string, + Question[] +>; + +// Unpack the list of simple questions into convenient constants +const [ADDITION_QUESTION, LETTER_QUESTION, COLOR_QUESTION, SHAPE_QUESTION] = + SIMPLE_QUESTIONS; +const [ + BACKUP_ADDITION_QUESTION, + BACKUP_LETTER_QUESTION, + BACKUP_COLOR_QUESTION, + BACKUP_SHAPE_QUESTION +] = BACKUP_SIMPLE_QUESTIONS; + +//////////////////////////////////////////// +// Actual tests + +describe("Testing the object functions", () => { + ////////////////////////////////// + // makeBlankQuestion + + test("Testing the makeBlankQuestion function", () => { + expect( + makeBlankQuestion(1, "Question 1", "multiple_choice_question") + ).toEqual(BLANK_QUESTIONS[0]); + expect( + makeBlankQuestion(47, "My New Question", "multiple_choice_question") + ).toEqual(BLANK_QUESTIONS[1]); + expect( + makeBlankQuestion(2, "Question 2", "short_answer_question") + ).toEqual(BLANK_QUESTIONS[2]); + }); + + /////////////////////////////////// + // isCorrect + test("Testing the isCorrect function", () => { + expect(isCorrect(ADDITION_QUESTION, "4")).toEqual(true); + expect(isCorrect(ADDITION_QUESTION, "2")).toEqual(false); + expect(isCorrect(ADDITION_QUESTION, " 4\n")).toEqual(true); + expect(isCorrect(LETTER_QUESTION, "Z")).toEqual(true); + expect(isCorrect(LETTER_QUESTION, "z")).toEqual(true); + expect(isCorrect(LETTER_QUESTION, "4")).toEqual(false); + expect(isCorrect(LETTER_QUESTION, "0")).toEqual(false); + expect(isCorrect(LETTER_QUESTION, "zed")).toEqual(false); + expect(isCorrect(COLOR_QUESTION, "red")).toEqual(true); + expect(isCorrect(COLOR_QUESTION, "apple")).toEqual(false); + expect(isCorrect(COLOR_QUESTION, "firetruck")).toEqual(false); + expect(isCorrect(SHAPE_QUESTION, "square")).toEqual(false); + expect(isCorrect(SHAPE_QUESTION, "triangle")).toEqual(false); + expect(isCorrect(SHAPE_QUESTION, "circle")).toEqual(true); + }); + + /////////////////////////////////// + // isValid + test("Testing the isValid function", () => { + expect(isValid(ADDITION_QUESTION, "4")).toEqual(true); + expect(isValid(ADDITION_QUESTION, "2")).toEqual(true); + expect(isValid(ADDITION_QUESTION, " 4\n")).toEqual(true); + expect(isValid(LETTER_QUESTION, "Z")).toEqual(true); + expect(isValid(LETTER_QUESTION, "z")).toEqual(true); + expect(isValid(LETTER_QUESTION, "4")).toEqual(true); + expect(isValid(LETTER_QUESTION, "0")).toEqual(true); + expect(isValid(LETTER_QUESTION, "zed")).toEqual(true); + expect(isValid(COLOR_QUESTION, "red")).toEqual(true); + expect(isValid(COLOR_QUESTION, "apple")).toEqual(true); + expect(isValid(COLOR_QUESTION, "firetruck")).toEqual(true); + expect(isValid(COLOR_QUESTION, "RED")).toEqual(false); + expect(isValid(COLOR_QUESTION, "orange")).toEqual(false); + expect(isValid(SHAPE_QUESTION, "square")).toEqual(true); + expect(isValid(SHAPE_QUESTION, "triangle")).toEqual(true); + expect(isValid(SHAPE_QUESTION, "circle")).toEqual(true); + expect(isValid(SHAPE_QUESTION, "circle ")).toEqual(false); + expect(isValid(SHAPE_QUESTION, "rhombus")).toEqual(false); + }); + + /////////////////////////////////// + // toShortForm + test("Testing the toShortForm function", () => { + expect(toShortForm(ADDITION_QUESTION)).toEqual("1: Addition"); + expect(toShortForm(LETTER_QUESTION)).toEqual("2: Letters"); + expect(toShortForm(COLOR_QUESTION)).toEqual("5: Colors"); + expect(toShortForm(SHAPE_QUESTION)).toEqual("9: Shapes"); + expect(toShortForm(BLANK_QUESTIONS[1])).toEqual("47: My New Que"); + }); + + /////////////////////////////////// + // toMarkdown + test("Testing the toMarkdown function", () => { + expect(toMarkdown(ADDITION_QUESTION)).toEqual(`# Addition +What is 2+2?`); + expect(toMarkdown(LETTER_QUESTION)).toEqual(`# Letters +What is the last letter of the English alphabet?`); + expect(toMarkdown(COLOR_QUESTION)).toEqual(`# Colors +Which of these is a color? +- red +- apple +- firetruck`); + expect(toMarkdown(SHAPE_QUESTION)).toEqual(`# Shapes +What shape can you make with one line? +- square +- triangle +- circle`); + }); + + afterEach(() => { + expect(ADDITION_QUESTION).toEqual(BACKUP_ADDITION_QUESTION); + expect(LETTER_QUESTION).toEqual(BACKUP_LETTER_QUESTION); + expect(SHAPE_QUESTION).toEqual(BACKUP_SHAPE_QUESTION); + expect(COLOR_QUESTION).toEqual(BACKUP_COLOR_QUESTION); + expect(BLANK_QUESTIONS).toEqual(BACKUP_BLANK_QUESTIONS); + }); + + /////////////////////////////////// + // renameQuestion + test("Testing the renameQuestion function", () => { + expect( + renameQuestion(ADDITION_QUESTION, "My Addition Question") + ).toEqual({ + id: 1, + name: "My Addition Question", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }); + expect( + renameQuestion(SHAPE_QUESTION, "I COMPLETELY CHANGED THIS NAME") + ).toEqual({ + id: 9, + name: "I COMPLETELY CHANGED THIS NAME", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + }); + }); + + /////////////////////////////////// + // publishQuestion + test("Testing the publishQuestion function", () => { + expect(publishQuestion(ADDITION_QUESTION)).toEqual({ + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: false + }); + expect(publishQuestion(LETTER_QUESTION)).toEqual({ + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: true + }); + expect(publishQuestion(publishQuestion(ADDITION_QUESTION))).toEqual({ + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }); + }); + + /////////////////////////////////// + // duplicateQuestion + test("Testing the duplicateQuestion function", () => { + expect(duplicateQuestion(9, ADDITION_QUESTION)).toEqual({ + id: 9, + name: "Copy of Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: false + }); + expect(duplicateQuestion(55, LETTER_QUESTION)).toEqual({ + id: 55, + name: "Copy of Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }); + }); + + /////////////////////////////////// + // addOption + test("Testing the addOption function", () => { + expect(addOption(SHAPE_QUESTION, "heptagon")).toEqual({ + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle", "heptagon"], + expected: "circle", + points: 2, + published: false + }); + expect(addOption(COLOR_QUESTION, "squiggles")).toEqual({ + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck", "squiggles"], + expected: "red", + points: 1, + published: true + }); + }); + + /////////////////////////////////// + // mergeQuestion + test("Testing the mergeQuestion function", () => { + expect( + mergeQuestion( + 192, + "More Points Addition", + ADDITION_QUESTION, + SHAPE_QUESTION + ) + ).toEqual({ + id: 192, + name: "More Points Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 2, + published: false + }); + + expect( + mergeQuestion( + 99, + "Less Points Shape", + SHAPE_QUESTION, + ADDITION_QUESTION + ) + ).toEqual({ + id: 99, + name: "Less Points Shape", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 1, + published: false + }); + }); +}); diff --git a/src/objects.ts b/src/objects.ts new file mode 100644 index 0000000000..d03dd473e3 --- /dev/null +++ b/src/objects.ts @@ -0,0 +1,141 @@ +/** QuestionType influences how a question is asked and what kinds of answers are possible */ +export type QuestionType = "multiple_choice_question" | "short_answer_question"; + +export interface Question { + /** A unique identifier for the question */ + id: number; + /** The human-friendly title of the question */ + name: string; + /** The instructions and content of the Question */ + body: string; + /** The kind of Question; influences how the user answers and what options are displayed */ + type: QuestionType; + /** The possible answers for a Question (for Multiple Choice questions) */ + options: string[]; + /** The actually correct answer expected */ + expected: string; + /** How many points this question is worth, roughly indicating its importance and difficulty */ + points: number; + /** Whether or not this question is ready to display to students */ + published: boolean; +} + +/** + * Create a new blank question with the given `id`, `name`, and `type. The `body` and + * `expected` should be empty strings, the `options` should be an empty list, the `points` + * should default to 1, and `published` should default to false. + */ +export function makeBlankQuestion( + id: number, + name: string, + type: QuestionType +): Question { + return {}; +} + +/** + * Consumes a question and a potential `answer`, and returns whether or not + * the `answer` is correct. You should check that the `answer` is equal to + * the `expected`, ignoring capitalization and trimming any whitespace. + * + * HINT: Look up the `trim` and `toLowerCase` functions. + */ +export function isCorrect(question: Question, answer: string): boolean { + return false; +} + +/** + * Consumes a question and a potential `answer`, and returns whether or not + * the `answer` is valid (but not necessarily correct). For a `short_answer_question`, + * any answer is valid. But for a `multiple_choice_question`, the `answer` must + * be exactly one of the options. + */ +export function isValid(question: Question, answer: string): boolean { + return false; +} + +/** + * Consumes a question and produces a string representation combining the + * `id` and first 10 characters of the `name`. The two strings should be + * separated by ": ". So for example, the question with id 9 and the + * name "My First Question" would become "9: My First Q". + */ +export function toShortForm(question: Question): string { + return ""; +} + +/** + * Consumes a question and returns a formatted string representation as follows: + * - The first line should be a hash sign, a space, and then the `name` + * - The second line should be the `body` + * - If the question is a `multiple_choice_question`, then the following lines + * need to show each option on its line, preceded by a dash and space. + * + * The example below might help, but don't include the border! + * ----------Example------------- + * |# Name | + * |The body goes here! | + * |- Option 1 | + * |- Option 2 | + * |- Option 3 | + * ------------------------------ + * Check the unit tests for more examples of what this looks like! + */ +export function toMarkdown(question: Question): string { + return ""; +} + +/** + * Return a new version of the given question, except the name should now be + * `newName`. + */ +export function renameQuestion(question: Question, newName: string): Question { + return question; +} + +/** + * Return a new version of the given question, except the `published` field + * should be inverted. If the question was not published, now it should be + * published; if it was published, now it should be not published. + */ +export function publishQuestion(question: Question): Question { + return question; +} + +/** + * Create a new question based on the old question, copying over its `body`, `type`, + * `options`, `expected`, and `points` without changes. The `name` should be copied + * over as "Copy of ORIGINAL NAME" (e.g., so "Question 1" would become "Copy of Question 1"). + * The `published` field should be reset to false. + */ +export function duplicateQuestion(id: number, oldQuestion: Question): Question { + return oldQuestion; +} + +/** + * Return a new version of the given question, with the `newOption` added to + * the list of existing `options`. Remember that the new Question MUST have + * its own separate copy of the `options` list, rather than the same reference + * to the original question's list! + * Check out the subsection about "Nested Fields" for more information. + */ +export function addOption(question: Question, newOption: string): Question { + return question; +} + +/** + * Consumes an id, name, and two questions, and produces a new question. + * The new question will use the `body`, `type`, `options`, and `expected` of the + * `contentQuestion`. The second question will provide the `points`. + * The `published` status should be set to false. + * Notice that the second Question is provided as just an object with a `points` + * field; but the function call would be the same as if it were a `Question` type! + */ +export function mergeQuestion( + id: number, + name: string, + contentQuestion: Question, + { points }: { points: number } +): Question { + return contentQuestion; +} From 09d3d4f104a2cacab2641271c5c6cab55424efd1 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sun, 6 Feb 2022 18:33:46 -0500 Subject: [PATCH 056/104] Move Question interface to separate file --- src/interfaces/question.ts | 21 +++++++++++++++++++++ src/objects.test.ts | 2 +- src/objects.ts | 22 +--------------------- 3 files changed, 23 insertions(+), 22 deletions(-) create mode 100644 src/interfaces/question.ts diff --git a/src/interfaces/question.ts b/src/interfaces/question.ts new file mode 100644 index 0000000000..a39431565e --- /dev/null +++ b/src/interfaces/question.ts @@ -0,0 +1,21 @@ +/** QuestionType influences how a question is asked and what kinds of answers are possible */ +export type QuestionType = "multiple_choice_question" | "short_answer_question"; + +export interface Question { + /** A unique identifier for the question */ + id: number; + /** The human-friendly title of the question */ + name: string; + /** The instructions and content of the Question */ + body: string; + /** The kind of Question; influences how the user answers and what options are displayed */ + type: QuestionType; + /** The possible answers for a Question (for Multiple Choice questions) */ + options: string[]; + /** The actually correct answer expected */ + expected: string; + /** How many points this question is worth, roughly indicating its importance and difficulty */ + points: number; + /** Whether or not this question is ready to display to students */ + published: boolean; +} diff --git a/src/objects.test.ts b/src/objects.test.ts index bcff7ab176..a9c76a334e 100644 --- a/src/objects.test.ts +++ b/src/objects.test.ts @@ -1,7 +1,7 @@ +import { Question } from "./interfaces/question"; import { makeBlankQuestion, isCorrect, - Question, isValid, toShortForm, toMarkdown, diff --git a/src/objects.ts b/src/objects.ts index d03dd473e3..3fd2072e5e 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -1,24 +1,4 @@ -/** QuestionType influences how a question is asked and what kinds of answers are possible */ -export type QuestionType = "multiple_choice_question" | "short_answer_question"; - -export interface Question { - /** A unique identifier for the question */ - id: number; - /** The human-friendly title of the question */ - name: string; - /** The instructions and content of the Question */ - body: string; - /** The kind of Question; influences how the user answers and what options are displayed */ - type: QuestionType; - /** The possible answers for a Question (for Multiple Choice questions) */ - options: string[]; - /** The actually correct answer expected */ - expected: string; - /** How many points this question is worth, roughly indicating its importance and difficulty */ - points: number; - /** Whether or not this question is ready to display to students */ - published: boolean; -} +import { Question, QuestionType } from "./interfaces/question"; /** * Create a new blank question with the given `id`, `name`, and `type. The `body` and From 9a2402444e847b2c05ce0c9a6887534a249d7c46 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 8 Feb 2022 00:36:21 -0500 Subject: [PATCH 057/104] Create answer interface --- src/interfaces/answer.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/interfaces/answer.ts diff --git a/src/interfaces/answer.ts b/src/interfaces/answer.ts new file mode 100644 index 0000000000..743ee8dff9 --- /dev/null +++ b/src/interfaces/answer.ts @@ -0,0 +1,13 @@ +/*** + * A representation of a students' answer in a quizzing game + */ +export interface Answer { + /** The ID of the question being answered. */ + questionId: number; + /** The text that the student entered for their answer. */ + text: string; + /** Whether or not the student has submitted this answer. */ + submitted: boolean; + /** Whether or not the students' answer matched the expected. */ + correct: boolean; +} From 879fe177e356794eedf9f893fe0e865e49f36eb7 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 8 Feb 2022 00:36:37 -0500 Subject: [PATCH 058/104] First stab at nested tasks --- src/nested.test.ts | 57 +++++++++++++++ src/nested.ts | 178 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) create mode 100644 src/nested.test.ts create mode 100644 src/nested.ts diff --git a/src/nested.test.ts b/src/nested.test.ts new file mode 100644 index 0000000000..1e3ff24b5c --- /dev/null +++ b/src/nested.test.ts @@ -0,0 +1,57 @@ +import { Question } from "./interfaces/question"; +import { getPublishedQuestions } from "./nested"; +import testQuestionData from "./data/questions.json"; +import backupQuestionData from "./data/questions.json"; + +const { BLANK_QUESTIONS, SIMPLE_QUESTIONS }: Record = + // Typecast the test data that we imported to be a record matching + // strings to the question list + testQuestionData as Record; + +// We have backup versions of the data to make sure all changes are immutable +const { + BLANK_QUESTIONS: BACKUP_BLANK_QUESTIONS, + SIMPLE_QUESTIONS: BACKUP_SIMPLE_QUESTIONS +}: Record = backupQuestionData as Record< + string, + Question[] +>; + +//////////////////////////////////////////// +// Actual tests + +describe("Testing the Question[] functions", () => { + ////////////////////////////////// + // getPublishedQuestions + + test("Testing the getPublishedQuestions function", () => { + expect(getPublishedQuestions(BLANK_QUESTIONS)).toEqual([]); + expect(getPublishedQuestions(SIMPLE_QUESTIONS)).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck"], + expected: "red", + points: 1, + published: true + } + ]); + }); + + afterEach(() => { + expect(BLANK_QUESTIONS).toEqual(BACKUP_BLANK_QUESTIONS); + expect(SIMPLE_QUESTIONS).toEqual(BACKUP_SIMPLE_QUESTIONS); + }); +}); diff --git a/src/nested.ts b/src/nested.ts new file mode 100644 index 0000000000..b9fb13f3cf --- /dev/null +++ b/src/nested.ts @@ -0,0 +1,178 @@ +import { Answer } from "./interfaces/answer"; +import { Question, QuestionType } from "./interfaces/question"; + +/** + * Consumes an array of questions and returns a new array with only the questions + * that are `published`. + */ +export function getPublishedQuestions(questions: Question[]): Question[] { + return []; +} + +/** + * Consumes an array of questions and returns a new array of only the questions that are + * considered "non-empty". An empty question has an empty string for its `body` and + * `expected`, and an empty array for its `options`. + */ +export function getNonEmptyQuestions(questions: Question[]): Question[] { + return []; +} + +/*** + * Consumes an array of questions and returns the question with the given `id`. If the + * question is not found, return `null` instead. + */ +export function findQuestion( + questions: Question[], + id: number +): Question | null { + return null; +} + +/** + * Consumes an array of questions and returns a new array that does not contain the question + * with the given `id`. + */ +export function removeQuestion(questions: Question[], id: number): Question[] { + return []; +} + +/*** + * Consumes an array of questions and returns a new array containing just the names of the + * questions, as an array. + */ +export function getNames(questions: Question[]): string[] { + return []; +} + +/*** + * Consumes an array of questions and returns the sum total of all their points added together. + */ +export function sumPoints(questions: Question[]): number { + return 0; +} + +/*** + * Consumes an array of questions and returns the sum total of the PUBLISHED questions. + */ +export function sumPublishedPoints(questions: Question[]): number { + return 0; +} + +/*** + * Consumes an array of questions, and produces a Comma-Separated Value (CSV) string representation. + * A CSV is a type of file frequently used to share tabular data; we will use a single string + * to represent the entire file. The first line of the file is the headers "id", "name", "options", + * "points", and "published". The following line contains the value for each question, separated by + * commas. For the `options` field, use the NUMBER of options. + * + * Here is an example of what this will look like (do not include the border). + *` +id,name,options,points,published +1,Addition,0,1,true +2,Letters,0,1,false +5,Colors,3,1,true +9,Shapes,3,2,false +` * + * Check the unit tests for more examples! + */ +export function toCSV(questions: Question[]): string { + return ""; +} + +/** + * Consumes an array of Questions and produces a corresponding array of + * Answers. Each Question gets its own Answer, copying over the `id` as the `questionId`, + * making the `text` an empty string, and using false for both `submitted` and `correct`. + */ +export function makeAnswers(questions: Question[]): Answer[] { + return []; +} + +/*** + * Consumes an array of Questions and produces a new array of questions, where + * each question is now published, regardless of its previous published status. + */ +export function publishAll(questions: Question[]): Question[] { + return []; +} + +/*** + * Consumes an array of Questions and produces whether or not all the questions + * are the same type. They can be any type, as long as they are all the SAME type. + */ +export function sameType(questions: Question[]): boolean { + return false; +} + +/*** + * Consumes an array of Questions and produces a new array of the same Questions, + * except that a blank question has been added onto the end. Reuse the `makeBlankQuestion` + * you defined in the `objects.ts` file. + */ +export function addNewQuestion( + questions: Question[], + id: number, + name: string, + type: QuestionType +): Question[] { + return []; +} + +/*** + * Consumes an array of Questions and produces a new array of Questions, where all + * the Questions are the same EXCEPT for the one with the given `targetId`. That + * Question should be the same EXCEPT that its name should now be `newName`. + */ +export function renameQuestionById( + questions: Question[], + targetId: number, + newName: string +): Question[] { + return []; +} + +/*** + * Consumes an array of Questions and produces a new array of Questions, where all + * the Questions are the same EXCEPT for the one with the given `targetId`. That + * Question should be the same EXCEPT that its `type` should now be the `newQuestionType` + * AND if the `newQuestionType` is no longer "multiple_choice_question" than the `options` + * must be set to an empty list. + */ +export function changeQuestionTypeById( + questions: Question[], + targetId: number, + newQuestionType: QuestionType +): Question[] { + return []; +} + +/** + * Consumes an array of Questions and produces a new array of Questions, where all + * the Questions are the same EXCEPT for the one with the given `targetId`. That + * Question should be the same EXCEPT that its `option` array should have a new element. + * If the `targetOptionIndex` is -1, the `newOption` should be added to the end of the list. + * Otherwise, it should *replace* the existing element at the `targetOptionIndex`. + */ +export function editOption( + questions: Question[], + targetId: number, + targetOptionIndex: number, + newOption: string +) { + return []; +} + +/*** + * Consumes an array of questions, and produces a new array based on the original array. + * The only difference is that the question with id `targetId` should now be duplicated, with + * the duplicate inserted directly after the original question. Use the `duplicateQuestion` + * function you defined previously; the `newId` is the parameter to use for the duplicate's ID. + */ +export function duplicateQuestionInArray( + questions: Question[], + targetId: number, + newId: number +): Question[] { + return []; +} From 4d29d2132a060f7f91420d71eea4e80ab72e7727 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 9 Feb 2022 13:20:35 -0500 Subject: [PATCH 059/104] Document Question interface --- src/interfaces/question.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/interfaces/question.ts b/src/interfaces/question.ts index a39431565e..5def48f2f7 100644 --- a/src/interfaces/question.ts +++ b/src/interfaces/question.ts @@ -1,6 +1,7 @@ /** QuestionType influences how a question is asked and what kinds of answers are possible */ export type QuestionType = "multiple_choice_question" | "short_answer_question"; +/** A representation of a Question in a quizzing application */ export interface Question { /** A unique identifier for the question */ id: number; From d71d9fcc94831dc1aea8d2aa847feeadeed6b9c4 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 9 Feb 2022 13:20:46 -0500 Subject: [PATCH 060/104] Expand questions test data --- src/data/questions.json | 147 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 3 deletions(-) diff --git a/src/data/questions.json b/src/data/questions.json index 3b19537526..0411f30afe 100644 --- a/src/data/questions.json +++ b/src/data/questions.json @@ -73,7 +73,148 @@ "published": false } ], - "SIMPLE_QUESTIONS_2": [], - "EMPTY_QUESTIONS": [], - "TRIVIA_QUESTIONS": [] + "TRIVIA_QUESTIONS": [ + { + "id": 1, + "name": "Mascot", + "body": "What is the name of the UD Mascot?", + "type": "multiple_choice_question", + "options": ["Bluey", "YoUDee", "Charles the Wonder Dog"], + "expected": "YoUDee", + "points": 7, + "published": false + }, + { + "id": 2, + "name": "Motto", + "body": "What is the University of Delaware's motto?", + "type": "multiple_choice_question", + "options": [ + "Knowledge is the light of the mind", + "Just U Do it", + "Nothing, what's the motto with you?" + ], + "expected": "Knowledge is the light of the mind", + "points": 3, + "published": false + }, + { + "id": 3, + "name": "Goats", + "body": "How many goats are there usually on the Green?", + "type": "multiple_choice_question", + "options": [ + "Zero, why would there be goats on the green?", + "18420", + "Two" + ], + "expected": "Two", + "points": 10, + "published": false + } + ], + "EMPTY_QUESTIONS": [ + { + "id": 1, + "name": "Empty 1", + "body": "This question is not empty, right?", + "type": "multiple_choice_question", + "options": ["correct", "it is", "not"], + "expected": "correct", + "points": 5, + "published": true + }, + { + "id": 2, + "name": "Empty 2", + "body": "", + "type": "multiple_choice_question", + "options": ["this", "one", "is", "not", "empty", "either"], + "expected": "one", + "points": 5, + "published": true + }, + { + "id": 3, + "name": "Empty 3", + "body": "This questions is not empty either!", + "type": "short_answer_question", + "options": [], + "expected": "", + "points": 5, + "published": true + }, + { + "id": 4, + "name": "Empty 4", + "body": "", + "type": "short_answer_question", + "options": [], + "expected": "Even this one is not empty", + "points": 5, + "published": true + }, + { + "id": 5, + "name": "Empty 5 (Actual)", + "body": "", + "type": "short_answer_question", + "options": [], + "expected": "", + "points": 5, + "published": false + } + ], + "SIMPLE_QUESTIONS_2": [ + { + "id": 478, + "name": "Students", + "body": "How many students are taking CISC275 this semester?", + "type": "short_answer_question", + "options": [], + "expected": "90", + "points": 53, + "published": true + }, + { + "id": 1937, + "name": "Importance", + "body": "On a scale of 1 to 10, how important is this quiz for them?", + "type": "short_answer_question", + "options": [], + "expected": "10", + "points": 47, + "published": true + }, + { + "id": 479, + "name": "Sentience", + "body": "Is it technically possible for this quiz to become sentient?", + "type": "short_answer_question", + "options": [], + "expected": "Yes", + "points": 40, + "published": true + }, + { + "id": 777, + "name": "Danger", + "body": "If this quiz became sentient, would it pose a danger to others?", + "type": "short_answer_question", + "options": [], + "expected": "Yes", + "points": 60, + "published": true + }, + { + "id": 1937, + "name": "Listening", + "body": "Is this quiz listening to us right now?", + "type": "short_answer_question", + "options": [], + "expected": "Yes", + "points": 100, + "published": true + } + ] } From c955718b2a52fe88f0f3b27b00b8fcb74e8be0ca Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 9 Feb 2022 13:21:43 -0500 Subject: [PATCH 061/104] Add a little hint for a tough one --- src/nested.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/nested.ts b/src/nested.ts index b9fb13f3cf..7934ec1741 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -153,6 +153,9 @@ export function changeQuestionTypeById( * Question should be the same EXCEPT that its `option` array should have a new element. * If the `targetOptionIndex` is -1, the `newOption` should be added to the end of the list. * Otherwise, it should *replace* the existing element at the `targetOptionIndex`. + * + * Remember, if a function starts getting too complicated, think about how a helper function + * can make it simpler! Break down complicated tasks into little pieces. */ export function editOption( questions: Question[], From c574699cc746d22d95223bfc85d2e0cb8d5843e8 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Wed, 9 Feb 2022 13:22:01 -0500 Subject: [PATCH 062/104] Nested tests (phew) --- src/nested.test.ts | 1187 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1184 insertions(+), 3 deletions(-) diff --git a/src/nested.test.ts b/src/nested.test.ts index 1e3ff24b5c..3d2b75406d 100644 --- a/src/nested.test.ts +++ b/src/nested.test.ts @@ -1,9 +1,32 @@ import { Question } from "./interfaces/question"; -import { getPublishedQuestions } from "./nested"; +import { + getPublishedQuestions, + getNonEmptyQuestions, + findQuestion, + removeQuestion, + getNames, + sumPoints, + sumPublishedPoints, + toCSV, + makeAnswers, + publishAll, + sameType, + addNewQuestion, + renameQuestionById, + changeQuestionTypeById, + editOption, + duplicateQuestionInArray +} from "./nested"; import testQuestionData from "./data/questions.json"; import backupQuestionData from "./data/questions.json"; -const { BLANK_QUESTIONS, SIMPLE_QUESTIONS }: Record = +const { + BLANK_QUESTIONS, + SIMPLE_QUESTIONS, + TRIVIA_QUESTIONS, + EMPTY_QUESTIONS, + SIMPLE_QUESTIONS_2 +}: Record = // Typecast the test data that we imported to be a record matching // strings to the question list testQuestionData as Record; @@ -11,12 +34,41 @@ const { BLANK_QUESTIONS, SIMPLE_QUESTIONS }: Record = // We have backup versions of the data to make sure all changes are immutable const { BLANK_QUESTIONS: BACKUP_BLANK_QUESTIONS, - SIMPLE_QUESTIONS: BACKUP_SIMPLE_QUESTIONS + SIMPLE_QUESTIONS: BACKUP_SIMPLE_QUESTIONS, + TRIVIA_QUESTIONS: BACKUP_TRIVIA_QUESTIONS, + EMPTY_QUESTIONS: BACKUP_EMPTY_QUESTIONS, + SIMPLE_QUESTIONS_2: BACKUP_SIMPLE_QUESTIONS_2 }: Record = backupQuestionData as Record< string, Question[] >; +const NEW_BLANK_QUESTION = { + id: 142, + name: "A new question", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false +}; + +const NEW_TRIVIA_QUESTION = { + id: 449, + name: "Colors", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + /*body: "The official colors of UD are Blue and ...?", + type: "multiple_choice_question", + options: ["Black, like my soul", "Blue again, we're tricky.", "#FFD200"], + expected: "#FFD200",*/ + points: 1, + published: false +}; + //////////////////////////////////////////// // Actual tests @@ -48,10 +100,1139 @@ describe("Testing the Question[] functions", () => { published: true } ]); + expect(getPublishedQuestions(TRIVIA_QUESTIONS)).toEqual([]); + expect(getPublishedQuestions(SIMPLE_QUESTIONS_2)).toEqual( + BACKUP_SIMPLE_QUESTIONS_2 + ); + expect(getPublishedQuestions(EMPTY_QUESTIONS)).toEqual([ + { + id: 1, + name: "Empty 1", + body: "This question is not empty, right?", + type: "multiple_choice_question", + options: ["correct", "it is", "not"], + expected: "correct", + points: 5, + published: true + }, + { + id: 2, + name: "Empty 2", + body: "", + type: "multiple_choice_question", + options: ["this", "one", "is", "not", "empty", "either"], + expected: "one", + points: 5, + published: true + }, + { + id: 3, + name: "Empty 3", + body: "This questions is not empty either!", + type: "short_answer_question", + options: [], + expected: "", + points: 5, + published: true + }, + { + id: 4, + name: "Empty 4", + body: "", + type: "short_answer_question", + options: [], + expected: "Even this one is not empty", + points: 5, + published: true + } + ]); + }); + + test("Testing the getNonEmptyQuestions functions", () => { + expect(getNonEmptyQuestions(BLANK_QUESTIONS)).toEqual([]); + expect(getNonEmptyQuestions(SIMPLE_QUESTIONS)).toEqual( + BACKUP_SIMPLE_QUESTIONS + ); + expect(getNonEmptyQuestions(TRIVIA_QUESTIONS)).toEqual( + BACKUP_TRIVIA_QUESTIONS + ); + expect(getNonEmptyQuestions(SIMPLE_QUESTIONS_2)).toEqual( + BACKUP_SIMPLE_QUESTIONS_2 + ); + expect(getNonEmptyQuestions(EMPTY_QUESTIONS)).toEqual([ + { + id: 1, + name: "Empty 1", + body: "This question is not empty, right?", + type: "multiple_choice_question", + options: ["correct", "it is", "not"], + expected: "correct", + points: 5, + published: true + }, + { + id: 2, + name: "Empty 2", + body: "", + type: "multiple_choice_question", + options: ["this", "one", "is", "not", "empty", "either"], + expected: "one", + points: 5, + published: true + }, + { + id: 3, + name: "Empty 3", + body: "This questions is not empty either!", + type: "short_answer_question", + options: [], + expected: "", + points: 5, + published: true + }, + { + id: 4, + name: "Empty 4", + body: "", + type: "short_answer_question", + options: [], + expected: "Even this one is not empty", + points: 5, + published: true + } + ]); + }); + + test("Testing the findQuestion function", () => { + expect(findQuestion(BLANK_QUESTIONS, 1)).toEqual(BLANK_QUESTIONS[0]); + expect(findQuestion(BLANK_QUESTIONS, 47)).toEqual(BLANK_QUESTIONS[1]); + expect(findQuestion(BLANK_QUESTIONS, 2)).toEqual(BLANK_QUESTIONS[2]); + expect(findQuestion(BLANK_QUESTIONS, 3)).toEqual(null); + expect(findQuestion(SIMPLE_QUESTIONS, 1)).toEqual(SIMPLE_QUESTIONS[0]); + expect(findQuestion(SIMPLE_QUESTIONS, 2)).toEqual(SIMPLE_QUESTIONS[1]); + expect(findQuestion(SIMPLE_QUESTIONS, 5)).toEqual(SIMPLE_QUESTIONS[2]); + expect(findQuestion(SIMPLE_QUESTIONS, 9)).toEqual(SIMPLE_QUESTIONS[3]); + expect(findQuestion(SIMPLE_QUESTIONS, 6)).toEqual(null); + expect(findQuestion(SIMPLE_QUESTIONS_2, 478)).toEqual( + SIMPLE_QUESTIONS_2[0] + ); + expect(findQuestion([], 0)).toEqual(null); + }); + + test("Testing the removeQuestion", () => { + expect(removeQuestion(BLANK_QUESTIONS, 1)).toEqual([ + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(removeQuestion(BLANK_QUESTIONS, 47)).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(removeQuestion(BLANK_QUESTIONS, 2)).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(removeQuestion(SIMPLE_QUESTIONS, 9)).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }, + { + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck"], + expected: "red", + points: 1, + published: true + } + ]); + expect(removeQuestion(SIMPLE_QUESTIONS, 5)).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }, + { + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + } + ]); + }); + + test("Testing the getNames function", () => { + expect(getNames(BLANK_QUESTIONS)).toEqual([ + "Question 1", + "My New Question", + "Question 2" + ]); + expect(getNames(SIMPLE_QUESTIONS)).toEqual([ + "Addition", + "Letters", + "Colors", + "Shapes" + ]); + expect(getNames(TRIVIA_QUESTIONS)).toEqual([ + "Mascot", + "Motto", + "Goats" + ]); + expect(getNames(SIMPLE_QUESTIONS_2)).toEqual([ + "Students", + "Importance", + "Sentience", + "Danger", + "Listening" + ]); + expect(getNames(EMPTY_QUESTIONS)).toEqual([ + "Empty 1", + "Empty 2", + "Empty 3", + "Empty 4", + "Empty 5 (Actual)" + ]); + }); + + test("Testing the sumPoints function", () => { + expect(sumPoints(BLANK_QUESTIONS)).toEqual(3); + expect(sumPoints(SIMPLE_QUESTIONS)).toEqual(5); + expect(sumPoints(TRIVIA_QUESTIONS)).toEqual(20); + expect(sumPoints(EMPTY_QUESTIONS)).toEqual(25); + expect(sumPoints(SIMPLE_QUESTIONS_2)).toEqual(300); + }); + + test("Testing the sumPublishedPoints function", () => { + expect(sumPublishedPoints(BLANK_QUESTIONS)).toEqual(0); + expect(sumPublishedPoints(SIMPLE_QUESTIONS)).toEqual(2); + expect(sumPublishedPoints(TRIVIA_QUESTIONS)).toEqual(0); + expect(sumPublishedPoints(EMPTY_QUESTIONS)).toEqual(20); + expect(sumPublishedPoints(SIMPLE_QUESTIONS_2)).toEqual(300); + }); + + test("Testing the toCSV function", () => { + expect(toCSV(BLANK_QUESTIONS)).toEqual(`id,name,options,points,published +1,Question 1,0,1,false +47,My New Question,0,1,false +2,Question 2,0,1,false`); + expect(toCSV(SIMPLE_QUESTIONS)) + .toEqual(`id,name,options,points,published +1,Addition,0,1,true +2,Letters,0,1,false +5,Colors,3,1,true +9,Shapes,3,2,false`); + expect(toCSV(TRIVIA_QUESTIONS)) + .toEqual(`id,name,options,points,published +1,Mascot,3,7,false +2,Motto,3,3,false +3,Goats,3,10,false`); + expect(toCSV(EMPTY_QUESTIONS)).toEqual(`id,name,options,points,published +1,Empty 1,3,5,true +2,Empty 2,6,5,true +3,Empty 3,0,5,true +4,Empty 4,0,5,true +5,Empty 5 (Actual),0,5,false`); + expect(toCSV(SIMPLE_QUESTIONS_2)) + .toEqual(`id,name,options,points,published +478,Students,0,53,true +1937,Importance,0,47,true +479,Sentience,0,40,true +777,Danger,0,60,true +1937,Listening,0,100,true`); + }); + + test("Testing the makeAnswers function", () => { + expect(makeAnswers(BLANK_QUESTIONS)).toEqual([ + { questionId: 1, correct: false, text: "", submitted: false }, + { questionId: 47, correct: false, text: "", submitted: false }, + { questionId: 2, correct: false, text: "", submitted: false } + ]); + expect(makeAnswers(SIMPLE_QUESTIONS)).toEqual([ + { questionId: 1, correct: false, text: "", submitted: false }, + { questionId: 2, correct: false, text: "", submitted: false }, + { questionId: 5, correct: false, text: "", submitted: false }, + { questionId: 9, correct: false, text: "", submitted: false } + ]); + expect(makeAnswers(TRIVIA_QUESTIONS)).toEqual([ + { questionId: 1, correct: false, text: "", submitted: false }, + { questionId: 2, correct: false, text: "", submitted: false }, + { questionId: 3, correct: false, text: "", submitted: false } + ]); + expect(makeAnswers(SIMPLE_QUESTIONS_2)).toEqual([ + { questionId: 478, correct: false, text: "", submitted: false }, + { questionId: 1937, correct: false, text: "", submitted: false }, + { questionId: 479, correct: false, text: "", submitted: false }, + { questionId: 777, correct: false, text: "", submitted: false }, + { questionId: 1937, correct: false, text: "", submitted: false } + ]); + expect(makeAnswers(EMPTY_QUESTIONS)).toEqual([ + { questionId: 1, correct: false, text: "", submitted: false }, + { questionId: 2, correct: false, text: "", submitted: false }, + { questionId: 3, correct: false, text: "", submitted: false }, + { questionId: 4, correct: false, text: "", submitted: false }, + { questionId: 5, correct: false, text: "", submitted: false } + ]); + }); + + test("Testing the publishAll function", () => { + expect(publishAll(BLANK_QUESTIONS)).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: true + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: true + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: true + } + ]); + expect(publishAll(SIMPLE_QUESTIONS)).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: true + }, + { + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck"], + expected: "red", + points: 1, + published: true + }, + { + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: true + } + ]); + expect(publishAll(TRIVIA_QUESTIONS)).toEqual([ + { + id: 1, + name: "Mascot", + body: "What is the name of the UD Mascot?", + type: "multiple_choice_question", + options: ["Bluey", "YoUDee", "Charles the Wonder Dog"], + expected: "YoUDee", + points: 7, + published: true + }, + { + id: 2, + name: "Motto", + body: "What is the University of Delaware's motto?", + type: "multiple_choice_question", + options: [ + "Knowledge is the light of the mind", + "Just U Do it", + "Nothing, what's the motto with you?" + ], + expected: "Knowledge is the light of the mind", + points: 3, + published: true + }, + { + id: 3, + name: "Goats", + body: "How many goats are there usually on the Green?", + type: "multiple_choice_question", + options: [ + "Zero, why would there be goats on the green?", + "18420", + "Two" + ], + expected: "Two", + points: 10, + published: true + } + ]); + expect(publishAll(EMPTY_QUESTIONS)).toEqual([ + { + id: 1, + name: "Empty 1", + body: "This question is not empty, right?", + type: "multiple_choice_question", + options: ["correct", "it is", "not"], + expected: "correct", + points: 5, + published: true + }, + { + id: 2, + name: "Empty 2", + body: "", + type: "multiple_choice_question", + options: ["this", "one", "is", "not", "empty", "either"], + expected: "one", + points: 5, + published: true + }, + { + id: 3, + name: "Empty 3", + body: "This questions is not empty either!", + type: "short_answer_question", + options: [], + expected: "", + points: 5, + published: true + }, + { + id: 4, + name: "Empty 4", + body: "", + type: "short_answer_question", + options: [], + expected: "Even this one is not empty", + points: 5, + published: true + }, + { + id: 5, + name: "Empty 5 (Actual)", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 5, + published: true + } + ]); + expect(publishAll(SIMPLE_QUESTIONS_2)).toEqual(SIMPLE_QUESTIONS_2); + }); + + test("Testing the sameType function", () => { + expect(sameType([])).toEqual(true); + expect(sameType(BLANK_QUESTIONS)).toEqual(false); + expect(sameType(SIMPLE_QUESTIONS)).toEqual(false); + expect(sameType(TRIVIA_QUESTIONS)).toEqual(true); + expect(sameType(EMPTY_QUESTIONS)).toEqual(false); + expect(sameType(SIMPLE_QUESTIONS_2)).toEqual(true); + }); + + test("Testing the addNewQuestion function", () => { + expect( + addNewQuestion([], 142, "A new question", "short_answer_question") + ).toEqual([NEW_BLANK_QUESTION]); + expect( + addNewQuestion( + BLANK_QUESTIONS, + 142, + "A new question", + "short_answer_question" + ) + ).toEqual([...BLANK_QUESTIONS, NEW_BLANK_QUESTION]); + expect( + addNewQuestion( + TRIVIA_QUESTIONS, + 449, + "Colors", + "multiple_choice_question" + ) + ).toEqual([...TRIVIA_QUESTIONS, NEW_TRIVIA_QUESTION]); + }); + + test("Testing the renameQuestionById function", () => { + expect(renameQuestionById(BLANK_QUESTIONS, 1, "New Name")).toEqual([ + { + id: 1, + name: "New Name", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(renameQuestionById(BLANK_QUESTIONS, 47, "Another Name")).toEqual( + [ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "Another Name", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ] + ); + expect(renameQuestionById(SIMPLE_QUESTIONS, 5, "Colours")).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }, + { + id: 5, + name: "Colours", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck"], + expected: "red", + points: 1, + published: true + }, + { + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + } + ]); + }); + + test("Test the changeQuestionTypeById function", () => { + expect( + changeQuestionTypeById( + BLANK_QUESTIONS, + 1, + "multiple_choice_question" + ) + ).toEqual(BLANK_QUESTIONS); + expect( + changeQuestionTypeById(BLANK_QUESTIONS, 1, "short_answer_question") + ).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect( + changeQuestionTypeById(BLANK_QUESTIONS, 47, "short_answer_question") + ).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect( + changeQuestionTypeById(TRIVIA_QUESTIONS, 3, "short_answer_question") + ).toEqual([ + { + id: 1, + name: "Mascot", + body: "What is the name of the UD Mascot?", + type: "multiple_choice_question", + options: ["Bluey", "YoUDee", "Charles the Wonder Dog"], + expected: "YoUDee", + points: 7, + published: false + }, + { + id: 2, + name: "Motto", + body: "What is the University of Delaware's motto?", + type: "multiple_choice_question", + options: [ + "Knowledge is the light of the mind", + "Just U Do it", + "Nothing, what's the motto with you?" + ], + expected: "Knowledge is the light of the mind", + points: 3, + published: false + }, + { + id: 3, + name: "Goats", + body: "How many goats are there usually on the Green?", + type: "short_answer_question", + options: [], + expected: "Two", + points: 10, + published: false + } + ]); + }); + + test("Testing the addEditQuestionOption function", () => { + expect(editOption(BLANK_QUESTIONS, 1, -1, "NEW OPTION")).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: ["NEW OPTION"], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(editOption(BLANK_QUESTIONS, 47, -1, "Another option")).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: ["Another option"], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(editOption(SIMPLE_QUESTIONS, 5, -1, "newspaper")).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }, + { + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "firetruck", "newspaper"], + expected: "red", + points: 1, + published: true + }, + { + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + } + ]); + expect(editOption(SIMPLE_QUESTIONS, 5, 0, "newspaper")).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }, + { + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["newspaper", "apple", "firetruck"], + expected: "red", + points: 1, + published: true + }, + { + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + } + ]); + + expect(editOption(SIMPLE_QUESTIONS, 5, 2, "newspaper")).toEqual([ + { + id: 1, + name: "Addition", + body: "What is 2+2?", + type: "short_answer_question", + options: [], + expected: "4", + points: 1, + published: true + }, + { + id: 2, + name: "Letters", + body: "What is the last letter of the English alphabet?", + type: "short_answer_question", + options: [], + expected: "Z", + points: 1, + published: false + }, + { + id: 5, + name: "Colors", + body: "Which of these is a color?", + type: "multiple_choice_question", + options: ["red", "apple", "newspaper"], + expected: "red", + points: 1, + published: true + }, + { + id: 9, + name: "Shapes", + body: "What shape can you make with one line?", + type: "multiple_choice_question", + options: ["square", "triangle", "circle"], + expected: "circle", + points: 2, + published: false + } + ]); + }); + + test("Testing the duplicateQuestionInArray function", () => { + expect(duplicateQuestionInArray(BLANK_QUESTIONS, 1, 27)).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 27, + name: "Copy of Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(duplicateQuestionInArray(BLANK_QUESTIONS, 47, 19)).toEqual([ + { + id: 1, + name: "Question 1", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 47, + name: "My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 19, + name: "Copy of My New Question", + body: "", + type: "multiple_choice_question", + options: [], + expected: "", + points: 1, + published: false + }, + { + id: 2, + name: "Question 2", + body: "", + type: "short_answer_question", + options: [], + expected: "", + points: 1, + published: false + } + ]); + expect(duplicateQuestionInArray(TRIVIA_QUESTIONS, 3, 111)).toEqual([ + { + id: 1, + name: "Mascot", + body: "What is the name of the UD Mascot?", + type: "multiple_choice_question", + options: ["Bluey", "YoUDee", "Charles the Wonder Dog"], + expected: "YoUDee", + points: 7, + published: false + }, + { + id: 2, + name: "Motto", + body: "What is the University of Delaware's motto?", + type: "multiple_choice_question", + options: [ + "Knowledge is the light of the mind", + "Just U Do it", + "Nothing, what's the motto with you?" + ], + expected: "Knowledge is the light of the mind", + points: 3, + published: false + }, + { + id: 3, + name: "Goats", + body: "How many goats are there usually on the Green?", + type: "multiple_choice_question", + options: [ + "Zero, why would there be goats on the green?", + "18420", + "Two" + ], + expected: "Two", + points: 10, + published: false + }, + { + id: 111, + name: "Copy of Goats", + body: "How many goats are there usually on the Green?", + type: "multiple_choice_question", + options: [ + "Zero, why would there be goats on the green?", + "18420", + "Two" + ], + expected: "Two", + points: 10, + published: false + } + ]); }); afterEach(() => { expect(BLANK_QUESTIONS).toEqual(BACKUP_BLANK_QUESTIONS); expect(SIMPLE_QUESTIONS).toEqual(BACKUP_SIMPLE_QUESTIONS); + expect(TRIVIA_QUESTIONS).toEqual(BACKUP_TRIVIA_QUESTIONS); + expect(SIMPLE_QUESTIONS_2).toEqual(BACKUP_SIMPLE_QUESTIONS_2); + expect(EMPTY_QUESTIONS).toEqual(BACKUP_EMPTY_QUESTIONS); }); }); From a368ad06a9847e4cb04fc2d544ff49997db54e90 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 19 Feb 2022 13:52:24 -0500 Subject: [PATCH 063/104] Forgot the task record! --- public/tasks/task-nested.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 public/tasks/task-nested.md diff --git a/public/tasks/task-nested.md b/public/tasks/task-nested.md new file mode 100644 index 0000000000..6d29f9369f --- /dev/null +++ b/public/tasks/task-nested.md @@ -0,0 +1,5 @@ +# Task - Nested + +Version: 0.0.1 + +Implement functions that work with nested arrays and objects immutably. From 304184e9c70c1ed35eff2a7e522c3e71d9204d17 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Tue, 1 Mar 2022 16:38:02 -0500 Subject: [PATCH 064/104] Fix typo in editOption test, and missing return type for editOption --- src/nested.test.ts | 2 +- src/nested.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nested.test.ts b/src/nested.test.ts index 3d2b75406d..572a7a028d 100644 --- a/src/nested.test.ts +++ b/src/nested.test.ts @@ -893,7 +893,7 @@ describe("Testing the Question[] functions", () => { ]); }); - test("Testing the addEditQuestionOption function", () => { + test("Testing the editOption function", () => { expect(editOption(BLANK_QUESTIONS, 1, -1, "NEW OPTION")).toEqual([ { id: 1, diff --git a/src/nested.ts b/src/nested.ts index 7934ec1741..562b6ca0df 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -162,7 +162,7 @@ export function editOption( targetId: number, targetOptionIndex: number, newOption: string -) { +): Question[] { return []; } From 1b76b8050daddd7c26e8cb372b2ad710974a66be Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 13:33:40 -0400 Subject: [PATCH 065/104] Fix formatting --- src/nested.test.ts | 338 +++++++++++++++++++++++---------------------- 1 file changed, 173 insertions(+), 165 deletions(-) diff --git a/src/nested.test.ts b/src/nested.test.ts index 572a7a028d..7f52bfdf94 100644 --- a/src/nested.test.ts +++ b/src/nested.test.ts @@ -15,7 +15,7 @@ import { renameQuestionById, changeQuestionTypeById, editOption, - duplicateQuestionInArray + duplicateQuestionInArray, } from "./nested"; import testQuestionData from "./data/questions.json"; import backupQuestionData from "./data/questions.json"; @@ -25,7 +25,7 @@ const { SIMPLE_QUESTIONS, TRIVIA_QUESTIONS, EMPTY_QUESTIONS, - SIMPLE_QUESTIONS_2 + SIMPLE_QUESTIONS_2, }: Record = // Typecast the test data that we imported to be a record matching // strings to the question list @@ -37,7 +37,7 @@ const { SIMPLE_QUESTIONS: BACKUP_SIMPLE_QUESTIONS, TRIVIA_QUESTIONS: BACKUP_TRIVIA_QUESTIONS, EMPTY_QUESTIONS: BACKUP_EMPTY_QUESTIONS, - SIMPLE_QUESTIONS_2: BACKUP_SIMPLE_QUESTIONS_2 + SIMPLE_QUESTIONS_2: BACKUP_SIMPLE_QUESTIONS_2, }: Record = backupQuestionData as Record< string, Question[] @@ -51,7 +51,7 @@ const NEW_BLANK_QUESTION = { options: [], expected: "", points: 1, - published: false + published: false, }; const NEW_TRIVIA_QUESTION = { @@ -66,7 +66,7 @@ const NEW_TRIVIA_QUESTION = { options: ["Black, like my soul", "Blue again, we're tricky.", "#FFD200"], expected: "#FFD200",*/ points: 1, - published: false + published: false, }; //////////////////////////////////////////// @@ -76,7 +76,7 @@ describe("Testing the Question[] functions", () => { ////////////////////////////////// // getPublishedQuestions - test("Testing the getPublishedQuestions function", () => { + test("(3 pts) Testing the getPublishedQuestions function", () => { expect(getPublishedQuestions(BLANK_QUESTIONS)).toEqual([]); expect(getPublishedQuestions(SIMPLE_QUESTIONS)).toEqual([ { @@ -87,7 +87,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "4", points: 1, - published: true + published: true, }, { id: 5, @@ -97,12 +97,12 @@ describe("Testing the Question[] functions", () => { options: ["red", "apple", "firetruck"], expected: "red", points: 1, - published: true - } + published: true, + }, ]); expect(getPublishedQuestions(TRIVIA_QUESTIONS)).toEqual([]); expect(getPublishedQuestions(SIMPLE_QUESTIONS_2)).toEqual( - BACKUP_SIMPLE_QUESTIONS_2 + BACKUP_SIMPLE_QUESTIONS_2, ); expect(getPublishedQuestions(EMPTY_QUESTIONS)).toEqual([ { @@ -113,7 +113,7 @@ describe("Testing the Question[] functions", () => { options: ["correct", "it is", "not"], expected: "correct", points: 5, - published: true + published: true, }, { id: 2, @@ -123,7 +123,7 @@ describe("Testing the Question[] functions", () => { options: ["this", "one", "is", "not", "empty", "either"], expected: "one", points: 5, - published: true + published: true, }, { id: 3, @@ -133,7 +133,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 5, - published: true + published: true, }, { id: 4, @@ -143,21 +143,21 @@ describe("Testing the Question[] functions", () => { options: [], expected: "Even this one is not empty", points: 5, - published: true - } + published: true, + }, ]); }); - test("Testing the getNonEmptyQuestions functions", () => { + test("(3 pts) Testing the getNonEmptyQuestions functions", () => { expect(getNonEmptyQuestions(BLANK_QUESTIONS)).toEqual([]); expect(getNonEmptyQuestions(SIMPLE_QUESTIONS)).toEqual( - BACKUP_SIMPLE_QUESTIONS + BACKUP_SIMPLE_QUESTIONS, ); expect(getNonEmptyQuestions(TRIVIA_QUESTIONS)).toEqual( - BACKUP_TRIVIA_QUESTIONS + BACKUP_TRIVIA_QUESTIONS, ); expect(getNonEmptyQuestions(SIMPLE_QUESTIONS_2)).toEqual( - BACKUP_SIMPLE_QUESTIONS_2 + BACKUP_SIMPLE_QUESTIONS_2, ); expect(getNonEmptyQuestions(EMPTY_QUESTIONS)).toEqual([ { @@ -168,7 +168,7 @@ describe("Testing the Question[] functions", () => { options: ["correct", "it is", "not"], expected: "correct", points: 5, - published: true + published: true, }, { id: 2, @@ -178,7 +178,7 @@ describe("Testing the Question[] functions", () => { options: ["this", "one", "is", "not", "empty", "either"], expected: "one", points: 5, - published: true + published: true, }, { id: 3, @@ -188,7 +188,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 5, - published: true + published: true, }, { id: 4, @@ -198,12 +198,12 @@ describe("Testing the Question[] functions", () => { options: [], expected: "Even this one is not empty", points: 5, - published: true - } + published: true, + }, ]); }); - test("Testing the findQuestion function", () => { + test("(3 pts) Testing the findQuestion function", () => { expect(findQuestion(BLANK_QUESTIONS, 1)).toEqual(BLANK_QUESTIONS[0]); expect(findQuestion(BLANK_QUESTIONS, 47)).toEqual(BLANK_QUESTIONS[1]); expect(findQuestion(BLANK_QUESTIONS, 2)).toEqual(BLANK_QUESTIONS[2]); @@ -214,12 +214,12 @@ describe("Testing the Question[] functions", () => { expect(findQuestion(SIMPLE_QUESTIONS, 9)).toEqual(SIMPLE_QUESTIONS[3]); expect(findQuestion(SIMPLE_QUESTIONS, 6)).toEqual(null); expect(findQuestion(SIMPLE_QUESTIONS_2, 478)).toEqual( - SIMPLE_QUESTIONS_2[0] + SIMPLE_QUESTIONS_2[0], ); expect(findQuestion([], 0)).toEqual(null); }); - test("Testing the removeQuestion", () => { + test("(3 pts) Testing the removeQuestion", () => { expect(removeQuestion(BLANK_QUESTIONS, 1)).toEqual([ { id: 47, @@ -229,7 +229,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 2, @@ -239,8 +239,8 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false - } + published: false, + }, ]); expect(removeQuestion(BLANK_QUESTIONS, 47)).toEqual([ { @@ -251,7 +251,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 2, @@ -261,8 +261,8 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false - } + published: false, + }, ]); expect(removeQuestion(BLANK_QUESTIONS, 2)).toEqual([ { @@ -273,7 +273,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 47, @@ -283,8 +283,8 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false - } + published: false, + }, ]); expect(removeQuestion(SIMPLE_QUESTIONS, 9)).toEqual([ { @@ -295,7 +295,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "4", points: 1, - published: true + published: true, }, { id: 2, @@ -305,7 +305,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "Z", points: 1, - published: false + published: false, }, { id: 5, @@ -315,8 +315,8 @@ describe("Testing the Question[] functions", () => { options: ["red", "apple", "firetruck"], expected: "red", points: 1, - published: true - } + published: true, + }, ]); expect(removeQuestion(SIMPLE_QUESTIONS, 5)).toEqual([ { @@ -327,7 +327,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "4", points: 1, - published: true + published: true, }, { id: 2, @@ -337,7 +337,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "Z", points: 1, - published: false + published: false, }, { id: 9, @@ -347,45 +347,45 @@ describe("Testing the Question[] functions", () => { options: ["square", "triangle", "circle"], expected: "circle", points: 2, - published: false - } + published: false, + }, ]); }); - test("Testing the getNames function", () => { + test("(3 pts) Testing the getNames function", () => { expect(getNames(BLANK_QUESTIONS)).toEqual([ "Question 1", "My New Question", - "Question 2" + "Question 2", ]); expect(getNames(SIMPLE_QUESTIONS)).toEqual([ "Addition", "Letters", "Colors", - "Shapes" + "Shapes", ]); expect(getNames(TRIVIA_QUESTIONS)).toEqual([ "Mascot", "Motto", - "Goats" + "Goats", ]); expect(getNames(SIMPLE_QUESTIONS_2)).toEqual([ "Students", "Importance", "Sentience", "Danger", - "Listening" + "Listening", ]); expect(getNames(EMPTY_QUESTIONS)).toEqual([ "Empty 1", "Empty 2", "Empty 3", "Empty 4", - "Empty 5 (Actual)" + "Empty 5 (Actual)", ]); }); - test("Testing the sumPoints function", () => { + test("(3 pts) Testing the sumPoints function", () => { expect(sumPoints(BLANK_QUESTIONS)).toEqual(3); expect(sumPoints(SIMPLE_QUESTIONS)).toEqual(5); expect(sumPoints(TRIVIA_QUESTIONS)).toEqual(20); @@ -393,7 +393,7 @@ describe("Testing the Question[] functions", () => { expect(sumPoints(SIMPLE_QUESTIONS_2)).toEqual(300); }); - test("Testing the sumPublishedPoints function", () => { + test("(3 pts) Testing the sumPublishedPoints function", () => { expect(sumPublishedPoints(BLANK_QUESTIONS)).toEqual(0); expect(sumPublishedPoints(SIMPLE_QUESTIONS)).toEqual(2); expect(sumPublishedPoints(TRIVIA_QUESTIONS)).toEqual(0); @@ -401,7 +401,7 @@ describe("Testing the Question[] functions", () => { expect(sumPublishedPoints(SIMPLE_QUESTIONS_2)).toEqual(300); }); - test("Testing the toCSV function", () => { + test("(3 pts) Testing the toCSV function", () => { expect(toCSV(BLANK_QUESTIONS)).toEqual(`id,name,options,points,published 1,Question 1,0,1,false 47,My New Question,0,1,false @@ -432,40 +432,40 @@ describe("Testing the Question[] functions", () => { 1937,Listening,0,100,true`); }); - test("Testing the makeAnswers function", () => { + test("(3 pts) Testing the makeAnswers function", () => { expect(makeAnswers(BLANK_QUESTIONS)).toEqual([ { questionId: 1, correct: false, text: "", submitted: false }, { questionId: 47, correct: false, text: "", submitted: false }, - { questionId: 2, correct: false, text: "", submitted: false } + { questionId: 2, correct: false, text: "", submitted: false }, ]); expect(makeAnswers(SIMPLE_QUESTIONS)).toEqual([ { questionId: 1, correct: false, text: "", submitted: false }, { questionId: 2, correct: false, text: "", submitted: false }, { questionId: 5, correct: false, text: "", submitted: false }, - { questionId: 9, correct: false, text: "", submitted: false } + { questionId: 9, correct: false, text: "", submitted: false }, ]); expect(makeAnswers(TRIVIA_QUESTIONS)).toEqual([ { questionId: 1, correct: false, text: "", submitted: false }, { questionId: 2, correct: false, text: "", submitted: false }, - { questionId: 3, correct: false, text: "", submitted: false } + { questionId: 3, correct: false, text: "", submitted: false }, ]); expect(makeAnswers(SIMPLE_QUESTIONS_2)).toEqual([ { questionId: 478, correct: false, text: "", submitted: false }, { questionId: 1937, correct: false, text: "", submitted: false }, { questionId: 479, correct: false, text: "", submitted: false }, { questionId: 777, correct: false, text: "", submitted: false }, - { questionId: 1937, correct: false, text: "", submitted: false } + { questionId: 1937, correct: false, text: "", submitted: false }, ]); expect(makeAnswers(EMPTY_QUESTIONS)).toEqual([ { questionId: 1, correct: false, text: "", submitted: false }, { questionId: 2, correct: false, text: "", submitted: false }, { questionId: 3, correct: false, text: "", submitted: false }, { questionId: 4, correct: false, text: "", submitted: false }, - { questionId: 5, correct: false, text: "", submitted: false } + { questionId: 5, correct: false, text: "", submitted: false }, ]); }); - test("Testing the publishAll function", () => { + test("(3 pts) Testing the publishAll function", () => { expect(publishAll(BLANK_QUESTIONS)).toEqual([ { id: 1, @@ -475,7 +475,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: true + published: true, }, { id: 47, @@ -485,7 +485,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: true + published: true, }, { id: 2, @@ -495,8 +495,8 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: true - } + published: true, + }, ]); expect(publishAll(SIMPLE_QUESTIONS)).toEqual([ { @@ -507,7 +507,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "4", points: 1, - published: true + published: true, }, { id: 2, @@ -517,7 +517,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "Z", points: 1, - published: true + published: true, }, { id: 5, @@ -527,7 +527,7 @@ describe("Testing the Question[] functions", () => { options: ["red", "apple", "firetruck"], expected: "red", points: 1, - published: true + published: true, }, { id: 9, @@ -537,8 +537,8 @@ describe("Testing the Question[] functions", () => { options: ["square", "triangle", "circle"], expected: "circle", points: 2, - published: true - } + published: true, + }, ]); expect(publishAll(TRIVIA_QUESTIONS)).toEqual([ { @@ -549,7 +549,7 @@ describe("Testing the Question[] functions", () => { options: ["Bluey", "YoUDee", "Charles the Wonder Dog"], expected: "YoUDee", points: 7, - published: true + published: true, }, { id: 2, @@ -559,11 +559,11 @@ describe("Testing the Question[] functions", () => { options: [ "Knowledge is the light of the mind", "Just U Do it", - "Nothing, what's the motto with you?" + "Nothing, what's the motto with you?", ], expected: "Knowledge is the light of the mind", points: 3, - published: true + published: true, }, { id: 3, @@ -573,12 +573,12 @@ describe("Testing the Question[] functions", () => { options: [ "Zero, why would there be goats on the green?", "18420", - "Two" + "Two", ], expected: "Two", points: 10, - published: true - } + published: true, + }, ]); expect(publishAll(EMPTY_QUESTIONS)).toEqual([ { @@ -589,7 +589,7 @@ describe("Testing the Question[] functions", () => { options: ["correct", "it is", "not"], expected: "correct", points: 5, - published: true + published: true, }, { id: 2, @@ -599,7 +599,7 @@ describe("Testing the Question[] functions", () => { options: ["this", "one", "is", "not", "empty", "either"], expected: "one", points: 5, - published: true + published: true, }, { id: 3, @@ -609,7 +609,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 5, - published: true + published: true, }, { id: 4, @@ -619,7 +619,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "Even this one is not empty", points: 5, - published: true + published: true, }, { id: 5, @@ -629,13 +629,13 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 5, - published: true - } + published: true, + }, ]); expect(publishAll(SIMPLE_QUESTIONS_2)).toEqual(SIMPLE_QUESTIONS_2); }); - test("Testing the sameType function", () => { + test("(3 pts) Testing the sameType function", () => { expect(sameType([])).toEqual(true); expect(sameType(BLANK_QUESTIONS)).toEqual(false); expect(sameType(SIMPLE_QUESTIONS)).toEqual(false); @@ -644,29 +644,29 @@ describe("Testing the Question[] functions", () => { expect(sameType(SIMPLE_QUESTIONS_2)).toEqual(true); }); - test("Testing the addNewQuestion function", () => { + test("(3 pts) Testing the addNewQuestion function", () => { expect( - addNewQuestion([], 142, "A new question", "short_answer_question") + addNewQuestion([], 142, "A new question", "short_answer_question"), ).toEqual([NEW_BLANK_QUESTION]); expect( addNewQuestion( BLANK_QUESTIONS, 142, "A new question", - "short_answer_question" - ) + "short_answer_question", + ), ).toEqual([...BLANK_QUESTIONS, NEW_BLANK_QUESTION]); expect( addNewQuestion( TRIVIA_QUESTIONS, 449, "Colors", - "multiple_choice_question" - ) + "multiple_choice_question", + ), ).toEqual([...TRIVIA_QUESTIONS, NEW_TRIVIA_QUESTION]); }); - test("Testing the renameQuestionById function", () => { + test("(3 pts) Testing the renameQuestionById function", () => { expect(renameQuestionById(BLANK_QUESTIONS, 1, "New Name")).toEqual([ { id: 1, @@ -676,7 +676,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 47, @@ -686,7 +686,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 2, @@ -696,8 +696,8 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false - } + published: false, + }, ]); expect(renameQuestionById(BLANK_QUESTIONS, 47, "Another Name")).toEqual( [ @@ -709,7 +709,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 47, @@ -719,7 +719,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 2, @@ -729,9 +729,9 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false - } - ] + published: false, + }, + ], ); expect(renameQuestionById(SIMPLE_QUESTIONS, 5, "Colours")).toEqual([ { @@ -742,7 +742,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "4", points: 1, - published: true + published: true, }, { id: 2, @@ -752,7 +752,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "Z", points: 1, - published: false + published: false, }, { id: 5, @@ -762,7 +762,7 @@ describe("Testing the Question[] functions", () => { options: ["red", "apple", "firetruck"], expected: "red", points: 1, - published: true + published: true, }, { id: 9, @@ -772,21 +772,21 @@ describe("Testing the Question[] functions", () => { options: ["square", "triangle", "circle"], expected: "circle", points: 2, - published: false - } + published: false, + }, ]); }); - test("Test the changeQuestionTypeById function", () => { + test("(3 pts) Test the changeQuestionTypeById function", () => { expect( changeQuestionTypeById( BLANK_QUESTIONS, 1, - "multiple_choice_question" - ) + "multiple_choice_question", + ), ).toEqual(BLANK_QUESTIONS); expect( - changeQuestionTypeById(BLANK_QUESTIONS, 1, "short_answer_question") + changeQuestionTypeById(BLANK_QUESTIONS, 1, "short_answer_question"), ).toEqual([ { id: 1, @@ -796,7 +796,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 47, @@ -806,7 +806,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 2, @@ -816,11 +816,15 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false - } + published: false, + }, ]); expect( - changeQuestionTypeById(BLANK_QUESTIONS, 47, "short_answer_question") + changeQuestionTypeById( + BLANK_QUESTIONS, + 47, + "short_answer_question", + ), ).toEqual([ { id: 1, @@ -830,7 +834,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 47, @@ -840,7 +844,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 2, @@ -850,11 +854,15 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false - } + published: false, + }, ]); expect( - changeQuestionTypeById(TRIVIA_QUESTIONS, 3, "short_answer_question") + changeQuestionTypeById( + TRIVIA_QUESTIONS, + 3, + "short_answer_question", + ), ).toEqual([ { id: 1, @@ -864,7 +872,7 @@ describe("Testing the Question[] functions", () => { options: ["Bluey", "YoUDee", "Charles the Wonder Dog"], expected: "YoUDee", points: 7, - published: false + published: false, }, { id: 2, @@ -874,11 +882,11 @@ describe("Testing the Question[] functions", () => { options: [ "Knowledge is the light of the mind", "Just U Do it", - "Nothing, what's the motto with you?" + "Nothing, what's the motto with you?", ], expected: "Knowledge is the light of the mind", points: 3, - published: false + published: false, }, { id: 3, @@ -888,12 +896,12 @@ describe("Testing the Question[] functions", () => { options: [], expected: "Two", points: 10, - published: false - } + published: false, + }, ]); }); - test("Testing the editOption function", () => { + test("(3 pts) Testing the editOption function", () => { expect(editOption(BLANK_QUESTIONS, 1, -1, "NEW OPTION")).toEqual([ { id: 1, @@ -903,7 +911,7 @@ describe("Testing the Question[] functions", () => { options: ["NEW OPTION"], expected: "", points: 1, - published: false + published: false, }, { id: 47, @@ -913,7 +921,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 2, @@ -923,8 +931,8 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false - } + published: false, + }, ]); expect(editOption(BLANK_QUESTIONS, 47, -1, "Another option")).toEqual([ { @@ -935,7 +943,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 47, @@ -945,7 +953,7 @@ describe("Testing the Question[] functions", () => { options: ["Another option"], expected: "", points: 1, - published: false + published: false, }, { id: 2, @@ -955,8 +963,8 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false - } + published: false, + }, ]); expect(editOption(SIMPLE_QUESTIONS, 5, -1, "newspaper")).toEqual([ { @@ -967,7 +975,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "4", points: 1, - published: true + published: true, }, { id: 2, @@ -977,7 +985,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "Z", points: 1, - published: false + published: false, }, { id: 5, @@ -987,7 +995,7 @@ describe("Testing the Question[] functions", () => { options: ["red", "apple", "firetruck", "newspaper"], expected: "red", points: 1, - published: true + published: true, }, { id: 9, @@ -997,8 +1005,8 @@ describe("Testing the Question[] functions", () => { options: ["square", "triangle", "circle"], expected: "circle", points: 2, - published: false - } + published: false, + }, ]); expect(editOption(SIMPLE_QUESTIONS, 5, 0, "newspaper")).toEqual([ { @@ -1009,7 +1017,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "4", points: 1, - published: true + published: true, }, { id: 2, @@ -1019,7 +1027,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "Z", points: 1, - published: false + published: false, }, { id: 5, @@ -1029,7 +1037,7 @@ describe("Testing the Question[] functions", () => { options: ["newspaper", "apple", "firetruck"], expected: "red", points: 1, - published: true + published: true, }, { id: 9, @@ -1039,8 +1047,8 @@ describe("Testing the Question[] functions", () => { options: ["square", "triangle", "circle"], expected: "circle", points: 2, - published: false - } + published: false, + }, ]); expect(editOption(SIMPLE_QUESTIONS, 5, 2, "newspaper")).toEqual([ @@ -1052,7 +1060,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "4", points: 1, - published: true + published: true, }, { id: 2, @@ -1062,7 +1070,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "Z", points: 1, - published: false + published: false, }, { id: 5, @@ -1072,7 +1080,7 @@ describe("Testing the Question[] functions", () => { options: ["red", "apple", "newspaper"], expected: "red", points: 1, - published: true + published: true, }, { id: 9, @@ -1082,12 +1090,12 @@ describe("Testing the Question[] functions", () => { options: ["square", "triangle", "circle"], expected: "circle", points: 2, - published: false - } + published: false, + }, ]); }); - test("Testing the duplicateQuestionInArray function", () => { + test("(3 pts) Testing the duplicateQuestionInArray function", () => { expect(duplicateQuestionInArray(BLANK_QUESTIONS, 1, 27)).toEqual([ { id: 1, @@ -1097,7 +1105,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 27, @@ -1107,7 +1115,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 47, @@ -1117,7 +1125,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 2, @@ -1127,8 +1135,8 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false - } + published: false, + }, ]); expect(duplicateQuestionInArray(BLANK_QUESTIONS, 47, 19)).toEqual([ { @@ -1139,7 +1147,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 47, @@ -1149,7 +1157,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 19, @@ -1159,7 +1167,7 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false + published: false, }, { id: 2, @@ -1169,8 +1177,8 @@ describe("Testing the Question[] functions", () => { options: [], expected: "", points: 1, - published: false - } + published: false, + }, ]); expect(duplicateQuestionInArray(TRIVIA_QUESTIONS, 3, 111)).toEqual([ { @@ -1181,7 +1189,7 @@ describe("Testing the Question[] functions", () => { options: ["Bluey", "YoUDee", "Charles the Wonder Dog"], expected: "YoUDee", points: 7, - published: false + published: false, }, { id: 2, @@ -1191,11 +1199,11 @@ describe("Testing the Question[] functions", () => { options: [ "Knowledge is the light of the mind", "Just U Do it", - "Nothing, what's the motto with you?" + "Nothing, what's the motto with you?", ], expected: "Knowledge is the light of the mind", points: 3, - published: false + published: false, }, { id: 3, @@ -1205,11 +1213,11 @@ describe("Testing the Question[] functions", () => { options: [ "Zero, why would there be goats on the green?", "18420", - "Two" + "Two", ], expected: "Two", points: 10, - published: false + published: false, }, { id: 111, @@ -1219,12 +1227,12 @@ describe("Testing the Question[] functions", () => { options: [ "Zero, why would there be goats on the green?", "18420", - "Two" + "Two", ], expected: "Two", points: 10, - published: false - } + published: false, + }, ]); }); From 23314f383f4ac6257b7c904bfff21496bf743a68 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 13:41:03 -0400 Subject: [PATCH 066/104] update point values for tests --- src/components/ChangeType.test.tsx | 10 +++++----- src/components/Counter.test.tsx | 10 +++++----- src/components/CycleHoliday.test.tsx | 8 ++++---- src/components/RevealAnswer.test.tsx | 8 ++++---- src/components/StartAttempt.test.tsx | 26 +++++++++++++------------- src/components/TwoDice.test.tsx | 16 ++++++++-------- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/components/ChangeType.test.tsx b/src/components/ChangeType.test.tsx index 10b4f0dc3c..ef7ea4b66e 100644 --- a/src/components/ChangeType.test.tsx +++ b/src/components/ChangeType.test.tsx @@ -6,25 +6,25 @@ describe("ChangeType Component tests", () => { beforeEach(() => { render(); }); - test("The initial type is Short Answer", () => { + test("(1 pts) The initial type is Short Answer", () => { // We use `getByText` because the text MUST be there const typeText = screen.getByText(/Short Answer/i); expect(typeText).toBeInTheDocument(); }); - test("The initial type is not Multiple Choice", () => { + test("(1 pts) The initial type is not Multiple Choice", () => { // We use `queryByText` because the text might not be there const typeText = screen.queryByText(/Multiple Choice/i); expect(typeText).toBeNull(); }); - test("There is a button labeled Change Type", () => { + test("(1 pts) There is a button labeled Change Type", () => { const changeTypeButton = screen.getByRole("button", { name: /Change Type/i }); expect(changeTypeButton).toBeInTheDocument(); }); - test("Clicking the button changes the type.", () => { + test("(1 pts) Clicking the button changes the type.", () => { const changeTypeButton = screen.getByRole("button", { name: /Change Type/i }); @@ -37,7 +37,7 @@ describe("ChangeType Component tests", () => { expect(typeTextSA).toBeNull(); }); - test("Clicking the button twice keeps the type the same.", () => { + test("(1 pts) Clicking the button twice keeps the type the same.", () => { const changeTypeButton = screen.getByRole("button", { name: /Change Type/i }); diff --git a/src/components/Counter.test.tsx b/src/components/Counter.test.tsx index 7a37c46e38..1ee2d1ff32 100644 --- a/src/components/Counter.test.tsx +++ b/src/components/Counter.test.tsx @@ -6,30 +6,30 @@ describe("Counter Component tests", () => { beforeEach(() => { render(); }); - test("The initial value is 0", () => { + test("(1 pts) The initial value is 0", () => { // We use `getByText` because the text MUST be there const valueText = screen.getByText(/0/i); expect(valueText).toBeInTheDocument(); }); - test("The initial value is not 1", () => { + test("(1 pts) The initial value is not 1", () => { // We use `queryByText` because the text might not be there const valueText = screen.queryByText(/1/i); expect(valueText).toBeNull(); }); - test("There is a button named Add One", () => { + test("(1 pts) There is a button named Add One", () => { const addOneButton = screen.getByRole("button", { name: /Add One/i }); expect(addOneButton).toBeInTheDocument(); }); - test("Clicking the button once adds one", () => { + test("(1 pts) Clicking the button once adds one", () => { const addOneButton = screen.getByRole("button", { name: /Add One/i }); addOneButton.click(); const valueText = screen.getByText(/1/i); expect(valueText).toBeInTheDocument(); }); - test("Clicking the button twice adds two", () => { + test("(1 pts) Clicking the button twice adds two", () => { const addOneButton = screen.getByRole("button", { name: /Add One/i }); addOneButton.click(); addOneButton.click(); diff --git a/src/components/CycleHoliday.test.tsx b/src/components/CycleHoliday.test.tsx index 145e2cb3c8..a20389d03e 100644 --- a/src/components/CycleHoliday.test.tsx +++ b/src/components/CycleHoliday.test.tsx @@ -7,12 +7,12 @@ describe("CycleHoliday Component tests", () => { render(); }); - test("An initial holiday is displayed", () => { + test("(1 pts) An initial holiday is displayed", () => { const initialHoliday = screen.getByText(/Holiday: (.*)/i); expect(initialHoliday).toBeInTheDocument(); }); - test("There are two buttons", () => { + test("(1 pts) There are two buttons", () => { const alphabetButton = screen.getByRole("button", { name: /Alphabet/i }); @@ -23,7 +23,7 @@ describe("CycleHoliday Component tests", () => { expect(yearButton).toBeInTheDocument(); }); - test("Can cycle through 5 distinct holidays alphabetically", () => { + test("(1 pts) Can cycle through 5 distinct holidays alphabetically", () => { const alphabetButton = screen.getByRole("button", { name: /Alphabet/i }); @@ -38,7 +38,7 @@ describe("CycleHoliday Component tests", () => { expect(states[0]).toEqual(states[5]); }); - test("Can cycle through 5 distinct holidays by year", () => { + test("(1 pts) Can cycle through 5 distinct holidays by year", () => { const yearButton = screen.getByRole("button", { name: /Year/i }); diff --git a/src/components/RevealAnswer.test.tsx b/src/components/RevealAnswer.test.tsx index aa7996e964..438f4d5c28 100644 --- a/src/components/RevealAnswer.test.tsx +++ b/src/components/RevealAnswer.test.tsx @@ -6,17 +6,17 @@ describe("RevealAnswer Component tests", () => { beforeEach(() => { render(); }); - test("The answer '42' is not visible initially", () => { + test("(1 pts) The answer '42' is not visible initially", () => { const answerText = screen.queryByText(/42/); expect(answerText).toBeNull(); }); - test("There is a Reveal Answer button", () => { + test("(1 pts) There is a Reveal Answer button", () => { const revealButton = screen.getByRole("button", { name: /Reveal Answer/i }); expect(revealButton).toBeInTheDocument(); }); - test("Clicking Reveal Answer button reveals the '42'", () => { + test("(1 pts) Clicking Reveal Answer button reveals the '42'", () => { const revealButton = screen.getByRole("button", { name: /Reveal Answer/i }); @@ -24,7 +24,7 @@ describe("RevealAnswer Component tests", () => { const answerText = screen.getByText(/42/); expect(answerText).toBeInTheDocument(); }); - test("Clicking Reveal Answer button twice hides the '42'", () => { + test("(1 pts) Clicking Reveal Answer button twice hides the '42'", () => { const revealButton = screen.getByRole("button", { name: /Reveal Answer/i }); diff --git a/src/components/StartAttempt.test.tsx b/src/components/StartAttempt.test.tsx index 3d41c953cf..8e5b9667cb 100644 --- a/src/components/StartAttempt.test.tsx +++ b/src/components/StartAttempt.test.tsx @@ -27,36 +27,36 @@ describe("StartAttempt Component tests", () => { beforeEach(() => { render(); }); - test("The Number of attempts is displayed initially, without other numbers", () => { + test("(1 pts) The Number of attempts is displayed initially, without other numbers", () => { const attemptNumber = screen.getByText(/(\d+)/); expect(attemptNumber).toBeInTheDocument(); }); - test("The Number of attempts is more than 0", () => { + test("(1 pts) The Number of attempts is more than 0", () => { const attemptNumber = extractDigits(screen.getByText(/(\d+)/)); expect(attemptNumber).toBeGreaterThan(0); }); - test("The Number of attempts is less than 10", () => { + test("(1 pts) The Number of attempts is less than 10", () => { const attemptNumber = extractDigits(screen.getByText(/(\d+)/)); expect(attemptNumber).toBeLessThan(10); }); - test("There is an initially enabled Start Quiz button", () => { + test("(1 pts) There is an initially enabled Start Quiz button", () => { const startButton = screen.getByRole("button", { name: /Start Quiz/i }); expect(startButton).toBeInTheDocument(); expect(startButton).toBeEnabled(); }); - test("There is an initially disabled Stop Quiz button", () => { + test("(1 pts) There is an initially disabled Stop Quiz button", () => { const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); expect(stopButton).toBeInTheDocument(); expect(stopButton).toBeDisabled(); }); - test("There is an initially enabled Mulligan button", () => { + test("(1 pts) There is an initially enabled Mulligan button", () => { const mulliganButton = screen.getByRole("button", { name: /Mulligan/i }); expect(mulliganButton).toBeInTheDocument(); expect(mulliganButton).toBeEnabled(); }); - test("Clicking Mulligan increases attempts", () => { + test("(1 pts) Clicking Mulligan increases attempts", () => { const attemptNumber: number = extractDigits(screen.getByText(/(\d+)/)) || 0; const mulliganButton = screen.getByRole("button", { @@ -66,7 +66,7 @@ describe("StartAttempt Component tests", () => { const attemptNumberLater = extractDigits(screen.getByText(/(\d+)/)); expect(attemptNumber + 1).toEqual(attemptNumberLater); }); - test("Clicking Mulligan twice increases attempts by two", () => { + test("(1 pts) Clicking Mulligan twice increases attempts by two", () => { const attemptNumber: number = extractDigits(screen.getByText(/(\d+)/)) || 0; const mulliganButton = screen.getByRole("button", { @@ -77,7 +77,7 @@ describe("StartAttempt Component tests", () => { const attemptNumberLater = extractDigits(screen.getByText(/(\d+)/)); expect(attemptNumber + 2).toEqual(attemptNumberLater); }); - test("Clicking Start Quiz decreases attempts", () => { + test("(1 pts) Clicking Start Quiz decreases attempts", () => { const attemptNumber: number = extractDigits(screen.getByText(/(\d+)/)) || 0; const startButton = screen.getByRole("button", { @@ -88,7 +88,7 @@ describe("StartAttempt Component tests", () => { extractDigits(screen.getByText(/(\d+)/)) || 0; expect(attemptNumber - 1).toEqual(attemptNumberLater); }); - test("Clicking Start Quiz changes enabled buttons", () => { + test("(1 pts) Clicking Start Quiz changes enabled buttons", () => { // Given the buttons... const startButton = screen.getByRole("button", { name: /Start Quiz/i @@ -104,7 +104,7 @@ describe("StartAttempt Component tests", () => { expect(stopButton).toBeEnabled(); expect(mulliganButton).toBeDisabled(); }); - test("Clicking Start and Stop Quiz changes enabled buttons", () => { + test("(1 pts) Clicking Start and Stop Quiz changes enabled buttons", () => { // Given the buttons and initial attempt number... const startButton = screen.getByRole("button", { name: /Start Quiz/i @@ -121,7 +121,7 @@ describe("StartAttempt Component tests", () => { expect(stopButton).toBeDisabled(); expect(mulliganButton).toBeEnabled(); }); - test("Clicking Start, Stop, Mulligan sets attempts to original", () => { + test("(1 pts) Clicking Start, Stop, Mulligan sets attempts to original", () => { // Given the buttons and initial attempt number... const startButton = screen.getByRole("button", { name: /Start Quiz/i @@ -146,7 +146,7 @@ describe("StartAttempt Component tests", () => { extractDigits(screen.getByText(/(\d+)/)) || 0; expect(attemptNumber).toEqual(attemptNumberLatest); }); - test("Cannot click start quiz when out of attempts", () => { + test("(1 pts) Cannot click start quiz when out of attempts", () => { // Given the buttons and initial attempt number... const startButton = screen.getByRole("button", { name: /Start Quiz/i diff --git a/src/components/TwoDice.test.tsx b/src/components/TwoDice.test.tsx index 7996051096..d1e5f812dd 100644 --- a/src/components/TwoDice.test.tsx +++ b/src/components/TwoDice.test.tsx @@ -22,13 +22,13 @@ describe("TwoDice Component tests", () => { beforeEach(() => { render(); }); - test("There is a `left-die` and `right-die` testid", () => { + test("(1 pts) There is a `left-die` and `right-die` testid", () => { const leftDie = screen.getByTestId("left-die"); const rightDie = screen.getByTestId("right-die"); expect(leftDie).toBeInTheDocument(); expect(rightDie).toBeInTheDocument(); }); - test("The `left-die` and `right-die` are two different numbers", () => { + test("(1 pts) The `left-die` and `right-die` are two different numbers", () => { const leftDie = screen.getByTestId("left-die"); const rightDie = screen.getByTestId("right-die"); const leftNumber = extractDigits(leftDie); @@ -39,13 +39,13 @@ describe("TwoDice Component tests", () => { // Then they are two different numbers expect(leftNumber).not.toEqual(rightNumber); }); - test("There are two buttons present", () => { + test("(1 pts) There are two buttons present", () => { const leftButton = screen.getByRole("button", { name: /Roll Left/i }); const rightButton = screen.getByRole("button", { name: /Roll Right/i }); expect(leftButton).toBeInTheDocument(); expect(rightButton).toBeInTheDocument(); }); - test("Clicking left button changes first number", () => { + test("(1 pts) Clicking left button changes first number", () => { const leftButton = screen.getByRole("button", { name: /Roll Left/i }); leftButton.click(); leftButton.click(); @@ -57,7 +57,7 @@ describe("TwoDice Component tests", () => { expect(leftNumber).toEqual(5); }); // Clicking right button changes second number - test("Clicking right button changes second number", () => { + test("(1 pts) Clicking right button changes second number", () => { const rightButton = screen.getByRole("button", { name: /Roll Right/i }); rightButton.click(); rightButton.click(); @@ -69,7 +69,7 @@ describe("TwoDice Component tests", () => { expect(rightNumber).toEqual(5); }); // Rolling two different numbers does not win or lose the game - test("Rolling two different numbers does not win or lose the game", () => { + test("(1 pts) Rolling two different numbers does not win or lose the game", () => { // Given const leftButton = screen.getByRole("button", { name: /Roll Left/i }); const rightButton = screen.getByRole("button", { name: /Roll Right/i }); @@ -90,7 +90,7 @@ describe("TwoDice Component tests", () => { const loseText = screen.queryByText(/Lose/i); expect(loseText).toBeNull(); }); - test("Getting snake eyes loses the game", () => { + test("(1 pts) Getting snake eyes loses the game", () => { // Given const leftButton = screen.getByRole("button", { name: /Roll Left/i }); const rightButton = screen.getByRole("button", { name: /Roll Right/i }); @@ -114,7 +114,7 @@ describe("TwoDice Component tests", () => { const loseText = screen.getByText(/Lose/i); expect(loseText).toBeInTheDocument(); }); - test("Getting matching numbers wins the game", () => { + test("(1 pts) Getting matching numbers wins the game", () => { // Given const leftButton = screen.getByRole("button", { name: /Roll Left/i }); const rightButton = screen.getByRole("button", { name: /Roll Right/i }); From 82faaccfdddde466de5a4d0080af257364271684 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 13:45:32 -0400 Subject: [PATCH 067/104] Fix react return value --- src/components/ChangeType.tsx | 2 +- src/components/Counter.tsx | 2 +- src/components/CycleHoliday.tsx | 2 +- src/components/RevealAnswer.tsx | 2 +- src/components/StartAttempt.tsx | 2 +- src/components/TwoDice.tsx | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/ChangeType.tsx b/src/components/ChangeType.tsx index 5608076f64..6e8f878020 100644 --- a/src/components/ChangeType.tsx +++ b/src/components/ChangeType.tsx @@ -2,6 +2,6 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; import { QuestionType } from "../interfaces/question"; -export function ChangeType(): JSX.Element { +export function ChangeType(): React.JSX.Element { return
      Change Type
      ; } diff --git a/src/components/Counter.tsx b/src/components/Counter.tsx index 1987698ed1..2a546c1747 100644 --- a/src/components/Counter.tsx +++ b/src/components/Counter.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; -export function Counter(): JSX.Element { +export function Counter(): React.JSX.Element { const [value, setValue] = useState(0); return ( diff --git a/src/components/CycleHoliday.tsx b/src/components/CycleHoliday.tsx index 7c671f889f..47e6d76444 100644 --- a/src/components/CycleHoliday.tsx +++ b/src/components/CycleHoliday.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; -export function CycleHoliday(): JSX.Element { +export function CycleHoliday(): React.JSX.Element { return
      Cycle Holiday
      ; } diff --git a/src/components/RevealAnswer.tsx b/src/components/RevealAnswer.tsx index 07db6f62d2..a48c0a0961 100644 --- a/src/components/RevealAnswer.tsx +++ b/src/components/RevealAnswer.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; -export function RevealAnswer(): JSX.Element { +export function RevealAnswer(): React.JSX.Element { return
      Reveal Answer
      ; } diff --git a/src/components/StartAttempt.tsx b/src/components/StartAttempt.tsx index 0c0a85fdb6..dec4ae7dcd 100644 --- a/src/components/StartAttempt.tsx +++ b/src/components/StartAttempt.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; -export function StartAttempt(): JSX.Element { +export function StartAttempt(): React.JSX.Element { return
      Start Attempt
      ; } diff --git a/src/components/TwoDice.tsx b/src/components/TwoDice.tsx index 372502fe64..a257594d35 100644 --- a/src/components/TwoDice.tsx +++ b/src/components/TwoDice.tsx @@ -11,6 +11,6 @@ export function d6(): number { return 1 + Math.floor(Math.random() * 6); } -export function TwoDice(): JSX.Element { +export function TwoDice(): React.JSX.Element { return
      Two Dice
      ; } From cc7d4db27c04fce10f1974a3f179adaaffc739f2 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 14:19:46 -0400 Subject: [PATCH 068/104] Update react tests to use async --- src/components/ChangeType.test.tsx | 24 ++++--- src/components/Counter.test.tsx | 18 ++++-- src/components/CycleHoliday.test.tsx | 22 ++++--- src/components/RevealAnswer.test.tsx | 24 ++++--- src/components/StartAttempt.test.tsx | 97 ++++++++++++++++++---------- src/components/TwoDice.test.tsx | 80 ++++++++++++++++------- 6 files changed, 175 insertions(+), 90 deletions(-) diff --git a/src/components/ChangeType.test.tsx b/src/components/ChangeType.test.tsx index ef7ea4b66e..0a126a9111 100644 --- a/src/components/ChangeType.test.tsx +++ b/src/components/ChangeType.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { ChangeType } from "./ChangeType"; @@ -19,16 +19,18 @@ describe("ChangeType Component tests", () => { test("(1 pts) There is a button labeled Change Type", () => { const changeTypeButton = screen.getByRole("button", { - name: /Change Type/i + name: /Change Type/i, }); expect(changeTypeButton).toBeInTheDocument(); }); - test("(1 pts) Clicking the button changes the type.", () => { + test("(1 pts) Clicking the button changes the type.", async () => { const changeTypeButton = screen.getByRole("button", { - name: /Change Type/i + name: /Change Type/i, + }); + await act(async () => { + changeTypeButton.click(); }); - changeTypeButton.click(); // Should be Multiple Choice const typeTextMC = screen.getByText(/Multiple Choice/i); expect(typeTextMC).toBeInTheDocument(); @@ -37,12 +39,16 @@ describe("ChangeType Component tests", () => { expect(typeTextSA).toBeNull(); }); - test("(1 pts) Clicking the button twice keeps the type the same.", () => { + test("(1 pts) Clicking the button twice keeps the type the same.", async () => { const changeTypeButton = screen.getByRole("button", { - name: /Change Type/i + name: /Change Type/i, + }); + await act(async () => { + changeTypeButton.click(); + }); + await act(async () => { + changeTypeButton.click(); }); - changeTypeButton.click(); - changeTypeButton.click(); // Should be Short Answer const typeTextSA = screen.getByText(/Short Answer/i); expect(typeTextSA).toBeInTheDocument(); diff --git a/src/components/Counter.test.tsx b/src/components/Counter.test.tsx index 1ee2d1ff32..d08773f5c6 100644 --- a/src/components/Counter.test.tsx +++ b/src/components/Counter.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { Counter } from "./Counter"; @@ -22,17 +22,23 @@ describe("Counter Component tests", () => { expect(addOneButton).toBeInTheDocument(); }); - test("(1 pts) Clicking the button once adds one", () => { + test("(1 pts) Clicking the button once adds one", async () => { const addOneButton = screen.getByRole("button", { name: /Add One/i }); - addOneButton.click(); + await act(async () => { + addOneButton.click(); + }); const valueText = screen.getByText(/1/i); expect(valueText).toBeInTheDocument(); }); - test("(1 pts) Clicking the button twice adds two", () => { + test("(1 pts) Clicking the button twice adds two", async () => { const addOneButton = screen.getByRole("button", { name: /Add One/i }); - addOneButton.click(); - addOneButton.click(); + await act(async () => { + addOneButton.click(); + }); + await act(async () => { + addOneButton.click(); + }); const valueText = screen.getByText(/2/i); expect(valueText).toBeInTheDocument(); }); diff --git a/src/components/CycleHoliday.test.tsx b/src/components/CycleHoliday.test.tsx index a20389d03e..4c1422f3df 100644 --- a/src/components/CycleHoliday.test.tsx +++ b/src/components/CycleHoliday.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { CycleHoliday } from "./CycleHoliday"; @@ -14,39 +14,43 @@ describe("CycleHoliday Component tests", () => { test("(1 pts) There are two buttons", () => { const alphabetButton = screen.getByRole("button", { - name: /Alphabet/i + name: /Alphabet/i, }); const yearButton = screen.getByRole("button", { - name: /Year/i + name: /Year/i, }); expect(alphabetButton).toBeInTheDocument(); expect(yearButton).toBeInTheDocument(); }); - test("(1 pts) Can cycle through 5 distinct holidays alphabetically", () => { + test("(1 pts) Can cycle through 5 distinct holidays alphabetically", async () => { const alphabetButton = screen.getByRole("button", { - name: /Alphabet/i + name: /Alphabet/i, }); const initialHoliday = screen.getByText(/Holiday ?[:)-](.*)/i); const states: string[] = []; for (let i = 0; i < 6; i++) { states.push(initialHoliday.textContent || ""); - alphabetButton.click(); + await act(async () => { + alphabetButton.click(); + }); } const uniqueStates = states.filter((x, y) => states.indexOf(x) == y); expect(uniqueStates).toHaveLength(5); expect(states[0]).toEqual(states[5]); }); - test("(1 pts) Can cycle through 5 distinct holidays by year", () => { + test("(1 pts) Can cycle through 5 distinct holidays by year", async () => { const yearButton = screen.getByRole("button", { - name: /Year/i + name: /Year/i, }); const initialHoliday = screen.getByText(/Holiday ?[:)-](.*)/i); const states: string[] = []; for (let i = 0; i < 6; i++) { states.push(initialHoliday.textContent || ""); - yearButton.click(); + await act(async () => { + yearButton.click(); + }); } const uniqueStates = states.filter((x, y) => states.indexOf(x) == y); expect(uniqueStates).toHaveLength(5); diff --git a/src/components/RevealAnswer.test.tsx b/src/components/RevealAnswer.test.tsx index 438f4d5c28..f5ad250bf9 100644 --- a/src/components/RevealAnswer.test.tsx +++ b/src/components/RevealAnswer.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { RevealAnswer } from "./RevealAnswer"; @@ -12,24 +12,30 @@ describe("RevealAnswer Component tests", () => { }); test("(1 pts) There is a Reveal Answer button", () => { const revealButton = screen.getByRole("button", { - name: /Reveal Answer/i + name: /Reveal Answer/i, }); expect(revealButton).toBeInTheDocument(); }); - test("(1 pts) Clicking Reveal Answer button reveals the '42'", () => { + test("(1 pts) Clicking Reveal Answer button reveals the '42'", async () => { const revealButton = screen.getByRole("button", { - name: /Reveal Answer/i + name: /Reveal Answer/i, + }); + await act(async () => { + revealButton.click(); }); - revealButton.click(); const answerText = screen.getByText(/42/); expect(answerText).toBeInTheDocument(); }); - test("(1 pts) Clicking Reveal Answer button twice hides the '42'", () => { + test("(1 pts) Clicking Reveal Answer button twice hides the '42'", async () => { const revealButton = screen.getByRole("button", { - name: /Reveal Answer/i + name: /Reveal Answer/i, + }); + await act(async () => { + revealButton.click(); + }); + await act(async () => { + revealButton.click(); }); - revealButton.click(); - revealButton.click(); const answerText = screen.queryByText(/42/); expect(answerText).toBeNull(); }); diff --git a/src/components/StartAttempt.test.tsx b/src/components/StartAttempt.test.tsx index 8e5b9667cb..74397290bb 100644 --- a/src/components/StartAttempt.test.tsx +++ b/src/components/StartAttempt.test.tsx @@ -1,5 +1,5 @@ -import React from "react"; -import { render, screen } from "@testing-library/react"; +import React, { act } from "react"; +import { render, screen, cleanup } from "@testing-library/react"; import { StartAttempt } from "./StartAttempt"; /*** @@ -27,6 +27,9 @@ describe("StartAttempt Component tests", () => { beforeEach(() => { render(); }); + afterEach(() => { + cleanup(); + }); test("(1 pts) The Number of attempts is displayed initially, without other numbers", () => { const attemptNumber = screen.getByText(/(\d+)/); expect(attemptNumber).toBeInTheDocument(); @@ -51,109 +54,129 @@ describe("StartAttempt Component tests", () => { }); test("(1 pts) There is an initially enabled Mulligan button", () => { const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i + name: /Mulligan/i, }); expect(mulliganButton).toBeInTheDocument(); expect(mulliganButton).toBeEnabled(); }); - test("(1 pts) Clicking Mulligan increases attempts", () => { + test("(1 pts) Clicking Mulligan increases attempts", async () => { const attemptNumber: number = extractDigits(screen.getByText(/(\d+)/)) || 0; const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i + name: /Mulligan/i, + }); + await act(async () => { + mulliganButton.click(); }); - mulliganButton.click(); const attemptNumberLater = extractDigits(screen.getByText(/(\d+)/)); expect(attemptNumber + 1).toEqual(attemptNumberLater); }); - test("(1 pts) Clicking Mulligan twice increases attempts by two", () => { + test("(1 pts) Clicking Mulligan twice increases attempts by two", async () => { const attemptNumber: number = extractDigits(screen.getByText(/(\d+)/)) || 0; const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i + name: /Mulligan/i, + }); + await act(async () => { + mulliganButton.click(); + }); + await act(async () => { + mulliganButton.click(); }); - mulliganButton.click(); - mulliganButton.click(); const attemptNumberLater = extractDigits(screen.getByText(/(\d+)/)); expect(attemptNumber + 2).toEqual(attemptNumberLater); }); - test("(1 pts) Clicking Start Quiz decreases attempts", () => { + test("(1 pts) Clicking Start Quiz decreases attempts", async () => { const attemptNumber: number = extractDigits(screen.getByText(/(\d+)/)) || 0; const startButton = screen.getByRole("button", { - name: /Start Quiz/i + name: /Start Quiz/i, + }); + await act(async () => { + startButton.click(); }); - startButton.click(); const attemptNumberLater = extractDigits(screen.getByText(/(\d+)/)) || 0; expect(attemptNumber - 1).toEqual(attemptNumberLater); }); - test("(1 pts) Clicking Start Quiz changes enabled buttons", () => { + test("(1 pts) Clicking Start Quiz changes enabled buttons", async () => { // Given the buttons... const startButton = screen.getByRole("button", { - name: /Start Quiz/i + name: /Start Quiz/i, }); const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i + name: /Mulligan/i, }); // When the start button is clicked - startButton.click(); + await act(async () => { + startButton.click(); + }); // Then the start is disabled, stop is enabled, and mulligan is disabled expect(startButton).toBeDisabled(); expect(stopButton).toBeEnabled(); expect(mulliganButton).toBeDisabled(); }); - test("(1 pts) Clicking Start and Stop Quiz changes enabled buttons", () => { + test("(1 pts) Clicking Start and Stop Quiz changes enabled buttons", async () => { // Given the buttons and initial attempt number... const startButton = screen.getByRole("button", { - name: /Start Quiz/i + name: /Start Quiz/i, }); const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i + name: /Mulligan/i, }); // When we click the start button and then the stop button - startButton.click(); - stopButton.click(); + await act(async () => { + startButton.click(); + }); + await act(async () => { + stopButton.click(); + }); // Then the start is enabled, stop is disabled, and mulligan is enabled expect(startButton).toBeEnabled(); expect(stopButton).toBeDisabled(); expect(mulliganButton).toBeEnabled(); }); - test("(1 pts) Clicking Start, Stop, Mulligan sets attempts to original", () => { + test("(1 pts) Clicking Start, Stop, Mulligan sets attempts to original", async () => { // Given the buttons and initial attempt number... const startButton = screen.getByRole("button", { - name: /Start Quiz/i + name: /Start Quiz/i, }); const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i + name: /Mulligan/i, }); const attemptNumber: number = extractDigits(screen.getByText(/(\d+)/)) || 0; // When we click the start button and then the stop button - startButton.click(); - stopButton.click(); + await act(async () => { + startButton.click(); + }); + await act(async () => { + stopButton.click(); + }); // Then the attempt is decreased const attemptNumberLater: number = extractDigits(screen.getByText(/(\d+)/)) || 0; expect(attemptNumber - 1).toEqual(attemptNumberLater); // And when we click the mulligan button - mulliganButton.click(); + await act(async () => { + mulliganButton.click(); + }); // Then the attempt is increased back to starting value const attemptNumberLatest: number = extractDigits(screen.getByText(/(\d+)/)) || 0; expect(attemptNumber).toEqual(attemptNumberLatest); }); - test("(1 pts) Cannot click start quiz when out of attempts", () => { + test("(1 pts) Cannot click start quiz when out of attempts", async () => { // Given the buttons and initial attempt number... const startButton = screen.getByRole("button", { - name: /Start Quiz/i + name: /Start Quiz/i, }); const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i + name: /Mulligan/i, }); let attemptNumber = extractDigits(screen.getByText(/(\d+)/)) || 0; const initialAttempt = attemptNumber; @@ -166,8 +189,12 @@ describe("StartAttempt Component tests", () => { expect(stopButton).toBeDisabled(); expect(mulliganButton).toBeEnabled(); // And when we Start and then immediately stop the quiz... - startButton.click(); - stopButton.click(); + await act(async () => { + startButton.click(); + }); + await act(async () => { + stopButton.click(); + }); // Then the number is going down, and doesn't go past 0 somehow attemptNumber = extractDigits(screen.getByText(/(\d+)/)) || 0; expect(attemptNumber).toBeGreaterThanOrEqual(0); @@ -183,7 +210,9 @@ describe("StartAttempt Component tests", () => { expect(stopButton).toBeDisabled(); expect(mulliganButton).toBeEnabled(); // And when we click the mulligan button - mulliganButton.click(); + await act(async () => { + mulliganButton.click(); + }); // Then the attempt is increased back to 1 const attemptNumberLatest: number = extractDigits(screen.getByText(/(\d+)/)) || 0; diff --git a/src/components/TwoDice.test.tsx b/src/components/TwoDice.test.tsx index d1e5f812dd..e5f9966deb 100644 --- a/src/components/TwoDice.test.tsx +++ b/src/components/TwoDice.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { TwoDice } from "./TwoDice"; import { extractDigits } from "./StartAttempt.test"; @@ -45,11 +45,17 @@ describe("TwoDice Component tests", () => { expect(leftButton).toBeInTheDocument(); expect(rightButton).toBeInTheDocument(); }); - test("(1 pts) Clicking left button changes first number", () => { + test("(1 pts) Clicking left button changes first number", async () => { const leftButton = screen.getByRole("button", { name: /Roll Left/i }); - leftButton.click(); - leftButton.click(); - leftButton.click(); + await act(async () => { + leftButton.click(); + }); + await act(async () => { + leftButton.click(); + }); + await act(async () => { + leftButton.click(); + }); // Then the random function should be called 3 times expect(mathRandomFunction).toBeCalledTimes(3); // And the number to be 5 @@ -57,11 +63,17 @@ describe("TwoDice Component tests", () => { expect(leftNumber).toEqual(5); }); // Clicking right button changes second number - test("(1 pts) Clicking right button changes second number", () => { + test("(1 pts) Clicking right button changes second number", async () => { const rightButton = screen.getByRole("button", { name: /Roll Right/i }); - rightButton.click(); - rightButton.click(); - rightButton.click(); + await act(async () => { + rightButton.click(); + }); + await act(async () => { + rightButton.click(); + }); + await act(async () => { + rightButton.click(); + }); // Then the random function should be called 3 times expect(mathRandomFunction).toBeCalledTimes(3); // And the number to be 5 @@ -69,15 +81,19 @@ describe("TwoDice Component tests", () => { expect(rightNumber).toEqual(5); }); // Rolling two different numbers does not win or lose the game - test("(1 pts) Rolling two different numbers does not win or lose the game", () => { + test("(1 pts) Rolling two different numbers does not win or lose the game", async () => { // Given const leftButton = screen.getByRole("button", { name: /Roll Left/i }); const rightButton = screen.getByRole("button", { name: /Roll Right/i }); const leftDie = screen.getByTestId("left-die"); const rightDie = screen.getByTestId("right-die"); // When the left and right buttons are rolled once each - leftButton.click(); - rightButton.click(); + await act(async () => { + leftButton.click(); + }); + await act(async () => { + rightButton.click(); + }); // Then the numbers are not equal const leftNumber = extractDigits(leftDie); const rightNumber = extractDigits(rightDie); @@ -90,18 +106,28 @@ describe("TwoDice Component tests", () => { const loseText = screen.queryByText(/Lose/i); expect(loseText).toBeNull(); }); - test("(1 pts) Getting snake eyes loses the game", () => { + test("(1 pts) Getting snake eyes loses the game", async () => { // Given const leftButton = screen.getByRole("button", { name: /Roll Left/i }); const rightButton = screen.getByRole("button", { name: /Roll Right/i }); const leftDie = screen.getByTestId("left-die"); const rightDie = screen.getByTestId("right-die"); // When the left and right buttons are rolled once each - leftButton.click(); - rightButton.click(); - rightButton.click(); - rightButton.click(); - rightButton.click(); + await act(async () => { + leftButton.click(); + }); + await act(async () => { + rightButton.click(); + }); + await act(async () => { + rightButton.click(); + }); + await act(async () => { + rightButton.click(); + }); + await act(async () => { + rightButton.click(); + }); // Then the numbers are not equal const leftNumber = extractDigits(leftDie); const rightNumber = extractDigits(rightDie); @@ -114,17 +140,25 @@ describe("TwoDice Component tests", () => { const loseText = screen.getByText(/Lose/i); expect(loseText).toBeInTheDocument(); }); - test("(1 pts) Getting matching numbers wins the game", () => { + test("(1 pts) Getting matching numbers wins the game", async () => { // Given const leftButton = screen.getByRole("button", { name: /Roll Left/i }); const rightButton = screen.getByRole("button", { name: /Roll Right/i }); const leftDie = screen.getByTestId("left-die"); const rightDie = screen.getByTestId("right-die"); // When the left and right buttons are rolled once each - leftButton.click(); - leftButton.click(); - leftButton.click(); - rightButton.click(); + await act(async () => { + leftButton.click(); + }); + await act(async () => { + leftButton.click(); + }); + await act(async () => { + leftButton.click(); + }); + await act(async () => { + rightButton.click(); + }); // Then the numbers are not equal const leftNumber = extractDigits(leftDie); const rightNumber = extractDigits(rightDie); From c419dc919275541b287452c52aac7bf55c2b409c Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 14:20:29 -0400 Subject: [PATCH 069/104] Fix linting --- src/components/ChangeType.test.tsx | 6 +++--- src/components/CycleHoliday.test.tsx | 8 ++++---- src/components/RevealAnswer.test.tsx | 6 +++--- src/components/StartAttempt.test.tsx | 24 ++++++++++++------------ 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/components/ChangeType.test.tsx b/src/components/ChangeType.test.tsx index 0a126a9111..f0ee545cc3 100644 --- a/src/components/ChangeType.test.tsx +++ b/src/components/ChangeType.test.tsx @@ -19,14 +19,14 @@ describe("ChangeType Component tests", () => { test("(1 pts) There is a button labeled Change Type", () => { const changeTypeButton = screen.getByRole("button", { - name: /Change Type/i, + name: /Change Type/i }); expect(changeTypeButton).toBeInTheDocument(); }); test("(1 pts) Clicking the button changes the type.", async () => { const changeTypeButton = screen.getByRole("button", { - name: /Change Type/i, + name: /Change Type/i }); await act(async () => { changeTypeButton.click(); @@ -41,7 +41,7 @@ describe("ChangeType Component tests", () => { test("(1 pts) Clicking the button twice keeps the type the same.", async () => { const changeTypeButton = screen.getByRole("button", { - name: /Change Type/i, + name: /Change Type/i }); await act(async () => { changeTypeButton.click(); diff --git a/src/components/CycleHoliday.test.tsx b/src/components/CycleHoliday.test.tsx index 4c1422f3df..ae364a0b5b 100644 --- a/src/components/CycleHoliday.test.tsx +++ b/src/components/CycleHoliday.test.tsx @@ -14,10 +14,10 @@ describe("CycleHoliday Component tests", () => { test("(1 pts) There are two buttons", () => { const alphabetButton = screen.getByRole("button", { - name: /Alphabet/i, + name: /Alphabet/i }); const yearButton = screen.getByRole("button", { - name: /Year/i, + name: /Year/i }); expect(alphabetButton).toBeInTheDocument(); expect(yearButton).toBeInTheDocument(); @@ -25,7 +25,7 @@ describe("CycleHoliday Component tests", () => { test("(1 pts) Can cycle through 5 distinct holidays alphabetically", async () => { const alphabetButton = screen.getByRole("button", { - name: /Alphabet/i, + name: /Alphabet/i }); const initialHoliday = screen.getByText(/Holiday ?[:)-](.*)/i); const states: string[] = []; @@ -42,7 +42,7 @@ describe("CycleHoliday Component tests", () => { test("(1 pts) Can cycle through 5 distinct holidays by year", async () => { const yearButton = screen.getByRole("button", { - name: /Year/i, + name: /Year/i }); const initialHoliday = screen.getByText(/Holiday ?[:)-](.*)/i); const states: string[] = []; diff --git a/src/components/RevealAnswer.test.tsx b/src/components/RevealAnswer.test.tsx index f5ad250bf9..6b2076ad1a 100644 --- a/src/components/RevealAnswer.test.tsx +++ b/src/components/RevealAnswer.test.tsx @@ -12,13 +12,13 @@ describe("RevealAnswer Component tests", () => { }); test("(1 pts) There is a Reveal Answer button", () => { const revealButton = screen.getByRole("button", { - name: /Reveal Answer/i, + name: /Reveal Answer/i }); expect(revealButton).toBeInTheDocument(); }); test("(1 pts) Clicking Reveal Answer button reveals the '42'", async () => { const revealButton = screen.getByRole("button", { - name: /Reveal Answer/i, + name: /Reveal Answer/i }); await act(async () => { revealButton.click(); @@ -28,7 +28,7 @@ describe("RevealAnswer Component tests", () => { }); test("(1 pts) Clicking Reveal Answer button twice hides the '42'", async () => { const revealButton = screen.getByRole("button", { - name: /Reveal Answer/i, + name: /Reveal Answer/i }); await act(async () => { revealButton.click(); diff --git a/src/components/StartAttempt.test.tsx b/src/components/StartAttempt.test.tsx index 74397290bb..fd326936e6 100644 --- a/src/components/StartAttempt.test.tsx +++ b/src/components/StartAttempt.test.tsx @@ -54,7 +54,7 @@ describe("StartAttempt Component tests", () => { }); test("(1 pts) There is an initially enabled Mulligan button", () => { const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i, + name: /Mulligan/i }); expect(mulliganButton).toBeInTheDocument(); expect(mulliganButton).toBeEnabled(); @@ -63,7 +63,7 @@ describe("StartAttempt Component tests", () => { const attemptNumber: number = extractDigits(screen.getByText(/(\d+)/)) || 0; const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i, + name: /Mulligan/i }); await act(async () => { mulliganButton.click(); @@ -75,7 +75,7 @@ describe("StartAttempt Component tests", () => { const attemptNumber: number = extractDigits(screen.getByText(/(\d+)/)) || 0; const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i, + name: /Mulligan/i }); await act(async () => { mulliganButton.click(); @@ -90,7 +90,7 @@ describe("StartAttempt Component tests", () => { const attemptNumber: number = extractDigits(screen.getByText(/(\d+)/)) || 0; const startButton = screen.getByRole("button", { - name: /Start Quiz/i, + name: /Start Quiz/i }); await act(async () => { startButton.click(); @@ -102,11 +102,11 @@ describe("StartAttempt Component tests", () => { test("(1 pts) Clicking Start Quiz changes enabled buttons", async () => { // Given the buttons... const startButton = screen.getByRole("button", { - name: /Start Quiz/i, + name: /Start Quiz/i }); const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i, + name: /Mulligan/i }); // When the start button is clicked await act(async () => { @@ -120,11 +120,11 @@ describe("StartAttempt Component tests", () => { test("(1 pts) Clicking Start and Stop Quiz changes enabled buttons", async () => { // Given the buttons and initial attempt number... const startButton = screen.getByRole("button", { - name: /Start Quiz/i, + name: /Start Quiz/i }); const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i, + name: /Mulligan/i }); // When we click the start button and then the stop button await act(async () => { @@ -141,11 +141,11 @@ describe("StartAttempt Component tests", () => { test("(1 pts) Clicking Start, Stop, Mulligan sets attempts to original", async () => { // Given the buttons and initial attempt number... const startButton = screen.getByRole("button", { - name: /Start Quiz/i, + name: /Start Quiz/i }); const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i, + name: /Mulligan/i }); const attemptNumber: number = extractDigits(screen.getByText(/(\d+)/)) || 0; @@ -172,11 +172,11 @@ describe("StartAttempt Component tests", () => { test("(1 pts) Cannot click start quiz when out of attempts", async () => { // Given the buttons and initial attempt number... const startButton = screen.getByRole("button", { - name: /Start Quiz/i, + name: /Start Quiz/i }); const stopButton = screen.getByRole("button", { name: /Stop Quiz/i }); const mulliganButton = screen.getByRole("button", { - name: /Mulligan/i, + name: /Mulligan/i }); let attemptNumber = extractDigits(screen.getByText(/(\d+)/)) || 0; const initialAttempt = attemptNumber; From 50a9c85dca9bc60f7dd875355f2c28c3988ca610 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 14:47:15 -0400 Subject: [PATCH 070/104] Update for new react --- src/bad-components/ChooseTeam.test.tsx | 54 ++++++++++++++++++-------- src/bad-components/ChooseTeam.tsx | 4 +- src/bad-components/ColoredBox.test.tsx | 26 ++++++++----- src/bad-components/ColoredBox.tsx | 14 ++++--- src/bad-components/DoubleHalf.test.tsx | 48 +++++++++++++++-------- src/bad-components/DoubleHalf.tsx | 26 ++++++++++--- src/bad-components/ShoveBox.test.tsx | 26 ++++++++----- src/bad-components/ShoveBox.tsx | 16 +++++--- 8 files changed, 144 insertions(+), 70 deletions(-) diff --git a/src/bad-components/ChooseTeam.test.tsx b/src/bad-components/ChooseTeam.test.tsx index f11465a2d6..66eee4be70 100644 --- a/src/bad-components/ChooseTeam.test.tsx +++ b/src/bad-components/ChooseTeam.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { ChooseTeam } from "./ChooseTeam"; @@ -6,54 +6,74 @@ describe("ChooseTeam Component tests", () => { beforeEach(() => { render(); }); - test("The initial team is empty", () => { + test("(2 pts) The initial team is empty", () => { const currentTeam = screen.queryAllByRole("listitem"); expect(currentTeam).toHaveLength(0); }); - test("There are 7 buttons.", () => { + test("(2 pts) There are 7 buttons.", () => { const adders = screen.queryAllByRole("button"); expect(adders).toHaveLength(7); }); - test("Clicking first team member works", () => { + test("(2 pts) Clicking first team member works", async () => { const first = screen.queryAllByRole("button")[0]; - first.click(); + await act(async () => { + first.click(); + }); const currentTeam = screen.queryAllByRole("listitem"); expect(currentTeam).toHaveLength(1); expect(currentTeam[0].textContent).toEqual(first.textContent); }); - test("Clicking the third team member works", () => { + test("(2 pts) Clicking the third team member works", async () => { const third = screen.queryAllByRole("button")[2]; - third.click(); + await act(async () => { + third.click(); + }); const currentTeam = screen.queryAllByRole("listitem"); expect(currentTeam).toHaveLength(1); expect(currentTeam[0].textContent).toEqual(third.textContent); }); - test("Clicking three team members works", () => { + test("(2 pts) Clicking three team members works", async () => { const [, second, third, , fifth] = screen.queryAllByRole("button"); - third.click(); - second.click(); - fifth.click(); + await act(async () => { + third.click(); + }); + await act(async () => { + second.click(); + }); + await act(async () => { + fifth.click(); + }); const currentTeam = screen.queryAllByRole("listitem"); expect(currentTeam).toHaveLength(3); expect(currentTeam[0].textContent).toEqual(third.textContent); expect(currentTeam[1].textContent).toEqual(second.textContent); expect(currentTeam[2].textContent).toEqual(fifth.textContent); }); - test("Clearing the team works", () => { + test("(2 pts) Clearing the team works", async () => { const [, second, third, fourth, fifth, , clear] = screen.queryAllByRole("button"); - third.click(); - second.click(); - fifth.click(); + await act(async () => { + third.click(); + }); + await act(async () => { + second.click(); + }); + await act(async () => { + fifth.click(); + }); let currentTeam = screen.queryAllByRole("listitem"); expect(currentTeam).toHaveLength(3); expect(currentTeam[0].textContent).toEqual(third.textContent); expect(currentTeam[1].textContent).toEqual(second.textContent); expect(currentTeam[2].textContent).toEqual(fifth.textContent); - clear.click(); + await act(async () => { + clear.click(); + }); currentTeam = screen.queryAllByRole("listitem"); expect(currentTeam).toHaveLength(0); - fourth.click(); + await act(async () => { + fourth.click(); + }); currentTeam = screen.queryAllByRole("listitem"); expect(currentTeam).toHaveLength(1); expect(currentTeam[0].textContent).toEqual(fourth.textContent); diff --git a/src/bad-components/ChooseTeam.tsx b/src/bad-components/ChooseTeam.tsx index c02334e661..e73f600083 100644 --- a/src/bad-components/ChooseTeam.tsx +++ b/src/bad-components/ChooseTeam.tsx @@ -7,10 +7,10 @@ const PEOPLE = [ "Ada Lovelace", "Charles Babbage", "Barbara Liskov", - "Margaret Hamilton" + "Margaret Hamilton", ]; -export function ChooseTeam(): JSX.Element { +export function ChooseTeam(): React.JSX.Element { const [allOptions, setAllOptions] = useState(PEOPLE); const [team, setTeam] = useState([]); diff --git a/src/bad-components/ColoredBox.test.tsx b/src/bad-components/ColoredBox.test.tsx index c17a13f66c..5762afefb6 100644 --- a/src/bad-components/ColoredBox.test.tsx +++ b/src/bad-components/ColoredBox.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { ColoredBox } from "./ColoredBox"; @@ -6,26 +6,32 @@ describe("ColoredBox Component tests", () => { beforeEach(() => { render(); }); - test("The ColoredBox is initially red.", () => { + test("(2 pts) The ColoredBox is initially red.", () => { const box = screen.getByTestId("colored-box"); expect(box).toHaveStyle({ backgroundColor: "red" }); }); - test("There is a button", () => { + test("(2 pts) There is a button", () => { expect(screen.getByRole("button")).toBeInTheDocument(); }); - test("Clicking the button advances the color.", () => { + test("(2 pts) Clicking the button advances the color.", async () => { const nextColor = screen.getByRole("button"); - nextColor.click(); + await act(async () => { + nextColor.click(); + }); expect(screen.getByTestId("colored-box")).toHaveStyle({ - backgroundColor: "blue" + backgroundColor: "blue", + }); + await act(async () => { + nextColor.click(); }); - nextColor.click(); expect(screen.getByTestId("colored-box")).toHaveStyle({ - backgroundColor: "green" + backgroundColor: "green", + }); + await act(async () => { + nextColor.click(); }); - nextColor.click(); expect(screen.getByTestId("colored-box")).toHaveStyle({ - backgroundColor: "red" + backgroundColor: "red", }); }); }); diff --git a/src/bad-components/ColoredBox.tsx b/src/bad-components/ColoredBox.tsx index a3c3c3f077..1fa2d770b2 100644 --- a/src/bad-components/ColoredBox.tsx +++ b/src/bad-components/ColoredBox.tsx @@ -4,16 +4,20 @@ import { Button } from "react-bootstrap"; export const COLORS = ["red", "blue", "green"]; const DEFAULT_COLOR_INDEX = 0; -function ChangeColor(): JSX.Element { +function ChangeColor(): React.JSX.Element { const [colorIndex, setColorIndex] = useState(DEFAULT_COLOR_INDEX); return ( - ); } -function ColorPreview(): JSX.Element { +function ColorPreview(): React.JSX.Element { return (
      ); } -export function ColoredBox(): JSX.Element { +export function ColoredBox(): React.JSX.Element { return (

      Colored Box

      diff --git a/src/bad-components/DoubleHalf.test.tsx b/src/bad-components/DoubleHalf.test.tsx index cbae5f68af..9b2a031acf 100644 --- a/src/bad-components/DoubleHalf.test.tsx +++ b/src/bad-components/DoubleHalf.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { DoubleHalf } from "./DoubleHalf"; @@ -6,50 +6,66 @@ describe("DoubleHalf Component tests", () => { beforeEach(() => { render(); }); - test("The DoubleHalf value is initially 10", () => { + test("(2 pts) The DoubleHalf value is initially 10", () => { expect(screen.getByText("10")).toBeInTheDocument(); expect(screen.queryByText("20")).not.toBeInTheDocument(); expect(screen.queryByText("5")).not.toBeInTheDocument(); }); - test("There are Double and Halve buttons", () => { + test("(2 pts) There are Double and Halve buttons", () => { expect( - screen.getByRole("button", { name: /Double/i }) + screen.getByRole("button", { name: /Double/i }), ).toBeInTheDocument(); expect( - screen.getByRole("button", { name: /Halve/i }) + screen.getByRole("button", { name: /Halve/i }), ).toBeInTheDocument(); }); - test("You can double the number.", () => { + test("(2 pts) You can double the number.", async () => { const double = screen.getByRole("button", { name: /Double/i }); - double.click(); + await act(async () => { + double.click(); + }); expect(screen.getByText("20")).toBeInTheDocument(); expect(screen.queryByText("10")).not.toBeInTheDocument(); }); - test("You can halve the number.", () => { + test("(2 pts) You can halve the number.", async () => { const halve = screen.getByRole("button", { name: /Halve/i }); - halve.click(); + await act(async () => { + halve.click(); + }); expect(screen.getByText("5")).toBeInTheDocument(); expect(screen.queryByText("10")).not.toBeInTheDocument(); }); - test("You can double AND halve the number.", () => { + test("(2 pts) You can double AND halve the number.", async () => { const double = screen.getByRole("button", { name: /Double/i }); const halve = screen.getByRole("button", { name: /Halve/i }); - double.click(); + await act(async () => { + double.click(); + }); expect(screen.getByText("20")).toBeInTheDocument(); expect(screen.queryByText("10")).not.toBeInTheDocument(); - double.click(); + await act(async () => { + double.click(); + }); expect(screen.getByText("40")).toBeInTheDocument(); expect(screen.queryByText("20")).not.toBeInTheDocument(); - halve.click(); + await act(async () => { + halve.click(); + }); expect(screen.getByText("20")).toBeInTheDocument(); expect(screen.queryByText("10")).not.toBeInTheDocument(); - halve.click(); + await act(async () => { + halve.click(); + }); expect(screen.getByText("10")).toBeInTheDocument(); expect(screen.queryByText("20")).not.toBeInTheDocument(); - halve.click(); + await act(async () => { + halve.click(); + }); expect(screen.getByText("5")).toBeInTheDocument(); expect(screen.queryByText("10")).not.toBeInTheDocument(); - halve.click(); + await act(async () => { + halve.click(); + }); expect(screen.getByText("2.5")).toBeInTheDocument(); expect(screen.queryByText("5")).not.toBeInTheDocument(); }); diff --git a/src/bad-components/DoubleHalf.tsx b/src/bad-components/DoubleHalf.tsx index 5ae9cf4501..8b01352f59 100644 --- a/src/bad-components/DoubleHalf.tsx +++ b/src/bad-components/DoubleHalf.tsx @@ -2,15 +2,31 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; import { dhValue, setDhValue } from "./DoubleHalfState"; -function Doubler(): JSX.Element { - return ; +function Doubler(): React.JSX.Element { + return ( + + ); } -function Halver(): JSX.Element { - return ; +function Halver(): React.JSX.Element { + return ( + + ); } -export function DoubleHalf(): JSX.Element { +export function DoubleHalf(): React.JSX.Element { return (

      Double Half

      diff --git a/src/bad-components/ShoveBox.test.tsx b/src/bad-components/ShoveBox.test.tsx index 2adec13d4e..e89abf2751 100644 --- a/src/bad-components/ShoveBox.test.tsx +++ b/src/bad-components/ShoveBox.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { ShoveBox } from "./ShoveBox"; @@ -6,26 +6,32 @@ describe("ShoveBox Component tests", () => { beforeEach(() => { render(); }); - test("The MoveableBox is initially nearby.", () => { + test("(2 pts) The MoveableBox is initially nearby.", () => { const box = screen.getByTestId("moveable-box"); expect(box).toHaveStyle({ marginLeft: "10px" }); }); - test("There is a button", () => { + test("(2 pts) There is a button", () => { expect(screen.getByRole("button")).toBeInTheDocument(); }); - test("Clicking the button moves the box.", () => { + test("(2 pts) Clicking the button moves the box.", async () => { const shoveBox = screen.getByRole("button"); - shoveBox.click(); + await act(async () => { + shoveBox.click(); + }); expect(screen.getByTestId("moveable-box")).toHaveStyle({ - marginLeft: "14px" + marginLeft: "14px", + }); + await act(async () => { + shoveBox.click(); }); - shoveBox.click(); expect(screen.getByTestId("moveable-box")).toHaveStyle({ - marginLeft: "18px" + marginLeft: "18px", + }); + await act(async () => { + shoveBox.click(); }); - shoveBox.click(); expect(screen.getByTestId("moveable-box")).toHaveStyle({ - marginLeft: "22px" + marginLeft: "22px", }); }); }); diff --git a/src/bad-components/ShoveBox.tsx b/src/bad-components/ShoveBox.tsx index 7c55142636..45cdcc335d 100644 --- a/src/bad-components/ShoveBox.tsx +++ b/src/bad-components/ShoveBox.tsx @@ -3,17 +3,23 @@ import { Button } from "react-bootstrap"; function ShoveBoxButton({ position, - setPosition + setPosition, }: { position: number; setPosition: (newPosition: number) => void; }) { return ( - + ); } -function MoveableBox(): JSX.Element { +function MoveableBox(): React.JSX.Element { const [position, setPosition] = useState(10); return (
      ); } -export function ShoveBox(): JSX.Element { +export function ShoveBox(): React.JSX.Element { const box = MoveableBox(); return ( From 084abb4bd6ed6df4bb12ac9bb3bc8fcd2dcc46d9 Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 14:58:21 -0400 Subject: [PATCH 071/104] Update the code for new version of react --- src/form-components/ChangeColor.test.tsx | 20 ++++--- src/form-components/CheckAnswer.test.tsx | 26 ++++++---- src/form-components/CheckAnswer.tsx | 4 +- src/form-components/EditMode.test.tsx | 52 +++++++++++++------ src/form-components/EditMode.tsx | 2 +- src/form-components/GiveAttempts.test.tsx | 44 +++++++++++----- src/form-components/GiveAttempts.tsx | 2 +- .../MultipleChoiceQuestion.test.tsx | 42 +++++++++------ .../MultipleChoiceQuestion.tsx | 4 +- 9 files changed, 130 insertions(+), 66 deletions(-) diff --git a/src/form-components/ChangeColor.test.tsx b/src/form-components/ChangeColor.test.tsx index d74ba37243..3b5e86bfff 100644 --- a/src/form-components/ChangeColor.test.tsx +++ b/src/form-components/ChangeColor.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { ChangeColor } from "./ChangeColor"; @@ -9,25 +9,33 @@ describe("ChangeColor Component tests", () => { expect(radios.length).toBeGreaterThanOrEqual(8); expect(screen.getByTestId("colored-box")).toBeInTheDocument(); }); - test("Switching the color switches the displayed color.", () => { + test("Switching the color switches the displayed color.", async () => { const radios: HTMLInputElement[] = screen.getAllByRole("radio"); // Switch to first - radios[0].click(); + await act(async () => { + radios[0].click(); + }); let coloredBox = screen.getByTestId("colored-box"); expect(coloredBox).toHaveTextContent(radios[0].value); expect(coloredBox).toHaveStyle({ backgroundColor: radios[0].value }); // Switch to third - radios[2].click(); + await act(async () => { + radios[2].click(); + }); coloredBox = screen.getByTestId("colored-box"); expect(coloredBox).toHaveTextContent(radios[2].value); expect(coloredBox).toHaveStyle({ backgroundColor: radios[2].value }); // Switch to 8th - radios[7].click(); + await act(async () => { + radios[7].click(); + }); coloredBox = screen.getByTestId("colored-box"); expect(coloredBox).toHaveTextContent(radios[7].value); expect(coloredBox).toHaveStyle({ backgroundColor: radios[7].value }); // Switch back to first - radios[0].click(); + await act(async () => { + radios[0].click(); + }); coloredBox = screen.getByTestId("colored-box"); expect(coloredBox).toHaveTextContent(radios[0].value); expect(coloredBox).toHaveStyle({ backgroundColor: radios[0].value }); diff --git a/src/form-components/CheckAnswer.test.tsx b/src/form-components/CheckAnswer.test.tsx index 076ab209a7..7bca8a64d0 100644 --- a/src/form-components/CheckAnswer.test.tsx +++ b/src/form-components/CheckAnswer.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { CheckAnswer } from "./CheckAnswer"; import userEvent from "@testing-library/user-event"; @@ -14,31 +14,39 @@ describe("CheckAnswer Component tests", () => { expect(screen.getByText(/❌/i)).toBeInTheDocument(); expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); }); - test("Entering the right answer makes it correct.", () => { + test("Entering the right answer makes it correct.", async () => { render(); const inputBox = screen.getByRole("textbox"); - userEvent.type(inputBox, "42"); + await act(async () => { + userEvent.type(inputBox, "42"); + }); expect(screen.getByText(/✔️/i)).toBeInTheDocument(); expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); }); - test("Entering the wrong answer makes it incorrect.", () => { + test("Entering the wrong answer makes it incorrect.", async () => { render(); const inputBox = screen.getByRole("textbox"); - userEvent.type(inputBox, "43"); + await act(async () => { + userEvent.type(inputBox, "43"); + }); expect(screen.getByText(/❌/i)).toBeInTheDocument(); expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); }); - test("Entering a different right answer makes it correct.", () => { + test("Entering a different right answer makes it correct.", async () => { render(); const inputBox = screen.getByRole("textbox"); - userEvent.type(inputBox, "Hello"); + await act(async () => { + userEvent.type(inputBox, "Hello"); + }); expect(screen.getByText(/✔️/i)).toBeInTheDocument(); expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); }); - test("Entering a different wrong answer still makes it incorrect.", () => { + test("Entering a different wrong answer still makes it incorrect.", async () => { render(); const inputBox = screen.getByRole("textbox"); - userEvent.type(inputBox, "42"); + await act(async () => { + userEvent.type(inputBox, "42"); + }); expect(screen.getByText(/❌/i)).toBeInTheDocument(); expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); }); diff --git a/src/form-components/CheckAnswer.tsx b/src/form-components/CheckAnswer.tsx index afb3dbf8a4..8aa74b5e2e 100644 --- a/src/form-components/CheckAnswer.tsx +++ b/src/form-components/CheckAnswer.tsx @@ -1,10 +1,10 @@ import React, { useState } from "react"; export function CheckAnswer({ - expectedAnswer + expectedAnswer, }: { expectedAnswer: string; -}): JSX.Element { +}): React.JSX.Element { return (

      Check Answer

      diff --git a/src/form-components/EditMode.test.tsx b/src/form-components/EditMode.test.tsx index b2f2a43a36..a630193f87 100644 --- a/src/form-components/EditMode.test.tsx +++ b/src/form-components/EditMode.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { EditMode } from "./EditMode"; import userEvent from "@testing-library/user-event"; @@ -14,35 +14,55 @@ describe("EditMode Component tests", () => { test("Initial text should be 'Your Name is a student'.", () => { expect(screen.getByText(/Your Name is a student/i)).toBeInTheDocument(); }); - test("Can switch into Edit Mode", () => { + test("Can switch into Edit Mode", async () => { const switchButton = screen.getByRole("checkbox"); - switchButton.click(); + await act(async () => { + switchButton.click(); + }); expect(screen.getByRole("textbox")).toBeInTheDocument(); expect(screen.getAllByRole("checkbox")).toHaveLength(2); }); - test("Editing the name and student status changes the text", () => { + test("Editing the name and student status changes the text", async () => { const switchButton = screen.getByRole("checkbox"); - switchButton.click(); + await act(async () => { + switchButton.click(); + }); const nameBox = screen.getByRole("textbox"); - userEvent.type(nameBox, "Ada Lovelace"); + await act(async () => { + userEvent.type(nameBox, "Ada Lovelace"); + }); const studentBox = screen.getByRole("checkbox", { name: /student/i }); - studentBox.click(); - switchButton.click(); + await act(async () => { + studentBox.click(); + }); + await act(async () => { + switchButton.click(); + }); expect( - screen.getByText(/Ada Lovelace is not a student/i) + screen.getByText(/Ada Lovelace is not a student/i), ).toBeInTheDocument(); }); - test("Different name, click student box twice changes the text", () => { + test("Different name, click student box twice changes the text", async () => { const switchButton = screen.getByRole("checkbox"); - switchButton.click(); + await act(async () => { + switchButton.click(); + }); const nameBox = screen.getByRole("textbox"); - userEvent.type(nameBox, "Alan Turing"); + await act(async () => { + userEvent.type(nameBox, "Alan Turing"); + }); const studentBox = screen.getByRole("checkbox", { name: /student/i }); - studentBox.click(); - studentBox.click(); - switchButton.click(); + await act(async () => { + studentBox.click(); + }); + await act(async () => { + studentBox.click(); + }); + await act(async () => { + switchButton.click(); + }); expect( - screen.getByText(/Alan Turing is a student/i) + screen.getByText(/Alan Turing is a student/i), ).toBeInTheDocument(); }); }); diff --git a/src/form-components/EditMode.tsx b/src/form-components/EditMode.tsx index fac8734531..ca9fdb9912 100644 --- a/src/form-components/EditMode.tsx +++ b/src/form-components/EditMode.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; -export function EditMode(): JSX.Element { +export function EditMode(): React.JSX.Element { return (

      Edit Mode

      diff --git a/src/form-components/GiveAttempts.test.tsx b/src/form-components/GiveAttempts.test.tsx index eb1c3e4a45..5fa917d6f0 100644 --- a/src/form-components/GiveAttempts.test.tsx +++ b/src/form-components/GiveAttempts.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { fireEvent, render, screen } from "@testing-library/react"; import { GiveAttempts } from "./GiveAttempts"; import userEvent from "@testing-library/user-event"; @@ -17,30 +17,48 @@ describe("GiveAttempts Component tests", () => { expect(screen.getByText(/3/i)).toBeInTheDocument(); }); - test("You can use attempts", () => { + test("You can use attempts", async () => { const use = screen.getByRole("button", { name: /use/i }); - use.click(); + await act(async () => { + use.click(); + }); expect(screen.getByText(/2/i)).toBeInTheDocument(); - use.click(); - use.click(); + await act(async () => { + use.click(); + }); + await act(async () => { + use.click(); + }); expect(screen.getByText(/0/i)).toBeInTheDocument(); expect(use).toBeDisabled(); }); - test("You can gain arbitrary attempts", () => { + test("You can gain arbitrary attempts", async () => { const gain = screen.getByRole("button", { name: /gain/i }); const amountToGive = screen.getByRole("spinbutton"); - userEvent.type(amountToGive, "10"); - gain.click(); + await act(async () => { + userEvent.type(amountToGive, "10"); + }); + await act(async () => { + gain.click(); + }); expect(screen.getByText(/13/i)).toBeInTheDocument(); - userEvent.type(amountToGive, "100"); - gain.click(); + await act(async () => { + userEvent.type(amountToGive, "100"); + }); + await act(async () => { + gain.click(); + }); expect(screen.getByText(/113/i)).toBeInTheDocument(); }); - test("Cannot gain blank amounts", () => { + test("Cannot gain blank amounts", async () => { const gain = screen.getByRole("button", { name: /gain/i }); const amountToGive = screen.getByRole("spinbutton"); - fireEvent.change(amountToGive, { target: { value: "" } }); - gain.click(); + await act(async () => { + fireEvent.change(amountToGive, { target: { value: "" } }); + }); + await act(async () => { + gain.click(); + }); expect(screen.getByText(/3/i)).toBeInTheDocument(); }); }); diff --git a/src/form-components/GiveAttempts.tsx b/src/form-components/GiveAttempts.tsx index 2ca61863fc..2aa1e51dfb 100644 --- a/src/form-components/GiveAttempts.tsx +++ b/src/form-components/GiveAttempts.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; -export function GiveAttempts(): JSX.Element { +export function GiveAttempts(): React.JSX.Element { return (

      Give Attempts

      diff --git a/src/form-components/MultipleChoiceQuestion.test.tsx b/src/form-components/MultipleChoiceQuestion.test.tsx index 03a520a818..132e98d93a 100644 --- a/src/form-components/MultipleChoiceQuestion.test.tsx +++ b/src/form-components/MultipleChoiceQuestion.test.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { act } from "react"; import { render, screen } from "@testing-library/react"; import { MultipleChoiceQuestion } from "./MultipleChoiceQuestion"; import userEvent from "@testing-library/user-event"; @@ -9,7 +9,7 @@ describe("MultipleChoiceQuestion Component tests", () => { + />, ); expect(screen.getByRole("combobox")).toBeInTheDocument(); }); @@ -18,61 +18,71 @@ describe("MultipleChoiceQuestion Component tests", () => { + />, ); expect(screen.getByText(/❌/i)).toBeInTheDocument(); expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); }); - test("Can choose the correct answer", () => { + test("Can choose the correct answer", async () => { render( + />, ); const select = screen.getByRole("combobox"); - userEvent.selectOptions(select, "2"); + await act(async () => { + userEvent.selectOptions(select, "2"); + }); expect(screen.getByText(/✔️/i)).toBeInTheDocument(); expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); }); - test("Can choose the correct answer and then incorrect", () => { + test("Can choose the correct answer and then incorrect", async () => { render( + />, ); const select = screen.getByRole("combobox"); - userEvent.selectOptions(select, "2"); + await act(async () => { + userEvent.selectOptions(select, "2"); + }); expect(screen.getByText(/✔️/i)).toBeInTheDocument(); expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); - userEvent.selectOptions(select, "3"); + await act(async () => { + userEvent.selectOptions(select, "3"); + }); expect(screen.getByText(/❌/i)).toBeInTheDocument(); expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); }); - test("Can start off initially correct", () => { + test("Can start off initially correct", async () => { render( + />, ); const select = screen.getByRole("combobox"); - userEvent.selectOptions(select, "Alpha"); + await act(async () => { + userEvent.selectOptions(select, "Alpha"); + }); expect(screen.getByText(/✔️/i)).toBeInTheDocument(); expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); }); - test("One more test of choosing the right answer", () => { + test("One more test of choosing the right answer", async () => { render( + />, ); expect(screen.getByText(/❌/i)).toBeInTheDocument(); expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); const select = screen.getByRole("combobox"); - userEvent.selectOptions(select, "World"); + await act(async () => { + userEvent.selectOptions(select, "World"); + }); expect(screen.getByText(/✔️/i)).toBeInTheDocument(); expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); }); diff --git a/src/form-components/MultipleChoiceQuestion.tsx b/src/form-components/MultipleChoiceQuestion.tsx index a84759862f..5e6f35c611 100644 --- a/src/form-components/MultipleChoiceQuestion.tsx +++ b/src/form-components/MultipleChoiceQuestion.tsx @@ -2,11 +2,11 @@ import React, { useState } from "react"; export function MultipleChoiceQuestion({ options, - expectedAnswer + expectedAnswer, }: { options: string[]; expectedAnswer: string; -}): JSX.Element { +}): React.JSX.Element { return (

      Multiple Choice Question

      From c95dc0f22f7445ec3cf77f546a76c556fde2d4ab Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 14:58:30 -0400 Subject: [PATCH 072/104] This one too --- src/form-components/ChangeColor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/form-components/ChangeColor.tsx b/src/form-components/ChangeColor.tsx index bcb584fcf9..514f5f893f 100644 --- a/src/form-components/ChangeColor.tsx +++ b/src/form-components/ChangeColor.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; -export function ChangeColor(): JSX.Element { +export function ChangeColor(): React.JSX.Element { return (

      Change Color

      From 3119c0f684409e3e87ac55a0cc78dd6cd0b8633e Mon Sep 17 00:00:00 2001 From: Austin Cory Bart Date: Sat, 24 Aug 2024 15:00:46 -0400 Subject: [PATCH 073/104] Add in points --- src/form-components/ChangeColor.test.tsx | 4 ++-- src/form-components/CheckAnswer.test.tsx | 12 ++++++------ src/form-components/EditMode.test.tsx | 10 +++++----- src/form-components/GiveAttempts.test.tsx | 10 +++++----- src/form-components/MultipleChoiceQuestion.test.tsx | 12 ++++++------ 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/form-components/ChangeColor.test.tsx b/src/form-components/ChangeColor.test.tsx index 3b5e86bfff..4055ee0508 100644 --- a/src/form-components/ChangeColor.test.tsx +++ b/src/form-components/ChangeColor.test.tsx @@ -4,12 +4,12 @@ import { ChangeColor } from "./ChangeColor"; describe("ChangeColor Component tests", () => { beforeEach(() => render()); - test("There are at least 8 radio buttons and the colored box", () => { + test("(2 pts) There are at least 8 radio buttons and the colored box", () => { const radios = screen.getAllByRole("radio"); expect(radios.length).toBeGreaterThanOrEqual(8); expect(screen.getByTestId("colored-box")).toBeInTheDocument(); }); - test("Switching the color switches the displayed color.", async () => { + test("(2 pts) Switching the color switches the displayed color.", async () => { const radios: HTMLInputElement[] = screen.getAllByRole("radio"); // Switch to first await act(async () => { diff --git a/src/form-components/CheckAnswer.test.tsx b/src/form-components/CheckAnswer.test.tsx index 7bca8a64d0..df11c436b6 100644 --- a/src/form-components/CheckAnswer.test.tsx +++ b/src/form-components/CheckAnswer.test.tsx @@ -4,17 +4,17 @@ import { CheckAnswer } from "./CheckAnswer"; import userEvent from "@testing-library/user-event"; describe("CheckAnswer Component tests", () => { - test("There is an input box", () => { + test("(2 pts) There is an input box", () => { render(); const inputBox = screen.getByRole("textbox"); expect(inputBox).toBeInTheDocument(); }); - test("The answer is originally incorrect.", () => { + test("(2 pts) The answer is originally incorrect.", () => { render(); expect(screen.getByText(/❌/i)).toBeInTheDocument(); expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); }); - test("Entering the right answer makes it correct.", async () => { + test("(2 pts) Entering the right answer makes it correct.", async () => { render(); const inputBox = screen.getByRole("textbox"); await act(async () => { @@ -23,7 +23,7 @@ describe("CheckAnswer Component tests", () => { expect(screen.getByText(/✔️/i)).toBeInTheDocument(); expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); }); - test("Entering the wrong answer makes it incorrect.", async () => { + test("(2 pts) Entering the wrong answer makes it incorrect.", async () => { render(); const inputBox = screen.getByRole("textbox"); await act(async () => { @@ -32,7 +32,7 @@ describe("CheckAnswer Component tests", () => { expect(screen.getByText(/❌/i)).toBeInTheDocument(); expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); }); - test("Entering a different right answer makes it correct.", async () => { + test("(2 pts) Entering a different right answer makes it correct.", async () => { render(); const inputBox = screen.getByRole("textbox"); await act(async () => { @@ -41,7 +41,7 @@ describe("CheckAnswer Component tests", () => { expect(screen.getByText(/✔️/i)).toBeInTheDocument(); expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); }); - test("Entering a different wrong answer still makes it incorrect.", async () => { + test("(2 pts) Entering a different wrong answer still makes it incorrect.", async () => { render(); const inputBox = screen.getByRole("textbox"); await act(async () => { diff --git a/src/form-components/EditMode.test.tsx b/src/form-components/EditMode.test.tsx index a630193f87..a9ffcf6cf9 100644 --- a/src/form-components/EditMode.test.tsx +++ b/src/form-components/EditMode.test.tsx @@ -5,16 +5,16 @@ import userEvent from "@testing-library/user-event"; describe("EditMode Component tests", () => { beforeEach(() => render()); - test("There is one checkbox and no textboxes", () => { + test("(2 pts) There is one checkbox and no textboxes", () => { const switchButton = screen.getByRole("checkbox"); expect(switchButton).toBeInTheDocument(); expect(switchButton.parentElement).toHaveClass("form-switch"); expect(screen.queryByRole("textbox")).not.toBeInTheDocument(); }); - test("Initial text should be 'Your Name is a student'.", () => { + test("(2 pts) Initial text should be 'Your Name is a student'.", () => { expect(screen.getByText(/Your Name is a student/i)).toBeInTheDocument(); }); - test("Can switch into Edit Mode", async () => { + test("(2 pts) Can switch into Edit Mode", async () => { const switchButton = screen.getByRole("checkbox"); await act(async () => { switchButton.click(); @@ -22,7 +22,7 @@ describe("EditMode Component tests", () => { expect(screen.getByRole("textbox")).toBeInTheDocument(); expect(screen.getAllByRole("checkbox")).toHaveLength(2); }); - test("Editing the name and student status changes the text", async () => { + test("(2 pts) Editing the name and student status changes the text", async () => { const switchButton = screen.getByRole("checkbox"); await act(async () => { switchButton.click(); @@ -42,7 +42,7 @@ describe("EditMode Component tests", () => { screen.getByText(/Ada Lovelace is not a student/i), ).toBeInTheDocument(); }); - test("Different name, click student box twice changes the text", async () => { + test("(2 pts) Different name, click student box twice changes the text", async () => { const switchButton = screen.getByRole("checkbox"); await act(async () => { switchButton.click(); diff --git a/src/form-components/GiveAttempts.test.tsx b/src/form-components/GiveAttempts.test.tsx index 5fa917d6f0..588d959708 100644 --- a/src/form-components/GiveAttempts.test.tsx +++ b/src/form-components/GiveAttempts.test.tsx @@ -8,16 +8,16 @@ describe("GiveAttempts Component tests", () => { render(); }); - test("There is a number entry box and two buttons", () => { + test("(2 pts) There is a number entry box and two buttons", () => { expect(screen.getByRole("spinbutton")).toBeInTheDocument(); expect(screen.getAllByRole("button")).toHaveLength(2); }); - test("There is are initially 3 attempts", () => { + test("(2 pts) There is are initially 3 attempts", () => { expect(screen.getByText(/3/i)).toBeInTheDocument(); }); - test("You can use attempts", async () => { + test("(2 pts) You can use attempts", async () => { const use = screen.getByRole("button", { name: /use/i }); await act(async () => { use.click(); @@ -32,7 +32,7 @@ describe("GiveAttempts Component tests", () => { expect(screen.getByText(/0/i)).toBeInTheDocument(); expect(use).toBeDisabled(); }); - test("You can gain arbitrary attempts", async () => { + test("(2 pts) You can gain arbitrary attempts", async () => { const gain = screen.getByRole("button", { name: /gain/i }); const amountToGive = screen.getByRole("spinbutton"); await act(async () => { @@ -50,7 +50,7 @@ describe("GiveAttempts Component tests", () => { }); expect(screen.getByText(/113/i)).toBeInTheDocument(); }); - test("Cannot gain blank amounts", async () => { + test("(2 pts) Cannot gain blank amounts", async () => { const gain = screen.getByRole("button", { name: /gain/i }); const amountToGive = screen.getByRole("spinbutton"); await act(async () => { diff --git a/src/form-components/MultipleChoiceQuestion.test.tsx b/src/form-components/MultipleChoiceQuestion.test.tsx index 132e98d93a..69ba4da806 100644 --- a/src/form-components/MultipleChoiceQuestion.test.tsx +++ b/src/form-components/MultipleChoiceQuestion.test.tsx @@ -4,7 +4,7 @@ import { MultipleChoiceQuestion } from "./MultipleChoiceQuestion"; import userEvent from "@testing-library/user-event"; describe("MultipleChoiceQuestion Component tests", () => { - test("There is a select box", () => { + test("(2 pts) There is a select box", () => { render( { ); expect(screen.getByRole("combobox")).toBeInTheDocument(); }); - test("The answer is initially incorrect", () => { + test("(2 pts) The answer is initially incorrect", () => { render( { expect(screen.getByText(/❌/i)).toBeInTheDocument(); expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); }); - test("Can choose the correct answer", async () => { + test("(2 pts) Can choose the correct answer", async () => { render( { expect(screen.getByText(/✔️/i)).toBeInTheDocument(); expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); }); - test("Can choose the correct answer and then incorrect", async () => { + test("(2 pts) Can choose the correct answer and then incorrect", async () => { render( { expect(screen.getByText(/❌/i)).toBeInTheDocument(); expect(screen.queryByText(/✔️/i)).not.toBeInTheDocument(); }); - test("Can start off initially correct", async () => { + test("(2 pts) Can start off initially correct", async () => { render( { expect(screen.getByText(/✔️/i)).toBeInTheDocument(); expect(screen.queryByText(/❌/i)).not.toBeInTheDocument(); }); - test("One more test of choosing the right answer", async () => { + test("(2 pts) One more test of choosing the right answer", async () => { render( Date: Thu, 5 Feb 2026 10:31:38 -0500 Subject: [PATCH 074/104] Added name to App --- src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index b77558eaac..956829ebb6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,7 @@ function App(): React.JSX.Element { return (
      - UD CISC275 with React Hooks and TypeScript + UD CISC275 with React Hooks and TypeScript By Valerie Owens

      Edit src/App.tsx and save. This page will From 599dcda2699f3338e86ff6e094d81d14e6ddaa52 Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 11 Feb 2026 12:54:56 -0500 Subject: [PATCH 075/104] Completed Task 2 (hopefully) --- package-lock.json | 155 ++++++++++++++++++++++++++-------------------- src/App.tsx | 3 +- 2 files changed, 90 insertions(+), 68 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c3779f487..8c4e862b08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -100,6 +100,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", @@ -785,6 +786,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.7.tgz", "integrity": "sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -1663,6 +1665,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.2.tgz", "integrity": "sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "@babel/helper-module-imports": "^7.24.7", @@ -2153,7 +2156,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" @@ -2166,7 +2169,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -3273,7 +3276,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" @@ -3436,7 +3439,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -3454,7 +3457,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -3470,7 +3473,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -3487,7 +3490,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -3500,14 +3503,14 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@jest/types/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -3517,7 +3520,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -3909,7 +3912,7 @@ "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@sinonjs/commons": { @@ -4222,8 +4225,8 @@ "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", - "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -4242,7 +4245,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -4258,7 +4260,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -4275,7 +4276,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -4288,14 +4288,12 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/@testing-library/dom/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4305,7 +4303,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -4470,35 +4467,34 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, "license": "MIT" }, "node_modules/@types/babel__core": { @@ -4728,7 +4724,8 @@ "version": "16.18.105", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.105.tgz", "integrity": "sha512-w2d0Z9yMk07uH3+Cx0N8lqFyi3yjXZxlbYappPj+AsOlT02OyxyiuNoNHdGt6EuiSm8Wtgp2YV7vWg+GMFrvFA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/node-forge": { "version": "1.3.11", @@ -4791,6 +4788,7 @@ "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/react": "*" } @@ -4911,7 +4909,7 @@ "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/yargs-parser": "*" @@ -5116,6 +5114,7 @@ "integrity": "sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.2.0", "@typescript-eslint/types": "8.2.0", @@ -5468,6 +5467,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5508,7 +5508,7 @@ "version": "8.3.3", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "acorn": "^8.11.0" @@ -5569,6 +5569,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -6556,6 +6557,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001646", "electron-to-chromium": "^1.5.4", @@ -7210,7 +7212,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/cross-spawn": { @@ -7947,7 +7949,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -8499,6 +8501,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -8555,6 +8558,7 @@ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -8595,6 +8599,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", "@typescript-eslint/scope-manager": "5.62.0", @@ -8629,6 +8634,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -12248,6 +12254,7 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -13021,7 +13028,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -13031,7 +13038,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -13949,7 +13956,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -13959,7 +13966,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "chalk": "^4.0.0", @@ -13994,7 +14001,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -14010,7 +14017,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -14027,7 +14034,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -14040,14 +14047,14 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/jest-resolve/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -14057,7 +14064,7 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", @@ -14075,7 +14082,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -14518,7 +14525,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -14536,7 +14543,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -14552,7 +14559,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -14569,7 +14576,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -14582,14 +14589,14 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/jest-util/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -14599,7 +14606,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -14612,7 +14619,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -14630,7 +14637,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -14646,7 +14653,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=10" @@ -14659,7 +14666,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -14676,7 +14683,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -14689,14 +14696,14 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/jest-validate/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -14706,7 +14713,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", @@ -14721,7 +14728,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=10" @@ -14734,14 +14741,14 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/jest-validate/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -14850,7 +14857,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/node": "*", @@ -14866,7 +14873,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -14876,7 +14883,7 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -15368,7 +15375,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, "license": "MIT", "bin": { "lz-string": "bin/bin.js" @@ -15402,7 +15408,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/makeerror": { @@ -16444,6 +16450,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.1", @@ -17650,6 +17657,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -17759,6 +17767,7 @@ "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -18290,6 +18299,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -18481,6 +18491,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -18512,6 +18523,7 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -19225,6 +19237,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", @@ -20668,7 +20681,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=10" @@ -20714,6 +20727,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "license": "MIT", + "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -20964,6 +20978,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -22588,8 +22603,9 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, + "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -22632,7 +22648,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/tsconfig-paths": { @@ -22721,6 +22737,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "license": "(MIT OR CC0-1.0)", + "peer": true, "engines": { "node": ">=10" }, @@ -22828,6 +22845,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -23056,7 +23074,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/v8-to-istanbul": { @@ -23205,6 +23223,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", @@ -23275,6 +23294,7 @@ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "license": "MIT", + "peer": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -23665,6 +23685,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -24118,7 +24139,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6" diff --git a/src/App.tsx b/src/App.tsx index 956829ebb6..15d17bdb14 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,8 @@ function App(): React.JSX.Element { return (

      - UD CISC275 with React Hooks and TypeScript By Valerie Owens + UD CISC275 with React Hooks and TypeScript By Valerie Owens and + Hello World

      Edit src/App.tsx and save. This page will From 075a1c59882d2565befe696ed34c7f8d593dc5da Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 11 Feb 2026 13:16:16 -0500 Subject: [PATCH 076/104] Changed it to test Pull Requests properly --- src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 15d17bdb14..8ec7035294 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,7 @@ function App(): React.JSX.Element { return (

      - UD CISC275 with React Hooks and TypeScript By Valerie Owens and + UD CISC275 with React Hooks and TypeScript By Valerie Owens + Hello World

      From 708bb61b67d39008d3adbb7841ef44d9c9b6f594 Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 11 Feb 2026 15:33:45 -0500 Subject: [PATCH 077/104] Completed all of Task 3 --- src/App.tsx | 108 ++++++++++++++++++++++++++++++++++++--- src/assets/gothfield.jpg | Bin 0 -> 77439 bytes 2 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 src/assets/gothfield.jpg diff --git a/src/App.tsx b/src/App.tsx index 8ec7035294..79c12e89c6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,17 +1,109 @@ import React from "react"; import "./App.css"; +import gothfield from "./assets/gothfield.jpg"; +import { Button, Col, Container, Row } from "react-bootstrap"; function App(): React.JSX.Element { return (

      -
      - UD CISC275 with React Hooks and TypeScript By Valerie Owens + - Hello World -
      -

      - Edit src/App.tsx and save. This page will - automatically reload. -

      + + +
      + +
      +
      + UD CISC275 with React Hooks and TypeScript By + Valerie Owens + Hello World +
      + + +
      +

      + Edit src/App.tsx and save. This + page will automatically reload. +

      + +
      +
      + +
      + +
      +

      + Dab on the h8rs +

      + + +
      + Garfield dressed in goth +

      Reasons Garfield would be a bad husband:

      + + +
      +
        +
      • He is a cat
      • +
      • He is lazy
      • +
      • He does not pay taxes
      • +
      + + +
      + + +
      +
      +
      ); } diff --git a/src/assets/gothfield.jpg b/src/assets/gothfield.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0b3c7351d66d11cd26aa0a727a7099607203ca78 GIT binary patch literal 77439 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 literal 0 HcmV?d00001 From a9b3f095f9c8359d34e2a98d7b8cf091c8daf2ce Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 18 Feb 2026 14:56:06 -0500 Subject: [PATCH 078/104] Task 4 done --- git | 0 src/functions.ts | 21 ++++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 git diff --git a/git b/git new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/functions.ts b/src/functions.ts index e614c81c0c..e8f65abac5 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -4,7 +4,8 @@ * C = (F - 32) * 5/9 */ export function fahrenheitToCelius(temperature: number): number { - return 0; + temperature = (temperature - 32) * (5 / 9); + return temperature; } /** @@ -12,7 +13,11 @@ export function fahrenheitToCelius(temperature: number): number { * if the number is greater than zero. */ export function add3(first: number, second: number, third: number): number { - return 0; + let sum = 0; + if (first > 0) sum += first; + if (second > 0) sum += second; + if (third > 0) sum += third; + return sum; } /** @@ -20,7 +25,7 @@ export function add3(first: number, second: number, third: number): number { * mark added to the end. */ export function shout(message: string): string { - return ""; + return message.toUpperCase() + "!"; } /** @@ -28,7 +33,7 @@ export function shout(message: string): string { * mark. Do not use an `if` statement in solving this question. */ export function isQuestion(message: string): boolean { - return true; + return message.endsWith("?"); } /** @@ -37,5 +42,11 @@ export function isQuestion(message: string): boolean { * upper or lower case), then return `false`. Otherwise, return `null`. */ export function convertYesNo(word: string): boolean | null { - return true; + if (word.toLowerCase() === "yes") { + return true; + } else if (word.toLowerCase() === "no") { + return false; + } else { + return null; + } } From ea99e73ddbe3c60227d01ae4311f6bcef31e41ec Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 18 Feb 2026 15:03:20 -0500 Subject: [PATCH 079/104] Rewrote it to be more understandable and uniform for me to look back on in the future --- src/functions.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/functions.ts b/src/functions.ts index e8f65abac5..f8e154210a 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -14,9 +14,15 @@ export function fahrenheitToCelius(temperature: number): number { */ export function add3(first: number, second: number, third: number): number { let sum = 0; - if (first > 0) sum += first; - if (second > 0) sum += second; - if (third > 0) sum += third; + if (first > 0) { + sum += first; + } + if (second > 0) { + sum += second; + } + if (third > 0) { + sum += third; + } return sum; } From 13b5841b3dbc6064cec976ed73ca996890de0c3a Mon Sep 17 00:00:00 2001 From: Valerie Date: Thu, 19 Feb 2026 12:46:34 -0500 Subject: [PATCH 080/104] Task 5 done --- src/arrays.ts | 117 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 108 insertions(+), 9 deletions(-) diff --git a/src/arrays.ts b/src/arrays.ts index 4a2ffe8e5b..c54fd0a31c 100644 --- a/src/arrays.ts +++ b/src/arrays.ts @@ -5,7 +5,14 @@ * the number twice. */ export function bookEndList(numbers: number[]): number[] { - return numbers; + if (numbers.length === 0) { + return []; + } + if (numbers.length === 1) { + return [numbers[0], numbers[0]]; + } + let numbers2: number[] = [numbers[0], numbers[numbers.length - 1]]; + return numbers2; } /** @@ -13,7 +20,11 @@ export function bookEndList(numbers: number[]): number[] { * number has been tripled (multiplied by 3). */ export function tripleNumbers(numbers: number[]): number[] { - return numbers; + let triple: number[] = []; + for (let num of numbers) { + triple.push(num * 3); + } + return triple; } /** @@ -21,7 +32,15 @@ export function tripleNumbers(numbers: number[]): number[] { * the number cannot be parsed as an integer, convert it to 0 instead. */ export function stringsToIntegers(numbers: string[]): number[] { - return []; + let integer: number[] = []; + for (let num of numbers) { + if (isNaN(Number(num))) { + integer.push(0); + } else { + integer.push(Number(num)); + } + } + return integer; } /** @@ -32,7 +51,23 @@ export function stringsToIntegers(numbers: string[]): number[] { */ // Remember, you can write functions as lambdas too! They work exactly the same. export const removeDollars = (amounts: string[]): number[] => { - return []; + let removedDol: number[] = []; + for (let amount of amounts) { + if (amount[0] === "$") { + if (isNaN(Number(amount.slice(1)))) { + removedDol.push(0); + } else { + removedDol.push(Number(amount.slice(1))); + } + } else { + if (isNaN(Number(amount))) { + removedDol.push(0); + } else { + removedDol.push(Number(amount)); + } + } + } + return removedDol; }; /** @@ -41,7 +76,15 @@ export const removeDollars = (amounts: string[]): number[] => { * in question marks ("?"). */ export const shoutIfExclaiming = (messages: string[]): string[] => { - return []; + let newMessage: string[] = []; + for (let message of messages) { + if (message.endsWith("!")) { + newMessage.push(message.toUpperCase()); + } else if (!message.endsWith("?")) { + newMessage.push(message); + } + } + return newMessage; }; /** @@ -49,7 +92,13 @@ export const shoutIfExclaiming = (messages: string[]): string[] => { * 4 letters long. */ export function countShortWords(words: string[]): number { - return 0; + let wordNum: string[] = []; + for (let word of words) { + if (word.length < 4) { + wordNum.push(word); + } + } + return wordNum.length; } /** @@ -58,7 +107,21 @@ export function countShortWords(words: string[]): number { * then return true. */ export function allRGB(colors: string[]): boolean { - return false; + if (colors.length === 0) { + return true; + } else { + let checkedList: string[] = []; + for (const color of colors) { + if (color === "red" || color === "blue" || color === "green") { + checkedList.push(color); + } + } + if (checkedList.length === colors.length) { + return true; + } else { + return false; + } + } } /** @@ -69,7 +132,24 @@ export function allRGB(colors: string[]): boolean { * And the array [] would become "0=0". */ export function makeMath(addends: number[]): string { - return ""; + let placeHoldString: string = ""; + let sum: number = 0; + if (addends.length === 0) { + return "0=0"; + } else { + for (let add of addends) { + sum = sum + add; + } + placeHoldString = sum.toString() + "="; + for (let add of addends) { + if (add === addends[addends.length - 1]) { + placeHoldString = placeHoldString + add.toString(); + } else { + placeHoldString = placeHoldString + add.toString() + "+"; + } + } + } + return placeHoldString; } /** @@ -82,5 +162,24 @@ export function makeMath(addends: number[]): string { * And the array [1, 9, 7] would become [1, 9, 7, 17] */ export function injectPositive(values: number[]): number[] { - return []; + let newArray: number[] = []; + let sum: number = 0; + let negativeHit: number = 0; + for (let value of values) { + if (value >= 0 && negativeHit === 0) { + newArray.push(value); + sum = sum + value; + } else if (value < 0 && negativeHit === 0) { + negativeHit = 1; + newArray.push(value); + newArray.push(sum); + } else { + newArray.push(value); + } + } + if (negativeHit === 0) { + newArray.push(sum); + } + + return newArray; } From 7c93e2d5851f3db4f505550b506672a60ba59b40 Mon Sep 17 00:00:00 2001 From: Valerie Date: Thu, 19 Feb 2026 17:06:32 -0500 Subject: [PATCH 081/104] Did task 5 right this time, with no for loops --- src/arrays.ts | 152 ++++++++++++++++++++------------------------------ 1 file changed, 60 insertions(+), 92 deletions(-) diff --git a/src/arrays.ts b/src/arrays.ts index c54fd0a31c..f964636208 100644 --- a/src/arrays.ts +++ b/src/arrays.ts @@ -20,10 +20,7 @@ export function bookEndList(numbers: number[]): number[] { * number has been tripled (multiplied by 3). */ export function tripleNumbers(numbers: number[]): number[] { - let triple: number[] = []; - for (let num of numbers) { - triple.push(num * 3); - } + let triple = numbers.map((number: number): number => number * 3); return triple; } @@ -32,15 +29,10 @@ export function tripleNumbers(numbers: number[]): number[] { * the number cannot be parsed as an integer, convert it to 0 instead. */ export function stringsToIntegers(numbers: string[]): number[] { - let integer: number[] = []; - for (let num of numbers) { - if (isNaN(Number(num))) { - integer.push(0); - } else { - integer.push(Number(num)); - } - } - return integer; + let newList = numbers.map((num: string): number => + isNaN(Number(num)) ? 0 : Number(num), + ); + return newList; } /** @@ -51,23 +43,13 @@ export function stringsToIntegers(numbers: string[]): number[] { */ // Remember, you can write functions as lambdas too! They work exactly the same. export const removeDollars = (amounts: string[]): number[] => { - let removedDol: number[] = []; - for (let amount of amounts) { - if (amount[0] === "$") { - if (isNaN(Number(amount.slice(1)))) { - removedDol.push(0); - } else { - removedDol.push(Number(amount.slice(1))); - } - } else { - if (isNaN(Number(amount))) { - removedDol.push(0); - } else { - removedDol.push(Number(amount)); - } - } - } - return removedDol; + let newList = amounts.map((amount: string): number => + amount[0] === "$" ? Number(amount.slice(1)) : Number(amount), + ); + let newList2 = newList.map((thing: number): number => + isNaN(thing) ? 0 : thing, + ); + return newList2; }; /** @@ -76,15 +58,14 @@ export const removeDollars = (amounts: string[]): number[] => { * in question marks ("?"). */ export const shoutIfExclaiming = (messages: string[]): string[] => { - let newMessage: string[] = []; - for (let message of messages) { - if (message.endsWith("!")) { - newMessage.push(message.toUpperCase()); - } else if (!message.endsWith("?")) { - newMessage.push(message); - } - } - return newMessage; + let newlist: string[] = []; + newlist = messages.filter( + (message: string): boolean => !message.endsWith("?"), + ); + let newList2 = newlist.map((thing: string): string => + thing.endsWith("!") ? thing.toUpperCase() : thing, + ); + return newList2; }; /** @@ -92,13 +73,8 @@ export const shoutIfExclaiming = (messages: string[]): string[] => { * 4 letters long. */ export function countShortWords(words: string[]): number { - let wordNum: string[] = []; - for (let word of words) { - if (word.length < 4) { - wordNum.push(word); - } - } - return wordNum.length; + let newList = words.filter((word: string): boolean => word.length < 4); + return newList.length; } /** @@ -107,20 +83,14 @@ export function countShortWords(words: string[]): number { * then return true. */ export function allRGB(colors: string[]): boolean { - if (colors.length === 0) { + let newList = colors.filter( + (color: string): boolean => + color === "red" || color === "blue" || color === "green", + ); + if (newList.length === colors.length) { return true; } else { - let checkedList: string[] = []; - for (const color of colors) { - if (color === "red" || color === "blue" || color === "green") { - checkedList.push(color); - } - } - if (checkedList.length === colors.length) { - return true; - } else { - return false; - } + return false; } } @@ -132,24 +102,22 @@ export function allRGB(colors: string[]): boolean { * And the array [] would become "0=0". */ export function makeMath(addends: number[]): string { - let placeHoldString: string = ""; - let sum: number = 0; - if (addends.length === 0) { + let sum: number = addends.reduce((accumulate, currentVal) => { + return accumulate + currentVal; + }, 0); + let newList: string[] = addends.map((add: number): string => + add === addends[addends.length - 1] ? + add.toString() + : add.toString() + "+", + ); + let sumString: string = sum.toString() + "="; + newList = [sumString, ...newList]; + let newString: string = newList.join(""); + if (newString === "0=") { return "0=0"; } else { - for (let add of addends) { - sum = sum + add; - } - placeHoldString = sum.toString() + "="; - for (let add of addends) { - if (add === addends[addends.length - 1]) { - placeHoldString = placeHoldString + add.toString(); - } else { - placeHoldString = placeHoldString + add.toString() + "+"; - } - } + return newString; } - return placeHoldString; } /** @@ -162,24 +130,24 @@ export function makeMath(addends: number[]): string { * And the array [1, 9, 7] would become [1, 9, 7, 17] */ export function injectPositive(values: number[]): number[] { - let newArray: number[] = []; - let sum: number = 0; - let negativeHit: number = 0; - for (let value of values) { - if (value >= 0 && negativeHit === 0) { - newArray.push(value); - sum = sum + value; - } else if (value < 0 && negativeHit === 0) { - negativeHit = 1; - newArray.push(value); - newArray.push(sum); - } else { - newArray.push(value); - } - } - if (negativeHit === 0) { - newArray.push(sum); - } + let valuesClone: number[] = [...values]; + let negativeIndex = valuesClone.some((num: number) => num < 0); - return newArray; + if (negativeIndex) { + let foundIndex: number = valuesClone.findIndex( + (num: number) => num < 0, + ); + let sumList: number[] = valuesClone.slice(0, foundIndex); + let sum: number = sumList.reduce((accumulate, currentValue) => { + return accumulate + currentValue; + }, 0); + valuesClone.splice(foundIndex + 1, 0, sum); + return valuesClone; + } else { + let sum: number = valuesClone.reduce((accumulate, currentValue) => { + return accumulate + currentValue; + }, 0); + valuesClone.push(sum); + return valuesClone; + } } From 8f7f8d6c7149db5b5dd7edb9bb6feb4505e640e5 Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 25 Feb 2026 15:45:49 -0500 Subject: [PATCH 082/104] First and Second Questions done --- src/objects.ts | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/objects.ts b/src/objects.ts index 3fd2072e5e..91e5feacf0 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -8,9 +8,19 @@ import { Question, QuestionType } from "./interfaces/question"; export function makeBlankQuestion( id: number, name: string, - type: QuestionType + type: QuestionType, ): Question { - return {}; + let newQuestion: Question = { + id: id, + name: name, + type: type, + body: "", + expected: "", + options: [], + points: 1, + published: false, + }; + return newQuestion; } /** @@ -21,7 +31,15 @@ export function makeBlankQuestion( * HINT: Look up the `trim` and `toLowerCase` functions. */ export function isCorrect(question: Question, answer: string): boolean { - return false; + let realAnswer: string = question.expected.trim(); + realAnswer = realAnswer.toLowerCase(); + let givenAnswer: string = answer.trim(); + givenAnswer = givenAnswer.toLowerCase(); + if (realAnswer === givenAnswer) { + return true; + } else { + return false; + } } /** @@ -115,7 +133,7 @@ export function mergeQuestion( id: number, name: string, contentQuestion: Question, - { points }: { points: number } + { points }: { points: number }, ): Question { return contentQuestion; } From 8e1ab596e0a5bf4e26148b27ebd615285530cbf4 Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 25 Feb 2026 15:58:14 -0500 Subject: [PATCH 083/104] Third and Fourth Questions done --- src/objects.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/objects.ts b/src/objects.ts index 91e5feacf0..634ccdfa56 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -49,7 +49,15 @@ export function isCorrect(question: Question, answer: string): boolean { * be exactly one of the options. */ export function isValid(question: Question, answer: string): boolean { - return false; + if (question.type === "multiple_choice_question") { + if (question.options.some((n: string) => n === answer)) { + return true; + } else { + return false; + } + } else { + return true; + } } /** @@ -59,7 +67,9 @@ export function isValid(question: Question, answer: string): boolean { * name "My First Question" would become "9: My First Q". */ export function toShortForm(question: Question): string { - return ""; + let first10char: string = question.name.slice(0, 10); + let idChar: string = question.id.toString(); + return idChar + ": " + first10char; } /** From 14e36b408dab46743ba991bd38730e3ee050db80 Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 25 Feb 2026 16:40:51 -0500 Subject: [PATCH 084/104] Fifth and Sixth Questions done --- src/objects.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/objects.ts b/src/objects.ts index 634ccdfa56..74d9e62ca4 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -90,7 +90,18 @@ export function toShortForm(question: Question): string { * Check the unit tests for more examples of what this looks like! */ export function toMarkdown(question: Question): string { - return ""; + let name: string = question.name; + let body: string = question.body; + if (question.type === "multiple_choice_question") { + let options: string = question.options + .reduce((addedString, currentValue) => { + return addedString + "- " + currentValue + "\n"; + }, "") + .trim(); + return "# " + name + "\n" + body + "\n" + options; + } else { + return "# " + name + "\n" + body; + } } /** @@ -98,6 +109,7 @@ export function toMarkdown(question: Question): string { * `newName`. */ export function renameQuestion(question: Question, newName: string): Question { + question.name = newName; return question; } From c3e329485b64c277baaa7cc0b2fa201bb0016b58 Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 25 Feb 2026 17:06:06 -0500 Subject: [PATCH 085/104] Seventh and Eigth Questions done --- src/objects.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/objects.ts b/src/objects.ts index 74d9e62ca4..fdffd0ec9d 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -109,8 +109,8 @@ export function toMarkdown(question: Question): string { * `newName`. */ export function renameQuestion(question: Question, newName: string): Question { - question.name = newName; - return question; + let newQuestion: Question = { ...question, name: newName }; + return newQuestion; } /** @@ -119,7 +119,13 @@ export function renameQuestion(question: Question, newName: string): Question { * published; if it was published, now it should be not published. */ export function publishQuestion(question: Question): Question { - return question; + let newQuestion: Question = { ...question, published: false }; + if (question.published) { + newQuestion.published = false; + } else { + newQuestion.published = true; + } + return newQuestion; } /** @@ -129,7 +135,11 @@ export function publishQuestion(question: Question): Question { * The `published` field should be reset to false. */ export function duplicateQuestion(id: number, oldQuestion: Question): Question { - return oldQuestion; + let newQuestion: Question = { ...oldQuestion }; + newQuestion.published = false; + newQuestion.name = "Copy of " + oldQuestion.name; + newQuestion.id = id; + return newQuestion; } /** From a395aa40b1776384421893f1b3449d71edb8b112 Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 25 Feb 2026 17:15:34 -0500 Subject: [PATCH 086/104] Ninth and Tenth Questions --- src/objects.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/objects.ts b/src/objects.ts index fdffd0ec9d..6f2e9dbe1f 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -150,7 +150,11 @@ export function duplicateQuestion(id: number, oldQuestion: Question): Question { * Check out the subsection about "Nested Fields" for more information. */ export function addOption(question: Question, newOption: string): Question { - return question; + let newQuestion: Question = { + ...question, + options: [...question.options, newOption], + }; + return newQuestion; } /** @@ -167,5 +171,15 @@ export function mergeQuestion( contentQuestion: Question, { points }: { points: number }, ): Question { - return contentQuestion; + let newQuestion: Question = { + id: id, + name: name, + body: contentQuestion.body, + type: contentQuestion.type, + options: [...contentQuestion.options], + expected: contentQuestion.expected, + points: points, + published: false, + }; + return newQuestion; } From 5dfe9ef598fedf40252babaf76d45fe65e6746d6 Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 25 Feb 2026 23:30:59 -0500 Subject: [PATCH 087/104] objects.ts restored --- src/objects.ts | 88 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/src/objects.ts b/src/objects.ts index 3fd2072e5e..6f2e9dbe1f 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -8,9 +8,19 @@ import { Question, QuestionType } from "./interfaces/question"; export function makeBlankQuestion( id: number, name: string, - type: QuestionType + type: QuestionType, ): Question { - return {}; + let newQuestion: Question = { + id: id, + name: name, + type: type, + body: "", + expected: "", + options: [], + points: 1, + published: false, + }; + return newQuestion; } /** @@ -21,7 +31,15 @@ export function makeBlankQuestion( * HINT: Look up the `trim` and `toLowerCase` functions. */ export function isCorrect(question: Question, answer: string): boolean { - return false; + let realAnswer: string = question.expected.trim(); + realAnswer = realAnswer.toLowerCase(); + let givenAnswer: string = answer.trim(); + givenAnswer = givenAnswer.toLowerCase(); + if (realAnswer === givenAnswer) { + return true; + } else { + return false; + } } /** @@ -31,7 +49,15 @@ export function isCorrect(question: Question, answer: string): boolean { * be exactly one of the options. */ export function isValid(question: Question, answer: string): boolean { - return false; + if (question.type === "multiple_choice_question") { + if (question.options.some((n: string) => n === answer)) { + return true; + } else { + return false; + } + } else { + return true; + } } /** @@ -41,7 +67,9 @@ export function isValid(question: Question, answer: string): boolean { * name "My First Question" would become "9: My First Q". */ export function toShortForm(question: Question): string { - return ""; + let first10char: string = question.name.slice(0, 10); + let idChar: string = question.id.toString(); + return idChar + ": " + first10char; } /** @@ -62,7 +90,18 @@ export function toShortForm(question: Question): string { * Check the unit tests for more examples of what this looks like! */ export function toMarkdown(question: Question): string { - return ""; + let name: string = question.name; + let body: string = question.body; + if (question.type === "multiple_choice_question") { + let options: string = question.options + .reduce((addedString, currentValue) => { + return addedString + "- " + currentValue + "\n"; + }, "") + .trim(); + return "# " + name + "\n" + body + "\n" + options; + } else { + return "# " + name + "\n" + body; + } } /** @@ -70,7 +109,8 @@ export function toMarkdown(question: Question): string { * `newName`. */ export function renameQuestion(question: Question, newName: string): Question { - return question; + let newQuestion: Question = { ...question, name: newName }; + return newQuestion; } /** @@ -79,7 +119,13 @@ export function renameQuestion(question: Question, newName: string): Question { * published; if it was published, now it should be not published. */ export function publishQuestion(question: Question): Question { - return question; + let newQuestion: Question = { ...question, published: false }; + if (question.published) { + newQuestion.published = false; + } else { + newQuestion.published = true; + } + return newQuestion; } /** @@ -89,7 +135,11 @@ export function publishQuestion(question: Question): Question { * The `published` field should be reset to false. */ export function duplicateQuestion(id: number, oldQuestion: Question): Question { - return oldQuestion; + let newQuestion: Question = { ...oldQuestion }; + newQuestion.published = false; + newQuestion.name = "Copy of " + oldQuestion.name; + newQuestion.id = id; + return newQuestion; } /** @@ -100,7 +150,11 @@ export function duplicateQuestion(id: number, oldQuestion: Question): Question { * Check out the subsection about "Nested Fields" for more information. */ export function addOption(question: Question, newOption: string): Question { - return question; + let newQuestion: Question = { + ...question, + options: [...question.options, newOption], + }; + return newQuestion; } /** @@ -115,7 +169,17 @@ export function mergeQuestion( id: number, name: string, contentQuestion: Question, - { points }: { points: number } + { points }: { points: number }, ): Question { - return contentQuestion; + let newQuestion: Question = { + id: id, + name: name, + body: contentQuestion.body, + type: contentQuestion.type, + options: [...contentQuestion.options], + expected: contentQuestion.expected, + points: points, + published: false, + }; + return newQuestion; } From 903ff58d71174d20b3bffbd433a6473c31be33d9 Mon Sep 17 00:00:00 2001 From: Valerie Date: Thu, 26 Feb 2026 00:16:36 -0500 Subject: [PATCH 088/104] Git questions 1, 3, 4, 5, and 6 done --- src/nested.ts | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/nested.ts b/src/nested.ts index 562b6ca0df..1f954e4e55 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -6,7 +6,10 @@ import { Question, QuestionType } from "./interfaces/question"; * that are `published`. */ export function getPublishedQuestions(questions: Question[]): Question[] { - return []; + let newQuestions: Question[] = questions.filter( + (question: Question): boolean => question.published, + ); + return newQuestions; } /** @@ -24,9 +27,16 @@ export function getNonEmptyQuestions(questions: Question[]): Question[] { */ export function findQuestion( questions: Question[], - id: number + id: number, ): Question | null { - return null; + if (questions.some((n: Question) => n.id === id)) { + let location: number = questions.findIndex( + (question: Question): boolean => question.id === id, + ); + return questions[location]; + } else { + return null; + } } /** @@ -34,7 +44,10 @@ export function findQuestion( * with the given `id`. */ export function removeQuestion(questions: Question[], id: number): Question[] { - return []; + let newQuestions: Question[] = questions.filter( + (question: Question): boolean => question.id != id, + ); + return newQuestions; } /*** @@ -42,14 +55,21 @@ export function removeQuestion(questions: Question[], id: number): Question[] { * questions, as an array. */ export function getNames(questions: Question[]): string[] { - return []; + let questionNames: string[] = questions.map( + (question: Question): string => question.name, + ); + return questionNames; } /*** * Consumes an array of questions and returns the sum total of all their points added together. */ export function sumPoints(questions: Question[]): number { - return 0; + let sum: number = questions.reduce( + (num, question) => num + question.points, + 0, + ); + return sum; } /*** @@ -114,7 +134,7 @@ export function addNewQuestion( questions: Question[], id: number, name: string, - type: QuestionType + type: QuestionType, ): Question[] { return []; } @@ -127,7 +147,7 @@ export function addNewQuestion( export function renameQuestionById( questions: Question[], targetId: number, - newName: string + newName: string, ): Question[] { return []; } @@ -142,7 +162,7 @@ export function renameQuestionById( export function changeQuestionTypeById( questions: Question[], targetId: number, - newQuestionType: QuestionType + newQuestionType: QuestionType, ): Question[] { return []; } @@ -161,7 +181,7 @@ export function editOption( questions: Question[], targetId: number, targetOptionIndex: number, - newOption: string + newOption: string, ): Question[] { return []; } @@ -175,7 +195,7 @@ export function editOption( export function duplicateQuestionInArray( questions: Question[], targetId: number, - newId: number + newId: number, ): Question[] { return []; } From 6d31317be98144a3f92123bc8546ec4241b55363 Mon Sep 17 00:00:00 2001 From: Valerie Date: Thu, 26 Feb 2026 00:37:07 -0500 Subject: [PATCH 089/104] Questions 1-8 all completed --- src/nested.ts | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/nested.ts b/src/nested.ts index 1f954e4e55..10c316a6ea 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -18,7 +18,15 @@ export function getPublishedQuestions(questions: Question[]): Question[] { * `expected`, and an empty array for its `options`. */ export function getNonEmptyQuestions(questions: Question[]): Question[] { - return []; + let newQuestions: Question[] = questions.filter( + (question: Question): boolean => + !( + question.body === "" && + question.expected === "" && + question.options.length === 0 + ), + ); + return newQuestions; } /*** @@ -76,7 +84,14 @@ export function sumPoints(questions: Question[]): number { * Consumes an array of questions and returns the sum total of the PUBLISHED questions. */ export function sumPublishedPoints(questions: Question[]): number { - return 0; + let newQuestions: Question[] = questions.filter( + (question: Question): boolean => question.published, + ); + let sum: number = newQuestions.reduce( + (num, question) => num + question.points, + 0, + ); + return sum; } /*** @@ -97,7 +112,24 @@ id,name,options,points,published * Check the unit tests for more examples! */ export function toCSV(questions: Question[]): string { - return ""; + let bigList: string = questions + .reduce( + (list, question) => + list + + question.id.toString() + + "," + + question.name + + "," + + question.options.length.toString() + + "," + + question.points.toString() + + "," + + question.published + + "\n", + "id,name,options,points,published\n", + ) + .trim(); + return bigList; } /** From e89152751f993e7faa32a07c290b41f76b164ab1 Mon Sep 17 00:00:00 2001 From: Valerie Date: Thu, 26 Feb 2026 11:26:58 -0500 Subject: [PATCH 090/104] Ninth and Tenth questions done --- src/nested.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/nested.ts b/src/nested.ts index 10c316a6ea..2f77e2a693 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -138,7 +138,15 @@ export function toCSV(questions: Question[]): string { * making the `text` an empty string, and using false for both `submitted` and `correct`. */ export function makeAnswers(questions: Question[]): Answer[] { - return []; + let answerList: Answer[] = questions.map( + (question: Question): Answer => ({ + questionId: question.id, + text: "", + submitted: false, + correct: false, + }), + ); + return answerList; } /*** @@ -146,7 +154,10 @@ export function makeAnswers(questions: Question[]): Answer[] { * each question is now published, regardless of its previous published status. */ export function publishAll(questions: Question[]): Question[] { - return []; + let questionList: Question[] = questions.map( + (question: Question): Question => ({ ...question, published: true }), + ); + return questionList; } /*** From 8202e3eeb7ba2873f501abf7c277122b4c85c6d8 Mon Sep 17 00:00:00 2001 From: Valerie Date: Thu, 26 Feb 2026 11:40:03 -0500 Subject: [PATCH 091/104] Eleventh and Twelth questions complete --- src/nested.ts | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/nested.ts b/src/nested.ts index 2f77e2a693..1efe5dea1b 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -1,5 +1,6 @@ import { Answer } from "./interfaces/answer"; import { Question, QuestionType } from "./interfaces/question"; +import { makeBlankQuestion } from "./objects"; /** * Consumes an array of questions and returns a new array with only the questions @@ -165,7 +166,32 @@ export function publishAll(questions: Question[]): Question[] { * are the same type. They can be any type, as long as they are all the SAME type. */ export function sameType(questions: Question[]): boolean { - return false; + let questionTypes: string[] = questions.map( + (question: Question): string => question.type, + ); + if (questionTypes[0] === "multiple_choice_question") { + if ( + questionTypes.some( + (question: string): boolean => + question === "short_answer_question", + ) + ) { + return false; + } else { + return true; + } + } else { + if ( + questionTypes.some( + (question: string): boolean => + question === "multiple_choice_question", + ) + ) + return false; + else { + return true; + } + } } /*** @@ -179,7 +205,14 @@ export function addNewQuestion( name: string, type: QuestionType, ): Question[] { - return []; + let dummyQuestions: Question[] = questions.map( + (question: Question): Question => ({ + ...question, + options: [...question.options], + }), + ); + dummyQuestions.push(makeBlankQuestion(id, name, type)); + return dummyQuestions; } /*** From db054298dd940a5a49cb63c4773f85b73403247a Mon Sep 17 00:00:00 2001 From: Valerie Date: Thu, 26 Feb 2026 12:08:58 -0500 Subject: [PATCH 092/104] Completed Task 7 --- src/nested.ts | 62 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/src/nested.ts b/src/nested.ts index 1efe5dea1b..e451149db8 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -1,6 +1,6 @@ import { Answer } from "./interfaces/answer"; import { Question, QuestionType } from "./interfaces/question"; -import { makeBlankQuestion } from "./objects"; +import { duplicateQuestion, makeBlankQuestion } from "./objects"; /** * Consumes an array of questions and returns a new array with only the questions @@ -225,7 +225,13 @@ export function renameQuestionById( targetId: number, newName: string, ): Question[] { - return []; + let newQuestion: Question[] = questions.map( + (question: Question): Question => + question.id === targetId ? + { ...question, options: [...question.options], name: newName } + : { ...question, options: [...question.options] }, + ); + return newQuestion; } /*** @@ -240,7 +246,23 @@ export function changeQuestionTypeById( targetId: number, newQuestionType: QuestionType, ): Question[] { - return []; + let newList: Question[] = questions.map( + (question: Question): Question => + question.id === targetId ? + { + ...question, + options: [...question.options], + type: newQuestionType, + } + : { ...question, options: [...question.options] }, + ); + let skimOptionsList: Question[] = newList.map( + (question: Question): Question => + question.type === "short_answer_question" ? + { ...question, options: [] } + : { ...question, options: [...question.options] }, + ); + return skimOptionsList; } /** @@ -259,7 +281,27 @@ export function editOption( targetOptionIndex: number, newOption: string, ): Question[] { - return []; + if (targetOptionIndex === -1) { + let newList: Question[] = questions.map( + (question: Question): Question => + question.id === targetId ? + { ...question, options: [...question.options, newOption] } + : { ...question, options: [...question.options] }, + ); + return newList; + } else { + let newList: Question[] = questions.map( + (question: Question): Question => ({ + ...question, + options: [...question.options], + }), + ); + let temp: number = questions.findIndex( + (question: Question): boolean => question.id === targetId, + ); + newList[temp].options[targetOptionIndex] = newOption; + return newList; + } } /*** @@ -273,5 +315,15 @@ export function duplicateQuestionInArray( targetId: number, newId: number, ): Question[] { - return []; + let newList: Question[] = questions.map( + (question: Question): Question => ({ + ...question, + options: [...question.options], + }), + ); + let temp: number = newList.findIndex( + (question: Question): boolean => question.id === targetId, + ); + newList.splice(temp + 1, 0, duplicateQuestion(newId, newList[temp])); + return newList; } From 95c45156b6b212d76a31542c7a5d987ba1513b9f Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 4 Mar 2026 14:03:39 -0500 Subject: [PATCH 093/104] Counter fixed and RevealAnswer completed --- src/components/Counter.tsx | 8 +++++++- src/components/RevealAnswer.tsx | 13 ++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/components/Counter.tsx b/src/components/Counter.tsx index 2a546c1747..3c603c7143 100644 --- a/src/components/Counter.tsx +++ b/src/components/Counter.tsx @@ -5,7 +5,13 @@ export function Counter(): React.JSX.Element { const [value, setValue] = useState(0); return ( - + to {value}. ); diff --git a/src/components/RevealAnswer.tsx b/src/components/RevealAnswer.tsx index a48c0a0961..4a57bf064a 100644 --- a/src/components/RevealAnswer.tsx +++ b/src/components/RevealAnswer.tsx @@ -2,5 +2,16 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; export function RevealAnswer(): React.JSX.Element { - return
      Reveal Answer
      ; + const [visible, setVisible] = useState(false); + + function flipVisible(): void { + setVisible(!visible); + } + + return ( +
      + + {visible &&
      42
      } +
      + ); } From 7762bec1f78e896b21506b888cdcd92941da91ee Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 4 Mar 2026 15:03:51 -0500 Subject: [PATCH 094/104] ChangeType and StartAttempt completely implemented and functional --- src/components/ChangeType.tsx | 23 ++++++++++++++++++++++- src/components/StartAttempt.tsx | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/components/ChangeType.tsx b/src/components/ChangeType.tsx index 6e8f878020..ca058203be 100644 --- a/src/components/ChangeType.tsx +++ b/src/components/ChangeType.tsx @@ -3,5 +3,26 @@ import { Button } from "react-bootstrap"; import { QuestionType } from "../interfaces/question"; export function ChangeType(): React.JSX.Element { - return
      Change Type
      ; + const [question, setQuestion] = useState( + "short_answer_question", + ); + + function flipQuestion(): void { + setQuestion( + question === "short_answer_question" ? + "multiple_choice_question" + : "short_answer_question", + ); + } + + return ( +
      + +
      + {question === "short_answer_question" ? + Short Answer + : Multiple Choice} +
      +
      + ); } diff --git a/src/components/StartAttempt.tsx b/src/components/StartAttempt.tsx index dec4ae7dcd..bd5817d83e 100644 --- a/src/components/StartAttempt.tsx +++ b/src/components/StartAttempt.tsx @@ -2,5 +2,36 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; export function StartAttempt(): React.JSX.Element { - return
      Start Attempt
      ; + const [attempts, setAttempt] = useState(4); + const [inProgress, setProgress] = useState(false); + + function start(): void { + setAttempt(attempts - 1); + setProgress(true); + } + + function upAttempt(): void { + setAttempt(attempts + 1); + } + + function stop(): void { + setProgress(false); + } + + return ( +
      + {attempts} +
      + + + +
      +
      + ); } From 2082f39796bd88117dca39f485b3ef5d0857497a Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 4 Mar 2026 15:20:21 -0500 Subject: [PATCH 095/104] TwoDice completed --- src/components/TwoDice.tsx | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/components/TwoDice.tsx b/src/components/TwoDice.tsx index a257594d35..b9b2027e5e 100644 --- a/src/components/TwoDice.tsx +++ b/src/components/TwoDice.tsx @@ -12,5 +12,29 @@ export function d6(): number { } export function TwoDice(): React.JSX.Element { - return
      Two Dice
      ; + const [dice1, setDice1] = useState(1); + const [dice2, setDice2] = useState(2); + + function rollRight(): void { + setDice2(d6()); + } + + function rollLeft(): void { + setDice1(d6()); + } + + return ( +
      + {dice1} + {dice2} +
      + + +
      +
      + {dice1 === dice2 && dice1 !== 1 && Win} + {dice1 === dice2 && dice1 === 1 && Lose} +
      +
      + ); } From 87d25f4fe92bcff871abc7666d5fe34da0632548 Mon Sep 17 00:00:00 2001 From: Valerie Date: Wed, 4 Mar 2026 15:58:05 -0500 Subject: [PATCH 096/104] CycleHoliday completed, all tests passed --- src/components/CycleHoliday.tsx | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/components/CycleHoliday.tsx b/src/components/CycleHoliday.tsx index 47e6d76444..35160f50e9 100644 --- a/src/components/CycleHoliday.tsx +++ b/src/components/CycleHoliday.tsx @@ -2,5 +2,35 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; export function CycleHoliday(): React.JSX.Element { - return
      Cycle Holiday
      ; + const [holidays, setHoliday] = useState("🐲"); + + function sortByAlphabet(): void { + setHoliday( + holidays === "🐲" ? "🎁" + : holidays === "🎁" ? "🌎" + : holidays === "🌎" ? "🍀" + : holidays === "🍀" ? "💖" + : "🐲", + ); + } + + function sortByDate(): void { + setHoliday( + holidays === "💖" ? "🐲" + : holidays === "🐲" ? "🍀" + : holidays === "🍀" ? "🌎" + : holidays === "🌎" ? "🎁" + : "💖", + ); + } + + return ( +
      + Holiday: {holidays} +
      + + +
      +
      + ); } From e309b68056587739271f035664ac93ca0962b2f1 Mon Sep 17 00:00:00 2001 From: Valerie Date: Sun, 8 Mar 2026 13:37:25 -0400 Subject: [PATCH 097/104] ChooseTeam and DoubleHalf working and passing tests. Removed redundant file. --- src/bad-components/ChooseTeam.tsx | 17 ++++++----- src/bad-components/DoubleHalf.tsx | 42 ++++++++++++-------------- src/bad-components/DoubleHalfState.tsx | 3 -- 3 files changed, 28 insertions(+), 34 deletions(-) delete mode 100644 src/bad-components/DoubleHalfState.tsx diff --git a/src/bad-components/ChooseTeam.tsx b/src/bad-components/ChooseTeam.tsx index e73f600083..735de1306d 100644 --- a/src/bad-components/ChooseTeam.tsx +++ b/src/bad-components/ChooseTeam.tsx @@ -14,18 +14,14 @@ export function ChooseTeam(): React.JSX.Element { const [allOptions, setAllOptions] = useState(PEOPLE); const [team, setTeam] = useState([]); - function chooseMember() { - /* + function chooseMember(newMember: string) { if (!team.includes(newMember)) { - team.push(newMember); + setTeam([...team, newMember]); } - */ } function clearTeam() { - /* - team = []; - */ + setTeam([]); } return ( @@ -36,7 +32,12 @@ export function ChooseTeam(): React.JSX.Element { {allOptions.map((option: string) => (
      Add{" "} -
      diff --git a/src/bad-components/DoubleHalf.tsx b/src/bad-components/DoubleHalf.tsx index 8b01352f59..fde5ef0066 100644 --- a/src/bad-components/DoubleHalf.tsx +++ b/src/bad-components/DoubleHalf.tsx @@ -1,40 +1,36 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; -import { dhValue, setDhValue } from "./DoubleHalfState"; -function Doubler(): React.JSX.Element { - return ( - - ); +function Doubler({ times2 }: { times2: () => void }): React.JSX.Element { + return ; } -function Halver(): React.JSX.Element { - return ( - - ); +function Halver({ + timesOneHalf, +}: { + timesOneHalf: () => void; +}): React.JSX.Element { + return ; } export function DoubleHalf(): React.JSX.Element { + const [dhValue, setDhValue] = useState(10); return (

      Double Half

      The current value is: {dhValue}
      - - + { + setDhValue(dhValue * 2); + }} + > + { + setDhValue(dhValue * 0.5); + }} + >
      ); } diff --git a/src/bad-components/DoubleHalfState.tsx b/src/bad-components/DoubleHalfState.tsx deleted file mode 100644 index 2b4569a37a..0000000000 --- a/src/bad-components/DoubleHalfState.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import { useState } from "react"; - -export const [dhValue, setDhValue] = useState(10); From 6432c5ef3dfe82e1c82d4681ffa4a3b741664a18 Mon Sep 17 00:00:00 2001 From: Valerie Date: Sun, 8 Mar 2026 13:41:11 -0400 Subject: [PATCH 098/104] Cleaned up ChooseTeam to get rid of the error with the unused code --- src/bad-components/ChooseTeam.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bad-components/ChooseTeam.tsx b/src/bad-components/ChooseTeam.tsx index 735de1306d..c4fa4c75d3 100644 --- a/src/bad-components/ChooseTeam.tsx +++ b/src/bad-components/ChooseTeam.tsx @@ -11,7 +11,7 @@ const PEOPLE = [ ]; export function ChooseTeam(): React.JSX.Element { - const [allOptions, setAllOptions] = useState(PEOPLE); + const [allOptions] = useState(PEOPLE); const [team, setTeam] = useState([]); function chooseMember(newMember: string) { From 815938117e1e77969d2205ba16b9a12cd3c78ebe Mon Sep 17 00:00:00 2001 From: Valerie Date: Sun, 8 Mar 2026 14:35:51 -0400 Subject: [PATCH 099/104] ColoredBox done and functional --- src/bad-components/ColoredBox.tsx | 42 +++++++++++++++++++------------ 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/bad-components/ColoredBox.tsx b/src/bad-components/ColoredBox.tsx index 1fa2d770b2..8e865f56dc 100644 --- a/src/bad-components/ColoredBox.tsx +++ b/src/bad-components/ColoredBox.tsx @@ -4,27 +4,29 @@ import { Button } from "react-bootstrap"; export const COLORS = ["red", "blue", "green"]; const DEFAULT_COLOR_INDEX = 0; -function ChangeColor(): React.JSX.Element { - const [colorIndex, setColorIndex] = useState(DEFAULT_COLOR_INDEX); - return ( - - ); +interface colorInterface { + colorList: string[]; + colIndex: number; } -function ColorPreview(): React.JSX.Element { +// onClick={() => {setColorIndex((1 + colorIndex) % COLORS.length);}} + +function ChangeColor({ + colorChange, +}: { + colorChange: () => void; +}): React.JSX.Element { + return ; +} + +function ColorPreview(props: colorInterface): React.JSX.Element { return (
      (DEFAULT_COLOR_INDEX); return (

      Colored Box

      - The current color is: {COLORS[DEFAULT_COLOR_INDEX]} + The current color is: {COLORS[colorIndex]}
      - - + { + setColorIndex((1 + colorIndex) % COLORS.length); + }} + > +
      ); From 8aaa4048a3a279bc6876a62fadd8aee41e0a98c0 Mon Sep 17 00:00:00 2001 From: Valerie Date: Sun, 8 Mar 2026 14:48:53 -0400 Subject: [PATCH 100/104] ShoveBox completed, all tests passed. All bad components fixed and functional in live website --- src/App.tsx | 2 +- src/bad-components/ShoveBox.tsx | 43 ++++++++++++++------------------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index eb80370e3a..d8ca1924f0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -18,7 +18,7 @@ function App(): React.JSX.Element { UD CISC275 with React Hooks and TypeScript
      - {/* */} +

      diff --git a/src/bad-components/ShoveBox.tsx b/src/bad-components/ShoveBox.tsx index 45cdcc335d..ce3ff952f8 100644 --- a/src/bad-components/ShoveBox.tsx +++ b/src/bad-components/ShoveBox.tsx @@ -1,26 +1,17 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; -function ShoveBoxButton({ - position, - setPosition, -}: { - position: number; - setPosition: (newPosition: number) => void; -}) { - return ( - - ); +// {position,setPosition,}: {position: number;setPosition: (newPosition: number) => void;} + +interface where { + place: number; } -function MoveableBox(): React.JSX.Element { - const [position, setPosition] = useState(10); +function ShoveBoxButton({ shove }: { shove: () => void }) { + return ; +} + +function MoveableBox(props: where): React.JSX.Element { return (
      ); } export function ShoveBox(): React.JSX.Element { - const box = MoveableBox(); + const [position, setPosition] = useState(10); + // const box = MoveableBox(); return (

      Shove Box

      - {/* The box is at: {box.position} + The box is at: {position}
      { + setPosition(4 + position); + }} > - {box} -
      */} + +
      ); } From 5990680e47eee674e67f6c3078f124a1bbd6118e Mon Sep 17 00:00:00 2001 From: Valerie Date: Sun, 15 Mar 2026 15:35:45 -0400 Subject: [PATCH 101/104] GiveAttempts and CheckAnswers completed and tested --- src/form-components/CheckAnswer.tsx | 16 +++++++++++++++ src/form-components/GiveAttempts.tsx | 30 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/form-components/CheckAnswer.tsx b/src/form-components/CheckAnswer.tsx index 8aa74b5e2e..678c007f79 100644 --- a/src/form-components/CheckAnswer.tsx +++ b/src/form-components/CheckAnswer.tsx @@ -1,13 +1,29 @@ import React, { useState } from "react"; +import { Form } from "react-bootstrap"; export function CheckAnswer({ expectedAnswer, }: { expectedAnswer: string; }): React.JSX.Element { + const [answer, setAnswer] = useState(); + + function update(event: React.ChangeEvent) { + setAnswer(event.target.value); + } + return (

      Check Answer

      + + Answer: + + +
      + {answer === expectedAnswer ? +
      ✔️
      + :
      } +
      ); } diff --git a/src/form-components/GiveAttempts.tsx b/src/form-components/GiveAttempts.tsx index 2aa1e51dfb..a9835faced 100644 --- a/src/form-components/GiveAttempts.tsx +++ b/src/form-components/GiveAttempts.tsx @@ -1,9 +1,39 @@ import React, { useState } from "react"; +import { Form, Button } from "react-bootstrap"; export function GiveAttempts(): React.JSX.Element { + const [attemptsLeft, setAttemptsLeft] = useState(3); + const [attemptsMore, setAttemptsMore] = useState(""); + const addedInt = parseInt(attemptsMore) || 0; + + function updateMore(event: React.ChangeEvent) { + setAttemptsMore(event.target.value); + } + + function updateLeft(): void { + setAttemptsLeft(attemptsLeft - 1); + } + + function addMore(): void { + setAttemptsLeft(addedInt + attemptsLeft); + } + return (

      Give Attempts

      + + Add Attempts + + +
      Attempts Left: {attemptsLeft}
      + +
      ); } From 1ee9dbeae2a19ac3154cc525e4c5b63c39e8565f Mon Sep 17 00:00:00 2001 From: Valerie Date: Sun, 15 Mar 2026 15:58:32 -0400 Subject: [PATCH 102/104] EditMode completed and tested --- src/form-components/EditMode.tsx | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/form-components/EditMode.tsx b/src/form-components/EditMode.tsx index ca9fdb9912..f947d99597 100644 --- a/src/form-components/EditMode.tsx +++ b/src/form-components/EditMode.tsx @@ -1,9 +1,58 @@ import React, { useState } from "react"; +import { Form } from "react-bootstrap"; export function EditMode(): React.JSX.Element { + const [editMode, setEditMode] = useState(false); + const [isStudent, setStudent] = useState(true); + const [userName, setName] = useState("Your Name"); + + function updateEdit(event: React.ChangeEvent) { + setEditMode(event.target.checked); + } + + function updateName(event: React.ChangeEvent) { + setName(event.target.value); + } + + function updateStudent(event: React.ChangeEvent) { + setStudent(event.target.checked); + } + return (

      Edit Mode

      +
      + +
      +
      + {isStudent ? +
      {userName} is a student.
      + :
      {userName} is not a student.
      } +
      +
      + {editMode ? + + Change Name: + + + + :
      } +
      ); } From 06bf189fd86be358b14c937093b3b170fd212ac3 Mon Sep 17 00:00:00 2001 From: Valerie Date: Sun, 15 Mar 2026 16:30:49 -0400 Subject: [PATCH 103/104] ChangeColor working and passing all tests --- src/form-components/ChangeColor.tsx | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/form-components/ChangeColor.tsx b/src/form-components/ChangeColor.tsx index 514f5f893f..6b45834e0a 100644 --- a/src/form-components/ChangeColor.tsx +++ b/src/form-components/ChangeColor.tsx @@ -1,9 +1,46 @@ import React, { useState } from "react"; +import { Form } from "react-bootstrap"; + +const COLOR_LIST = [ + "red", + "green", + "blue", + "yellow", + "purple", + "orange", + "cyan", + "white", +]; +const COLOR_DEFAULT = COLOR_LIST[0]; export function ChangeColor(): React.JSX.Element { + const [colors, setColors] = useState(COLOR_DEFAULT); + + function updateColor(event: React.ChangeEvent) { + setColors(event.target.value); + } + return (

      Change Color

      +
      + {COLOR_LIST.map((color: string) => ( + + ))} +
      +
      + {colors} +
      ); } From 30a6bbcd5bb4b4a6c0be61088ac6b5bed21c156f Mon Sep 17 00:00:00 2001 From: Valerie Date: Sun, 15 Mar 2026 16:49:28 -0400 Subject: [PATCH 104/104] MultipleChoiceQuestions completed and functional --- src/App.tsx | 2 +- .../MultipleChoiceQuestion.tsx | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 35a22c36bd..54b99c454e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -36,7 +36,7 @@ function App(): React.JSX.Element { expectedAnswer="b" >
      - {/* */} +

      diff --git a/src/form-components/MultipleChoiceQuestion.tsx b/src/form-components/MultipleChoiceQuestion.tsx index 5e6f35c611..cc95cac9a2 100644 --- a/src/form-components/MultipleChoiceQuestion.tsx +++ b/src/form-components/MultipleChoiceQuestion.tsx @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import { Form } from "react-bootstrap"; export function MultipleChoiceQuestion({ options, @@ -7,9 +8,28 @@ export function MultipleChoiceQuestion({ options: string[]; expectedAnswer: string; }): React.JSX.Element { + const [answer, setAnswer] = useState(options[0]); + + function update(event: React.ChangeEvent) { + setAnswer(event.target.value); + } + return (

      Multiple Choice Question

      +
      + + Pick An Option: + + {options.map((option: string) => ( + + ))} + + +
      +
      {answer === expectedAnswer ? "✔️" : "❌"}
      ); }