import Foundation import Testing @testable import Workouts /// Locks the `info.md` parsing contract: the bundled reference pages must parse into /// the summary / metadata / sections shape the library detail screen renders, and /// every shipped motion rig must come with one (the authoring convention). struct ExerciseInfoTests { @Test func bundledChestPressInfoParses() throws { let info = try #require(ExerciseInfoLibrary.info(for: "Chest Press")) #expect(info.summary.contains("machine bench press")) #expect(info.category == "Main circuit") #expect(info.type == "Machine-based horizontal press") #expect(info.isMachineBased) #expect(info.targets == ["Pectorals", "anterior deltoids", "triceps"]) #expect(info.sections.map(\.title) == ["Setup", "Execution", "Cues", "Common Mistakes", "Progression"]) let execution = try #require(info.sections.first { $0.title == "Execution" }) #expect(execution.items.count == 3) guard case .step(let firstStep) = execution.items[0] else { Issue.record("expected Execution to start with a numbered step") return } // Hard-wrapped source lines must rejoin into one step. #expect(firstStep.contains("stop short of locking the elbows")) let cues = try #require(info.sections.first { $0.title == "Cues" }) guard case .bullet = cues.items[0] else { Issue.record("expected Cues to be bullets") return } } /// The spoken script (fed to `AVSpeechSynthesizer`) leads with the exercise name and /// its summary, and `brief` drops the reference-only sections so a hands-free workout /// cue reads Setup/Execution/Cues but not Common Mistakes/Progression. @Test func spokenScriptBriefDropsReferenceSections() throws { let info = try #require(ExerciseInfoLibrary.info(for: "Chest Press")) let full = info.spokenScript(name: "Chest Press", brief: false) #expect(full.hasPrefix("Chest Press.")) #expect(full.contains("Setup.")) #expect(full.contains("Execution.")) #expect(full.contains("Cues.")) #expect(full.contains("Common Mistakes.")) #expect(full.contains("Progression.")) let brief = info.spokenScript(name: "Chest Press", brief: true) #expect(brief.hasPrefix("Chest Press.")) #expect(brief.contains("Setup.")) #expect(brief.contains("Execution.")) #expect(brief.contains("Cues.")) #expect(!brief.contains("Common Mistakes.")) #expect(!brief.contains("Progression.")) } /// Every bundled motion rig ships a parseable info page — the exporter copies /// `info.md` per entry, and the detail screen counts on the shape. @Test func everyBundledExerciseHasParseableInfo() throws { #expect(!ExerciseMotionLibrary.exerciseNames.isEmpty) for name in ExerciseMotionLibrary.exerciseNames { let info = try #require(ExerciseInfoLibrary.info(for: name), "\(name) has no bundled info.md") #expect(!info.summary.isEmpty, "\(name): empty summary") #expect(!info.targets.isEmpty, "\(name): no targets") #expect(!info.sections.isEmpty, "\(name): no sections") } } }