kgames

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

commit 001e26b7497c2ad35770e162656bdca46bb96ef5
parent a0399f95f80379b6954e7715b12051325e3b30b1
Author: Kyle Barlow <kb@kylebarlow.com>
Date:   Mon, 17 Aug 2026 20:07:20 -0700

Stories changed to human made public domain stories

Diffstat:
Mgames/work/index.html | 41+++++++++++++++++++++++++++++++++++++++++
Mgames/work/lib.js | 442+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mtests/browser/runner.js | 10++++++++++
Mtests/unit/work-lib.test.js | 9+++++++++
4 files changed, 462 insertions(+), 40 deletions(-)

diff --git a/games/work/index.html b/games/work/index.html @@ -42,6 +42,19 @@ user-select: none; } #audio-controls button:hover { background: rgba(0,0,0,0.1); color: #112; } + #story-credits { + flex-shrink: 0; + padding: 0.4rem 1.25rem 0.6rem; + font-size: 0.72rem; + line-height: 1.35; + color: #8a8a97; + text-align: center; + max-height: 4.6rem; + overflow-y: auto; + } + #story-credits a { color: #667; text-decoration: underline; } + #story-credits a:hover { color: #1D7CF2; } + #story-credits strong { font-weight: 600; color: #778; } #audio-controls button.muted { color: #aab; border-color: rgba(0,0,0,0.07); } </style> </head> @@ -51,12 +64,40 @@ <button id="btn-sfx">Sound: ON</button> </div> <div id="game-container"></div> + <footer id="story-credits"> + <strong>For grown-ups:</strong> every fill-in-the-blank tale in Stories mode is adapted from a + public-domain children&rsquo;s classic. Tap a title to read the original on + <a href="https://www.gutenberg.org/" target="_blank" rel="noopener">Project Gutenberg</a>. + <span id="story-credits-list"></span> + </footer> <script src="https://cdn.jsdelivr.net/npm/phaser@3.80.1/dist/phaser.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/tone@14.7.77/build/Tone.js"></script> <script src="../../js/shared/game-shared.js"></script> <script src="lib.js"></script> <script src="game.js"></script> <script src="../../js/shared/touch-controls.js"></script> + <script> + // Build the parent credits list straight from the story data so the two + // can never drift apart. + (function () { + const el = document.getElementById('story-credits-list'); + if (!el || !window.WorkLib) return; + const seen = new Set(); + const parts = []; + WorkLib.MADLIB_TEMPLATES.forEach(function (t) { + const src = t.source; + if (!src || seen.has(src.title)) return; + seen.add(src.title); + const a = document.createElement('a'); + a.href = src.url; + a.target = '_blank'; + a.rel = 'noopener'; + a.textContent = src.title; + parts.push(a.outerHTML + ' by ' + src.author); + }); + el.innerHTML = '<br>' + parts.join(' &middot; ') + '.'; + }()); + </script> <script>KGames.initTouchControls({ layout: 'text' });</script> </body> </html> diff --git a/games/work/lib.js b/games/work/lib.js @@ -2,64 +2,426 @@ 'use strict'; // ── Mad Libs templates ────────────────────────────────────────────────────── - // Kid-friendly category labels only (never "noun/verb/adjective"). + // Every story is adapted from a public-domain children’s classic (see the + // `source` field — surfaced to parents in the page footer). Kid-friendly + // category labels only (never "noun/verb/adjective"). // `story` uses {key} placeholders matching each blank's `key`. const MADLIB_TEMPLATES = [ { - id: 'zoo', - title: 'A Day at the Zoo', + id: 'peter-rabbit', + title: 'The Tale of Peter Rabbit', + source: { + title: 'The Tale of Peter Rabbit', + author: 'Beatrix Potter', + url: 'https://www.gutenberg.org/ebooks/14838', + }, blanks: [ - { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, - { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, - { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, - { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, - { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, + { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, + { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, ], - story: 'One day, {name} went to the zoo and saw a {color} {animal}. ' + - 'It was eating {food}! When it saw {name}, the {animal} said "{silly}!" ' + - 'and did a happy dance. It was the best day ever.', + story: 'Once upon a time there were four little rabbits, and the naughtiest one was named {name}. ' + + 'Mother said, "Do not go into Mr. McGregor’s garden!" But {name} squeezed under the gate ' + + 'and ate some {food} until his tummy felt funny. Then Mr. McGregor came running, waving a rake! ' + + '{name} lost one {color} shoe among the cabbages and hid in a watering can with a little {animal}. ' + + 'He sneezed "{silly}!" and ran all the way home to bed.', }, { - id: 'space', - title: 'Blast Off!', + id: 'three-pigs', + title: 'The Three Little Pigs', + source: { + title: 'The Story of the Three Little Pigs (English Fairy Tales)', + author: 'Joseph Jacobs', + url: 'https://www.gutenberg.org/ebooks/7439', + }, blanks: [ - { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, - { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, - { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, - { key: 'number', label: 'Tell me a number!', voice: 'Tell me a number!' }, - { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'stuff', label: 'Tell me something you can build with!', voice: 'Tell me something you can build with!' }, + { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, ], - story: 'Captain {name} flew a rocket all the way to {place}. ' + - 'On board was a brave {animal} and {number} jellybeans. ' + - 'When they landed, everyone shouted "{silly}!" and bounced around in space.', + story: 'There were three little pigs, and the cleverest one was named {name}. ' + + 'The first built a house of straw, the second a house of sticks, but {name} built a strong house of {stuff}. ' + + 'Along came the wolf and called, "Little pig, little pig, let me come in!" ' + + '"Not by the hair of my chinny chin chin!" said {name}. ' + + 'So the wolf huffed, and puffed, and shouted "{silly}!" — but the house of {stuff} would not fall down. ' + + 'The wolf ran away, and {name} had {food} for supper.', }, { - id: 'party', - title: 'The Silly Party', + id: 'three-bears', + title: 'The Story of the Three Bears', + source: { + title: 'The Story of the Three Bears (English Fairy Tales)', + author: 'Joseph Jacobs', + url: 'https://www.gutenberg.org/ebooks/7439', + }, blanks: [ - { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, - { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, - { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, - { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, - { key: 'action', label: 'Tell me something you can do!', voice: 'Tell me something you can do!' }, + { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, + { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, + { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, + { key: 'action', label: 'Tell me something you can do!', voice: 'Tell me something you can do!' }, ], - story: '{name} had a party with a giant {color} cake made of {food}. ' + - 'A friendly {animal} came too, and everybody started to {action}! ' + - 'It was the silliest party in the whole world.', + story: 'Three bears lived in a little house in the wood, and each one had a bowl of {food}. ' + + 'While it cooled, a girl named {name} walked right in. ' + + 'She tasted the great big bowl — too hot! The middle bowl — too cold! ' + + 'The teeny tiny bowl was just right, so she ate it all up. ' + + 'Then she sat in the teeny tiny chair until it went CRASH, and fell fast asleep upstairs. ' + + 'When the bears came home the teeny tiny bear squeaked, "Somebody has been eating my {food}!" ' + + '{name} woke up, saw a {animal} peeking through the window, and began to {action} all the way home.', }, { - id: 'breakfast', - title: 'Funny Breakfast', + id: 'lion-mouse', + title: 'The Lion and the Mouse', + source: { + title: 'The Lion and the Mouse (The Fables of Æsop)', + author: 'Æsop, told anew by Joseph Jacobs', + url: 'https://www.gutenberg.org/ebooks/28', + }, blanks: [ - { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, - { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, - { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, - { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, ], - story: 'This morning {name} ate {food} for breakfast. ' + - 'Then a sleepy {animal} hopped onto the table and yelled "{silly}!" ' + - 'It wanted breakfast too, so {name} shared. Yum yum!', + story: 'A lion was fast asleep in {place} when a little mouse named {name} ran right across his nose. ' + + 'The lion woke with a roar and caught the mouse in his big paw. ' + + '"Please let me go," said {name}, "and one day I will help you!" ' + + 'The lion laughed "{silly}!" and let the mouse go. ' + + 'Not long after, hunters tied the lion up with ropes — and tiny {name} nibbled every rope through. ' + + 'Even a little friend can be a big help.', + }, + { + id: 'hare-tortoise', + title: 'The Hare and the Tortoise', + source: { + title: 'The Hare and the Tortoise (The Fables of Æsop)', + author: 'Æsop, told anew by Joseph Jacobs', + url: 'https://www.gutenberg.org/ebooks/28', + }, + blanks: [ + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, + { key: 'number', label: 'Tell me a number!', voice: 'Tell me a number!' }, + { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, + ], + story: 'A hare was bragging how fast he could run, so a slow tortoise named {name} said, ' + + '"I will race you all the way to {place}!" The hare laughed and zoomed ahead. ' + + 'He was so far in front that he stopped for a nap and {number} bites of {food}. ' + + 'Plod, plod, plod went {name}, never stopping once. ' + + 'When the hare woke up and ran his fastest, {name} was already there. Slow and steady wins the race!', + }, + { + id: 'town-country-mouse', + title: 'The Town Mouse and the Country Mouse', + source: { + title: 'The Town Mouse and the Country Mouse (The Fables of Æsop)', + author: 'Æsop, told anew by Joseph Jacobs', + url: 'https://www.gutenberg.org/ebooks/28', + }, + blanks: [ + { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, + { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, + { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + ], + story: 'A town mouse named {name} came to visit a country mouse, who gave him beans and crusts for dinner. ' + + '"Come to my house in town," said {name}, "and you shall have {food} instead!" ' + + 'So off they went, and the table in town was piled high. ' + + 'But just as they began to nibble, a big {animal} came in and they had to squeak "{silly}!" and hide in a hole. ' + + 'The country mouse said, "I would rather have beans in peace than {food} in fright," and went home.', + }, + { + id: 'ugly-duckling', + title: 'The Ugly Duckling', + source: { + title: 'The Ugly Duckling (Andersen’s Fairy Tales)', + author: 'Hans Christian Andersen', + url: 'https://www.gutenberg.org/ebooks/1597', + }, + blanks: [ + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, + { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + ], + story: 'Down by the water, a mother duck’s eggs went crack, crack, crack. ' + + 'Out came the ducklings — and the very last one was big and {color}, so everyone called him {name}. ' + + '"You look funny!" said the other birds, and poor {name} waddled away all by himself. ' + + 'He was cold all winter, with only a little {food} to eat. ' + + 'But in the spring he looked into the water and said "{silly}!" — ' + + 'for he was not a duckling at all. He had grown into a beautiful swan.', + }, + { + id: 'emperors-clothes', + title: 'The Emperor’s New Clothes', + source: { + title: 'The Emperor’s New Clothes (Andersen’s Fairy Tales)', + author: 'Hans Christian Andersen', + url: 'https://www.gutenberg.org/ebooks/1597', + }, + blanks: [ + { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, + { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, + { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + ], + story: 'There was an emperor who loved new clothes more than anything. ' + + 'Two tricky tailors said they could sew him a {color} suit so magic that only clever people could see it. ' + + 'They sewed with nothing at all! But nobody wanted to look silly, so everyone said, "How fine!" ' + + 'The emperor marched down the street in his underwear, with a {animal} following behind. ' + + 'Then a little kid named {name} shouted, "{silly}! He has no clothes on!" ' + + 'And everybody laughed, because it was true.', + }, + { + id: 'elves-shoemaker', + title: 'The Elves and the Shoemaker', + source: { + title: 'The Elves and the Shoemaker (Grimms’ Fairy Tales)', + author: 'Jacob and Wilhelm Grimm', + url: 'https://www.gutenberg.org/ebooks/2591', + }, + blanks: [ + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, + { key: 'number', label: 'Tell me a number!', voice: 'Tell me a number!' }, + { key: 'action', label: 'Tell me something you can do!', voice: 'Tell me something you can do!' }, + ], + story: 'A shoemaker named {name} had only enough leather left for one pair of shoes, ' + + 'so {name} cut it out and went to bed. ' + + 'In the morning — surprise! — the shoes were already finished, and they were {color} and perfect. ' + + 'This happened again and again, until {name} peeked and saw {number} tiny elves ' + + 'hop onto the table and {action} all night long. ' + + 'So {name} sewed them tiny coats to say thank you, and the elves danced right out the door.', + }, + { + id: 'bremen-musicians', + title: 'The Bremen Town Musicians', + source: { + title: 'The Bremen Town Musicians (Grimms’ Fairy Tales)', + author: 'Jacob and Wilhelm Grimm', + url: 'https://www.gutenberg.org/ebooks/2591', + }, + blanks: [ + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, + { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + ], + story: 'An old donkey named {name} set off to be a musician in {place}. ' + + 'Along the way a dog, a cat, a rooster, and even a {animal} came too. ' + + 'When night fell they found a little house full of robbers, so they stood one on top of the other ' + + 'and sang their song: hee-haw, woof, meow, cock-a-doodle-doo, "{silly}!" ' + + 'The robbers ran away as fast as they could, and the four friends slept there happily ever after.', + }, + { + id: 'frog-prince', + title: 'The Frog Prince', + source: { + title: 'The Frog Prince (Grimms’ Fairy Tales)', + author: 'Jacob and Wilhelm Grimm', + url: 'https://www.gutenberg.org/ebooks/2591', + }, + blanks: [ + { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, + { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, + { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + ], + story: 'A princess named {name} was playing with a {color} golden ball when — plop! — it fell into the well. ' + + '"I will fetch it," said a frog, "if you let me eat {food} from your little plate." ' + + '{name} said yes, and the frog dived down and brought the ball back up. ' + + 'That night the frog knocked at the palace door: tap, tap, "{silly}!" ' + + 'A promise is a promise, so {name} let him in — and in the morning the frog was a prince.', + }, + { + id: 'jack-beanstalk', + title: 'Jack and the Beanstalk', + source: { + title: 'Jack and the Beanstalk (English Fairy Tales)', + author: 'Joseph Jacobs', + url: 'https://www.gutenberg.org/ebooks/7439', + }, + blanks: [ + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'number', label: 'Tell me a number!', voice: 'Tell me a number!' }, + { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, + { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, + ], + story: 'A boy named {name} traded the family cow for {number} magic beans. ' + + 'His mother tossed them out the window — and by morning a beanstalk had grown up past the clouds! ' + + '{name} climbed and climbed, all the way to a giant’s castle in the sky. ' + + 'The giant roared, "Fee-fi-fo-fum!" but he was busy eating {food}, ' + + 'so {name} grabbed the hen that laid golden eggs and a {animal} that could sing. ' + + 'Down the beanstalk he scrambled, chop-chop, and he and his mother were never hungry again.', + }, + { + id: 'red-riding-hood', + title: 'Little Red Riding Hood', + source: { + title: 'Little Red Riding Hood (English Fairy Tales)', + author: 'Flora Annie Steel', + url: 'https://www.gutenberg.org/ebooks/17034', + }, + blanks: [ + { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, + { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, + { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + ], + story: 'A little girl named {name} had a {color} hooded cape, so everyone called her Little {color} Riding Hood. ' + + 'One morning she carried a basket of {food} through the wood to her grandmother’s house. ' + + 'A sly wolf hurried on ahead and hopped into Grandmother’s bed. ' + + '"What big eyes you have!" said {name}. "What big ears you have!" ' + + 'Then the woodcutter came in shouting "{silly}!" and the wolf ran off into the trees. ' + + 'Grandmother came out of the closet, and they all shared the {food}.', + }, + { + id: 'elephants-child', + title: 'The Elephant’s Child', + source: { + title: 'The Elephant’s Child (Just So Stories)', + author: 'Rudyard Kipling', + url: 'https://www.gutenberg.org/ebooks/2781', + }, + blanks: [ + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, + { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + ], + story: 'In the long ago, elephants had no trunks at all — only a little squashy nose. ' + + 'But one baby elephant named {name} was full of questions, like ' + + '"What does the crocodile have for dinner?" ' + + 'So {name} went all the way to {place} to find out, and the crocodile grabbed that little nose and PULLED. ' + + '{name} pulled back, and pulled, and hollered "{silly}!" — ' + + 'and when the crocodile let go, the nose had stretched into a long, useful trunk. ' + + 'Now {name} could pick {food} right off the tallest tree.', + }, + { + id: 'camel-hump', + title: 'How the Camel Got His Hump', + source: { + title: 'How the Camel Got His Hump (Just So Stories)', + author: 'Rudyard Kipling', + url: 'https://www.gutenberg.org/ebooks/2781', + }, + blanks: [ + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'action', label: 'Tell me something you can do!', voice: 'Tell me something you can do!' }, + { key: 'number', label: 'Tell me a number!', voice: 'Tell me a number!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + ], + story: 'At the beginning of the world, a lazy camel named {name} lived in the middle of the desert. ' + + 'The horse said, "Come and {action} with us!" The dog said it too, and the ox said it too. ' + + 'But {name} only answered, "{silly}!" and would not work at all. ' + + 'So the Djinn of the desert came along and gave {name} a great big humph — I mean, hump — ' + + 'right on his back. Now {name} can work {number} whole days without stopping for dinner, ' + + 'and camels have had humps ever since.', + }, + { + id: 'wizard-of-oz', + title: 'The Wonderful Wizard of Oz', + source: { + title: 'The Wonderful Wizard of Oz', + author: 'L. Frank Baum', + url: 'https://www.gutenberg.org/ebooks/55', + }, + blanks: [ + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, + { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, + { key: 'action', label: 'Tell me something you can do!', voice: 'Tell me something you can do!' }, + ], + story: 'A cyclone picked up a little house with {name} and a small dog inside, and set it down in the land of Oz. ' + + '"We are not in Kansas any more!" said {name}. ' + + 'To get home, {name} followed the road of yellow brick, ' + + 'and along the way met a scarecrow, a tin woodman, a lion, and a friendly {animal}. ' + + 'In the {color} City the great wizard turned out to be an ordinary man behind a curtain, ' + + 'but he could still {action}, and the silver shoes carried {name} all the way home.', + }, + { + id: 'alice-rabbit-hole', + title: 'Alice Down the Rabbit Hole', + source: { + title: 'Alice’s Adventures in Wonderland', + author: 'Lewis Carroll', + url: 'https://www.gutenberg.org/ebooks/11', + }, + blanks: [ + { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, + { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, + { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + ], + story: 'A girl named {name} saw a white rabbit run past, saying, "Oh dear! I shall be late!" ' + + 'Down the rabbit hole she went, tumbling and tumbling. ' + + 'At the bottom she found a little bottle marked DRINK ME and grew very small, ' + + 'then a bit of {food} marked EAT ME and grew very TALL. ' + + 'A grinning {animal} showed her the way to a tea party where everyone shouted "{silly}!" ' + + 'and passed the cups around. What a curious dream {name} had that day.', + }, + { + id: 'velveteen-rabbit', + title: 'The Velveteen Rabbit', + source: { + title: 'The Velveteen Rabbit', + author: 'Margery Williams Bianco', + url: 'https://www.gutenberg.org/ebooks/11757', + }, + blanks: [ + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, + { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, + { key: 'action', label: 'Tell me something you can do!', voice: 'Tell me something you can do!' }, + ], + story: 'On Christmas morning a boy named {name} found a {color} velveteen rabbit in his stocking. ' + + 'The old rocking horse said, "When a child loves you for a long, long time, you become Real." ' + + '{name} took the rabbit everywhere — even to {place} — and hugged him until his fur was rubbed away. ' + + '"Am I Real yet?" asked the rabbit. ' + + 'Then one spring day a fairy kissed him, and the rabbit found he could hop and {action} ' + + 'with all the wild rabbits. He was Real at last.', + }, + { + id: 'tom-kitten', + title: 'The Tale of Tom Kitten', + source: { + title: 'The Tale of Tom Kitten', + author: 'Beatrix Potter', + url: 'https://www.gutenberg.org/ebooks/14837', + }, + blanks: [ + { key: 'name', label: 'Tell me a friend’s name!', voice: 'Tell me a friend’s name!' }, + { key: 'color', label: 'Tell me a color!', voice: 'Tell me a color!' }, + { key: 'food', label: 'Tell me a yummy food!', voice: 'Tell me a yummy food!' }, + { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, + ], + story: 'Mrs. Tabitha Twitchit had three kittens, and the naughtiest was named {name}. ' + + 'Company was coming for {food}, so she scrubbed their faces and dressed them in {color} clothes. ' + + '"Now keep clean!" she said, and put them out in the garden. ' + + 'Of course {name} climbed the wall straightaway, and all the buttons popped off — pop, pop, pop. ' + + 'A passing {animal} tried the clothes on and found them a very good fit. ' + + 'And that is why the kittens had to stay upstairs when the company came.', + }, + { + id: 'mother-goose', + title: 'Hey Diddle Diddle', + source: { + title: 'Hey Diddle Diddle (The Real Mother Goose)', + author: 'compiled by Blanche Fisher Wright', + url: 'https://www.gutenberg.org/ebooks/10607', + }, + blanks: [ + { key: 'animal', label: 'Tell me an animal!', voice: 'Tell me an animal!' }, + { key: 'place', label: 'Tell me a place!', voice: 'Tell me a place!' }, + { key: 'name', label: 'Tell me your name!', voice: 'Tell me your name!' }, + { key: 'silly', label: 'Tell me a silly word!', voice: 'Tell me a silly word!' }, + ], + story: 'Hey diddle diddle, the {animal} and the fiddle, ' + + 'the cow jumped over {place}! ' + + 'The little dog laughed to see such fun, and shouted "{silly}!" ' + + 'And the dish ran away with the spoon — and with {name}, too.', }, ]; diff --git a/tests/browser/runner.js b/tests/browser/runner.js @@ -186,6 +186,16 @@ async function main() { if (!canvas) throw new Error('No canvas element'); }); + await test('work: story credits link to public-domain sources', async (page, url) => { + await page.goto(`${url}/games/work/`, { waitUntil: 'domcontentloaded' }); + await page.waitForTimeout(500); + const hrefs = await page.$$eval('#story-credits a', (as) => as.map(a => a.getAttribute('href'))); + if (!hrefs.length) throw new Error('No source links in #story-credits'); + if (!hrefs.every(h => /^https:\/\/www\.gutenberg\.org\//.test(h))) { + throw new Error('Credit links must point at Project Gutenberg: ' + hrefs.join(', ')); + } + }); + await test('work: canvas renders multiple colors', async (page, url) => { await page.goto(`${url}/games/work/`, { waitUntil: 'domcontentloaded' }); await page.waitForTimeout(1500); diff --git a/tests/unit/work-lib.test.js b/tests/unit/work-lib.test.js @@ -61,3 +61,12 @@ test('pickTemplate returns a real template', () => { const t = lib.pickTemplate(() => 0); assert.equal(t, lib.MADLIB_TEMPLATES[0]); }); + +test('every mad lib cites a public-domain source with author and link', () => { + lib.MADLIB_TEMPLATES.forEach((tpl) => { + assert.ok(tpl.source, `${tpl.id}: missing source`); + assert.ok(tpl.source.title && tpl.source.title.length > 3, `${tpl.id}: source needs a title`); + assert.ok(tpl.source.author && tpl.source.author.length > 2, `${tpl.id}: source needs an author`); + assert.match(tpl.source.url, /^https:\/\/www\.gutenberg\.org\//, `${tpl.id}: source needs a public link`); + }); +});