Library detail screens now lead with the user's recorded machine settings (per-split when they disagree, empty-state card for machine-based entries) and append the weight progression chart. Starter seeds mark machine exercises with an empty machineSettings list so the settings UI lights up before first use. The figure rig gains a frontal body profile for face-on machines, props that can ride mid joints (knees/elbows), and an alternating four-frame Bird Dog loop. Claude-Session: https://claude.ai/code/session_01LEoff8bXGBS83tK1c55Mf7
47 lines
2.1 KiB
Swift
47 lines
2.1 KiB
Swift
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
|
|
}
|
|
}
|
|
|
|
/// 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")
|
|
}
|
|
}
|
|
}
|