Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for utils.js #74

Merged
merged 2 commits into from
Jun 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions __tests__/utils.unit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/* global global */

import { mod, pick, wait, timeoutId } from "../source/utils.js";

// Yes. I generated these tests with ChatGPT. 😎

describe("pick", () => {
// Test with an array of numbers
test("picks a number from the given array", () => {
const options = [1, 2, 3, 4, 5];
const result = pick(options);
expect(options).toContain(result);
});

// Test with an array of strings
test("picks a string from the given array", () => {
const options = ["apple", "banana", "cherry", "date"];
const result = pick(options);
expect(options).toContain(result);
});

// Test with an array of objects
test("picks an object from the given array", () => {
const options = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" },
];
const result = pick(options);
expect(options).toContain(result);
});

// Test with an empty array
test("returns undefined for an empty array", () => {
const options = [];
const result = pick(options);
expect(result).toBeUndefined();
});

// Test with a single-element array
test("returns the only option for a single-element array", () => {
const options = ["only"];
const result = pick(options);
expect(result).toEqual("only");
});

// Test with a large array
test("picks an element from a large array", () => {
const options = Array.from({ length: 1000 }, (_, index) => index);
const result = pick(options);
expect(options).toContain(result);
});
});

describe("mod", () => {
// Test with positive numbers
test("returns the correct modulus for positive numbers", () => {
expect(mod(10, 3)).toBe(1);
expect(mod(15, 6)).toBe(3);
expect(mod(20, 7)).toBe(6);
});

// Test with negative numbers
test("returns the correct modulus for negative numbers", () => {
expect(mod(-10, 3)).toBe(2);
expect(mod(-15, 6)).toBe(3);
expect(mod(-20, 7)).toBe(1);
});

// Test with zero divisor
test("returns NaN when the divisor is zero", () => {
expect(mod(10, 0)).toBeNaN();
expect(mod(-10, 0)).toBeNaN();
expect(mod(0, 0)).toBeNaN();
});

// Test with large numbers
test("returns the correct modulus for large numbers", () => {
expect(mod(987654321, 123456789)).toBe(9);
expect(mod(123456789, 987654321)).toBe(123456789);
});
});

jest.useFakeTimers();

describe("wait", () => {
let setTimeoutMock;

beforeEach(() => {
setTimeoutMock = jest.spyOn(global, "setTimeout");
});

afterEach(() => {
jest.clearAllTimers();
setTimeoutMock.mockRestore();
});

test("resolves after the specified delay", () => {
const delay = 1000;
const promise = wait(delay);
expect(setTimeoutMock).toHaveBeenCalledTimes(1);
expect(setTimeoutMock).toHaveBeenCalledWith(expect.any(Function), delay);
jest.advanceTimersByTime(delay);
return expect(promise).resolves.toBeUndefined();
});

test("does not resolve if clearTimeout is called", () => {
const delay = 1000;
const promise = wait(delay);
expect(setTimeoutMock).toHaveBeenCalledTimes(1);
expect(setTimeoutMock).toHaveBeenCalledWith(expect.any(Function), delay);
clearTimeout(timeoutId);
jest.advanceTimersByTime(delay);
const passSymbol = Symbol();
return expect(
Promise.race([promise, Promise.resolve(passSymbol)])
).resolves.toBe(passSymbol);
});
});
58 changes: 55 additions & 3 deletions source/FortuneCookie/fortuneCookie.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ const background = document.getElementById("background");
const resetButton = document.getElementById("reset-button");
const cancelButton = document.getElementById("cancel-animation-btn");

/**
* Stops the current animation and resets all animatable parts of the app to the
* specified view (`state`).
* @param {'fortune' | 'cookie'} state - Whether to set the view to showing the
* fortune (`'fortune'`) or the cookie (`'cookie'`).
*/
function reset(state) {
clearTimeout(timeoutId);
fortuneAudioCrack.ontimeupdate = null;
Expand Down Expand Up @@ -43,7 +49,6 @@ function reset(state) {
cookieButton.disabled = false;
}
}
window.reset = reset; // TEMP

