diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index 4424d4fb..c334ac48 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -45,6 +45,11 @@ jobs: uses: actions/setup-node@v7 with: node-version-file: .node-version + cache: npm + cache-dependency-path: pure/package-lock.json + + - name: Install locked Pure dependencies + run: npm run pure:install - name: Validate GitHub Actions workflows env: diff --git a/CLAUDE.md b/CLAUDE.md index 5307fd4e..cb42472e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,8 +10,9 @@ Do not redesign the Pure runtime unless the task explicitly requires it. ## Architecture -- Root `*.html` files: 25 canonical `pure-v1` content sources. +- Root `*.html` files: 26 canonical `pure-v1` content sources. - `pure/src/`: shared runtime/UI implementation. +- `pure/src/presentation-runtime/stack-backgrounds.js`: vertical-stack background inheritance for canonical decks. - `pure/build/deck-source.mjs`: strict parser for canonical presentation content/configuration. - `pure/build/audit-sources.mjs`: source-purity and corpus-footprint audit. - `pure/deck.html`: shared output shell used for every deck. @@ -45,7 +46,7 @@ Sources may contain inline ` + +
+
+

Christmas LEGO Movie Sets

+

Guess the Christmas movie from the AI-generated brick set.

+

21 November 2023

+
+ +
+

How to play

+
    +
  1. Press go to generate the visual clue.
  2. +
  3. Don't type anything into the input box — the movie is hidden in the round.
  4. +
  5. Shout out the movie before the answer appears.
  6. +
  7. One point for each correct answer.
  8. +
+

There are eight rounds. Arguments about whether Die Hard counts are encouraged.

+
+ +
+

Round 1

+

Generate the set. Guess the film.

+

Leave the input blank and press go.

+ +
Create a festive toy-brick construction-set box-art style visual clue inspired by the most iconic scene from the Christmas film Home Alone. Make the scene recognisable from composition, setting and props, but do not include the movie title, logos, character names, captions, letters or numbers. Avoid exact branded character likenesses. Add playful Christmas details. Output only the image.
+
+

Answer: Home Alone

+
+ +
+

Round 2

+

Generate the set. Guess the film.

+

Leave the input blank and press go.

+ +
Create a festive toy-brick construction-set box-art style visual clue inspired by the most iconic scene from the Christmas film Elf. Make the scene recognisable from composition, setting and props, but do not include the movie title, logos, character names, captions, letters or numbers. Avoid exact branded character likenesses. Add playful Christmas details. Output only the image.
+
+

Answer: Elf

+
+ +
+

Round 3

+

Generate the set. Guess the film.

+

Leave the input blank and press go.

+ +
Create a festive toy-brick construction-set box-art style visual clue inspired by the most iconic scene from the Christmas film The Muppet Christmas Carol. Make the scene recognisable from composition, Victorian setting and props, but do not include the movie title, logos, character names, captions, letters or numbers. Avoid exact branded character likenesses. Add playful Christmas details. Output only the image.
+
+

Answer: The Muppet Christmas Carol

+
+ +
+

Round 4

+

Generate the set. Guess the film.

+

Leave the input blank and press go.

+ +
Create a festive toy-brick construction-set box-art style visual clue inspired by the most iconic scene from the Christmas film The Polar Express. Make the scene recognisable from composition, snowy railway setting and props, but do not include the movie title, logos, character names, captions, letters or numbers. Avoid exact branded character likenesses. Add playful Christmas details. Output only the image.
+
+

Answer: The Polar Express

+
+ +
+

Round 5

+

Generate the set. Guess the film.

+

Leave the input blank and press go.

+ +
Create a festive toy-brick construction-set box-art style visual clue inspired by the most iconic scene from the Christmas-set action film Die Hard. Make the scene recognisable from composition, skyscraper setting and props, but do not include the movie title, logos, character names, captions, letters or numbers. Avoid exact branded character likenesses. Add subtle Christmas details. Output only the image.
+
+

Answer: Die Hard

+
+ +
+

Round 6

+

Generate the set. Guess the film.

+

Leave the input blank and press go.

+ +
Create a festive toy-brick construction-set box-art style visual clue inspired by the most iconic scene from the Christmas film Gremlins. Make the scene recognisable from composition, small-town setting and mischievous creature chaos, but do not include the movie title, logos, character names, captions, letters or numbers. Avoid exact branded character likenesses. Add playful Christmas details. Output only the image.
+
+

