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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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/129] 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: Tue, 3 Feb 2026 09:57:39 -0500 Subject: [PATCH 074/129] Added name to App --- src/App.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index b77558eaac..bae62d13cb 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 + UD CISC275 with React Hooks and TypeScript. Jacob says Hello + world!

      Edit src/App.tsx and save. This page will From 7272459e46bec8dd3e00f02f8cf5cbe04cc7edfd Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Thu, 5 Feb 2026 10:29:40 -0500 Subject: [PATCH 075/129] Passed all tests --- package-lock.json | 123 +++++++++++++++++++++------------------------- src/App.tsx | 4 +- 2 files changed, 59 insertions(+), 68 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c3779f487..1e6922997e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2153,7 +2153,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 +2166,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 +3273,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 +3436,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 +3454,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 +3470,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 +3487,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 +3500,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 +3517,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 +3909,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,7 +4222,6 @@ "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", "dependencies": { "@babel/code-frame": "^7.10.4", @@ -4242,7 +4241,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 +4256,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 +4272,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 +4284,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 +4299,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 +4463,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": { @@ -4911,7 +4903,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": "*" @@ -5508,7 +5500,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" @@ -7210,7 +7202,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 +7939,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" @@ -13021,7 +13013,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 +13023,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 +13941,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 +13951,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 +13986,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 +14002,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 +14019,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 +14032,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 +14049,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 +14067,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 +14510,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 +14528,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 +14544,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 +14561,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 +14574,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 +14591,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 +14604,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 +14622,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 +14638,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 +14651,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 +14668,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 +14681,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 +14698,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 +14713,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 +14726,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 +14842,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 +14858,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 +14868,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 +15360,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 +15393,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": { @@ -20668,7 +20659,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" @@ -22588,7 +22579,7 @@ "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", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -22632,7 +22623,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": { @@ -23056,7 +23047,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": { @@ -24118,7 +24109,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 bae62d13cb..f9632b13aa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,8 +5,8 @@ function App(): React.JSX.Element { return (

      - UD CISC275 with React Hooks and TypeScript. Jacob says Hello - world! + UD CISC275 with React Hooks and TypeScript. Jacob Donn. Hello + World

      Edit src/App.tsx and save. This page will From bd6f679a40fafa6f6aafd4bef4681046c40eb4fd Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 10 Feb 2026 09:55:50 -0500 Subject: [PATCH 076/129] Finish adding header & image --- src/App.tsx | 7 +++++++ src/images/budgies.jpg | Bin 0 -> 34074 bytes 2 files changed, 7 insertions(+) create mode 100644 src/images/budgies.jpg diff --git a/src/App.tsx b/src/App.tsx index f9632b13aa..05350ef5c0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,10 @@ import React from "react"; import "./App.css"; +import budgies from "./images/budgies.jpg"; + +export function Header(): React.JSX.Element { + return

      This is my header

      ; +} function App(): React.JSX.Element { return ( @@ -12,6 +17,8 @@ function App(): React.JSX.Element { Edit src/App.tsx and save. This page will automatically reload.

      +

      This is a header!

      + A budgie couple
      ); } diff --git a/src/images/budgies.jpg b/src/images/budgies.jpg new file mode 100644 index 0000000000000000000000000000000000000000..904e239135fe6ebb0beccf28791fc295ded4a645 GIT binary patch literal 34074 zcmb4pRahHL6K-&K2p%L5oZ?cf6nA$i7Tn#6ySqD~xFk3fx8SaY7PnH0wLoe6o&Fc+ z;#{70FK2dUp4okN$Nt;;Zy!Lcs;Hs}KtTZjl>aTjf1dzy08DfYObm2PObkpcEKF=% zVq9Dt99(ijB79;>aw;lHav+d~o|S=y7EA{OGIBG5A#5C+9MlXvd_3%Ytn3`@{}Y0O zg@uKSjZ21$OU6zEq+$R6#(%>A5^R)x)Fm_&W&kP)3K|K@f8zi;000F8<$vb>Utj=G z(aS=qPBYXc(C2|A2`GK>0@{LC0X?CzU1BvBqTfip(Szcv}fy#sce( zP}m&Y00s5DS>*JygnXi+vv1|A5dXx`{%_a+qXdA8g7(iTSpSGZBmh)Y6m$SO1`75+ zuK$^Vf=YshPRhiOK_;t1&TL(|{1%f!K-Vi1eh_&B1lx@Kw*$cc$N7(o1Rw)=EQJ2h zN3d*NH_qotu8RvqTB30m32h-@Ki=HKm>-SqApNrU@BHgxdMVnZeQVz@spt1(_p#;o zQG-<=n@Z$a3c667*NG=HZ1H4jXYI@F+s!*_WO>sOAcXFiHcRI zZZ)q%!zcVKii(G#=yX4e=N73YwEXvLnd&Nf9B?#{VCmkB@z!C(p=d*90n@g9o{=p~ zc3D=AC3ax_D()eMNCphc*}4-ECRNE%@*|lUJO^odVwL84l@D?0x>XXec(l*-r<}R< z=XsF5s!Lt|E&4-ux>PxbHcAOq)SD|Y?{wo_DAY6{b;VGum(8vSUV44kI9oczP9~7* zs;53)J6+lVq@@-qc#fi^OVjd=%N08n&JXny@~H$dbxL}0{zz5JLgOxFTkPhHvCDZx zQ=ua_5mCzLXS|NIM{4=%vEN0WmKkhqA<)j0HLjy)zCFteTFe0N-A%K6b+=B*PEUO3 zUO#&`lDUyiq%Z4aF5_&6lL}fp@-ZLajU;<^i1FX_KHJY$P^}))232RY+sUBhpz?r5 zAku1^^4_=_E_F3rIU2jo{OODqsV8O$S@okU22Y>V8b3_fh^Q}+53j_C0g9gAe&J3g zB`-i$hYmJ0WjA~;wVG}HNiE-J4{|9Jps_cI1-DHA~6nXoQ& zj4{_iIa+dg?_7Hi(Z|HFLrx8jksL?73a;$lN<}HhL_+l2ggQ?rzHn%gf?FKR9DxJ7 zbXbsIgg%+-VZ)QV&!f?RX%Y;_#E9|rTh&^yvSJFOuwD_*hOt&?bJjY_v29Z9(*pZi zU{`>l^i(&V&#cbAX*(B0qNwVt@B7^j@t!qch?wur^xbE|%$(^@x!e=sE&ES^j z2<09tP2AixkfSXk;yPQ9_O#2Un4*tg_Y(D4CuK>Ci{+1e_6$6=)FHQ41XH*0gP=$`W%RLQaRtlD-haTy^0n2? zP$c|H-*B>iT}>#m&68<=8W~cVWxstwI6o`p;468Y6=V?_7|7+iB+9wSo`+v5&)r0p z^~RJyKx6cunq(1ETUiu_7{Ge@%4c>k#B=M}{3_!Z?rXSqO5kKX@J=AHD)p=Q+qAEE z4acO1JWi_z%<9d#1;NkSN?9r=M0s!DIo*_wo?od@Y}c%wiOy_Z=vr_?$^!79)qVBW zYfRPKP09XsthTLC4~zkI?D(y|*p)L#Q*axd^+xO4QG?bt;y^>DNif2^?Qk@yioBE> zrZ!wr8EUW%Rrw%{_EA8!Hx-9dQ4vR;ztR+_n4yh?&vVeDT*z0&=-L|-q`lE%)NO=9_i%*TW5;G`WC3kENvDJX_>mm00YL{N}LY@Xh%sIL6P z5WZ`iNdkauj}OHtONW*?^p9NCM9ZW&yL^3HM`1*Oc1cA@6Ra=s>xuE?9Pf4SuaDmz zNbDnKo!OKPjA3msu1pRQ^LWY+g&YdtVf_;%S|6!vL_}FmN(rL_dr(UX{Ipay)A~CV zr0^}l&t>x#x{=dbTRdE81++cKpgE9JxyujrswGkAkxDU!TYx&03j zzzRHgELz2k$5JC{_-odm2St&YV+S9HD(!87KYD)DbvZ!SXcu?Mf6qZ-@et=<-moLT z^cR&*TL$he!&xE#ELgF)h{&&Tm5mT3$g;3h-o2|&#BJkia^vW1C3wjT+Y|Rw6*}XN zyE3DD+W5_B3q-kNwjbj}_B?i$o}zNbkqL7aKz@^vrNnpK>RTQq9xFwnQCsb{QA5@Z-%apg!sgwhs?% zc*y8C?zv1PB9%Caj-B6t^uhX+Gh@e>lg|d*jkG5oQ!pEG>f0Q(UZY#@dWV=Cg92~t z>wap_ZHuDcu7ecOIg%TOk~7yI#6J%eoocB~OGNA0p{k~+^I(dX)!!a}pD~E6>Y68% z_(nO-xH5Zq?3rq9N09UE8smIZB%x5M@3g421{WjFK+7?7Q$m2aFr5=KGnv> zDzq$wBR2DK3pwqmt-P@^w*!f=XI^Q3yH7e(=@^2Lqx&GkPmfidqgzw2j&9e0KmRG$ zNQSLJuy37;g`jZ2hwBU1Yx_%Y*2Z|T9A+_O*jHDUERN6^Mmh?csAk$>lz<8C6Ppg= z2y|O)g1m5$#qvjL_dIo&0u8=mwDKeuDpukv>`9?Ys2AdaA}K1TG}ri;yzgRI4?i>= z8mV$JQmONzY;flTaNw#=!sQ&#rmZ{m?YKs~E(>b!>ufmPOEyv8jZ)d;RUb@P6bEXW zygH#=t?k=`g2M+>FTCA>bTHXFH_tsQE^jg6yf`c6*Qn*3)=LP3cHQ&V{0vkg>30$> z4vW2eSb4fyT$W-u9F!J$dSM&dD|ZEUoG0jz{nnkC^BGp0TZ)9Hn?hO4!~Xy~xZl(C zy~Yp8qbNDF<+oB6bOvo;{4@q9ZftiMT|&piu=rQLqDQH-%k zv(0^dt7h6fB@gyBNEQNFzB(mUC6ZL!)T7H(V<+t=k0E~sb4nMPl4%fAjvyJE?bAyQ z`bI_%cVG@^>>a5aQi}9Q-}4{y43fksTzZKG*e5|ExpnLAoq;qJ6+E88H1>s|_HH#u zCmW(tzCGRl0HjWImCwDypTBbRNkDYvv+W{`tmmu!+qX@ko^_N8hq!D;Ml6c!`~YUX z8s$1S6H$oF)1niF+_GSCJpy5PH@dKtI&Br=bsS7ZqOOx}#lx?BcA7Vws~5JSV_xX) zdZ@+8U*O@(GNUSc;%A7SQF9H8>1Z4nhfvmeA@HiaNpGhm8{XvF07>ifgfQ|{4-M+d zzN$}7vURh^7{_pDdzoVp#l2lq#7BHS505b=cMo!|(ZdzGxfC{QWH=lqG~_!0*0C&l zUf0g-YAiR+Y=}tzT0FaiZuSvKhot+ycFhWDw2n&f=7mBj$@(h6vpLZ5dYVs0D+zgg z0_zD?h;nl0V}8!s3=DkED|OhDjiJ1R$pO>{p0RX^E6&1cLPM_QQ5Be_pZ{4q5Ev+5 zh|Lzu9r#W2IdX*xk+yvjAvtZZY*D!v|(UpJ_x?=DCS$9o5XHz|XC4AbZ3W6z1e8|*~P^%j;#R4A*WD`i+29YBbKCkn^Df> zszif5DVTTCxAdRH4K;F%P-Qsy}BwLiNBO?DATM^ zb6(Pu$RWzTlN8`PlR6e|c0bN;18_p~mnmlC3%4x{RkW>~XwsTrDG~~P#tYT#4oSV} z>z8g^8Yuqxn$A#=%g|2G!RKc{hHVOHJRsn>bE28ww@<9#G=s*J9pzYxQcde1Yg!li z_2TQ_*sU)P`8FXqC0|aR#RG5Tto&quuh3}`((3Xvz;E{_GOn8Hi$~Oh8?F!REA&%f|heUuAdH_1D#io~4vSqS*ilN=j5Njn& z59~;>u`{HGhxraS2Y40QI`r@>V~Md>@hqY^bCYB@r1rWmf$&rJw0@Ss;@0X14p-IJ z7gVdXhs1~|k5is^?Jbu~l9kHfSs}3}e=)rh8F?d2NWo~Hp-8T$^Q_~-eEJG;%Eo## z$PHj zWPN4kc%^67VY1b@3XTH>xvIZqN>41NjvsT+7AywQrr}vXB?ObADtXz%D*EOk%q{Q= zS~?pMWfY>p`nH2$PrQD?jXY}4CXdw#x3?|r^dRr&+nR)^b~YOkCEy9gSQ=6eOn#|w zqoG^NwVyLq;E9TfEcTl4Y_Jm3Rs;iL#}`EkoiR1i=h0t5$cCy`r>U3-r>6|Ygav2> zvOALHAS@KH`N=aoT28c3-aPKa+&1|%p=v-@i!|ABDM*~v`E#8v_7Uf?cCMt@-=t&D zVD8l!4ok7zf`S@X=_GqTqNI6_EzpH=$Y=6@y=S%+j$NZ&iEgD7Vd%X_t*Z zWHO%Lrf0!HGS50FfIBT$vznX<$2e>$5?6C~5$&Tk=BE@|?(BkF`JSGSuE~c-ncF_3 zd`WUtHAR}USu9g-x=e&K)e2k$!Og?Iv#I9UG04wkkiZsQSC%;4I?dTmNd$(jNvy77 zT{7C)sOZ~rh5>|N-u{WHqz}`934T8=tf`UQK4+3lxl05G94kJ;=-KoX%PiS03=wB# zhvP_s7sgs<9QlhwdDC+1u@BMxnOsA;|JHNFM;0GD&r#3QVW1dgb zsNx6jXg(bshT*-hIT9qo zD+z$(x80mKb|T7SMW)wqn~&*!WmF3($Ulnau2J9n>2!QMtDf6?IkE4y6Z0k&)~rh& z9`^Dwhe5Hy@0fLxa~~W%2T7_6=PItdJ`sI}tEQ=$93lVWr+z7$UdZ`~Hcabg#`GM7 zPgJYA^oG~dtJ<=hoH@~sSR!+ujp7?H=5GR9@vUKDazFraj#V-!6UM_qj+Q#w{RD?I@iEi-^4cdry#H z1mK4$%DTvhr?%?<03#Ycb{Z1Ma;fg4VR4PiS8i|9XvNaXqrcy?1@7rI7; zPZR3-1A%=cyde=KyGKw=J*5>bjcF%=>3xb&sXq7gF*lW^>^idDvj}q>Te;i5>ZdOK z#$X1Ix!S>pDDUH;brJG_-U;2t?a2sk6G4x%^p>{wGQN{jO0~H4E?7$>S1(F(2M@4# zPkCQS97G(ZE@a1c{q&$qi;A?hHg}zg6IT>0IsfyE*QMxig=n%Vn8!ZjbbR>ZX`{bP zmPIzkbkn31e?&DhSI=Ji{6tA;L)`p){)6dEaLgL+)K{{*j@QxC7P_Gta2l9f6AqrpR29u8c!-gCl6qhFA>WnCI3#eyL!^bXJ{Hk?vaA#n$2LG0N zDee}hvVnLN6N4|XK`s5fLyrB!`zyv4fDQ-z54kkTAh2g%2;Iw~O00=hAsuCq#Fg7! z{{pl6Cou zS^(9EDd)f#v8w?k?FjL>sHw}Xkkb5cg*u?}{qF>2R=I;>Q@SR>P|k2W(a~?u4NY|$ z&OHVp+#lSfD9(4rZT(W#z`IS?`ucNcvKW@*EX0LiQC-{A^&nQq9ERcSm;j3Li1wXu zyAa%;=I>Q}-(lQ*!sV!JGe8zz*HXRk`sDJb{IrxtLJWDozRM=~`Sy$4rvc4$HyL2Bs)yV^`^$9Rlo8(GH*Ra5Ry;Pp^R1^;)z6*V&kIDu#YeO;`ElU zU4WY_+mI*NAW?850c;3Uosa}`8g-LQWylscjGD#hI$|bO)?Ayv=T&bD$K9#954zRFx z>Ye0VN#-m5rE(TSV4Yl2nbD6?orN`e)H)K5IcTMAZa!n1a^iL5X~$_D%GJE7=d(kz z+6={|%}x3FA7IWle%!TV+GurJ_rdrj+Oc%LVDli}-@twtK%X~b&R*V+CHru69)En0 zcXe;7*g$e0D%^`oPW$M)~4gfZ9otlHh1&t6TDA6*m!= zsf7|L)yS)x(zCwVOLc0`S)hc>Eg+mH_wD!YqKn_^q+Ww2Rk+X9dariy4aKY(#RndD z`Z>j=g-8lf@>@zQmU}6OhW(p=Z&cAaC?%77Km3U6nx-W)+tOZ4!WLdZ3;Tw90atS8 z{-AS|jbF|-|7AjhR(Fjaq*(UYr;MKNtHENXfqL3MltI@@o2;^Ci>!wW(3pDEj)|zg zyp!4fEm>hR7x|njB6W!4I1sltE!kA|uI;t{wzOz^T^HL&p-Hwjw{e2YVR#>cgY~Vb zLrFLRA3{@F2q3lqIyi_@5Z}*wwF*7&Yoz|E3{S~AQ30Fo7J4CVVU9v3;qfQ;3J>4d z$TZWh`t0fWA4IKCcynQQ8eeqo2WP;wDod=%Ov63Ji~u@piO7lU#pkSVmeKEe&}w?V zkB%qfkyPOo4*Mx3CeTilS^oXfPy@5I#V;AK3FtBpc*;I=(UtDo6|<8oE7}5dWOXya zAI=o#;)E-trTQ%Gz?mnu;RYq^{M&S1?Se$V^}zJ0Q^^$j-4yMV{wcU9%@VyDxKHAd z_5GSZJs6DEY$sQQdP}||4mCWAly~>>5{^lwP8#6&r92^)i=zj-+%0A%=}iRhcCc@} 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 literal 0 HcmV?d00001 From 255fdc6ac02332bb2f13cfb73fb11c8c93ca6488 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 10 Feb 2026 10:00:23 -0500 Subject: [PATCH 077/129] Finish adding (unordered) list --- src/App.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/App.tsx b/src/App.tsx index 05350ef5c0..27cc5747f6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,8 +17,19 @@ function App(): React.JSX.Element { Edit src/App.tsx and save. This page will automatically reload.

      +

      This is a header!

      + A budgie couple + +
      + My go-to Dominos order: +
        +
      • Pepperoni
      • +
      • Sausage
      • +
      • Green peppers
      • +
      +
      ); } From b20f96641d8ac277f1bc24d57d51720ff263a59c Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 10 Feb 2026 10:11:50 -0500 Subject: [PATCH 078/129] Finish adding "Hello World" logger button --- src/App.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 27cc5747f6..a7fe42ac11 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,7 @@ import React from "react"; import "./App.css"; import budgies from "./images/budgies.jpg"; +import { Button } from "react-bootstrap"; export function Header(): React.JSX.Element { return

      This is my header

      ; @@ -19,9 +20,7 @@ function App(): React.JSX.Element {

      This is a header!

      - A budgie couple -
      My go-to Dominos order:
        @@ -30,6 +29,15 @@ function App(): React.JSX.Element {
      • Green peppers
      +
      + +
      ); } From 2720ca4f45e4d260601111aa01aea6336e05aa65 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 10 Feb 2026 10:39:56 -0500 Subject: [PATCH 079/129] Finish all tests --- src/App.css | 4 ++-- src/App.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.css b/src/App.css index ad32fac073..bce3b2b3c0 100644 --- a/src/App.css +++ b/src/App.css @@ -15,14 +15,14 @@ .App-header { width: 100%; - background-color: #282c34; + background-color: rgb(255, 0, 0); min-height: 40vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); - color: white; + color: rgb(255, 255, 255); } .App-link { diff --git a/src/App.tsx b/src/App.tsx index a7fe42ac11..120223acf5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,7 +10,7 @@ export function Header(): React.JSX.Element { function App(): React.JSX.Element { return (
      -
      +
      UD CISC275 with React Hooks and TypeScript. Jacob Donn. Hello World
      From e5bfcf80542bf268c2f1cea495ea2e50dfd8ab98 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 11 Feb 2026 13:10:06 -0500 Subject: [PATCH 080/129] Finish first func --- src/functions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/functions.ts b/src/functions.ts index e614c81c0c..ddaa3af7b2 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; + let celsiusTemp = (temperature - 32) * (5 / 9); + return celsiusTemp; } /** From dbf54332e0610cc470bdf3b34e814a601334cc63 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 11 Feb 2026 13:11:37 -0500 Subject: [PATCH 081/129] Finish second func --- src/functions.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/functions.ts b/src/functions.ts index ddaa3af7b2..375c2405be 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -13,7 +13,18 @@ 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; } /** From 40ad4321d99a1e3e969abb69ae694f1da896d573 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 11 Feb 2026 13:13:44 -0500 Subject: [PATCH 082/129] Finish third func --- src/functions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/functions.ts b/src/functions.ts index 375c2405be..3714b0fe97 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -32,7 +32,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() + "!"; } /** From 1e40289eb54b544efedde2316963edec94b37a28 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 11 Feb 2026 13:17:26 -0500 Subject: [PATCH 083/129] Finish fourth func --- src/functions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/functions.ts b/src/functions.ts index 3714b0fe97..e99c4570de 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -40,7 +40,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.slice(-1) === "?"; } /** From e55ad1812f50683e908417ab5da184129c550eb6 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 11 Feb 2026 13:18:42 -0500 Subject: [PATCH 084/129] Finish 5th func --- src/functions.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/functions.ts b/src/functions.ts index e99c4570de..9acfb261f6 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -49,5 +49,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 === "yes" || word === "YES") { + return true; + } else if (word === "no" || word === "NO") { + return false; + } + + return null; } From 026542958f838d52b300e7f0c7debb4319ea836e Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Thu, 19 Feb 2026 12:54:00 -0500 Subject: [PATCH 085/129] Finish all array funcs --- src/arrays.ts | 81 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 9 deletions(-) diff --git a/src/arrays.ts b/src/arrays.ts index 4a2ffe8e5b..2636138546 100644 --- a/src/arrays.ts +++ b/src/arrays.ts @@ -1,3 +1,5 @@ +//import { cursorTo } from "readline"; + /** * Consume an array of numbers, and return a new array containing * JUST the first and last number. If there are no elements, return @@ -5,7 +7,14 @@ * the number twice. */ export function bookEndList(numbers: number[]): number[] { - return numbers; + if (numbers.length === 0) { + return []; + } else if (numbers.length === 1) { + return [...numbers, ...numbers]; + } else { + const firstLast: number[] = [numbers[0], numbers[numbers.length - 1]]; + return firstLast; + } } /** @@ -13,7 +22,8 @@ export function bookEndList(numbers: number[]): number[] { * number has been tripled (multiplied by 3). */ export function tripleNumbers(numbers: number[]): number[] { - return numbers; + const tripled: number[] = numbers.map((num: number): number => num * 3); + return tripled; } /** @@ -21,7 +31,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[] { - return []; + const ints: number[] = numbers.map((num: string): number => + !isNaN(+num) ? +num : 0, + ); + return ints; } /** @@ -32,7 +45,10 @@ 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 []; + const justNums: number[] = amounts.map((amount: string): number => + +amount.replace("$", "") ? +amount.replace("$", "") : 0, + ); + return justNums; }; /** @@ -41,7 +57,12 @@ export const removeDollars = (amounts: string[]): number[] => { * in question marks ("?"). */ export const shoutIfExclaiming = (messages: string[]): string[] => { - return []; + const newMessages: string[] = messages + .map((message: string): string => + message.slice(-1) === "!" ? message.toUpperCase() : message, + ) + .filter((message: string): boolean => message.slice(-1) !== "?"); + return newMessages; }; /** @@ -49,7 +70,10 @@ export const shoutIfExclaiming = (messages: string[]): string[] => { * 4 letters long. */ export function countShortWords(words: string[]): number { - return 0; + const shortWords: string[] = words.filter( + (word: string): boolean => word.length < 4, + ); + return shortWords.length; } /** @@ -58,7 +82,13 @@ export function countShortWords(words: string[]): number { * then return true. */ export function allRGB(colors: string[]): boolean { - return false; + if (colors.length === 0) { + return true; + } + return colors.every( + (color: string): boolean => + color === "red" || color === "blue" || color === "green", + ); } /** @@ -69,7 +99,16 @@ export function allRGB(colors: string[]): boolean { * And the array [] would become "0=0". */ export function makeMath(addends: number[]): string { - return ""; + if (addends.length === 0) { + return "0=0"; + } + + const sum = addends.reduce( + (total: number, curr: number): number => (total += curr), + 0, + ); + const additionString: string = addends.join("+"); + return sum + "=" + additionString; } /** @@ -81,6 +120,30 @@ export function makeMath(addends: number[]): string { * 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, 7, 17] */ + export function injectPositive(values: number[]): number[] { - return []; + const firstNeg: number | undefined = values.find( + (num: number): boolean => num < 0, + ); + + if (firstNeg === undefined) { + const sum = values.reduce( + (total: number, curr: number): number => total + curr, + 0, + ); + return [...values, sum]; + } + + const firstNegIndex = values.findIndex((num: number): boolean => num < 0); + const valuesUpToNeg = values.slice(0, firstNegIndex); + const sumBeforeNeg = valuesUpToNeg.reduce( + (total: number, num: number): number => total + num, + 0, + ); + const finalArr: number[] = [ + ...values.slice(0, firstNegIndex + 1), + sumBeforeNeg, + ...values.slice(firstNegIndex + 1), + ]; + return finalArr; } From 4d5511a5ef2792f01bd734dc4339e2958c1cf7b5 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 22 Feb 2026 11:46:03 -0500 Subject: [PATCH 086/129] Finish makeBlankQuestion --- src/objects.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/objects.ts b/src/objects.ts index 3fd2072e5e..e2e467a374 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -8,9 +8,18 @@ import { Question, QuestionType } from "./interfaces/question"; export function makeBlankQuestion( id: number, name: string, - type: QuestionType + type: QuestionType, ): Question { - return {}; + return { + id: id, + name: name, + type: type, + body: "", + expected: "", + options: [], + points: 1, + published: false, + }; } /** @@ -115,7 +124,7 @@ export function mergeQuestion( id: number, name: string, contentQuestion: Question, - { points }: { points: number } + { points }: { points: number }, ): Question { return contentQuestion; } From b2fb1d05747b3dc9110fe6a0ad5fcdc0ba0d5811 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 22 Feb 2026 11:50:44 -0500 Subject: [PATCH 087/129] Finish isCorrect() --- src/objects.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objects.ts b/src/objects.ts index e2e467a374..695d0ca370 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -30,7 +30,7 @@ export function makeBlankQuestion( * HINT: Look up the `trim` and `toLowerCase` functions. */ export function isCorrect(question: Question, answer: string): boolean { - return false; + return question.expected.toLowerCase() === answer.toLowerCase().trim(); } /** From 553433f005d002286a6d6a31a3c67cf72aefe1e3 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 22 Feb 2026 13:44:27 -0500 Subject: [PATCH 088/129] Finish toShortForm() --- src/objects.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/objects.ts b/src/objects.ts index 695d0ca370..1a352a12b9 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -40,7 +40,11 @@ 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") { + return question.expected === answer; + } + + return answer != ""; } /** @@ -50,7 +54,7 @@ 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 ""; + return `${question.id}: ${question.name.slice(0, 10)}`; } /** From 917c88598c699ed7b4470211ffb086335e368d97 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 22 Feb 2026 13:48:39 -0500 Subject: [PATCH 089/129] Finish renameQuestion() --- src/objects.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/objects.ts b/src/objects.ts index 1a352a12b9..4f4f1dd9c6 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -83,7 +83,8 @@ export function toMarkdown(question: Question): string { * `newName`. */ export function renameQuestion(question: Question, newName: string): Question { - return question; + const newQuestion: Question = { ...question, name: newName }; + return newQuestion; } /** From 3dbc6f18ffee883cf5ceac8e7f18f5a16871e294 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 22 Feb 2026 13:51:50 -0500 Subject: [PATCH 090/129] Finish publishQuestion() --- src/objects.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/objects.ts b/src/objects.ts index 4f4f1dd9c6..8ed3a7f271 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -93,7 +93,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; + if (question.published) { + const newQuestion = { ...question, published: false }; + return newQuestion; + } + + const newQuestion = { ...question, published: true }; + return newQuestion; } /** From 1c80284f1345716fce712c5b00cda50509801306 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 22 Feb 2026 14:03:25 -0500 Subject: [PATCH 091/129] Finish duplicateQuestion() --- src/objects.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/objects.ts b/src/objects.ts index 8ed3a7f271..11957c1f8c 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -109,7 +109,13 @@ export function publishQuestion(question: Question): Question { * The `published` field should be reset to false. */ export function duplicateQuestion(id: number, oldQuestion: Question): Question { - return oldQuestion; + const newQuestion = { + ...oldQuestion, + id: id, + published: false, + name: `Copy of ${oldQuestion.name}`, + }; + return newQuestion; } /** From d8d09752cb79a5ebe0c82bbd10449f3bfe3afea0 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 22 Feb 2026 14:12:57 -0500 Subject: [PATCH 092/129] Finish addOption() --- src/objects.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/objects.ts b/src/objects.ts index 11957c1f8c..84a1770d32 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -126,7 +126,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; + const newQuestion = { + ...question, + options: [...question.options, newOption], + }; + return newQuestion; } /** From 907117cbc541ace73573373c67713ebd9c2bd1d4 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 22 Feb 2026 17:14:28 -0500 Subject: [PATCH 093/129] Finish isValid() --- src/objects.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objects.ts b/src/objects.ts index 84a1770d32..f88b41c144 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -41,7 +41,7 @@ export function isCorrect(question: Question, answer: string): boolean { */ export function isValid(question: Question, answer: string): boolean { if (question.type === "multiple_choice_question") { - return question.expected === answer; + return question.options.includes(answer); } return answer != ""; From 1ba4c3a2b2f31d1580e5ede9c418a30215605af4 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 22 Feb 2026 17:19:39 -0500 Subject: [PATCH 094/129] Finish toMarkdown() --- src/objects.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/objects.ts b/src/objects.ts index f88b41c144..bc0f36aa5c 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -75,7 +75,11 @@ 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 ""; + if (question.type === "short_answer_question") { + return `# ${question.name}\n${question.body}`; + } + + return `# ${question.name}\n${question.body}\n- ${question.options[0]}\n- ${question.options[1]}\n- ${question.options[2]}`; } /** From e7461aef030cf771ca7d8135e215708efca195a9 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 22 Feb 2026 17:26:38 -0500 Subject: [PATCH 095/129] Finish mergeQuestion --- src/objects.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/objects.ts b/src/objects.ts index bc0f36aa5c..3f94c14cb2 100644 --- a/src/objects.ts +++ b/src/objects.ts @@ -151,5 +151,12 @@ export function mergeQuestion( contentQuestion: Question, { points }: { points: number }, ): Question { - return contentQuestion; + const mergedQuestion = { + ...contentQuestion, + id: id, + name: name, + published: false, + points: { points }.points, + }; + return mergedQuestion; } From 293ef5fb891d4796a06f9282d60f9ac2e1219658 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 24 Feb 2026 21:27:10 -0500 Subject: [PATCH 096/129] Finish getPublishedQuestions() --- src/nested.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/nested.ts b/src/nested.ts index 562b6ca0df..c0b5018ae5 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -6,7 +6,9 @@ import { Question, QuestionType } from "./interfaces/question"; * that are `published`. */ export function getPublishedQuestions(questions: Question[]): Question[] { - return []; + return questions.filter( + (question: Question): boolean => question.published, + ); } /** @@ -14,9 +16,7 @@ export function getPublishedQuestions(questions: Question[]): Question[] { * 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 []; -} +export function getNonEmptyQuestions(questions: Question[]): Question[] {} /*** * Consumes an array of questions and returns the question with the given `id`. If the @@ -24,7 +24,7 @@ export function getNonEmptyQuestions(questions: Question[]): Question[] { */ export function findQuestion( questions: Question[], - id: number + id: number, ): Question | null { return null; } @@ -114,7 +114,7 @@ export function addNewQuestion( questions: Question[], id: number, name: string, - type: QuestionType + type: QuestionType, ): Question[] { return []; } @@ -127,7 +127,7 @@ export function addNewQuestion( export function renameQuestionById( questions: Question[], targetId: number, - newName: string + newName: string, ): Question[] { return []; } @@ -142,7 +142,7 @@ export function renameQuestionById( export function changeQuestionTypeById( questions: Question[], targetId: number, - newQuestionType: QuestionType + newQuestionType: QuestionType, ): Question[] { return []; } @@ -161,7 +161,7 @@ export function editOption( questions: Question[], targetId: number, targetOptionIndex: number, - newOption: string + newOption: string, ): Question[] { return []; } @@ -175,7 +175,7 @@ export function editOption( export function duplicateQuestionInArray( questions: Question[], targetId: number, - newId: number + newId: number, ): Question[] { return []; } From e82e13b486fb8624d6b13a192f413eda463b960f Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 24 Feb 2026 21:29:52 -0500 Subject: [PATCH 097/129] Finish getNonEmptyQuestions --- src/nested.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/nested.ts b/src/nested.ts index c0b5018ae5..4fedd00835 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -16,7 +16,16 @@ export function getPublishedQuestions(questions: Question[]): Question[] { * 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[] {} +export function getNonEmptyQuestions(questions: Question[]): Question[] { + return questions.filter( + (question: Question): boolean => + !( + question.expected === "" && + question.body === "" && + question.options.length === 0 + ), + ); +} /*** * Consumes an array of questions and returns the question with the given `id`. If the From 159b7c9a2feae9155576cdaa249851dc419df68c Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 24 Feb 2026 21:31:27 -0500 Subject: [PATCH 098/129] Finish findQuestion() --- src/nested.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/nested.ts b/src/nested.ts index 4fedd00835..927b0285a0 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -35,7 +35,13 @@ export function findQuestion( questions: Question[], id: number, ): Question | null { - return null; + const foundQuestion = questions.find( + (question: Question): boolean => question.id === id, + ); + if (foundQuestion === undefined) { + return null; + } + return foundQuestion; } /** From f064245067c7bc4fc48880e7bb0cf90da96b16d5 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 24 Feb 2026 21:33:49 -0500 Subject: [PATCH 099/129] Finish removeQuestion() --- src/nested.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nested.ts b/src/nested.ts index 927b0285a0..f896791977 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -49,7 +49,9 @@ export function findQuestion( * with the given `id`. */ export function removeQuestion(questions: Question[], id: number): Question[] { - return []; + return questions.filter( + (question: Question): boolean => question.id !== id, + ); } /*** From 700d14d2f7aced5536d8ee14f7c1d76763361132 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 24 Feb 2026 21:35:10 -0500 Subject: [PATCH 100/129] Finish getNames() --- src/nested.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nested.ts b/src/nested.ts index f896791977..e89ec03c24 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -59,7 +59,7 @@ export function removeQuestion(questions: Question[], id: number): Question[] { * questions, as an array. */ export function getNames(questions: Question[]): string[] { - return []; + return questions.map((question: Question): string => question.name); } /*** From a5db038ba50e9db3e2c26bc2250f427fc8dc7e2f Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 24 Feb 2026 21:54:16 -0500 Subject: [PATCH 101/129] Finish sumPoints() --- src/nested.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/nested.ts b/src/nested.ts index e89ec03c24..a6a38ccd77 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -66,7 +66,10 @@ export function getNames(questions: Question[]): string[] { * Consumes an array of questions and returns the sum total of all their points added together. */ export function sumPoints(questions: Question[]): number { - return 0; + return questions.reduce( + (total: number, curr: Question) => total + curr.points, + 0, + ); } /*** From 05e2a930ff56789a2876ae70f748cce82d9b2fee Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 24 Feb 2026 22:07:19 -0500 Subject: [PATCH 102/129] Finish sumPublishedPoints() --- src/nested.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nested.ts b/src/nested.ts index a6a38ccd77..87d68c933f 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -76,7 +76,9 @@ 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; + return questions + .filter((question: Question): boolean => question.published) + .reduce((total: number, curr: Question) => total + curr.points, 0); } /*** From a8b1bfb7f15150ec2fbf2d0a6bf2f68a72471d49 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 25 Feb 2026 20:45:48 -0500 Subject: [PATCH 103/129] Finish makeAnswers() --- src/nested.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/nested.ts b/src/nested.ts index 87d68c933f..4413d289b6 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -99,7 +99,7 @@ id,name,options,points,published * Check the unit tests for more examples! */ export function toCSV(questions: Question[]): string { - return ""; + const finalStr: string = "id,name,options,points,published\n"; } /** @@ -108,7 +108,14 @@ 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 []; + return questions.map( + (question: Question): Answer => ({ + questionId: question.id, + text: "", + submitted: false, + correct: false, + }), + ); } /*** From 2f65e820ac78c94233603600fd18e6debd50ecd7 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 25 Feb 2026 20:48:32 -0500 Subject: [PATCH 104/129] Finish publishAll() --- src/nested.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nested.ts b/src/nested.ts index 4413d289b6..575011522c 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -123,7 +123,9 @@ export function makeAnswers(questions: Question[]): Answer[] { * each question is now published, regardless of its previous published status. */ export function publishAll(questions: Question[]): Question[] { - return []; + return questions.map( + (question: Question): Question => ({ ...question, published: true }), + ); } /*** From 93cf6088ca78d2ad5b4a53f430f83cd4c99b75a2 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 25 Feb 2026 20:49:34 -0500 Subject: [PATCH 105/129] Finish sameType() --- src/nested.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nested.ts b/src/nested.ts index 575011522c..6687e238b2 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -133,7 +133,9 @@ 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; + return questions.every( + (question: Question): boolean => question.type === questions[0].type, + ); } /*** From 8b2fdf25497792f28d43c17d3e6f63293af27d80 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 25 Feb 2026 20:52:42 -0500 Subject: [PATCH 106/129] Finish addNewQuestion() --- src/nested.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nested.ts b/src/nested.ts index 6687e238b2..93ad44dda1 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 @@ -149,7 +150,8 @@ export function addNewQuestion( name: string, type: QuestionType, ): Question[] { - return []; + const newQuestion: Question = makeBlankQuestion(id, name, type); + return [...questions, newQuestion]; } /*** From 4040f179d6f49fa9354cb2ca1e4b5eaeae5d52d8 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 25 Feb 2026 21:40:57 -0500 Subject: [PATCH 107/129] Finish renameQuestionById() --- src/nested.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/nested.ts b/src/nested.ts index 93ad44dda1..c450e5f1e7 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -164,7 +164,14 @@ export function renameQuestionById( targetId: number, newName: string, ): Question[] { - return []; + const targetIndex: number = questions.findIndex( + (question: Question): boolean => question.id === targetId, + ); + const questionsCopy: Question[] = questions.map( + (question: Question): Question => ({ ...question }), + ); + questionsCopy[targetIndex].name = newName; + return questionsCopy; } /*** From 253897e228c212e792638989200fbe28a8baf0a9 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 25 Feb 2026 21:47:11 -0500 Subject: [PATCH 108/129] Finish changeQuestionTypeById() --- src/nested.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/nested.ts b/src/nested.ts index c450e5f1e7..7923f7efc8 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -186,7 +186,18 @@ export function changeQuestionTypeById( targetId: number, newQuestionType: QuestionType, ): Question[] { - return []; + const targetIndex: number = questions.findIndex( + (question: Question): boolean => question.id === targetId, + ); + const questionsCopy: Question[] = questions.map( + (question: Question): Question => ({ ...question }), + ); + questionsCopy[targetIndex].options = + newQuestionType === "short_answer_question" ? + [] + : questionsCopy[targetIndex].options; + questionsCopy[targetIndex].type = newQuestionType; + return questionsCopy; } /** From cd21d81be9736edd8f4cde1c63d5ef8e3156a6e1 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Fri, 27 Feb 2026 15:40:54 -0500 Subject: [PATCH 109/129] Finish duplicateQuestionInArray() --- src/nested.ts | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/nested.ts b/src/nested.ts index 7923f7efc8..21a5682cb2 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 { makeBlankQuestion, duplicateQuestion } from "./objects"; /** * Consumes an array of questions and returns a new array with only the questions @@ -216,7 +216,9 @@ export function editOption( targetOptionIndex: number, newOption: string, ): Question[] { - return []; + const targetQuestion = questions.find( + (question: Question): boolean => question.id === targetId, + ); } /*** @@ -230,5 +232,17 @@ export function duplicateQuestionInArray( targetId: number, newId: number, ): Question[] { - return []; + const targetQuestion: Question | undefined = questions.find( + (question: Question): boolean => question.id === targetId, + ); + const duplicatequestion: Question = duplicateQuestion( + newId, + targetQuestion!, + ); + const origQuestionIndex: number = questions.findIndex( + (question: Question): boolean => question.id === targetId, + ); + const newArr = [...questions]; + newArr.splice(origQuestionIndex + 1, 0, duplicatequestion); + return newArr; } From 3991aea0f9c659d4a047fea333ddbd822db21f6f Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sat, 28 Feb 2026 20:54:52 -0500 Subject: [PATCH 110/129] Finish editOption() --- src/nested.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/nested.ts b/src/nested.ts index 21a5682cb2..5decb9132b 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -219,6 +219,34 @@ export function editOption( const targetQuestion = questions.find( (question: Question): boolean => question.id === targetId, ); + + // if (targetOptionIndex === -1) { + // targetQuestion!.options = [...targetQuestion!.options, newOption]; + // } else { + // targetQuestion!.options.splice(targetOptionIndex, 1, newOption); + // } + + // return questions.map( + // (question: Question): Question => + // question.id === targetId ? targetQuestion! : question, + // ); + + let targetQuestionOptions: string[] = []; + if (targetOptionIndex === -1) { + targetQuestionOptions = [...targetQuestion!.options, newOption]; + } else { + targetQuestionOptions = [...targetQuestion!.options]; + targetQuestionOptions.splice(targetOptionIndex, 1, newOption); + } + + const newOptionQuestion: Question = { + ...targetQuestion!, + options: targetQuestionOptions, + }; + return questions.map( + (question: Question): Question => + question.id === targetId ? newOptionQuestion : question, + ); } /*** @@ -242,6 +270,7 @@ export function duplicateQuestionInArray( const origQuestionIndex: number = questions.findIndex( (question: Question): boolean => question.id === targetId, ); + const newArr = [...questions]; newArr.splice(origQuestionIndex + 1, 0, duplicatequestion); return newArr; From 9a7d427c529a2b8ff88b7a39cbc86afe717e6ca7 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sat, 28 Feb 2026 21:23:53 -0500 Subject: [PATCH 111/129] Finish toCSV() --- src/nested.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/nested.ts b/src/nested.ts index 5decb9132b..7811e3fcc5 100644 --- a/src/nested.ts +++ b/src/nested.ts @@ -100,7 +100,14 @@ id,name,options,points,published * Check the unit tests for more examples! */ export function toCSV(questions: Question[]): string { - const finalStr: string = "id,name,options,points,published\n"; + const top: string = "id,name,options,points,published\n"; + const data: string[] = questions.map( + (question: Question): string => + `${question.id},${question.name},${question.options.length},${question.points},${question.published}\n`, + ); + + const final: string[] = [top, ...data]; + return final.join("").slice(0, -1); } /** @@ -220,17 +227,6 @@ export function editOption( (question: Question): boolean => question.id === targetId, ); - // if (targetOptionIndex === -1) { - // targetQuestion!.options = [...targetQuestion!.options, newOption]; - // } else { - // targetQuestion!.options.splice(targetOptionIndex, 1, newOption); - // } - - // return questions.map( - // (question: Question): Question => - // question.id === targetId ? targetQuestion! : question, - // ); - let targetQuestionOptions: string[] = []; if (targetOptionIndex === -1) { targetQuestionOptions = [...targetQuestion!.options, newOption]; From 659cbf2e9f78cc80451e2bfc1c90624a14c92e25 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 1 Mar 2026 13:22:10 -0500 Subject: [PATCH 112/129] Delete unnecessary files per textbook instructions --- src/HtmlCss.test.tsx | 83 -------------------------------------------- src/text.test.tsx | 9 ----- 2 files changed, 92 deletions(-) delete mode 100644 src/HtmlCss.test.tsx delete mode 100644 src/text.test.tsx diff --git a/src/HtmlCss.test.tsx b/src/HtmlCss.test.tsx deleted file mode 100644 index 320cb97524..0000000000 --- a/src/HtmlCss.test.tsx +++ /dev/null @@ -1,83 +0,0 @@ -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("(2 pts) There is a header", () => { - render(); - const header = screen.getByRole("heading"); - expect(header).toBeInTheDocument(); - }); - - test("(2 pts) There is an image with alt text", () => { - render(); - const image = screen.getByRole("img"); - expect(image).toBeInTheDocument(); - expect(image).toHaveAttribute("alt"); - }); - - test("(2 pts) 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("(2 pts) Some basic CSS is added.", () => { - 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("(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 }); - expect(button).toBeInTheDocument(); - expect(button).toHaveClass("btn"); - expect(button).toHaveClass("btn-primary"); - }); - - 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("(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 }); - userEvent.click(button); - expect(consoleSpy).toHaveBeenCalledWith("Hello World!"); - }); -}); - -describe("Some additional CSS was added", () => { - 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("*"); - - // 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); - }); -}); diff --git a/src/text.test.tsx b/src/text.test.tsx deleted file mode 100644 index f99a063e76..0000000000 --- a/src/text.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; -import { render, screen } from "@testing-library/react"; -import App from "./App"; - -test("renders the text 'Hello World' somewhere", () => { - render(); - const texts = screen.getAllByText(/Hello World/); - expect(texts.length).toBeGreaterThanOrEqual(1); -}); From 838c21cde8a7ac5f536af5cc67d10eb33d3d443b Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 1 Mar 2026 20:10:46 -0500 Subject: [PATCH 113/129] Finish Counter component --- src/App.tsx | 14 +++++++------- src/components/Counter.tsx | 8 +++++++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 651321819d..c880bcc394 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +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"; +// 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"; import { Counter } from "./components/Counter"; export function Header(): React.JSX.Element { @@ -21,7 +21,7 @@ function App(): React.JSX.Element {

      - + {/*

      @@ -29,7 +29,7 @@ function App(): React.JSX.Element {

      - + */}
      ); } 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}. ); From acef48aea7bdc6814bef75424a4cf8842a29493e Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 1 Mar 2026 21:38:00 -0500 Subject: [PATCH 114/129] Finish RevealAnswer() component --- src/App.tsx | 6 +++--- src/components/RevealAnswer.tsx | 14 +++++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index c880bcc394..c691036b0c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import React from "react"; import "./App.css"; // import { ChangeType } from "./components/ChangeType"; -// import { RevealAnswer } from "./components/RevealAnswer"; +import { RevealAnswer } from "./components/RevealAnswer"; // import { StartAttempt } from "./components/StartAttempt"; // import { TwoDice } from "./components/TwoDice"; // import { CycleHoliday } from "./components/CycleHoliday"; @@ -21,9 +21,9 @@ function App(): React.JSX.Element {

      - {/* +
      - + {/*

      diff --git a/src/components/RevealAnswer.tsx b/src/components/RevealAnswer.tsx index a48c0a0961..39d7541835 100644 --- a/src/components/RevealAnswer.tsx +++ b/src/components/RevealAnswer.tsx @@ -2,5 +2,17 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; export function RevealAnswer(): React.JSX.Element { - return
      Reveal Answer
      ; + const [visible, setVisible] = useState(false); + return ( + + {" "} + {visible && 42} + + ); } From 2e7f86643471aed6f81af348421ed6e0b91c0eb0 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 1 Mar 2026 22:48:55 -0500 Subject: [PATCH 115/129] Finish startAttempt() component --- src/App.tsx | 6 +++--- src/components/StartAttempt.tsx | 34 ++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index c691036b0c..590a1a6957 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,7 +2,7 @@ import React from "react"; import "./App.css"; // import { ChangeType } from "./components/ChangeType"; import { RevealAnswer } from "./components/RevealAnswer"; -// import { StartAttempt } from "./components/StartAttempt"; +import { StartAttempt } from "./components/StartAttempt"; // import { TwoDice } from "./components/TwoDice"; // import { CycleHoliday } from "./components/CycleHoliday"; import { Counter } from "./components/Counter"; @@ -23,9 +23,9 @@ function App(): React.JSX.Element {

      - {/* +
      - + {/*

      diff --git a/src/components/StartAttempt.tsx b/src/components/StartAttempt.tsx index dec4ae7dcd..1a1683bc16 100644 --- a/src/components/StartAttempt.tsx +++ b/src/components/StartAttempt.tsx @@ -2,5 +2,37 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; export function StartAttempt(): React.JSX.Element { - return
      Start Attempt
      ; + const [numAttempts, setNumAttempts] = useState(4); + const [inProgress, setInProgress] = useState(false); + + return ( + + + + + Number of Attempts: {numAttempts}; + + ); } From a196ed467b63dfc1d1464e15fa185110a5f2f600 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Mon, 2 Mar 2026 20:14:21 -0500 Subject: [PATCH 116/129] Finish TwoDice() component --- src/App.tsx | 6 +++--- src/components/TwoDice.tsx | 30 +++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 590a1a6957..1831469e1d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,7 +3,7 @@ import "./App.css"; // import { ChangeType } from "./components/ChangeType"; import { RevealAnswer } from "./components/RevealAnswer"; import { StartAttempt } from "./components/StartAttempt"; -// import { TwoDice } from "./components/TwoDice"; +import { TwoDice } from "./components/TwoDice"; // import { CycleHoliday } from "./components/CycleHoliday"; import { Counter } from "./components/Counter"; @@ -25,9 +25,9 @@ function App(): React.JSX.Element {

      - {/* +
      - + {/*
      */}
      diff --git a/src/components/TwoDice.tsx b/src/components/TwoDice.tsx index a257594d35..6b2b023894 100644 --- a/src/components/TwoDice.tsx +++ b/src/components/TwoDice.tsx @@ -12,5 +12,33 @@ export function d6(): number { } export function TwoDice(): React.JSX.Element { - return
      Two Dice
      ; + const [die1, setDie1] = useState(1); + const [die2, setDie2] = useState(2); + + return ( +
      + + + {die1} + + + + {die2} + + {die1 === 1 && die2 === 1 && "Lose"} + {die1 === die2 && die1 !== 1 && die2 !== 2 && "Win"} +
      + ); } From 369a92c433114862d86fdb8b3ae25f1e3b1707ba Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 3 Mar 2026 17:26:56 -0500 Subject: [PATCH 117/129] Finish changeType() component --- src/App.tsx | 6 +++--- src/components/ChangeType.tsx | 20 +++++++++++++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 1831469e1d..8b6ca00ad4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,6 @@ import React from "react"; import "./App.css"; -// import { ChangeType } from "./components/ChangeType"; +import { ChangeType } from "./components/ChangeType"; import { RevealAnswer } from "./components/RevealAnswer"; import { StartAttempt } from "./components/StartAttempt"; import { TwoDice } from "./components/TwoDice"; @@ -27,9 +27,9 @@ function App(): React.JSX.Element {

      - {/* +
      - */} + {/* */}
      ); } diff --git a/src/components/ChangeType.tsx b/src/components/ChangeType.tsx index 6e8f878020..caf4c20d25 100644 --- a/src/components/ChangeType.tsx +++ b/src/components/ChangeType.tsx @@ -3,5 +3,23 @@ import { Button } from "react-bootstrap"; import { QuestionType } from "../interfaces/question"; export function ChangeType(): React.JSX.Element { - return
      Change Type
      ; + const [questType, setQuestType] = useState( + "short_answer_question", + ); + + const changeType = (): void => { + setQuestType( + questType === "short_answer_question" ? + "multiple_choice_question" + : "short_answer_question", + ); + }; + + return ( +
      + + {questType === "short_answer_question" && "Short Answer"} + {questType === "multiple_choice_question" && "Multiple Choice"} +
      + ); } From d540ad0a0e4fb0d71cbcac68bfb19c5263372e59 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Tue, 3 Mar 2026 18:03:13 -0500 Subject: [PATCH 118/129] Finish cycleHoliday() --- src/App.tsx | 4 ++-- src/components/CycleHoliday.tsx | 36 ++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 8b6ca00ad4..651321819d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,7 +4,7 @@ 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"; +import { CycleHoliday } from "./components/CycleHoliday"; import { Counter } from "./components/Counter"; export function Header(): React.JSX.Element { @@ -29,7 +29,7 @@ function App(): React.JSX.Element {

      - {/* */} +
      ); } diff --git a/src/components/CycleHoliday.tsx b/src/components/CycleHoliday.tsx index 47e6d76444..c4f441a52c 100644 --- a/src/components/CycleHoliday.tsx +++ b/src/components/CycleHoliday.tsx @@ -2,5 +2,39 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; export function CycleHoliday(): React.JSX.Element { - return
      Cycle Holiday
      ; + type Holiday = "πŸŽ„" | "πŸŽ†" | "πŸŽƒ" | "πŸ‡ΊπŸ‡Έ" | "πŸ¦ƒ"; + const holidaysAlphabet: Record = { + "πŸŽ„": "πŸŽ†", + "πŸŽ†": "πŸŽƒ", + "πŸŽƒ": "πŸ‡ΊπŸ‡Έ", + "πŸ‡ΊπŸ‡Έ": "πŸ¦ƒ", + "πŸ¦ƒ": "πŸŽ„", + }; + const holidaysYear: Record = { + "πŸ‡ΊπŸ‡Έ": "πŸŽ†", + "πŸŽ†": "πŸŽƒ", + "πŸŽƒ": "πŸ¦ƒ", + "πŸ¦ƒ": "πŸŽ„", + "πŸŽ„": "πŸ‡ΊπŸ‡Έ", + }; + const [holiday, setHoliday] = useState("πŸŽ„"); + + const changeAlphabetically = (): void => { + // setHoliday(holidaysAlphabet[holiday]); + const newHoliday = holidaysAlphabet[holiday]; + setHoliday(newHoliday); + }; + + const changeYearly = (): void => { + const newHoliday = holidaysYear[holiday]; + setHoliday(newHoliday); + }; + + return ( +
      + Holiday: {holiday} + + +
      + ); } From 8180305290c6589384bc8c37070a2b104dfdf010 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 4 Mar 2026 19:19:12 -0500 Subject: [PATCH 119/129] Finish doubleHalf() component --- src/App.tsx | 2 +- src/bad-components/DoubleHalf.tsx | 15 ++++++++++----- src/bad-components/DoubleHalfState.tsx | 3 --- 3 files changed, 11 insertions(+), 9 deletions(-) delete mode 100644 src/bad-components/DoubleHalfState.tsx diff --git a/src/App.tsx b/src/App.tsx index 59e3e0e769..cf9fc2daf5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -23,7 +23,7 @@ function App(): React.JSX.Element { World
      - {/* */} +

      diff --git a/src/bad-components/DoubleHalf.tsx b/src/bad-components/DoubleHalf.tsx index 8b01352f59..bf6e7763ca 100644 --- a/src/bad-components/DoubleHalf.tsx +++ b/src/bad-components/DoubleHalf.tsx @@ -1,8 +1,12 @@ import React, { useState } from "react"; import { Button } from "react-bootstrap"; -import { dhValue, setDhValue } from "./DoubleHalfState"; -function Doubler(): React.JSX.Element { +interface buttonPrompts { + dhValue: number; + setDhValue: (newDhValue: number) => void; +} + +function Doubler({ dhValue, setDhValue }: buttonPrompts): React.JSX.Element { return (
      From b55a91f296c2ca7596d09b67ba12396e5b8ed8c4 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Thu, 5 Mar 2026 10:57:47 -0500 Subject: [PATCH 121/129] Finish coloredBox() component --- src/bad-components/ColoredBox.tsx | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/bad-components/ColoredBox.tsx b/src/bad-components/ColoredBox.tsx index 1fa2d770b2..c696c846c2 100644 --- a/src/bad-components/ColoredBox.tsx +++ b/src/bad-components/ColoredBox.tsx @@ -4,8 +4,19 @@ 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); +interface changeColorProps { + colorIndex: number; + setColorIndex: (newColorIndex: number) => void; +} + +interface colorPreviewProps { + colorIndex: number; +} + +function ChangeColor({ + colorIndex, + setColorIndex, +}: changeColorProps): React.JSX.Element { return ( + + + {attsLeft}
      ); } From 02f29c2cabb48ec95d9e1c99c8c185e40f4e5fae Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Wed, 11 Mar 2026 17:05:32 -0400 Subject: [PATCH 125/129] Pass first 3 tests from EditMode; "113" test of GiveAttempts still failing --- src/App.tsx | 10 ++--- src/form-components/EditMode.tsx | 46 +++++++++++++++++++++++ src/form-components/GiveAttempts.test.tsx | 1 + src/form-components/GiveAttempts.tsx | 3 +- 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index a080f4e1ac..7126577f9d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,7 +12,7 @@ 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 { EditMode } from "./form-components/EditMode"; // import { MultipleChoiceQuestion } from "./form-components/MultipleChoiceQuestion"; // import { ChangeColor } from "./form-components/ChangeColor"; @@ -32,15 +32,15 @@ function App(): React.JSX.Element {

      - {/* +
      - + {/*
      */} -
      + > +
      */}
      diff --git a/src/form-components/EditMode.tsx b/src/form-components/EditMode.tsx index ca9fdb9912..3c079162f2 100644 --- a/src/form-components/EditMode.tsx +++ b/src/form-components/EditMode.tsx @@ -1,9 +1,55 @@ import React, { useState } from "react"; +import { Form } from "react-bootstrap"; export function EditMode(): React.JSX.Element { + const [inEditMode, setInEditMode] = useState(false); + const [username, setUsername] = useState("Your Name"); + const [isStudent, setIsStudent] = useState(true); + + function updateIsStudent(event: React.ChangeEvent) { + setIsStudent(event.target.checked); + } + + function updateInEditMode(event: React.ChangeEvent) { + setInEditMode(event.target.checked); + } + + function updateUsername(event: React.ChangeEvent) { + setUsername(event.target.value); + } + return (

      Edit Mode

      + + {/* {!inEditMode ? + `${username} is a student` + : `${username} is not a student`} */} + {isStudent && `${username} is a student`} + {!isStudent && `${username} is not a student`} + + {inEditMode && ( + + )} + + + + {inEditMode && ( + + Edit username + + + )}
      ); } diff --git a/src/form-components/GiveAttempts.test.tsx b/src/form-components/GiveAttempts.test.tsx index 588d959708..1952757685 100644 --- a/src/form-components/GiveAttempts.test.tsx +++ b/src/form-components/GiveAttempts.test.tsx @@ -49,6 +49,7 @@ describe("GiveAttempts Component tests", () => { gain.click(); }); expect(screen.getByText(/113/i)).toBeInTheDocument(); + //expect(screen.getByTestId("attsLeft")) }); test("(2 pts) Cannot gain blank amounts", async () => { const gain = screen.getByRole("button", { name: /gain/i }); diff --git a/src/form-components/GiveAttempts.tsx b/src/form-components/GiveAttempts.tsx index 77777771c0..9d2e43efda 100644 --- a/src/form-components/GiveAttempts.tsx +++ b/src/form-components/GiveAttempts.tsx @@ -37,7 +37,8 @@ export function GiveAttempts(): React.JSX.Element { > gain - {attsLeft} + {/* {attsLeft} */} +

      Attempts left: {attsLeft}

      ); } From e578e2d074e1db880f6560e5a72d4e0aaade55c1 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Thu, 12 Mar 2026 16:37:12 -0400 Subject: [PATCH 126/129] Finish giveAttempts() --- src/form-components/GiveAttempts.test.tsx | 1 - src/form-components/GiveAttempts.tsx | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/form-components/GiveAttempts.test.tsx b/src/form-components/GiveAttempts.test.tsx index 1952757685..588d959708 100644 --- a/src/form-components/GiveAttempts.test.tsx +++ b/src/form-components/GiveAttempts.test.tsx @@ -49,7 +49,6 @@ describe("GiveAttempts Component tests", () => { gain.click(); }); expect(screen.getByText(/113/i)).toBeInTheDocument(); - //expect(screen.getByTestId("attsLeft")) }); test("(2 pts) Cannot gain blank amounts", async () => { const gain = screen.getByRole("button", { name: /gain/i }); diff --git a/src/form-components/GiveAttempts.tsx b/src/form-components/GiveAttempts.tsx index 9d2e43efda..c20050ac12 100644 --- a/src/form-components/GiveAttempts.tsx +++ b/src/form-components/GiveAttempts.tsx @@ -3,7 +3,7 @@ import { Button, Form } from "react-bootstrap"; export function GiveAttempts(): React.JSX.Element { const [attsLeft, setAttsLeft] = useState(3); - const [attsRequested, setAttsRequested] = useState("3"); + const [attsRequested, setAttsRequested] = useState(""); function updateAttsRequested(event: React.ChangeEvent) { setAttsRequested(event.target.value); @@ -37,7 +37,6 @@ export function GiveAttempts(): React.JSX.Element { > gain - {/* {attsLeft} */}

      Attempts left: {attsLeft}

      ); From 03581682e6cdf8f23372de5b3ada282b81d014e8 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Thu, 12 Mar 2026 19:59:40 -0400 Subject: [PATCH 127/129] Finish editMode() form --- src/form-components/EditMode.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/form-components/EditMode.tsx b/src/form-components/EditMode.tsx index 3c079162f2..750e38b7d2 100644 --- a/src/form-components/EditMode.tsx +++ b/src/form-components/EditMode.tsx @@ -32,6 +32,7 @@ export function EditMode(): React.JSX.Element { From 5f62ad9ce41e3e3b8104e875892bdd65828b459a Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Fri, 13 Mar 2026 17:31:17 -0400 Subject: [PATCH 128/129] Finish changeColor() --- src/App.tsx | 6 +++--- src/form-components/ChangeColor.tsx | 30 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 7126577f9d..0a224c0ad2 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 { ChangeColor } from "./form-components/ChangeColor"; +import { ChangeColor } from "./form-components/ChangeColor"; export function Header(): React.JSX.Element { return

      This is my header

      ; @@ -34,9 +34,9 @@ function App(): React.JSX.Element {

      - {/* +
      - diff --git a/src/form-components/ChangeColor.tsx b/src/form-components/ChangeColor.tsx index 514f5f893f..694b7ad12c 100644 --- a/src/form-components/ChangeColor.tsx +++ b/src/form-components/ChangeColor.tsx @@ -1,9 +1,39 @@ import React, { useState } from "react"; +import { Form } from "react-bootstrap"; export function ChangeColor(): React.JSX.Element { + const colors: string[] = [ + "red", + "blue", + "green", + "orange", + "purple", + "cyan", + "magenta", + "white", + ]; + const [color, setColor] = useState("red"); + function updateColor(event: React.ChangeEvent) { + setColor(event.target.value); + } + return (

      Change Color

      + {colors.map((currColor: string) => ( + + ))} + + You have chosen{color} +
      ); } From b3d3767cdf97774fbab9e3da153ed09c06027b66 Mon Sep 17 00:00:00 2001 From: jacobdonn Date: Sun, 15 Mar 2026 12:09:14 -0400 Subject: [PATCH 129/129] Finish MultipleChoiceQuestion() form --- src/App.tsx | 6 +++--- .../MultipleChoiceQuestion.tsx | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 0a224c0ad2..69b35ad39f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,7 +13,7 @@ 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 { MultipleChoiceQuestion } from "./form-components/MultipleChoiceQuestion"; import { ChangeColor } from "./form-components/ChangeColor"; export function Header(): React.JSX.Element { @@ -36,11 +36,11 @@ function App(): React.JSX.Element {

      - {/* -
      */} +

      diff --git a/src/form-components/MultipleChoiceQuestion.tsx b/src/form-components/MultipleChoiceQuestion.tsx index 5e6f35c611..3d5dbcfdd1 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,27 @@ export function MultipleChoiceQuestion({ options: string[]; expectedAnswer: string; }): React.JSX.Element { + const [currAnswer, setCurrAnswer] = useState(options[0]); + return (

      Multiple Choice Question

      + {currAnswer === expectedAnswer &&

      Your answer: βœ”οΈ

      } + {currAnswer !== expectedAnswer &&

      Your answer: ❌

      } + + + Select Answer + ) => { + setCurrAnswer(event.target.value); + }} + > + + + + +
      ); }