From e4a88469783de3376b2531ecd735982e466c0725 Mon Sep 17 00:00:00 2001 From: Oliver Eyton-Williams Date: Fri, 20 Sep 2024 10:33:05 +0200 Subject: [PATCH] feat: add RandomMocker class to mock Math.random --- lib/__tests__/curriculum-helper.test.ts | 45 +++++++++++++++++++++++++ lib/index.ts | 31 +++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/lib/__tests__/curriculum-helper.test.ts b/lib/__tests__/curriculum-helper.test.ts index 34b08d9..fec7014 100644 --- a/lib/__tests__/curriculum-helper.test.ts +++ b/lib/__tests__/curriculum-helper.test.ts @@ -22,6 +22,51 @@ const { jsCodeWithCommentedCall, } = jsTestValues; +describe("RandomMocker", () => { + let random: () => number; + + beforeEach(() => { + random = Math.random; + }); + + afterEach(() => { + Math.random = random; + }); + + describe("mock", () => { + it('should replace "Math.random" with a mock function', () => { + const mocker = new helper.RandomMocker(); + mocker.mock(); + expect(Math.random).not.toBe(random); + }); + + it('should mock "Math.random" with a pseudorandom function', () => { + const mocker = new helper.RandomMocker(); + mocker.mock(); + // Predictable random values: + expect(Math.random()).toBe(0.2523451747838408); + expect(Math.random()).toBe(0.08812504541128874); + }); + + it("should reset the pseudorandom function when called multiple times", () => { + const mocker = new helper.RandomMocker(); + mocker.mock(); + expect(Math.random()).toBe(0.2523451747838408); + mocker.mock(); + expect(Math.random()).toBe(0.2523451747838408); + }); + }); + + describe("restore", () => { + it('should restore "Math.random" to its original function', () => { + const mocker = new helper.RandomMocker(); + mocker.mock(); + mocker.restore(); + expect(Math.random).toBe(random); + }); + }); +}); + describe("removeWhiteSpace", () => { const { removeWhiteSpace } = helper; it("returns a string", () => { diff --git a/lib/index.ts b/lib/index.ts index 4cfd8d9..e1a47ff 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,6 +1,37 @@ import { strip } from "./strip"; import astHelpers from "../python/py_helpers.py"; +/** + * The `RandomMocker` class provides functionality to mock and restore the global `Math.random` function. + * It replaces the default random number generator with a deterministic pseudo-random number generator. + */ +export class RandomMocker { + private random: () => number; + + constructor() { + this.random = Math.random; + } + + private createRandom() { + let seed = 42; + const a = 1664525; + const c = 1013904223; + const mod = 2 ** 32; + return () => { + seed = (a * seed + c) % mod; + return seed / mod; + }; + } + + mock(): void { + globalThis.Math.random = this.createRandom(); + } + + restore(): void { + globalThis.Math.random = this.random; + } +} + /** * Removes every HTML-comment from the string that is provided * @param {String} str a HTML-string where the comments need to be removed of