kgames

KGames — free keyboard games for kids
git clone https://www.keyboard.games/code/kgames.git
Log | Files | Refs | README | LICENSE

work-lib.test.js (2854B)


      1 'use strict';
      2 const { test } = require('node:test');
      3 const assert   = require('node:assert/strict');
      4 
      5 const lib = require('../../games/work/lib.js');
      6 
      7 test('fillTemplate substitutes all blanks and leaves no braces', () => {
      8   lib.MADLIB_TEMPLATES.forEach((tpl) => {
      9     const values = {};
     10     tpl.blanks.forEach((b) => { values[b.key] = 'X' + b.key; });
     11     const out = lib.fillTemplate(tpl, values);
     12     assert.ok(!/[{}]/.test(out), `${tpl.id}: leftover braces in "${out}"`);
     13     tpl.blanks.forEach((b) => {
     14       assert.ok(out.includes('X' + b.key), `${tpl.id}: missing value for ${b.key}`);
     15     });
     16   });
     17 });
     18 
     19 test('every story placeholder has a matching blank key', () => {
     20   lib.MADLIB_TEMPLATES.forEach((tpl) => {
     21     const placeholders = new Set((tpl.story.match(/\{(\w+)\}/g) || []).map(s => s.slice(1, -1)));
     22     const keys = new Set(tpl.blanks.map(b => b.key));
     23     placeholders.forEach((p) => assert.ok(keys.has(p), `${tpl.id}: placeholder {${p}} has no blank`));
     24     keys.forEach((k) => assert.ok(placeholders.has(k), `${tpl.id}: blank ${k} unused in story`));
     25   });
     26 });
     27 
     28 test('typewriterSteps passes through common prefix then ends at target', () => {
     29   const steps = lib.typewriterSteps('cat', 'car');
     30   assert.equal(steps[steps.length - 1], 'car');
     31   assert.ok(steps.includes('ca'), 'should backspace to common prefix "ca"');
     32 });
     33 
     34 test('typewriterSteps from empty only grows', () => {
     35   assert.deepEqual(lib.typewriterSteps('', 'hi'), ['h', 'hi']);
     36 });
     37 
     38 test('normalizeName trims, collapses, lowercases', () => {
     39   assert.equal(lib.normalizeName('  Sam   Smith '), 'sam smith');
     40 });
     41 
     42 test('isNameReady needs at least 2 chars', () => {
     43   assert.equal(lib.isNameReady('a'), false);
     44   assert.equal(lib.isNameReady(' a '), false);
     45   assert.equal(lib.isNameReady('Bo'), true);
     46 });
     47 
     48 test('formatTime formats seconds to 2 decimals', () => {
     49   assert.equal(lib.formatTime(3420), '3.42 s');
     50   assert.equal(lib.formatTime(0), '0.00 s');
     51 });
     52 
     53 test('isNewRecord: null best, faster, slower', () => {
     54   assert.equal(lib.isNewRecord(1000, null), true);
     55   assert.equal(lib.isNewRecord(900, 1000), true);
     56   assert.equal(lib.isNewRecord(1000, 1000), false);
     57   assert.equal(lib.isNewRecord(1100, 1000), false);
     58 });
     59 
     60 test('pickTemplate returns a real template', () => {
     61   const t = lib.pickTemplate(() => 0);
     62   assert.equal(t, lib.MADLIB_TEMPLATES[0]);
     63 });
     64 
     65 test('every mad lib cites a public-domain source with author and link', () => {
     66   lib.MADLIB_TEMPLATES.forEach((tpl) => {
     67     assert.ok(tpl.source, `${tpl.id}: missing source`);
     68     assert.ok(tpl.source.title && tpl.source.title.length > 3, `${tpl.id}: source needs a title`);
     69     assert.ok(tpl.source.author && tpl.source.author.length > 2, `${tpl.id}: source needs an author`);
     70     assert.match(tpl.source.url, /^https:\/\/www\.gutenberg\.org\//, `${tpl.id}: source needs a public link`);
     71   });
     72 });