Files
functional-programming-snake/js/test/m01-util.test.js
T
Lucas WintherandClaude Fable 5.2 edd57f0148 feat(course): audit fixes and twelve extensions (Modules 7b, 12, 13)
Audit pass:
- Frame rule counts frames since the last move, so every step lasts
  exactly movesEvery(score) frames, also right after a speed-up
- clampTime + MAX_GAP_MS: a hidden tab no longer replays the whole gap
- Pong Restart keeps held keys
- Fact corrections (mulberry32 output coverage, Haskell IO, uniform)
- New fixture cases from a mutation analysis

Extensions (all three tracks, shared fixtures, course text):
- Turn queue (Exercise 10.6), persisted high score (10.7),
  replay viewer (10.8), play() end-to-end helper (9.5)
- Optional State (4.7) and optional Module 7b, stack-safe IO
- Module 12, property-based testing with generators, shrinking,
  invariants and a model-based oracle
- Module 13, Conway's Life with a Click event
- JS type checking via jsconfig, JSDoc and types.d.ts
- Maintainer tools: scripts/mutate.mjs, scripts/typecheck-js.sh,
  Playwright end-to-end suite (e2e/, scripts/e2e.sh)

Docs: docs/contract.md is the canonical contract; specs and build log
under docs/superpowers.

Verified: JS 728, Scala 689, F# 691 unit tests; stubs and progression
M01-M13 and M07b; 176 end-to-end tests on Chromium and WebKit.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-11 12:05:29 +01:00

65 lines
2.7 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { range, dropLast, contains, pipe, compose, deepFreeze } from '../src/util.js'
import { freeze } from './helpers.js'
test('m01 range(0) is empty', () => assert.deepEqual(range(0), []))
test('m01 range(3) counts from zero up to n-1', () => assert.deepEqual(range(3), [0, 1, 2]))
test('m01 dropLast of an empty list is empty', () => assert.deepEqual(dropLast([]), []))
test('m01 dropLast removes only the last element', () => assert.deepEqual(dropLast([1, 2, 3]), [1, 2]))
test('m01 dropLast leaves its input untouched', () => {
const xs = freeze([1, 2, 3])
assert.deepEqual(dropLast(xs), [1, 2])
assert.deepEqual(xs, [1, 2, 3])
})
const sameParity = (a, b) => a % 2 === b % 2
test('m01 contains finds an element using the supplied equality', () =>
assert.equal(contains([1, 2, 3], 5, sameParity), true))
test('m01 contains is false when no element is equal', () =>
assert.equal(contains([2, 4], 3, sameParity), false))
test('m01 contains is false for an empty list', () =>
assert.equal(contains([], 1, (a, b) => a === b), false))
test('m01 contains compares structurally when given a structural equality', () => {
const eq = (a, b) => a.x === b.x && a.y === b.y
assert.equal(contains(freeze([{ x: 1, y: 2 }]), { x: 1, y: 2 }, eq), true)
})
test('m01 pipe threads a value through functions left to right', () =>
assert.equal(pipe(2, (x) => x + 1, (x) => x * 10), 30))
test('m01 pipe with no functions returns the value', () => assert.equal(pipe(5), 5))
test('m01 compose applies functions right to left', () =>
assert.equal(compose((x) => x * 10, (x) => x + 1)(2), 30))
test('m01 compose with no functions is the identity', () => assert.equal(compose()(5), 5))
test('m01 deepFreeze freezes nested objects and arrays', () => {
const o = deepFreeze({ a: { b: [1, { c: 2 }] } })
assert.ok(Object.isFrozen(o))
assert.ok(Object.isFrozen(o.a))
assert.ok(Object.isFrozen(o.a.b))
assert.ok(Object.isFrozen(o.a.b[1]))
})
test('m01 deepFreeze returns the same object it was given', () => {
const o = {}
assert.equal(deepFreeze(o), o)
})
test('m01 deepFreeze reaches unfrozen children of an already-frozen parent', () => {
const o = deepFreeze({ a: Object.freeze({ b: { c: 1 } }) })
assert.ok(Object.isFrozen(o.a.b))
})
test('m01 deepFreeze leaves null and other non-objects alone', () => {
const o = deepFreeze({ apple: null, score: 3, name: 'x', missing: undefined })
assert.ok(Object.isFrozen(o))
assert.equal(deepFreeze(null), null)
assert.equal(deepFreeze(7), 7)
})
test('m01 deepFreeze handles cyclic objects', () => {
const o = { child: {} }
o.child.parent = o
deepFreeze(o)
assert.ok(Object.isFrozen(o) && Object.isFrozen(o.child))
})