Skip to main content

ui_storybook/fixtures/
contact_sheet.rs

1//! Contact-sheet aggregator (M-UI.22 / AUT-142).
2//!
3//! Each per-surface fixture module declares its own canonical
4//! samples. This module re-collects them into a single deterministic
5//! `UiFixtureSet` so the contact-sheet story + design-review docs
6//! have one source of truth.
7
8use crate::components::cursor::CursorAppearanceView;
9use crate::components::library::RecordingCardView;
10use crate::components::recorder::{AudioAppView, CaptureSourceView, DisplaySourceView};
11use crate::components::shell::WorkspaceView;
12
13/// Aggregated fixture bundle. Field order is stable; tests assert
14/// non-empty.
15#[derive(Clone, Debug, PartialEq)]
16pub struct UiFixtureSet {
17    /// Workspace switcher rows.
18    pub workspaces: Vec<WorkspaceView>,
19    /// Display sources for the screen tab.
20    pub displays: Vec<DisplaySourceView>,
21    /// Camera + microphone capture rows.
22    pub devices: DeviceFixtureSet,
23    /// System-audio app rows.
24    pub audio_apps: Vec<AudioAppView>,
25    /// Library recording cards.
26    pub recordings: Vec<RecordingCardView>,
27    /// Cursor appearance presets.
28    pub cursor_presets: Vec<CursorAppearanceView>,
29}
30
31/// Pair of capture-source rows so callers don't have to fish them
32/// out of the device fixture module individually.
33#[derive(Clone, Debug, PartialEq)]
34pub struct DeviceFixtureSet {
35    /// Camera capture row.
36    pub camera: CaptureSourceView,
37    /// Microphone capture row.
38    pub microphone: CaptureSourceView,
39}
40
41/// Deterministic fixture bundle used by the contact sheet + the
42/// design-review chapter.
43#[must_use]
44pub fn default_ui_fixtures() -> UiFixtureSet {
45    use crate::fixtures::audio_apps::sample_audio_apps;
46    use crate::fixtures::cursor::{
47        sample_cursor_appearance, sample_cursor_appearance_pulse,
48        sample_cursor_appearance_spotlight, sample_cursor_appearance_trail_on,
49    };
50    use crate::fixtures::devices::{
51        sample_capture_source_camera, sample_capture_source_microphone, sample_display_source,
52        sample_display_source_small, sample_display_source_wide,
53    };
54    use crate::fixtures::library::sample_recording_cards;
55    use crate::fixtures::workspaces::sample_workspace_views;
56
57    UiFixtureSet {
58        workspaces: sample_workspace_views(),
59        displays: vec![
60            sample_display_source(true),
61            sample_display_source_wide(),
62            sample_display_source_small(),
63        ],
64        devices: DeviceFixtureSet {
65            camera: sample_capture_source_camera(true, false),
66            microphone: sample_capture_source_microphone(true, false, Some(0.35)),
67        },
68        audio_apps: sample_audio_apps(),
69        recordings: sample_recording_cards(),
70        cursor_presets: vec![
71            sample_cursor_appearance(),
72            sample_cursor_appearance_pulse(),
73            sample_cursor_appearance_spotlight(),
74            sample_cursor_appearance_trail_on(),
75        ],
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn fixtures_are_non_empty_and_deterministic() {
85        let a = default_ui_fixtures();
86        let b = default_ui_fixtures();
87        assert_eq!(a, b);
88        assert!(!a.workspaces.is_empty());
89        assert!(!a.displays.is_empty());
90        assert!(!a.audio_apps.is_empty());
91        assert!(!a.recordings.is_empty());
92        assert!(!a.cursor_presets.is_empty());
93    }
94
95    #[test]
96    fn fixture_ids_are_stable() {
97        let a = default_ui_fixtures();
98        let ids: Vec<_> = a.recordings.iter().map(|r| r.id.clone()).collect();
99        // Documented stability — the first card is always `rec-01`.
100        assert_eq!(ids.first().map(String::as_str), Some("rec-01"));
101    }
102}