diff --git a/Example/SampleSnapshot/SnapshotTests.swift b/Example/SampleSnapshot/SnapshotTests.swift index 4187fed..59baf38 100644 --- a/Example/SampleSnapshot/SnapshotTests.swift +++ b/Example/SampleSnapshot/SnapshotTests.swift @@ -14,7 +14,7 @@ final class SnapshotTests: XCTestCase { format: .png, scale: 1, keyWindow: getKeyWindow(), - devices: [.iPhone11Pro(.portrait)] + devices: [.iPhone15Pro(.portrait)] ) ) } diff --git a/Playbook.xcodeproj/project.pbxproj b/Playbook.xcodeproj/project.pbxproj index 9fb93ad..925955f 100644 --- a/Playbook.xcodeproj/project.pbxproj +++ b/Playbook.xcodeproj/project.pbxproj @@ -20,6 +20,8 @@ 334B859A3103A83A4EC996BF /* GalleryScenarios.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BAD3FE8D577886981218938 /* GalleryScenarios.swift */; }; 341406194DDF8C7A82AB96D0 /* ScenarioContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63CD111722A3B8067BFE548 /* ScenarioContext.swift */; }; 358D72B04367A43CD12978FD /* ScenarioViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77D5B1C44FC9E87FD3237459 /* ScenarioViewController.swift */; }; + 35BFB062D9494EEADDA44B7E /* PlaybookCatalogContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE860C77A4DCDA2055ACC0C /* PlaybookCatalogContent.swift */; }; + 3A51E61C126D173E133CCF48 /* PlaybookGalleryContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21A6A1D8849030D7DD8BE135 /* PlaybookGalleryContent.swift */; }; 3D179776ADD757235527A929 /* GalleryThumbnail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80C668330D834FEE619E6735 /* GalleryThumbnail.swift */; }; 4104FE671C498D67FB0134C3 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B12B3F422F9EE7600CA7B8C /* ImageCache.swift */; }; 43EC47CA52837935B25B6A54 /* Playbook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08B7BB8752007FC3BB78EC8A /* Playbook.framework */; }; @@ -148,6 +150,7 @@ 0B038E054D3A0F626DD32C93 /* PlaybookUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PlaybookUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 108C38EAC8F960E10B0FA627 /* SnapshotError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotError.swift; sourceTree = ""; }; 11067B4E4A9D6494867D706A /* Separator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Separator.swift; sourceTree = ""; }; + 21A6A1D8849030D7DD8BE135 /* PlaybookGalleryContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybookGalleryContent.swift; sourceTree = ""; }; 2532CFEE0AB6105AEB5B74E2 /* SelectData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectData.swift; sourceTree = ""; }; 25344536B72D76889805B50D /* OrderedStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedStorage.swift; sourceTree = ""; }; 29B5E679D86934A2FB5A0856 /* UnavailableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnavailableView.swift; sourceTree = ""; }; @@ -200,6 +203,7 @@ BEC649C8437116C1FFCA36D9 /* GalleryState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryState.swift; sourceTree = ""; }; C509DD2858530167BEC5BB11 /* SearchResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResult.swift; sourceTree = ""; }; C925FCA9E427334E3128E21D /* CatalogSplit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalogSplit.swift; sourceTree = ""; }; + CDE860C77A4DCDA2055ACC0C /* PlaybookCatalogContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybookCatalogContent.swift; sourceTree = ""; }; CEB51C85B7D63CEF5DC83B60 /* ScenarioSwiftUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScenarioSwiftUITests.swift; sourceTree = ""; }; D15384CB93B122425863A487 /* CatalogScenarios.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalogScenarios.swift; sourceTree = ""; }; D26212C25465F186B196A9E9 /* SearchState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchState.swift; sourceTree = ""; }; @@ -391,6 +395,8 @@ 80C668330D834FEE619E6735 /* GalleryThumbnail.swift */, BCBE2887B8D2BD7EA50CD94B /* HighlightText.swift */, 85A9B46FFAE9273D84C8A395 /* MaterialView.swift */, + CDE860C77A4DCDA2055ACC0C /* PlaybookCatalogContent.swift */, + 21A6A1D8849030D7DD8BE135 /* PlaybookGalleryContent.swift */, B446C3A548A2DC847F032CA0 /* ScenarioContentView.swift */, 559B640B10AF07E0E4755344 /* SearchBar.swift */, 11067B4E4A9D6494867D706A /* Separator.swift */, @@ -648,7 +654,9 @@ B44C35245CFBC0043183D67C /* ImageSource.swift in Sources */, 1F6EB9E17EC82DFFE52C8FE9 /* MaterialView.swift in Sources */, 668D80F30C0A09A5667C592A /* PlaybookCatalog.swift in Sources */, + 35BFB062D9494EEADDA44B7E /* PlaybookCatalogContent.swift in Sources */, 2C4D9ABC6FC9696E42BE8080 /* PlaybookGallery.swift in Sources */, + 3A51E61C126D173E133CCF48 /* PlaybookGalleryContent.swift in Sources */, AE0DE7D74E721B0D254353B2 /* ScenarioContentView.swift in Sources */, 0A26157EE6E7B940067CA211 /* SearchBar.swift in Sources */, 08DB4240FAF948F16398C1C9 /* SearchResult.swift in Sources */, diff --git a/Sources/PlaybookUI/Internal/Views/CatalogDrawer.swift b/Sources/PlaybookUI/Internal/Views/CatalogDrawer.swift index ee815bf..21d2003 100644 --- a/Sources/PlaybookUI/Internal/Views/CatalogDrawer.swift +++ b/Sources/PlaybookUI/Internal/Views/CatalogDrawer.swift @@ -11,9 +11,6 @@ internal struct CatalogDrawer: View { CatalogTop() Drawer(isCollapsed: $catalogState.isSearchPainCollapsed) } - .onChange(of: catalogState.selected?.id) { _ in - catalogState.isSearchPainCollapsed = true - } } } diff --git a/Sources/PlaybookUI/Internal/Views/CatalogSearchPane.swift b/Sources/PlaybookUI/Internal/Views/CatalogSearchPane.swift index 8291b90..73970c6 100644 --- a/Sources/PlaybookUI/Internal/Views/CatalogSearchPane.swift +++ b/Sources/PlaybookUI/Internal/Views/CatalogSearchPane.swift @@ -60,6 +60,7 @@ internal struct CatalogSearchPane: View { isSelected: catalogState.selected?.id == select.id ) { catalogState.selected = select + catalogState.isSearchPainCollapsed = true } } } diff --git a/Sources/PlaybookUI/Internal/Views/PlaybookCatalogContent.swift b/Sources/PlaybookUI/Internal/Views/PlaybookCatalogContent.swift new file mode 100644 index 0000000..041ff28 --- /dev/null +++ b/Sources/PlaybookUI/Internal/Views/PlaybookCatalogContent.swift @@ -0,0 +1,54 @@ +import Playbook +import SwiftUI + +@available(iOS 15.0, *) +internal struct PlaybookCatalogContent: View { + let title: String? + + @EnvironmentObject + private var searchState: SearchState + @EnvironmentObject + private var catalogState: CatalogState + @EnvironmentObject + private var shareState: ShareState + @Environment(\.horizontalSizeClass) + private var horizontalSizeClass + @Environment(\.verticalSizeClass) + private var verticalSizeClass + + var body: some View { + Group { + switch (horizontalSizeClass, verticalSizeClass) { + case (.regular, .regular): + CatalogSplit() + + default: + CatalogDrawer() + } + } + .safeAreaInset(edge: .bottom, spacing: 0) { + CatalogBottomBar( + title: title, + primaryItemSymbol: primaryBarItemSymbol + ) + } + .ignoresSafeArea(.keyboard) + .preferredColorScheme(catalogState.colorScheme) + .onAppear { + catalogState.selectInitial(searchResult: searchState.result) + } + } +} + +@available(iOS 15.0, *) +private extension PlaybookCatalogContent { + var primaryBarItemSymbol: Image.SFSymbols { + switch (horizontalSizeClass, verticalSizeClass) { + case (.regular, .regular): + return .sidebarLeft + + default: + return .magnifyingglass + } + } +} diff --git a/Sources/PlaybookUI/Internal/Views/PlaybookGalleryContent.swift b/Sources/PlaybookUI/Internal/Views/PlaybookGalleryContent.swift new file mode 100644 index 0000000..658e3ae --- /dev/null +++ b/Sources/PlaybookUI/Internal/Views/PlaybookGalleryContent.swift @@ -0,0 +1,102 @@ +import Playbook +import SwiftUI + +@available(iOS 15.0, *) +internal struct PlaybookGalleryContent: View { + let title: String? + + @EnvironmentObject + private var searchState: SearchState + @EnvironmentObject + private var galleryState: GalleryState + @EnvironmentObject + private var imageLoader: ImageLoader + @FocusState + private var isFocused + + var body: some View { + NavigationView { + List { + Group { + Spacer.fixed(length: 16) + SearchBar(text: $searchState.query) + .focused($isFocused) + + Counter( + count: searchState.result.count, + total: searchState.result.total + ) + .onDisappear { + isFocused = false + } + + if searchState.result.kinds.isEmpty { + UnavailableView( + symbol: .magnifyingglass, + description: "No Result for \"\(searchState.query)\"" + ) + } + else { + ForEach(searchState.result.kinds, id: \.kind) { data in + GalleryKindRow(data: data) { selected in + galleryState.selected = SelectData( + kind: selected.kind, + scenario: selected.scenario + ) + } + } + } + + Spacer.fixed(length: 24) + } + .listRowSpacing(.zero) + .listRowInsets(EdgeInsets()) + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + .transition(.identity) + } + .listStyle(.plain) + .environment(\.defaultMinListRowHeight, 0) + .navigationTitleIfPresent(title) + .background { + Color(.background) + .ignoresSafeArea() + } + .ignoresSafeArea(.keyboard) + .sheet(item: $galleryState.selected) { data in + GalleryDetail(data: data) + } + .toolbar { + ToolbarItem(placement: .topBarTrailing) { + HStack { + Menu { + Button("Clear Thumbnail Cache") { + galleryState.clearImageCache() + } + } label: { + Image(symbol: .ellipsisCircle) + .imageStyle(font: .subheadline) + } + + ColorSchemePicker(colorScheme: $galleryState.colorScheme) + } + } + } + } + .navigationViewStyle(.stack) + .preferredColorScheme(galleryState.colorScheme) + } +} + +@available(iOS 14.0, *) +private extension View { + @ViewBuilder + func navigationTitleIfPresent(_ title: S?) -> some View { + if let title { + navigationTitle(title) + } + else { + self + } + } +} diff --git a/Sources/PlaybookUI/PlaybookCatalog.swift b/Sources/PlaybookUI/PlaybookCatalog.swift index 995b9f6..b2e10fb 100644 --- a/Sources/PlaybookUI/PlaybookCatalog.swift +++ b/Sources/PlaybookUI/PlaybookCatalog.swift @@ -11,10 +11,6 @@ public struct PlaybookCatalog: View { private var catalogState = CatalogState() @StateObject private var shareState = ShareState() - @Environment(\.horizontalSizeClass) - private var horizontalSizeClass - @Environment(\.verticalSizeClass) - private var verticalSizeClass public init( title: String? = nil, @@ -25,41 +21,9 @@ public struct PlaybookCatalog: View { } public var body: some View { - Group { - switch (horizontalSizeClass, verticalSizeClass) { - case (.regular, .regular): - CatalogSplit() - - default: - CatalogDrawer() - } - } - .safeAreaInset(edge: .bottom, spacing: 0) { - CatalogBottomBar( - title: title, - primaryItemSymbol: primaryBarItemSymbol - ) - } - .ignoresSafeArea(.keyboard) - .preferredColorScheme(catalogState.colorScheme) - .environmentObject(searchState) - .environmentObject(catalogState) - .environmentObject(shareState) - .onAppear { - catalogState.selectInitial(searchResult: searchState.result) - } - } -} - -@available(iOS 15.0, *) -private extension PlaybookCatalog { - var primaryBarItemSymbol: Image.SFSymbols { - switch (horizontalSizeClass, verticalSizeClass) { - case (.regular, .regular): - return .sidebarLeft - - default: - return .magnifyingglass - } + PlaybookCatalogContent(title: title) + .environmentObject(searchState) + .environmentObject(catalogState) + .environmentObject(shareState) } } diff --git a/Sources/PlaybookUI/PlaybookGallery.swift b/Sources/PlaybookUI/PlaybookGallery.swift index 898bf07..d85b192 100644 --- a/Sources/PlaybookUI/PlaybookGallery.swift +++ b/Sources/PlaybookUI/PlaybookGallery.swift @@ -11,8 +11,6 @@ public struct PlaybookGallery: View { private var galleryState = GalleryState() @StateObject private var imageLoader = ImageLoader() - @FocusState - private var isFocused public init( title: String? = nil, @@ -23,89 +21,9 @@ public struct PlaybookGallery: View { } public var body: some View { - NavigationView { - List { - Group { - Spacer.fixed(length: 16) - SearchBar(text: $searchState.query) - .focused($isFocused) - - Counter( - count: searchState.result.count, - total: searchState.result.total - ) - .onDisappear { - isFocused = false - } - - if searchState.result.kinds.isEmpty { - UnavailableView( - symbol: .magnifyingglass, - description: "No Result for \"\(searchState.query)\"" - ) - } - else { - ForEach(searchState.result.kinds, id: \.kind) { data in - GalleryKindRow(data: data) { selected in - galleryState.selected = SelectData( - kind: selected.kind, - scenario: selected.scenario - ) - } - } - } - - Spacer.fixed(length: 24) - } - .listRowSpacing(.zero) - .listRowInsets(EdgeInsets()) - .listRowSeparator(.hidden) - .listRowBackground(Color.clear) - .transition(.identity) - } - .listStyle(.plain) - .environment(\.defaultMinListRowHeight, 0) - .navigationTitleIfPresent(title) - .background { - Color(.background) - .ignoresSafeArea() - } - .ignoresSafeArea(.keyboard) - .sheet(item: $galleryState.selected) { data in - GalleryDetail(data: data) - } - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - HStack { - Menu { - Button("Clear Thumbnail Cache") { - galleryState.clearImageCache() - } - } label: { - Image(symbol: .ellipsisCircle) - .imageStyle(font: .subheadline) - } - - ColorSchemePicker(colorScheme: $galleryState.colorScheme) - } - } - } - } - .navigationViewStyle(.stack) - .preferredColorScheme(galleryState.colorScheme) - .environmentObject(imageLoader) - } -} - -@available(iOS 14.0, *) -private extension View { - @ViewBuilder - func navigationTitleIfPresent(_ title: S?) -> some View { - if let title { - navigationTitle(title) - } - else { - self - } + PlaybookGalleryContent(title: title) + .environmentObject(searchState) + .environmentObject(galleryState) + .environmentObject(imageLoader) } } diff --git a/Tests/AllScenarios.swift b/Tests/AllScenarios.swift index 2621ade..35e4538 100644 --- a/Tests/AllScenarios.swift +++ b/Tests/AllScenarios.swift @@ -1,5 +1,6 @@ import PlaybookUI +@available(iOS 15.0, *) struct AllScenarios: ScenarioProvider { static func addScenarios(into playbook: Playbook) { playbook diff --git a/Tests/CatalogScenarios.swift b/Tests/CatalogScenarios.swift index e38b5e3..2ca25d7 100644 --- a/Tests/CatalogScenarios.swift +++ b/Tests/CatalogScenarios.swift @@ -2,103 +2,78 @@ import SwiftUI @testable import PlaybookUI +@available(iOS 15.0, *) enum CatalogScenarios: ScenarioProvider { + @MainActor static func addScenarios(into playbook: Playbook) { playbook.addScenarios(of: "Catalog") { Scenario("Drawer close", layout: .fill) { - PlaybookCatalogInternal( - name: "TEST", - playbook: .test, - store: CatalogStore( - playbook: .test, - selectedScenario: .stub, - isSearchTreeHidden: true - ) - .start() - ) + let catalogState = CatalogState() + catalogState.isSearchPainCollapsed = true + + return PlaybookCatalogContent(title: nil) + .environmentObject(SearchState(playbook: .test)) + .environmentObject(catalogState) + .environmentObject(ShareState()) } Scenario("Drawer open", layout: .fill) { - PlaybookCatalogInternal( - name: "TEST", - playbook: .test, - store: CatalogStore( - playbook: .test, - selectedScenario: .stub, - openedKinds: ["Kind 1"], - isSearchTreeHidden: false - ) - .start() - ) + let catalogState = CatalogState() + catalogState.isSearchPainCollapsed = false + + return PlaybookCatalogContent(title: nil) + .environmentObject(SearchState(playbook: .test)) + .environmentObject(catalogState) + .environmentObject(ShareState()) } Scenario("Drawer close empty", layout: .fill) { - PlaybookCatalogInternal( - name: "TEST", - playbook: Playbook(), - store: CatalogStore( - playbook: Playbook(), - isSearchTreeHidden: true - ) - ) + let searchState = SearchState(playbook: Playbook()) + let catalogState = CatalogState() + catalogState.isSearchPainCollapsed = true + + return PlaybookCatalogContent(title: nil) + .environmentObject(searchState) + .environmentObject(catalogState) + .environmentObject(ShareState()) } Scenario("Drawer open empty", layout: .fill) { - PlaybookCatalogInternal( - name: "TEST", - playbook: Playbook(), - store: CatalogStore( - playbook: Playbook(), - isSearchTreeHidden: false - ) - ) - } + let searchState = SearchState(playbook: Playbook()) + let catalogState = CatalogState() + catalogState.isSearchPainCollapsed = false - Scenario("Searching", layout: .fill) { - PlaybookCatalogInternal( - name: "TEST", - playbook: .test, - store: CatalogStore( - playbook: .test, - selectedScenario: .stub, - openedSearchingKinds: Set(Playbook.test.stores.map { $0.kind }), - isSearchTreeHidden: false - ) - .start(with: "2") - ) + return PlaybookCatalogContent(title: nil) + .environmentObject(searchState) + .environmentObject(catalogState) + .environmentObject(ShareState()) } - Scenario("Drawer", layout: .sizing(h: .fixed(300), v: .fill)) { - ScenarioSearchTreeIOS13() - .environmentObject( - CatalogStore( - playbook: .test, - selectedScenario: .stub, - openedKinds: ["Kind 1"], - isSearchTreeHidden: false - ) - .start() - ) + Scenario("Drawer open searching", layout: .fill) { + let searchState = SearchState(playbook: .test) + let catalogState = CatalogState() + searchState.query = "1" + catalogState.isSearchPainCollapsed = false + + return PlaybookCatalogContent(title: nil) + .environmentObject(searchState) + .environmentObject(catalogState) + .environmentObject(ShareState()) } - } - #if swift(>=5.3) - if #available(iOS 14.0, *) { - playbook.addScenarios(of: "Catalog") { - Scenario("Drawer iOS14", layout: .sizing(h: .fixed(300), v: .fill)) { - ScenarioSearchTreeIOS14() - .environmentObject( - CatalogStore( - playbook: .test, - selectedScenario: .stub, - openedKinds: ["Kind 1"], - isSearchTreeHidden: false - ) - .start() - ) - } + Scenario("SearchPane", layout: .sizing(h: .fixed(300), v: .fill)) { + let catalogState = CatalogState() + catalogState.selected = SelectData( + kind: "Kind 1", + scenario: .stub("Scenario 1") + ) + catalogState.expandedKinds = ["Kind 1"] + catalogState.isSearchPainCollapsed = false + + return CatalogSearchPane() + .environmentObject(SearchState(playbook: .test)) + .environmentObject(catalogState) } } - #endif } } diff --git a/Tests/GalleryScenarios.swift b/Tests/GalleryScenarios.swift index 9e1aa27..e8beb96 100644 --- a/Tests/GalleryScenarios.swift +++ b/Tests/GalleryScenarios.swift @@ -2,292 +2,37 @@ import SwiftUI @testable import PlaybookUI +@available(iOS 15.0, *) enum GalleryScenarios: ScenarioProvider { + @MainActor static func addScenarios(into playbook: Playbook) { playbook.addScenarios(of: "Gallery") { - Scenario("Ready", layout: .fill) { context in - PlaybookGalleryIOS13( - name: "TEST", - snapshotColorScheme: .light, - store: GalleryStore( - playbook: .test, - preSnapshotCountLimit: 100, - screenSize: context.screenSize.portrait, - userInterfaceStyle: .light, - status: .ready - ) - .takeSnapshots() - .start() - ) - .environment( - \.galleryDependency, - GalleryDependency( - scheduler: SchedulerMock(), - context: context - ) - ) - } - - Scenario("Preparing", layout: .fill) { context in - PlaybookGalleryIOS13( - name: "TEST", - snapshotColorScheme: .light, - store: GalleryStore( - playbook: .test, - preSnapshotCountLimit: 0, - screenSize: context.screenSize.portrait, - userInterfaceStyle: .light - ) - .start() - ) + Scenario("Thumbnails", layout: .fill) { context in + PlaybookGallery(playbook: .test) } Scenario("Empty", layout: .fill) { context in - PlaybookGalleryIOS13( - name: "TEST", - snapshotColorScheme: .light, - store: GalleryStore( - playbook: Playbook(), - preSnapshotCountLimit: 0, - screenSize: context.screenSize.portrait, - userInterfaceStyle: .light - ) - ) - .environment( - \.galleryDependency, - GalleryDependency( - scheduler: SchedulerMock(), - context: context - ) - ) + PlaybookGallery(playbook: Playbook()) } Scenario("Searching", layout: .fill) { context in - PlaybookGalleryIOS13( - name: "TEST", - snapshotColorScheme: .light, - store: GalleryStore( - playbook: .test, - preSnapshotCountLimit: 100, - screenSize: context.screenSize.portrait, - userInterfaceStyle: .light, - status: .ready - ) - .takeSnapshots() - .start(with: "2") - ) - .environment( - \.galleryDependency, - GalleryDependency( - scheduler: SchedulerMock(), - context: context - ) - ) - } - - Scenario("Sheet", layout: .fill) { context in - ScenarioDisplaySheet(data: .stub, onClose: {}) - .environmentObject( - GalleryStore( - playbook: .test, - preSnapshotCountLimit: 0, - screenSize: context.screenSize.portrait, - userInterfaceStyle: .light - ) - ) - } - - Scenario("Display list", layout: .fillH) { context in - ScenarioDisplayList( - data: SearchedListData( - kind: "Long Kind Long Kind Long Kind Long Kind", - shouldHighlight: true, - scenarios: [.stub, .stub, .stub] - ), - safeAreaInsets: EdgeInsets(), - serialDispatcher: SerialMainDispatcher(interval: 0, scheduler: SchedulerMock()), - onSelect: { _ in } - ) - .environmentObject( - GalleryStore( - playbook: .test, - preSnapshotCountLimit: 100, - screenSize: context.screenSize.portrait, - userInterfaceStyle: .light - ) - .takeSnapshots() - ) - .environment( - \.galleryDependency, - GalleryDependency( - scheduler: SchedulerMock(), - context: context - ) - ) - } - - Scenario("Display empty", layout: .compressed) { context in - ScenarioDisplay( - store: ScenarioDisplayStore( - data: SearchedData( - scenario: .stub("Long Name Long Name Long Name"), - kind: "Kind", - shouldHighlight: true - ), - snapshotLoader: SnapshotLoaderMock( - device: SnapshotDevice(name: "TEST", size: context.screenSize.portrait), - loadImageResult: .success(nil) - ), - serialDispatcher: SerialMainDispatcher(interval: 0, scheduler: SchedulerMock()), - scheduler: SchedulerMock() - ) - ) - } + let searchState = SearchState(playbook: .test) + searchState.query = "1" - Scenario("Display failure", layout: .compressed) { context in - ScenarioDisplay( - store: ScenarioDisplayStore( - data: .stub, - snapshotLoader: SnapshotLoaderMock( - device: SnapshotDevice(name: "TEST", size: context.screenSize.portrait), - loadImageResult: .failure(TestError()) - ), - serialDispatcher: SerialMainDispatcher(interval: 0, scheduler: SchedulerMock()), - scheduler: SchedulerMock() - ) - ) + return PlaybookGalleryContent(title: nil) + .environmentObject(searchState) + .environmentObject(GalleryState()) + .environmentObject(ImageLoader()) } - Scenario("Display failure dark", layout: .compressed) { context in - ScenarioDisplay( - store: ScenarioDisplayStore( - data: .stub, - snapshotLoader: SnapshotLoaderMock( - device: SnapshotDevice(name: "TEST", size: context.screenSize.portrait), - loadImageResult: .failure(TestError()) - ), - serialDispatcher: SerialMainDispatcher(interval: 0, scheduler: SchedulerMock()), - scheduler: SchedulerMock() - ) - ) + Scenario("Row", layout: .fillH) { context in + GalleryKindRow(data: .stub()) { _ in } + .environmentObject(ImageLoader()) } - } - - #if swift(>=5.3) - if #available(iOS 14.0, *) { - playbook.addScenarios(of: "Gallery") { - Scenario("Ready iOS14", layout: .fill) { context in - PlaybookGalleryIOS14( - name: "TEST", - snapshotColorScheme: .light, - store: GalleryStore( - playbook: .test, - preSnapshotCountLimit: 100, - screenSize: context.screenSize.portrait, - userInterfaceStyle: .light, - status: .ready - ) - .takeSnapshots() - .start() - ) - .environment( - \.galleryDependency, - GalleryDependency( - scheduler: SchedulerMock(), - context: context - ) - ) - } - - Scenario("Preparing iOS14", layout: .fill) { context in - PlaybookGalleryIOS14( - name: "TEST", - snapshotColorScheme: .light, - store: GalleryStore( - playbook: .test, - preSnapshotCountLimit: 0, - screenSize: context.screenSize.portrait, - userInterfaceStyle: .light - ) - .start() - ) - } - - Scenario("Empty iOS14", layout: .fill) { context in - PlaybookGalleryIOS14( - name: "TEST", - snapshotColorScheme: .light, - store: GalleryStore( - playbook: Playbook(), - preSnapshotCountLimit: 0, - screenSize: context.screenSize.portrait, - userInterfaceStyle: .light - ) - ) - .environment( - \.galleryDependency, - GalleryDependency( - scheduler: SchedulerMock(), - context: context - ) - ) - } - - Scenario("Searching iOS14", layout: .fill) { context in - PlaybookGalleryIOS14( - name: "TEST", - snapshotColorScheme: .light, - store: GalleryStore( - playbook: .test, - preSnapshotCountLimit: 100, - screenSize: context.screenSize.portrait, - userInterfaceStyle: .light, - status: .ready - ) - .takeSnapshots() - .start(with: "2") - ) - .environment( - \.galleryDependency, - GalleryDependency( - scheduler: SchedulerMock(), - context: context - ) - ) - } - Scenario("Dark snapshots", layout: .fill) { context in - PlaybookGalleryIOS14( - name: "TEST", - snapshotColorScheme: .dark, - store: GalleryStore( - playbook: .test, - preSnapshotCountLimit: 100, - screenSize: context.screenSize.portrait, - userInterfaceStyle: .dark, - status: .ready - ) - .takeSnapshots() - .start() - ) - .environment( - \.galleryDependency, - GalleryDependency( - scheduler: SchedulerMock(), - context: context - ) - ) - } + Scenario("Detail", layout: .fill) { context in + GalleryDetail(data: .stub()) } } - #endif - } -} - -private struct TestError: Error {} - -private extension CGSize { - var portrait: CGSize { - CGSize(width: min(width, height), height: max(width, height)) } } diff --git a/Tests/Mocks.swift b/Tests/Mocks.swift index d42a9d2..8f2b8dc 100644 --- a/Tests/Mocks.swift +++ b/Tests/Mocks.swift @@ -3,42 +3,6 @@ import SwiftUI @testable import PlaybookUI -struct SchedulerMock: SchedulerProtocol { - func schedule(on queue: DispatchQueue, action: @escaping () -> Void) { - action() - } - - func schedule(on: DispatchQueue, after interval: TimeInterval, action: @escaping () -> Void) { - action() - } -} - -final class SnapshotLoaderMock: SnapshotLoaderProtocol { - let device: SnapshotDevice - let takeSnapshotResult: Data - let loadImageResult: Result - - init( - device: SnapshotDevice, - takeSnapshotResult: Data = Data(), - loadImageResult: Result = .success(nil) - ) { - self.device = device - self.takeSnapshotResult = takeSnapshotResult - self.loadImageResult = loadImageResult - } - - func takeSnapshot(for scenario: Scenario, kind: ScenarioKind, completion: ((Data) -> Void)?) { - completion?(takeSnapshotResult) - } - - func loadImage(kind: ScenarioKind, name: ScenarioName) -> Result { - loadImageResult - } - - func clean() {} -} - extension Playbook { static let test: Playbook = { let playbok = Playbook() @@ -55,12 +19,30 @@ extension Playbook { }() } +extension SelectData { + static func stub() -> Self { + SelectData(kind: "Kind", scenario: .stub("Scenario")) + } +} + extension SearchedData { - static var stub: Self { + static func stub(_ index: Int) -> Self { SearchedData( - scenario: .stub("Scenario 1"), - kind: "Kind 1", - shouldHighlight: false + kind: "Kind", + scenario: .stub("Scenario \(index)"), + highlightRange: nil + ) + } +} + +extension SearchedKindData { + static func stub( + scenarios: [SearchedData] = (0..<3).map { .stub($0) } + ) -> Self { + SearchedKindData( + kind: "Kind", + highlightRange: nil, + scenarios: scenarios ) } } diff --git a/Tests/SnapshotTests.swift b/Tests/SnapshotTests.swift index 15b75d7..7bd5f50 100644 --- a/Tests/SnapshotTests.swift +++ b/Tests/SnapshotTests.swift @@ -1,6 +1,7 @@ import PlaybookSnapshot import XCTest +@available(iOS 15.0, *) final class SnapshotTests: XCTestCase { func testTakeSnapshot() throws { guard let directory = ProcessInfo.processInfo.environment["SNAPSHOT_DIR"] else { @@ -17,9 +18,9 @@ final class SnapshotTests: XCTestCase { scale: 1, keyWindow: getKeyWindow(), devices: [ - .iPhone11Pro(.portrait), - .iPhone11Pro(.landscape).style(.dark), - .iPhoneSE(.portrait).style(.dark), + .iPhone15Pro(.portrait), + .iPhone15Pro(.landscape).style(.dark), + .iPhone13Mini(.portrait), .iPadPro12_9(.landscape), ], viewPreprocessor: { view in