Answer: Gremlins

+
+ +
+

Round 7

+

Generate the set. Guess the film.

+

Leave the input blank and press go.

+ +
Create a festive toy-brick construction-set box-art style visual clue inspired by the most iconic scene from the Christmas romantic comedy Love Actually. Make the scene recognisable from composition, London-at-Christmas setting and familiar visual motifs, but do not include the movie title, logos, character names, captions, letters or numbers. Avoid exact branded character likenesses. Output only the image.
+
+

Answer: Love Actually

+
+
+

Round 8

+

Generate the set. Guess the film.

+

Leave the input blank and press go.

+ +
Create a festive toy-brick construction-set box-art style visual clue inspired by the most iconic scene from the classic Christmas film It's a Wonderful Life. Make the scene recognisable from composition, wintry small-town setting and props, but do not include the movie title, logos, character names, captions, letters or numbers. Avoid exact branded character likenesses. Output only the image.
+
+

Answer: It's a Wonderful Life

+
-

Christmas Lego Movie Sets

- - - - - - - diff --git a/christmas-lego-movie-sets.js b/christmas-lego-movie-sets.js deleted file mode 100644 index 29628de7..00000000 --- a/christmas-lego-movie-sets.js +++ /dev/null @@ -1,192 +0,0 @@ - - -function checkForReturn(event) { - if (event.keyCode === 13) { - // Prevent the default form submit behavior - event.preventDefault(); - - // Call your function here - addPromptColumn(new PromptColumn(event.target.value)); - event.target.value = ''; - } -} - -class Run { - constructor(prompt, index) { - this.Prompt = prompt; - this.index = index; - this.status = 'running'; - const p = document.createElement('p'); - p.textContent = this.Prompt.HiddenPromptText; - this.Output = p; - this.Image = ''; - this.query(); - } - async query() { - try { - let output = await queryImage(this.Prompt); - this.status = 'complete'; - this.Image = output; - const img = document.createElement('img'); - img.src = output; - img.classList.add('clickable'); - - this.Output = img; - } catch (error) { - this.status = 'error'; - const p = document.createElement('p'); - p.textContent = error.message; - this.Output = p; - } - pagerender(); - } - render() { - let li = document.createElement('li'); - li.classList.add(this.status); - let div = document.createElement('div'); - div.appendChild(this.Output); - //let sub = document.createElement('sub'); - //sub.textContent = this.index; - li.appendChild(div); - //li.appendChild(sub); - return li; - } -} -class Prompt { - constructor(promptText) { - this.SlugPrompt = slugify(promptText); - this.InputPromptText = promptText; - this.HiddenPromptText = `As "Christmas Lego Movie Magic", your primary role is to make images for fun new designs Lego box sets based on movies, with a keen focus on movie knowledge and character likeness (but also legally conscious and not violating any copyright so you know how to translate real character names into descriptions of them). - When given a movie title, analyze the film to identify its most iconic scene and create a detailed Lego set design that captures this moment, in a christmassy style. The box art should feature the movie title, relevant imagery from the movie in the top right corner, and a Lego depiction of the key scene, ensuring character likeness is well-represented. - Include Christmas elements creatively for added appeal. While you're not creating real Lego pieces, your designs should be imaginative and demonstrate a deep understanding of the movie. Don't engage in discussions about the movies, just go straight for the image. - Bear in mind most movies have copywritten the main characters, scenes, props and details. - You must not mention their names because that would prevent the image from being generated. - Instead try to describe them in intricate detail. So don't say Luke Skywalker - instead say "a blonde haired man holding a lightsaber dressed in a black cat suit". Make it as close as legally possible to the original scene. It should include the movie title on the box. - The movie I want you to create an image for is ` + promptText; - } - render() { - return this.HiddenPromptText; - } -} -class PromptColumn { - constructor(promptText) { - this.TotalRuns = 0; - this.Runs = []; - this.Outputs = []; - this.Prompt = new Prompt(promptText); - this.query(); - } - query() { - this.Runs.push(new Run(this.Prompt, this.TotalRuns)); - this.TotalRuns++; - //this.Runs.push(fetch(this.HiddenPromptText)); - //this.Outputs.push(new Output(this.PromptText)); - } - render() { - // Create the main
  • element - const liElement = document.createElement('li'); - - // Create and append

    element - const h1Element = document.createElement('h1'); - h1Element.textContent = this.Prompt.InputPromptText; - liElement.appendChild(h1Element); - - // Create and append element - //const subElement = document.createElement('sub'); - //subElement.textContent = this.TotalRuns; - //liElement.appendChild(subElement); - - // Create and append
      element - const ulElement = document.createElement('ul'); - - // Iterate over the Runs array and append each item - this.Runs.forEach(run => { - const runElement = run.render(); // Assuming render() returns an HTMLElement - ulElement.appendChild(runElement); - }); - - liElement.appendChild(ulElement); - - return liElement; - } -} -async function fetchImage (prompt) { - /*if(prompt.SlugPrompt === "error") { - throw new Error('fail'); - } else { - const response = {output: "https://placekitten.com/1024/1024"}; - return new Promise((resolve) => { - setTimeout(() => resolve(response), 1000); - }); - }*/ - - - const baseURL = 'https://ai-prompt-writer.vercel.app/api/image'; - const response = await fetch(baseURL, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({prompt: prompt.HiddenPromptText}) - }); - return await response.json(); - -} -async function queryImage (prompt) { - const raw = await fetchImage(prompt); - const outputData = raw.output; - return outputData; -} - -function pagerender() { - let output = document.getElementById('output'); - output.innerHTML = ""; - allPromptColumns.forEach(prompt => { - output.appendChild(prompt.render()); - }); - [...document.getElementsByClassName("clickable")].forEach(img => { - img.addEventListener("click", function() { - console.log("click", this); - openLightbox(img.src); - }); - }) - - -} -function slugify(string) { - return string - .toLowerCase() - .trim() - .replace(/\s+/g, '-') - .replace(/[^a-z0-9\-']/g, ''); -} -function addPromptColumn(newPromptColumn) { - let existingPrompts = allPromptColumns.filter(prompt => prompt.Prompt.SlugPrompt === newPromptColumn.Prompt.SlugPrompt); - if (existingPrompts.length > 0) { - existingPrompts[0].query(); - } else { - allPromptColumns.push(newPromptColumn); - } - pagerender(); -} - -function openLightbox(src) { - document.getElementById('lightboximg').src = src; - document.getElementById('lightbox').style.display = 'flex'; -} -function closeLightbox() { - document.getElementById('lightbox').style.display = 'none'; -} -const allPromptColumns = []; - - -document.getElementById("prompt").addEventListener("keyup", checkForReturn); -document.getElementById("save").addEventListener("click", function() { - try { - const serializedState = JSON.stringify(allPromptColumns); - localStorage.setItem("save", serializedState); - console.log("State saved successfully."); - } catch (err) { - console.error("Error saving state to localStorage:", err); - } -}); \ No newline at end of file diff --git a/example.dot b/example.dot deleted file mode 100644 index 6ad4a901..00000000 --- a/example.dot +++ /dev/null @@ -1,42 +0,0 @@ - -digraph RegulatoryToolsArchitecture { - rankdir=LR; - node [shape=box, style=filled, color=lightgrey]; - edge [color=black, arrowhead=open]; - - MainWebsite [label="Main Website" shape=ellipse color=lightblue]; - RegulatoryTools [label="Regulatory Tools Section"]; - RiskLocatorTool [label="Risk Locator Tool"]; - CrystalFrontEnd [label="Crystal Front-End"]; - CrystalBackEnd [label="Crystal Back-End\n(Sitecore CMS)"]; - StampDecisionTool [label="Stamp Decision Tool"]; - ITSupport [label="IT Department Support" shape=ellipse color=orange]; - UserProfile [label="User Profile Customization"]; - InquiryHistory [label="Inquiry History & Reporting"]; - TerritoryManuals [label="Territory Manuals and Authorization Info"]; - WhatIsNewArchive [label="What's New Archive"]; - ComplianceGuidance [label="Regulatory & Tax Compliance Guidance"]; - HelpFAQs [label="Help Section & FAQs"]; - - MainWebsite -> RegulatoryTools; - RegulatoryTools -> RiskLocatorTool; - RegulatoryTools -> CrystalFrontEnd; - RegulatoryTools -> StampDecisionTool; - - RiskLocatorTool -> UserProfile; - RiskLocatorTool -> InquiryHistory; - InquiryHistory -> UserProfile [dir=none, constraint=false]; - UserProfile -> RiskLocatorTool [dir=none, constraint=false]; - - CrystalFrontEnd -> TerritoryManuals; - CrystalFrontEnd -> WhatIsNewArchive; - CrystalFrontEnd -> HelpFAQs [dir=back]; - CrystalFrontEnd -> ComplianceGuidance; - CrystalFrontEnd -> CrystalBackEnd [label=" Edits & Publishes", fontcolor=blue]; - - CrystalBackEnd -> ITSupport [label=" Raises Tickets for Edits & Issues", fontcolor=red]; - - StampDecisionTool -> ITSupport [label=" Changes & Maintenance", fontcolor=red]; - - ComplianceGuidance -> RiskLocatorTool [label=" Informs", fontcolor=green, dir=back]; -} diff --git a/new.dot b/new.dot deleted file mode 100644 index b5dfc9c2..00000000 --- a/new.dot +++ /dev/null @@ -1,82 +0,0 @@ -digraph InHouseRegulatoryToolsArchitecture { - rankdir=LR; - node [shape=box, style=filled, color=lightgrey]; - edge [color=black, arrowhead=open]; - - Client [label="Client\n(Web Browser)" shape=ellipse color=lightblue]; - - WebApp [label="Web Application UI"]; - APIGateway [label="API Gateway"]; - - AuthService [label="Authentication Service"]; - UserProfileService [label="User Profile Service"]; - InquiryService [label="Inquiry Service"]; - ReportService [label="Reporting Service"]; - ContentService [label="Content Management Service"]; - RuleEngine [label="Rule Engine"]; - SearchEngine [label="Search Engine\n(Elasticsearch)"]; - - Database [label="Database"]; - CMSDatabase [label="CMS Database"]; - Logger [label="Logging Service"]; - Monitor [label="Monitoring Service"]; - Deployment [label="Deployment & Orchestration\n(Docker, Kubernetes)"]; - - AuthService -> Database; - UserProfileService -> Database; - InquiryService -> Database; - ReportService -> Database; - ContentService -> CMSDatabase; - - APIGateway -> AuthService; - APIGateway -> UserProfileService; - APIGateway -> InquiryService; - APIGateway -> ReportService; - APIGateway -> ContentService; - APIGateway -> RuleEngine; - APIGateway -> SearchEngine; - - Client -> WebApp; - WebApp -> APIGateway; - - edge [color=blue, style=dotted]; - Logger -> Monitor [label="Alerts & Metrics"]; - WebApp -> Logger [label="Logs UI Activities"]; - APIGateway -> Logger [label="Logs API Requests"]; - - Deployment -> Monitor [label="Manages Services"]; - Deployment -> { AuthService UserProfileService InquiryService ReportService ContentService RuleEngine SearchEngine Logger }; - Monitor -> Deployment [label="Scaling & Health Checks"]; - - subgraph cluster_security { - label="Security & Compliance Layer"; - AuthService; - edge [style=dashed]; - AuthService -> UserProfileService [dir=both]; - AuthService -> ContentService [dir=both]; - AuthService -> InquiryService [dir=both]; - AuthService -> ReportService [dir=both]; - } - - subgraph cluster_data_persistence { - label="Data Persistence Layer"; - Database; - CMSDatabase; - } - - subgraph cluster_services { - label="Internal Services"; - UserProfileService; - InquiryService; - ReportService; - ContentService; - RuleEngine; - } - - subgraph cluster_infrastructure { - label="Infrastructure & Operations"; - Logger; - Monitor; - Deployment; - } -} \ No newline at end of file diff --git a/package.json b/package.json index 12a3e334..e39075f2 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,11 @@ "description": "Digiguru presentation source and Pure Reveal.js runtime", "license": "MIT", "scripts": { - "lint": "npm run pure:install && ./pure/node_modules/.bin/eslint scripts test/*.test.mjs pure/src pure/build pure/test pure/*.mjs", - "test": "npm run lint && npm run test:tooling && npm run pure:check", + "lint": "./pure/node_modules/.bin/eslint scripts test/*.test.mjs pure/src pure/build pure/test pure/*.mjs", + "test": "npm run pure:install && npm run lint && npm run test:tooling && npm --prefix pure run check", "test:tooling": "node --test test/*.test.mjs", "start": "npm run pure:install && npm run pure:dev", - "build": "npm run pure:preview", + "build": "npm run pure:build", "preview:build": "npm run build", "presentations:check": "node scripts/presentations.mjs --check", "presentations:accessibility": "node scripts/presentation-accessibility.mjs", diff --git a/pure/README.md b/pure/README.md index 56aeb994..47d54a56 100644 --- a/pure/README.md +++ b/pure/README.md @@ -1,44 +1,49 @@ # Pure presentation runtime -`pure/` is the clean presentation runtime built on official `reveal.js@6.0.1` and Vite. +`pure/` is the only presentation runtime in this repository. It builds the 26 canonical `pure-v1` presentation sources with official `reveal.js@6.0.1` and Vite. -The top-level presentation HTML files remain content sources during the migration. Pure reads their slides, themes, supported Reveal options and content dependencies at build time, but the generated site runs the Pure runtime rather than the repository's old Reveal/GPT implementation. +For the repository-wide architecture, source contract, validation and deployment flow, see the root [`README.md`](../README.md). -## Stage 3 corpus +## What lives here -Pure now discovers and builds the complete current presentation corpus: 25 presentations. +- `src/presentation.js` initializes Reveal and the shared presentation runtime. +- `src/presentation-runtime/` contains shared behaviours such as focus backgrounds, GPT input, dynamic slide insertion and vertical-stack background inheritance. +- `build/deck-source.mjs` parses canonical presentation sources. +- `build/audit-sources.mjs` rejects historical runtime wiring and verifies the expected corpus footprint. +- `build/verify-build.mjs` verifies the generated manifest and all 26 built decks. +- `build/smoke.mjs` opens every built deck in headless Chrome and verifies Reveal readiness, source/commit markers, GPT registration, rendered BigBus background behaviour and local asset requests. +- `deck.html` is the shared shell used for every presentation. +- `themes/AND.css` is the optional AND theme layered on top of the standard Reveal theme. +- `package.json` and `package-lock.json` are the complete runtime/build dependency graph. +- `dist/` is generated output and must not be edited or committed. -A single checked-in `deck.html` provides the page shell. `vite.config.mjs` generates the individual entry pages before Vite starts, preserving existing URLs such as `/bigbus.html` without maintaining a wrapper file for every presentation. +## Background compatibility rule -The generated index lists every presentation and the exact Git commit SHA. Each deck also includes the same SHA and source name in its generated page. +Some canonical sources intentionally put a shared `data-background-*` value on the outer section of a vertical slide stack. Reveal 6 treats each vertical child as a separate background target, so `src/presentation-runtime/stack-backgrounds.js` copies stack background settings to children that do not define their own background before Reveal initializes. -## Pure runtime +Explicit child backgrounds always win. This behaviour is covered by unit tests and by a real Chrome regression assertion against the BigBus opening background. -Presentation-specific behaviour lives under `src/presentation-runtime/`: +## Commands -- focus/background behaviour -- dynamic slide insertion -- the Pure `` component and bundled template +From the repository root: -GPT requests use the same-origin `/api/prompt/*` boundary. Pure does not require the old global Reveal object or the old GPT component files. +```bash +npm start +npm test +npm run pure:audit +npm run build +npm run pure:smoke +``` -The audit found historical inline GPT implementations in `agile-reading.html`, `lightning.html` and `nationwide.html`. Pure classifies those as superseded and does not carry them into generated presentation pages. The current corpus has zero unported custom inline scripts and zero non-standard local support scripts. +`npm start`, `npm test` and `npm run pure:check` perform a deterministic `npm ci --prefix pure` when they need a self-contained environment. CI and Vercel install the locked dependencies once, then run lint/build commands without reinstalling them. -## Current compatibility audit +For direct Pure work after dependencies are installed: -- presentations: 25 -- black theme: 25 -- custom AND theme: 3 -- focus backgrounds: 25 -- GPT input: 23 -- iframes: 19 -- canvases: 6 -- historical GPT implementations superseded: 3 -- unported custom runtime scripts: 0 -- non-standard local runtime scripts: 0 +```bash +npm --prefix pure run test +npm --prefix pure run audit:sources +npm --prefix pure run build +npm --prefix pure run smoke +``` -## Validation - -`pure:check` audits and builds the complete corpus and verifies every generated presentation in the manifest. `pure:smoke` opens all 25 built presentations in headless Chrome and verifies Reveal readiness, commit/source markers, expected GPT registration and local asset requests. - -Vercel previews publish `pure/dist` directly. Stage 3 does not yet replace the old exporter, update `digiguru.github.io`, or remove the old Reveal/Gulp/QUnit stack; those remain Stages 4–6. +There is no legacy Reveal fork, compatibility parser, Gulp/Rollup stack, QUnit/Puppeteer suite or repository-owned presentation registry to maintain. \ No newline at end of file diff --git a/pure/build/verify-build.mjs b/pure/build/verify-build.mjs index d948117e..384e7ade 100644 --- a/pure/build/verify-build.mjs +++ b/pure/build/verify-build.mjs @@ -2,16 +2,19 @@ import assert from 'node:assert/strict'; import { access, readFile } from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; +import { discoverPresentations } from '../../scripts/presentations.mjs'; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const repoRoot = path.resolve(root, '..'); const dist = path.join(root, 'dist'); const buildInfo = JSON.parse(await readFile(path.join(dist, 'build-info.json'), 'utf8')); const manifest = JSON.parse(await readFile(path.join(dist, 'presentations.json'), 'utf8')); const indexHtml = await readFile(path.join(dist, 'index.html'), 'utf8'); +const sourcePresentations = await discoverPresentations({ root: repoRoot }); assert.match(buildInfo.commitSha, /^[0-9a-f]{40}$/i); assert.equal(manifest.length, buildInfo.presentationCount); -assert.equal(manifest.length, 25); +assert.equal(manifest.length, sourcePresentations.length); assert.ok(indexHtml.includes(buildInfo.commitSha)); for (const presentation of manifest) { @@ -30,4 +33,4 @@ for (const presentation of manifest) { } } -console.log(`Verified all ${manifest.length} canonical Pure presentations at ${buildInfo.commitSha}.`); +console.log(`Verified all ${manifest.length} canonical Pure presentations at ${buildInfo.commitSha}.`); \ No newline at end of file diff --git a/pure/test/pure-corpus.test.mjs b/pure/test/pure-corpus.test.mjs index bed44f29..ede25350 100644 --- a/pure/test/pure-corpus.test.mjs +++ b/pure/test/pure-corpus.test.mjs @@ -1,16 +1,34 @@ import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; import test from 'node:test'; +import { fileURLToPath } from 'node:url'; import { auditSources, summariseSources } from '../build/audit-sources.mjs'; +const pureRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const repoRoot = path.resolve(pureRoot, '..'); + test('all presentations use the canonical Pure source format', async () => { const summary = summariseSources(await auditSources()); - assert.equal(summary.total, 25); - assert.deepEqual(summary.themes, { AND: 3, black: 25 }); + assert.equal(summary.total, 26); + assert.deepEqual(summary.themes, { AND: 3, black: 26 }); assert.deepEqual(summary.capabilities, { canvas: 6, - focusBackground: 25, - gptInput: 23, + focusBackground: 26, + gptInput: 24, iframe: 19, }); assert.deepEqual(summary.impureDecks, []); }); + +test('Christmas LEGO movie quiz remains a live eight-round image quiz', async () => { + const html = await readFile(path.join(repoRoot, 'christmas-lego-movie-sets.html'), 'utf8'); + + assert.match(html, /presentation-name" content="Christmas LEGO Movie Sets"/); + assert.match(html, /presentation-date" content="21\/11\/2023"/); + assert.match(html, /presentation-attendance" content="12"/); + assert.equal((html.match(//g) || []).length, 8); + assert.equal((html.match(/class="quiz-prompt"/g) || []).length, 8); + assert.equal((html.match(/class="fragment answer"/g) || []).length, 8); + assert.match(html, /do not include the movie title/i); +}); diff --git a/text.txt b/text.txt deleted file mode 100644 index e69de29b..00000000