resetButton.addEventListener("click", () => {
resetButton.disabled = true;
Expand Down Expand Up @@ -96,16 +101,23 @@ function disableButton() {
}

/**
* Enables button so user can click it
* A handler called whenever the animation for opening the fortune cookie ends.
*/
function handleFortuneEnd() {
reset("fortune");
}

/**
* A handler called whenever the animation for dropping a new fortune cookie
* ends.
*/
function handleCookieReady() {
reset("cookie");
}

/**
* Sets the animation to make the left half of the cookie fall.
*/
function fallLeft() {
elem = cookieLeft;
x = 0;
Expand All @@ -116,6 +128,9 @@ function fallLeft() {
rotv = -0.05;
shakeIntensity = 10;
}
/**
* Sets the animation to make the right half of the cookie fall.
*/
function fallRight() {
elem = cookieButton;
x = 0;
Expand All @@ -126,6 +141,9 @@ function fallRight() {
rotv = 0.05;
shakeIntensity = 0;
}
/**
* Sets the animation to make the fortune paper fall.
*/
function fallFortune() {
elem = fortunePaper;
x = 0;
Expand All @@ -135,6 +153,9 @@ function fallFortune() {
rot = 0;
rotv = -0.05;
}
/**
* Sets the animation to make a new, full fortune cookie fall.
*/
function fallNewCookie() {
cookieLeft.style.transform = null;
cookieButton.style.transform = null;
Expand Down Expand Up @@ -211,15 +232,34 @@ cancelButton.addEventListener("click", () => {
}
});

/** in px/ms^2 */
/**
* The acceleration due to "gravity" applied on all falling objects in the
* animation, in px/ms^2.
* @type {number}
*/
const GRAVITY = 0.002;
let elem, x, y, xv, yv, rot, rotv;
/**
* The decrease in shake intensity, in px/ms.
* @type {number}
*/
const shakeV = -0.02;
let shakeIntensity;
let cookieY, cookieYV;
/**
* Whether to animate a new cookie falling.
* @type {boolean}
*/
let cookieFalling = false;

/**
* The timestamp of the last time `paint` was called.
* @type {number}
*/
let lastTime = Date.now();
/**
* Draws the next frame of the cookie falling animation.
*/
function paint() {
const now = Date.now();
const elapsed = Math.min(now - lastTime, 200);
Expand Down Expand Up @@ -267,8 +307,20 @@ function paint() {
}
paint();

/**
* A reference to `window.speechSynthesis`.
* @type {SpeechSynthesis}
*/
const synth = window.speechSynthesis;
/**
* The voice selection dropdown.
* @type {HTMLSelectElement}
*/
const voiceSelect = document.querySelector("select");
/**
* A list of voices available by the browser.
* @type {SpeechSynthesisVoice[]}
*/
let voices = [];

/**
Expand Down
1 change: 1 addition & 0 deletions source/FortuneCookie/fortunes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
* Array of general, college, and collage-romance type fortunes
* @type {string[]}
*/
export const fortunes = [
"Your future is bright, embrace it with open arms.",
Expand Down
4 changes: 2 additions & 2 deletions source/PalmReading/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ <h1 class="title">Palm Reading</h1>
<div id="results-frame">
<h2 id="results-title">Results</h2>
<img id="palm-guide" class="align-div" src="images/palm-reading.PNG" />
<divs class="tabs-wrapper">
<div class="tabs-wrapper">
<div class="tabs">
<label class="tab" style="--color: #f7ac80">
<input type="radio" name="tab" value="head" checked />
Expand All @@ -77,7 +77,7 @@ <h2 id="results-title">Results</h2>
<span class="tab-label">Sun</span>
</label>
</div>
</divs>
</div>
<div class="content-wrapper">
<div class="tab-content" id="head-tab" style="--color: #f7ac80">
<h3>Head Line</h3>
Expand Down
8 changes: 8 additions & 0 deletions source/PalmReading/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@ export function handleFortune() {
console.log("TODO", fortune);
}

/**
* The tabs' `<input type=radio>` elements.
* @type {ArrayLike<HTMLInputElement>}
*/
const tabLabels = document.querySelectorAll('input[name="tab"]');
/**
* Each tabs' tab content.
* @type {ArrayLike<HTMLDivElement>}
*/
const tabContents = document.querySelectorAll(".tab-content");

tabLabels.forEach(function (label) {
Expand Down
Loading