Initial commit: Learning Lab Express
Includes fixes found while reviewing the app: - Results: open each result by a type+agent+id key, so a video and a material draft with the same id no longer open each other's popup - AuthGate: clear the query cache during render, before pages build their queries, and remount pages on organization switch (pages no longer stay on skeletons for up to a minute after sign-in or refresh) - Instruction: keep only failed rules after a partial approve, so retrying does not create the successful schedules twice; drop the monthly note once count or frequency is edited - Parser/schedules: never schedule a count below one - Audio: ignore the end event of a cancelled utterance when changing speed - Downloads: revoke the object URL later so Safari/Firefox can read it - Results: refetch SCORM content when any part's revision changes; show a failed video as failed instead of "still rendering" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
54
src/express/results.test.ts
Normal file
54
src/express/results.test.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { correctIndex, questionsOf, toMarkdown, type Material } from './results'
|
||||
import { sentencesOf, scriptLang } from '../components/AudioPlayer'
|
||||
|
||||
describe('correctIndex', () => {
|
||||
const options = ['Listen first', 'Interrupt', 'Leave']
|
||||
|
||||
it('finds the answer as option text, letter or index', () => {
|
||||
expect(correctIndex(options, 'listen first')).toBe(0)
|
||||
expect(correctIndex(options, 'B')).toBe(1)
|
||||
expect(correctIndex(options, 'C. Leave')).toBe(2)
|
||||
expect(correctIndex(options, '1')).toBe(1)
|
||||
})
|
||||
|
||||
it('returns -1 for an answer outside the options', () => {
|
||||
expect(correctIndex(options, 'D')).toBe(-1)
|
||||
expect(correctIndex([], 'An essay answer')).toBe(-1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('questionsOf', () => {
|
||||
it('reads the engine’s question shapes', () => {
|
||||
const [q] = questionsOf({ questions: [{ text: 'Q', choices: [{ text: 'x' }, { label: 'y' }], correct_answer: 'y', explanation: 'because' }] })
|
||||
expect(q).toEqual({ question: 'Q', options: ['x', 'y'], answer: 'y', explanation: 'because', correct: 1 })
|
||||
})
|
||||
})
|
||||
|
||||
describe('toMarkdown', () => {
|
||||
const material = (kind: Material['kind']): Material => ({
|
||||
id: 1, agentId: 1, kind, status: 'ready', revision: 1, parts: [], planTitle: 'Onboarding', composeSetId: null,
|
||||
sources: ['Handbook'], createdAt: '2026-10-01T00:00:00Z', rule: { type: 'content', kind, count: 1, per: 'week', topic: '' },
|
||||
})
|
||||
|
||||
it('writes a SCORM module as the lesson followed by its quiz', () => {
|
||||
const md = toMarkdown(material('scorm'), { title: 'Onboarding', text: 'Welcome.', questions: [{ question: 'Q?', options: ['a', 'b'], answer: 'b' }] })
|
||||
expect(md).toMatch(/^# Onboarding\n\nWelcome\.\n\n## Quiz\n\n1\. Q\?\n {3}- a\n {3}- b\n/)
|
||||
expect(md).toContain('Sources: Handbook')
|
||||
})
|
||||
|
||||
it('writes an audio lesson as its script', () => {
|
||||
expect(toMarkdown(material('audio'), { text: 'Hello team.' })).toBe('# Onboarding\n\nHello team.\n\n---\nSources: Handbook\n')
|
||||
})
|
||||
})
|
||||
|
||||
describe('audio script helpers', () => {
|
||||
it('splits a script into sentences without Markdown', () => {
|
||||
expect(sentencesOf('# Intro\nHello **team**. Ready? Let’s go!\n\n- one')).toEqual(['Intro', 'Hello team.', 'Ready?', 'Let’s go!', 'one'])
|
||||
})
|
||||
|
||||
it('guesses the script language for the voice', () => {
|
||||
expect(scriptLang('Ini adalah pelajaran yang penting untuk kita semua dan tim.')).toBe('id')
|
||||
expect(scriptLang('This is the lesson that we will share with the team.')).toBe('en')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user