Author the machine exercise circuit with props and corrected hip machine motions
Fourteen Planet Fitness machine exercises join the rig library, each with authored motion, info page, and schematic equipment via the new props layer (scene shapes, cables, bars, pads) rendered in lockstep by render.py and the in-app figure renderer. Abductor and Adductor are authored face-on with the real seated machine motion — knees-bent legs swinging apart/together against knee pads — replacing the earlier middle-split depiction. The watch target now bundles the figure renderer and motion rigs too. Claude-Session: https://claude.ai/code/session_01LEoff8bXGBS83tK1c55Mf7
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
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.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
|
||||
}
|
||||
}
|
||||
|
||||
/// 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user