diff --git a/AppleWatch/Views/Components/WatchlistSelectorView.swift b/AppleWatch/Views/Components/WatchlistSelectorView.swift index 134c8bfe..3387816a 100644 --- a/AppleWatch/Views/Components/WatchlistSelectorView.swift +++ b/AppleWatch/Views/Components/WatchlistSelectorView.swift @@ -56,6 +56,7 @@ struct WatchlistSelectorView: View { Text(list.itemTitle) } } + .accessibilityIdentifier("\(list.itemTitle)") } } header: { Text("Your Lists") diff --git a/CronicaUITests/AppNavigator.swift b/CronicaUITests/AppNavigator.swift new file mode 100644 index 00000000..bcff0ee9 --- /dev/null +++ b/CronicaUITests/AppNavigator.swift @@ -0,0 +1,54 @@ +// +// AppNavigator.swift +// CronicaUITests +// +// Created by Moataz Akram on 13/09/2024. +// + +import XCTest +@testable import Cronica + +final class AppNavigator { + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + func navigateToTab(_ tab: Screens) { + dismissWelcomeScreenIfAppearingOnLaunch() + let tabBar = app.tabBars["Tab Bar"] + + switch tab { + case .home: + tabBar.buttons["Home"].tap() + case .explore: + tabBar.buttons["Discover"].tap() + case .watchlist: + tabBar.buttons["Watchlist"].tap() + case .search: + tabBar.buttons["Search"].tap() + case .notifications: + navigateToHomeTab() + app.buttons["Notifications"].tap() + case .settings: + navigateToHomeTab() + app.buttons["Settings"].tap() + } + } + + private func navigateToHomeTab() { + let tabBar = app.tabBars["Tab Bar"] + tabBar.buttons["Home"].tap() + } + + func dismissWelcomeScreenIfAppearingOnLaunch() { + let welcomeViewPredicate = NSPredicate(format: "identifier == 'Welcome View'") + let welcomeView = app.otherElements.containing(welcomeViewPredicate).firstMatch + let continueButton = welcomeView.buttons["Continue"] + if continueButton.exists { + continueButton.tap() + } + } + +} diff --git a/CronicaUITests/CronicaUITests.swift b/CronicaUITests/CronicaUITests.swift new file mode 100644 index 00000000..94f715e3 --- /dev/null +++ b/CronicaUITests/CronicaUITests.swift @@ -0,0 +1,87 @@ +// +// CronicaUITests.swift +// CronicaUITests +// +// Created by Moataz Akram on 08/09/2024. +// + +@testable import Cronica +import XCTest + +final class CronicaUITests: XCTestCase { + var app: XCUIApplication! + var appNavigator: AppNavigator! + override func setUp() { + super.setUp() + app = XCUIApplication() + app.launch() + appNavigator = AppNavigator(app: app) + } + + override func tearDown() { + app = nil + appNavigator = nil + super.tearDown() + } + + func testHomeScreen() { + appNavigator.navigateToTab(.home) + + let homeViewPredicate = NSPredicate(format: "identifier == 'Home View'") + let homeView = app.otherElements.containing(homeViewPredicate).firstMatch + + let exists = homeView.waitForExistence(timeout: 1) + XCTAssertTrue(exists, "Home View should appear.") + } + + func testDiscoverScreen() { + appNavigator.navigateToTab(.explore) + + let homeViewPredicate = NSPredicate(format: "identifier == 'Discover View'") + let homeView = app.otherElements.containing(homeViewPredicate).firstMatch + + let exists = homeView.waitForExistence(timeout: 1) + XCTAssertTrue(exists, "Discover View should appear.") + } + + func testWatchListScreen() { + appNavigator.navigateToTab(.watchlist) + + let watchlistViewPredicate = NSPredicate(format: "identifier == 'Watchlist View'") + let watchlistView = app.otherElements.containing(watchlistViewPredicate).firstMatch + + let exists = watchlistView.waitForExistence(timeout: 1) + XCTAssertTrue(exists, "Watchlist View should appear.") + } + + func testSearchScreen() { + appNavigator.navigateToTab(.search) + + let searchViewPredicate = NSPredicate(format: "identifier == 'Search View'") + let searchView = app.otherElements.containing(searchViewPredicate).firstMatch + + let exists = searchView.waitForExistence(timeout: 1) + XCTAssertTrue(exists, "Search View should appear.") + } + + func testSettingsScreen() { + appNavigator.navigateToTab(.settings) + + let settingsViewPredicate = NSPredicate(format: "identifier == 'Settings View'") + let settingsView = app.otherElements.containing(settingsViewPredicate).firstMatch + + let exists = settingsView.waitForExistence(timeout: 1) + XCTAssertTrue(exists, "Settings View should appear.") + } + + func testNotificationListScreen() { + appNavigator.navigateToTab(.notifications) + + let notificationListViewPredicate = NSPredicate(format: "identifier == 'Notification List View'") + let notificationListView = app.otherElements.containing(notificationListViewPredicate).firstMatch + + let exists = notificationListView.waitForExistence(timeout: 1) + XCTAssertTrue(exists, "Notification List should appear.") + } + +} diff --git a/CronicaUITests/CronicaUITestsLaunchTests.swift b/CronicaUITests/CronicaUITestsLaunchTests.swift new file mode 100644 index 00000000..926214c5 --- /dev/null +++ b/CronicaUITests/CronicaUITestsLaunchTests.swift @@ -0,0 +1,32 @@ +// +// CronicaUITestsLaunchTests.swift +// CronicaUITests +// +// Created by Moataz Akram on 08/09/2024. +// + +import XCTest + +final class CronicaUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} diff --git a/CronicaUITests/HomeUITests.swift b/CronicaUITests/HomeUITests.swift new file mode 100644 index 00000000..2160aea6 --- /dev/null +++ b/CronicaUITests/HomeUITests.swift @@ -0,0 +1,76 @@ +// +// HomeUITests.swift +// CronicaUITests +// +// Created by Moataz Akram on 12/09/2024. +// + +import XCTest +@testable import Cronica + +final class HomeUITests: XCTestCase { + var app: XCUIApplication! + var appNavigator: AppNavigator! + + override func setUp() { + super.setUp() + app = XCUIApplication() + app.launchArguments.append("--mock-data") + app.launch() + appNavigator = AppNavigator(app: app) + } + + override func tearDown() { + app = nil + appNavigator = nil + super.tearDown() + } + + func testFullHomeScreen() { + appNavigator.navigateToTab(.home) + + let navigationHomeTitle = app.navigationBars["Home"].staticTexts["Home"] + XCTAssertTrue(navigationHomeTitle.exists) + + // MARK: Trending section -> do not appear when no internet connection + let trendingTitle = app.staticTexts["Trending"] + XCTAssertTrue(trendingTitle.exists) + let todaySubtitle = app.staticTexts["Today"] + XCTAssertTrue(todaySubtitle.exists) + + let trendingList = app.scrollViews["Trending Horizontal List"] + XCTAssertTrue(trendingList.exists, "Trending List should appear.") + + // MARK: Upcoming section + let upcomingTitle = app.staticTexts["Up Coming"] + XCTAssertTrue(upcomingTitle.exists) + let upcomingSubtitle = app.staticTexts["Coming Soon To Theaters"] + XCTAssertTrue(upcomingSubtitle.exists) + + let upcomingList = app.scrollViews["Up Coming Horizontal List"] + if !upcomingList.exists { + app.swipeUp() + } + XCTAssertTrue(upcomingList.exists, "Up Coming List should appear.") + + // MARK: Latest Movies section + let latestMoviesTitle = app.staticTexts["Latest Movies"] + XCTAssertTrue(latestMoviesTitle.exists) + let latestMoviesSubtitle = app.staticTexts["Recently Released"] + XCTAssertTrue(latestMoviesSubtitle.exists) + + let latestMoviesList = app.scrollViews["Latest Movies Horizontal List"] + if !latestMoviesList.exists { + app.swipeUp() + } + XCTAssertTrue(latestMoviesList.exists, "Latest Movies List should appear.") + + // MARK: bottom section + app.swipeUp() + let tmdbImage = app.images["PrimaryCompact"].firstMatch + XCTAssertTrue(tmdbImage.exists) + let bottomText = app.staticTexts["This product uses the TMDb API but is not endorsed or certified by TMDb."] + XCTAssertTrue(bottomText.exists) + } + +} diff --git a/CronicaUITests/ItemContentDetailsUITests.swift b/CronicaUITests/ItemContentDetailsUITests.swift new file mode 100644 index 00000000..ace95afa --- /dev/null +++ b/CronicaUITests/ItemContentDetailsUITests.swift @@ -0,0 +1,176 @@ +// +// ItemContentDetailsUITests.swift +// CronicaUITests +// +// Created by Moataz Akram on 12/09/2024. +// + +import XCTest +@testable import Cronica + +final class ItemContentDetailsUITests: XCTestCase { + var app: XCUIApplication! + var appNavigator: AppNavigator! + + override func setUp() { + super.setUp() + app = XCUIApplication() + app.launchArguments.append("--mock-data") + app.launch() + appNavigator = AppNavigator(app: app) + } + + override func tearDown() { + appNavigator = nil + app = nil + super.tearDown() + } + + func navigateToItemContentDetails() { + app.scrollViews["Trending Horizontal List"].buttons["Zack Snyder's Justice League"].tap() + } + + func testItemContentDetailsFullScreen() { + appNavigator.navigateToTab(.home) + navigateToItemContentDetails() + + //MARK: Head Section + headSectionChecks() + + //MARK: Buttons Section + buttonsSectionChecks() + + //MARK: About Section + aboutSectionChecks() + app.swipeUp(velocity: .slow) + + //MARK: Where To Watch Section + whereToWatchSectionChecks() + + //MARK: Trailers Section + trailersSectionChecks() + app.swipeUp(velocity: .slow) + + //MARK: Cast Section + castSectionChecks() + + //MARK: Recommendations Section + recommendationsSectionChecks() + app.swipeUp(velocity: .slow) + + //MARK: Information Section + informationSectionChecks() + } + + func headSectionChecks() { + let itemTitle = app.staticTexts["Item Title"] + XCTAssertEqual(itemTitle.label, "Zack Snyder's Justice League") + let itemGenres = app.staticTexts["Item Genres"] + XCTAssertEqual(itemGenres.label, "Action, Adventure, Fantasy") + let itemInfo = app.staticTexts["Item Info"] + XCTAssertEqual(itemInfo.label, "18 Mar 2021 • 4h 2m") + } + + func buttonsSectionChecks() { + let listButton = app.buttons["List Button"] + XCTAssertTrue(listButton.exists) + + let addRemoveButton = app.buttons["Add Remove Button"] + XCTAssertTrue(addRemoveButton.exists) + let addRemoveButtonIcon = app.images["Add Remove Button Icon"] + if addRemoveButtonIcon.label == "Remove" { + addRemoveButtonIcon.tap() + } + XCTAssertTrue(addRemoveButtonIcon.exists) + XCTAssertEqual(addRemoveButtonIcon.label, "Add") + addRemoveButtonIcon.tap() + XCTAssertEqual(addRemoveButtonIcon.label, "Remove") + + addRemoveButtonIcon.tap() + XCTAssertEqual(addRemoveButtonIcon.label, "Add") + + let watchedButton = app.buttons["Watch Button"] + XCTAssertTrue(watchedButton.exists) + let watchedButtonIcon = app.images["Watch Button Icon"] + XCTAssertTrue(watchedButtonIcon.exists) + XCTAssertEqual(watchedButtonIcon.label, "rectangle.badge.checkmark") + watchedButton.tap() + XCTAssertEqual(watchedButtonIcon.label, "rectangle.badge.checkmark.fill") + watchedButton.tap() + XCTAssertEqual(watchedButtonIcon.label, "rectangle.badge.checkmark") + } + + func aboutSectionChecks() { + let aboutText = app.staticTexts["About Text"] + XCTAssertEqual(aboutText.label, "About") + let aboutDescription = app.staticTexts["Overview Text"] + XCTAssertEqual(aboutDescription.label, "Determined to ensure Superman's ultimate sacrifice was not in vain, Bruce Wayne aligns forces with Diana Prince with plans to recruit a team of metahumans to protect the world from an approaching threat of catastrophic proportions.") + + } + + func whereToWatchSectionChecks() { + let whereToWatchText = app.staticTexts["Where to Watch"] + XCTAssertTrue(whereToWatchText.exists) + let providedByText = app.staticTexts["Provided by JustWatch"] + XCTAssertTrue(providedByText.exists) + let providersList = app.otherElements["Watch Providers List"] + XCTAssertTrue(providersList.exists) + let appleTVProvider = app.buttons["Apple TV provider"] + XCTAssertTrue(appleTVProvider.exists) + let maxProvider = app.buttons["Max provider"] + XCTAssertTrue(maxProvider.exists) + + } + + func trailersSectionChecks() { + let trailersText = app.staticTexts["Trailers"] + XCTAssertTrue(trailersText.exists) + let trailersList = app.scrollViews["Trailers List"] + XCTAssertTrue(trailersList.exists) + let firstTrailer = app.staticTexts["Official UK Trailer"] + XCTAssertTrue(firstTrailer.exists) + } + + func castSectionChecks() { + let castTitle = app.staticTexts["Cast & Crew"] + XCTAssertTrue(castTitle.exists) + let castList = app.scrollViews["Cast List"] + XCTAssertTrue(castList.exists) + let firstCard = app.buttons["Ben Affleck Card"] + XCTAssertTrue(firstCard.exists) + } + + func recommendationsSectionChecks() { + let recommendationsTitle = app.staticTexts["Recommendations"] + XCTAssertTrue(recommendationsTitle.exists) + + let recommendationsList = app.scrollViews["Recommendations Horizontal List"] + XCTAssertTrue(recommendationsList.exists, "Recommendations List should appear.") + + let recommendationTitle = app.staticTexts["Justice League"] + XCTAssertTrue(recommendationTitle.exists) + + let recommendationCard = app.buttons["Justice League Card"] + XCTAssertTrue(recommendationCard.exists) + } + + func informationSectionChecks() { + let informationSection = app.otherElements["Information Section"] + XCTAssertTrue(informationSection.exists) + XCTAssertTrue(app.staticTexts["Original Title"].exists) + XCTAssertTrue(app.staticTexts["Zack Snyder's Justice League"].exists) + XCTAssertTrue(app.staticTexts["Run Time"].exists) + XCTAssertTrue(app.staticTexts["4 hours, 2 minutes"].exists) + XCTAssertTrue(app.staticTexts["Release Date"].exists) + XCTAssertTrue(app.staticTexts["18 Mar 2021"].exists) + XCTAssertTrue(app.staticTexts["8.1/10"].exists) + XCTAssertTrue(app.staticTexts["Status"].exists) + XCTAssertTrue(app.staticTexts["Released"].exists) + XCTAssertTrue(app.staticTexts["Genres"].exists) + XCTAssertTrue(app.staticTexts["Action, Adventure, Fantasy"].exists) + XCTAssertTrue(app.staticTexts["Region of Origin"].exists) + XCTAssertTrue(app.staticTexts["United States of America"].exists) + XCTAssertTrue(app.staticTexts["Production Companies"].exists) + XCTAssertTrue(app.staticTexts["Warner Bros. Pictures, The Stone Quarry, Atlas Entertainment, Access Entertainment, Dune Entertainment, DC Films"].exists) + } +} diff --git a/CronicaUITests/SettingsUITests.swift b/CronicaUITests/SettingsUITests.swift new file mode 100644 index 00000000..c8855a3b --- /dev/null +++ b/CronicaUITests/SettingsUITests.swift @@ -0,0 +1,60 @@ +// +// SettingsUITests.swift +// CronicaUITests +// +// Created by Moataz Akram on 13/09/2024. +// + +import XCTest +@testable import Cronica + +final class SettingsUITests: XCTestCase { + var app: XCUIApplication! = XCUIApplication() + var appNavigator: AppNavigator! + + override func setUp() { + super.setUp() + app = XCUIApplication() + app.launch() + appNavigator = AppNavigator(app: app) + } + + override func tearDown() { + appNavigator = nil + app = nil + super.tearDown() + } + + func testSettingsFullScreen() { + appNavigator.navigateToTab(.settings) + // MARK: General Section + XCTAssertTrue(app.staticTexts["GENERAL"].exists) + XCTAssertTrue(app.staticTexts["Behavior Tab"].exists) + XCTAssertTrue(app.images["hand.tap Icon"].exists) + XCTAssertTrue(app.staticTexts["Appearance Tab"].exists) + XCTAssertTrue(app.images["paintbrush Icon"].exists) + XCTAssertTrue(app.staticTexts["Notification Tab"].exists) + XCTAssertTrue(app.images["bell Icon"].exists) + + // MARK: App Features Section + XCTAssertTrue(app.staticTexts["APP FEATURES"].exists) + XCTAssertTrue(app.staticTexts["Watchlist Tab"].exists) + XCTAssertTrue(app.images["rectangle.on.rectangle Icon"].exists) + XCTAssertTrue(app.staticTexts["Season & Up Next Tab"].exists) + XCTAssertTrue(app.images["tv Icon"].exists) + XCTAssertTrue(app.staticTexts["Watch Provider Tab"].exists) + XCTAssertTrue(app.images["globe Icon"].exists) + + // MARK: Support & About Section + XCTAssertTrue(app.staticTexts["SUPPORT & ABOUT"].exists) + XCTAssertTrue(app.staticTexts["Feedback Tab"].exists) + XCTAssertTrue(app.images["envelope.fill Icon"].exists) + XCTAssertTrue(app.staticTexts["Privacy Policy Tab"].exists) + XCTAssertTrue(app.images["hand.raised Icon"].exists) + XCTAssertTrue(app.staticTexts["Tip Jar Tab"].exists) + XCTAssertTrue(app.images["heart Icon"].exists) + XCTAssertTrue(app.staticTexts["About Tab"].exists) + XCTAssertTrue(app.images["info.circle Icon"].exists) + } + +} diff --git a/CronicaUITests/WatchListUITests.swift b/CronicaUITests/WatchListUITests.swift new file mode 100644 index 00000000..ae0a66df --- /dev/null +++ b/CronicaUITests/WatchListUITests.swift @@ -0,0 +1,66 @@ +// +// WatchListUITests.swift +// CronicaUITests +// +// Created by Moataz Akram on 13/09/2024. +// + +import XCTest +@testable import Cronica + +final class WatchListUITests: XCTestCase { + var app: XCUIApplication! + var appNavigator: AppNavigator! + + override func setUp() { + super.setUp() + app = XCUIApplication() + app.launchArguments.append("--delete-cache") + app.launch() + appNavigator = AppNavigator(app: app) + } + + override func tearDown() { + appNavigator = nil + app = nil + super.tearDown() + } + + func testWatchListFullScreen() { + appNavigator.navigateToTab(.home) + app.scrollViews["Trending Horizontal List"].buttons["Zack Snyder's Justice League"].tap() + + let addRemoveButton = app.buttons["Add Remove Button"] + XCTAssertTrue(addRemoveButton.exists) + let addRemoveButtonIcon = app.images["Add Remove Button Icon"] + if addRemoveButtonIcon.label == "Add" { + addRemoveButtonIcon.tap() + } + + appNavigator.navigateToTab(.watchlist) + let app = XCUIApplication() + let watchlistNavigationBar = app.navigationBars["Watchlist"] + let listDropDown = watchlistNavigationBar.images["Go Down"] + listDropDown.tap() + app.buttons["New List"].tap() + + let listTitleTextField = app.textFields["ListTitleTextField"] + XCTAssertTrue(listTitleTextField.exists) + listTitleTextField.tap() + listTitleTextField.typeText("Watch Again") + let listDescriptionTextField = app.textFields["ListDescriptionTextField"] + XCTAssertTrue(listDescriptionTextField.exists) + + let createButton = app.buttons["CreateNewListButton"] + createButton.tap() + + app.navigationBars["Watch Again"].images["Go Down"].tap() + let watchAgainList = app.collectionViews.children(matching: .cell).element(boundBy: 2).buttons["Watch Again, 0 items"] + watchAgainList.swipeLeft() + app.buttons["Edit"].tap() + let deleteButton = app.buttons["DeleteListButton"] + deleteButton.tap() + let confirmButton = app.buttons["ConfirmDeleteButton"] + confirmButton.tap() + } +} diff --git a/Shared/Configuration/CronicaApp.swift b/Shared/Configuration/CronicaApp.swift index f8eb2506..03c36efc 100644 --- a/Shared/Configuration/CronicaApp.swift +++ b/Shared/Configuration/CronicaApp.swift @@ -30,6 +30,9 @@ struct CronicaApp: App { init() { CronicaTelemetry.shared.setup() registerRefreshBGTask() + if CommandLine.arguments.contains("--delete-cache") { + PersistenceController.shared.deleteAll() + } #if os(iOS) UNUserNotificationCenter.current().delegate = notificationDelegate #endif diff --git a/Shared/Controllers/PersistenceController-CustomList.swift b/Shared/Controllers/PersistenceController-CustomList.swift index 03e30f92..c6a10cf4 100644 --- a/Shared/Controllers/PersistenceController-CustomList.swift +++ b/Shared/Controllers/PersistenceController-CustomList.swift @@ -6,6 +6,7 @@ // import Foundation +import CoreData extension PersistenceController { func createList(title: String, description: String, items: Set, isPin: Bool) -> CustomList? { @@ -30,6 +31,20 @@ extension PersistenceController { save() } + func deleteAll() { + let viewContext = container.viewContext + let fetchRequest: NSFetchRequest = CustomList.fetchRequest() + + let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) + + do { + try viewContext.execute(deleteRequest) + save() // Ensure changes are saved after deletion + } catch let error as NSError { + print("Could not delete all items. \(error), \(error.userInfo)") + } + } + func isItemOnList(id: String, list: CustomList) -> Bool { return list.itemsSet.contains(where: { $0.itemContentID == id }) } diff --git a/Shared/Localization/Localizable.xcstrings b/Shared/Localization/Localizable.xcstrings index 5d1daab5..a1962e9d 100644 --- a/Shared/Localization/Localizable.xcstrings +++ b/Shared/Localization/Localizable.xcstrings @@ -11823,6 +11823,9 @@ } } } + }, + "Preview" : { + }, "Primary Left Gesture" : { "localizations" : { @@ -17451,6 +17454,9 @@ } } } + }, + "This is a preview" : { + }, "This list has been deleted." : { "localizations" : { diff --git a/Shared/Network/NetworkService.swift b/Shared/Network/NetworkService.swift index 9e3e7be8..114ea5fd 100644 --- a/Shared/Network/NetworkService.swift +++ b/Shared/Network/NetworkService.swift @@ -51,11 +51,16 @@ final class NetworkService: Sendable { } func fetchItems(from path: String, page: String = "1") async throws -> [ItemContent] { - guard let url = urlBuilder(path: path, page: page) else { - throw NetworkError.invalidEndpoint + if CommandLine.arguments.contains("--mock-data") { + let data: ItemContentResponse? = try? Bundle.main.decode(from: "content") + return ItemContent.examples + } else { + guard let url = urlBuilder(path: path, page: page) else { + throw NetworkError.invalidEndpoint + } + let response: ItemContentResponse = try await self.fetch(url: url) + return response.results } - let response: ItemContentResponse = try await self.fetch(url: url) - return response.results } func fetchYearContent(year: String, type: MediaType, page: Int = 1) async throws -> [ItemContent] { diff --git a/Shared/View/Components/OverviewBoxView.swift b/Shared/View/Components/OverviewBoxView.swift index aecd26d8..a2e63eb3 100644 --- a/Shared/View/Components/OverviewBoxView.swift +++ b/Shared/View/Components/OverviewBoxView.swift @@ -29,6 +29,7 @@ struct OverviewBoxView: View { .padding([.top], 2) .lineLimit(showFullText ? nil : 4) .multilineTextAlignment(.leading) + .accessibilityIdentifier("Overview Text") #if os(iOS) .background( // Render the limited text and measure its size @@ -72,6 +73,7 @@ struct OverviewBoxView: View { } label: { Text(type == .person ? "Biography" : "About") .unredacted() + .accessibilityIdentifier("About Text") } .onTapGesture { #if os(iOS) @@ -90,6 +92,7 @@ struct OverviewBoxView: View { #endif } .accessibilityElement(children: .combine) + .accessibilityIdentifier("About Section") .contextMenu { ShareLink(item: overview) } .popover(isPresented: $showSheet) { ScrollView { diff --git a/Shared/View/ItemContent/Components/ItemContentCardView.swift b/Shared/View/ItemContent/Components/ItemContentCardView.swift index e12c28f9..5a450b73 100644 --- a/Shared/View/ItemContent/Components/ItemContentCardView.swift +++ b/Shared/View/ItemContent/Components/ItemContentCardView.swift @@ -145,6 +145,7 @@ struct ItemContentCardView: View { .frame(width: DrawingConstants.imageWidth) Spacer() } + .accessibilityIdentifier("\(item.title ?? "") Card") .task { withAnimation { isInWatchlist = context.isItemSaved(id: item.itemContentID) diff --git a/Shared/View/ItemContent/HorizontalItemContentListView.swift b/Shared/View/ItemContent/HorizontalItemContentListView.swift index 796c1280..a5c9fccf 100644 --- a/Shared/View/ItemContent/HorizontalItemContentListView.swift +++ b/Shared/View/ItemContent/HorizontalItemContentListView.swift @@ -59,6 +59,7 @@ struct HorizontalItemContentListView: View { #endif } } + .accessibilityIdentifier("\(title) Horizontal List") } } } diff --git a/Shared/View/ItemContent/ItemContentDetails.swift b/Shared/View/ItemContent/ItemContentDetails.swift index 41504925..9c6d2674 100644 --- a/Shared/View/ItemContent/ItemContentDetails.swift +++ b/Shared/View/ItemContent/ItemContentDetails.swift @@ -162,17 +162,20 @@ struct ItemContentDetails: View { .padding(.horizontal, 8) .padding(.bottom, 4) .unredacted() + .accessibilityIdentifier("Item Title") if let genres = viewModel.content?.itemGenres, !genres.isEmpty { Text(genres) .font(.caption) .foregroundColor(.secondary) .fontDesign(.rounded) + .accessibilityIdentifier("Item Genres") } if let info = viewModel.content?.itemQuickInfo, !info.isEmpty { Text(info) .font(.caption) .foregroundColor(.secondary) .fontDesign(.rounded) + .accessibilityIdentifier("Item Info") } HStack { @@ -641,6 +644,7 @@ struct ItemContentDetails: View { } } } + .accessibilityIdentifier("Information Section") .groupBoxStyle(TransparentGroupBox()) } #endif @@ -703,6 +707,7 @@ extension ItemContentDetails { Image(systemName: viewModel.isInWatchlist ? "minus.circle.fill" : "plus.circle.fill") .symbolEffect(viewModel.isInWatchlist ? .bounce.down : .bounce.up, value: viewModel.isInWatchlist) + .accessibilityIdentifier("Add Remove Button Icon") #if !os(tvOS) Text(viewModel.isInWatchlist ? "Remove" : "Add") @@ -719,6 +724,7 @@ extension ItemContentDetails { #endif #endif } + .accessibilityIdentifier("Add Remove Button") .buttonStyle(.borderedProminent) #if os(macOS) .controlSize(.large) @@ -754,6 +760,7 @@ extension ItemContentDetails { Image(systemName: viewModel.isWatched ? "rectangle.badge.checkmark.fill" : "rectangle.badge.checkmark") .symbolEffect(viewModel.isWatched ? .bounce.down : .bounce.up, value: viewModel.isWatched) + .accessibilityIdentifier("Watch Button Icon") #if !os(tvOS) Text("Watched") @@ -766,6 +773,7 @@ extension ItemContentDetails { .frame(width: DrawingConstants.buttonWidth, height: DrawingConstants.buttonHeight) #endif } + .accessibilityIdentifier("Watch Button") #if !os(tvOS) .keyboardShortcut("w", modifiers: [.option]) #endif @@ -859,6 +867,7 @@ extension ItemContentDetails { #else VStack { Image(systemName: viewModel.isItemAddedToAnyList ? "rectangle.on.rectangle.angled.fill" : "rectangle.on.rectangle.angled") + .accessibilityIdentifier("List Button Icon") Text("Lists") .padding(.top, 2) .font(.caption) @@ -868,6 +877,7 @@ extension ItemContentDetails { .frame(width: DrawingConstants.buttonWidth, height: DrawingConstants.buttonHeight) #endif } + .accessibilityIdentifier("List Button") #if !os(tvOS) && !os(macOS) .controlSize(.small) #elseif !os(tvOS) diff --git a/Shared/View/ItemContent/NotificationListView.swift b/Shared/View/ItemContent/NotificationListView.swift index 52722a52..8d5d22a8 100644 --- a/Shared/View/ItemContent/NotificationListView.swift +++ b/Shared/View/ItemContent/NotificationListView.swift @@ -26,6 +26,7 @@ struct NotificationListView: View { EmptyView() } } + .accessibilityIdentifier("Notification List View") .overlay { if !hasLoaded { CronicaLoadingPopupView() diff --git a/Shared/View/Lists/EditCustomList.swift b/Shared/View/Lists/EditCustomList.swift index 60a38cd2..b3557afc 100644 --- a/Shared/View/Lists/EditCustomList.swift +++ b/Shared/View/Lists/EditCustomList.swift @@ -53,6 +53,7 @@ struct EditCustomList: View { Button("Delete", role: .destructive) { askConfirmationForDeletion = true } + .accessibilityIdentifier("DeleteListButton") .foregroundColor(.red) #if os(macOS) .buttonStyle(.link) @@ -73,6 +74,7 @@ struct EditCustomList: View { PersistenceController.shared.delete(item) } } + .accessibilityIdentifier("ConfirmDeleteButton") } } } diff --git a/Shared/View/Lists/NewCustomListView.swift b/Shared/View/Lists/NewCustomListView.swift index 444a0740..29dbe21c 100644 --- a/Shared/View/Lists/NewCustomListView.swift +++ b/Shared/View/Lists/NewCustomListView.swift @@ -25,8 +25,9 @@ struct NewCustomListView: View { Form { Section { TextField("Title", text: $title) + .accessibilityIdentifier("ListTitleTextField") TextField("Description", text: $note) - + .accessibilityIdentifier("ListDescriptionTextField") #if os(watchOS) || os(tvOS) || os(macOS) createList #endif @@ -61,6 +62,7 @@ struct NewCustomListView: View { private var createList: some View { Button("Create", action: save).disabled(title.isEmpty) + .accessibilityIdentifier("CreateNewListButton") } private var cancelButton: some View { diff --git a/Shared/View/Navigation/ExploreView.swift b/Shared/View/Navigation/ExploreView.swift index a6cd86ac..b296b6df 100644 --- a/Shared/View/Navigation/ExploreView.swift +++ b/Shared/View/Navigation/ExploreView.swift @@ -112,6 +112,7 @@ struct ExploreView: View { } #endif } + .accessibilityIdentifier("Discover View") .sheet(isPresented: $showFilters) { NavigationStack { Form { diff --git a/Shared/View/Navigation/HomeView.swift b/Shared/View/Navigation/HomeView.swift index 721adcce..7c6c06b9 100644 --- a/Shared/View/Navigation/HomeView.swift +++ b/Shared/View/Navigation/HomeView.swift @@ -53,6 +53,7 @@ struct HomeView: View { AttributionView() } } + .accessibilityIdentifier("Home View") #if os(iOS) .refreshable { reloadHome = true diff --git a/Shared/View/Navigation/SearchView.swift b/Shared/View/Navigation/SearchView.swift index 5a80c310..efe18c59 100644 --- a/Shared/View/Navigation/SearchView.swift +++ b/Shared/View/Navigation/SearchView.swift @@ -28,6 +28,7 @@ struct SearchView: View { posterView #endif } + .accessibilityIdentifier("Search View") .task { if !viewModel.items.isEmpty, viewModel.query.isEmpty { viewModel.items.removeAll() diff --git a/Shared/View/Navigation/TabBarView.swift b/Shared/View/Navigation/TabBarView.swift index d1538307..d5d94349 100644 --- a/Shared/View/Navigation/TabBarView.swift +++ b/Shared/View/Navigation/TabBarView.swift @@ -118,32 +118,36 @@ struct TabBarView: View { @available(iOS 18, *) private var newTabView: some View { TabView(selection: selectedTab) { - Tab("Home", systemImage: "house", value: .home) { - NavigationStack(path: $homePath) { - HomeView() - } + NavigationStack(path: $homePath) { + HomeView() + .tabItem { + Label("Home", systemImage: "house") + } } - Tab("Discover", systemImage: "popcorn", value: .explore) { - NavigationStack(path: $explorePath) { - ExploreView() - } + NavigationStack(path: $explorePath) { + ExploreView() + .tabItem { + Label("Discover", systemImage: "popcorn") + } } - Tab("Watchlist", systemImage: "rectangle.on.rectangle", value: .watchlist) { - NavigationStack(path: $watchlistPath) { - WatchlistView() - .environment(\.managedObjectContext, persistence.container.viewContext) - } + NavigationStack(path: $watchlistPath) { + WatchlistView() + .environment(\.managedObjectContext, persistence.container.viewContext) + .tabItem { + Label("Watchlist", systemImage: "rectangle.on.rectangle") + } } - Tab("Search", systemImage: "magnifyingglass", value: .search, role: .search) { - NavigationStack(path: $searchPath) { - SearchView(shouldFocusOnSearchField: $shouldOpenOnSearchField) - } + NavigationStack(path: $searchPath) { + SearchView(shouldFocusOnSearchField: $shouldOpenOnSearchField) + .tabItem { + Label("Search", systemImage: "magnifyingglass") + } } } - .tabViewStyle(.sidebarAdaptable) +// .tabViewStyle(.sidebarAdaptable) .appTheme() } diff --git a/Shared/View/Navigation/WatchlistView.swift b/Shared/View/Navigation/WatchlistView.swift index 3485cda2..65699a7a 100644 --- a/Shared/View/Navigation/WatchlistView.swift +++ b/Shared/View/Navigation/WatchlistView.swift @@ -34,6 +34,7 @@ struct WatchlistView: View { } } .actionPopup(isShowing: $showPopup, for: popupType) + .accessibilityIdentifier("Watchlist View") #if !os(tvOS) .navigationTitle(navigationTitle) #endif diff --git a/Shared/View/Onboard/WelcomeView.swift b/Shared/View/Onboard/WelcomeView.swift index 50879dd7..ed9aebd8 100644 --- a/Shared/View/Onboard/WelcomeView.swift +++ b/Shared/View/Onboard/WelcomeView.swift @@ -116,6 +116,7 @@ struct WelcomeView: View { ) } + .accessibilityIdentifier("Welcome View") } private func informationItem( diff --git a/Shared/View/Person/CastListView.swift b/Shared/View/Person/CastListView.swift index 411997e1..05406a49 100644 --- a/Shared/View/Person/CastListView.swift +++ b/Shared/View/Person/CastListView.swift @@ -48,6 +48,7 @@ struct CastListView: View { .padding(.trailing) } } + .accessibilityIdentifier("Cast List") #if os(tvOS) .padding() #endif diff --git a/Shared/View/Person/PersonCardView.swift b/Shared/View/Person/PersonCardView.swift index 76a1125f..6f198da0 100644 --- a/Shared/View/Person/PersonCardView.swift +++ b/Shared/View/Person/PersonCardView.swift @@ -94,6 +94,7 @@ struct PersonCardView: View { } #endif } + .accessibilityIdentifier("\(person.name) Card") #elseif os(tvOS) VStack(alignment: .leading) { NavigationLink(value: person) { diff --git a/Shared/View/Settings/SettingsView.swift b/Shared/View/Settings/SettingsView.swift index 3c401d5c..b7b1800c 100644 --- a/Shared/View/Settings/SettingsView.swift +++ b/Shared/View/Settings/SettingsView.swift @@ -94,6 +94,7 @@ struct SettingsView: View { } } } + .accessibilityIdentifier("Settings View") .navigationTitle("Settings") .navigationBarTitleDisplayMode(.inline) .scrollBounceBehavior(.basedOnSize, axes: .vertical) @@ -147,11 +148,13 @@ struct SettingsView: View { .clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous)) Image(systemName: icon) .foregroundColor(.white) + .accessibilityIdentifier("\(icon) Icon") } .frame(width: 30, height: 30, alignment: .center) .padding(.trailing, 8) .accessibilityHidden(true) Text(title) + .accessibilityIdentifier("\(title) Tab") } .padding(.vertical, 2) } diff --git a/Shared/View/Trailers/TrailerListView.swift b/Shared/View/Trailers/TrailerListView.swift index 234b1e36..693a45bd 100644 --- a/Shared/View/Trailers/TrailerListView.swift +++ b/Shared/View/Trailers/TrailerListView.swift @@ -30,10 +30,12 @@ struct TrailerListView: View { .padding(.trailing, trailer.id == self.trailers.last?.id ? 64 : 0) #endif .padding(.top, 8) + .accessibilityIdentifier("\(trailer.title)") } } } } + .accessibilityIdentifier("Trailers List") } } } diff --git a/Shared/View/WatchProviders/WatchProvidersList.swift b/Shared/View/WatchProviders/WatchProvidersList.swift index efbbea0f..ff73112f 100644 --- a/Shared/View/WatchProviders/WatchProvidersList.swift +++ b/Shared/View/WatchProviders/WatchProvidersList.swift @@ -44,6 +44,7 @@ struct WatchProvidersList: View { .padding(.horizontal, 6) .padding(.top, 8) .applyHoverEffect() + .accessibilityIdentifier("\(item.providerName ?? "") provider") #else VStack { Button { @@ -75,6 +76,7 @@ struct WatchProvidersList: View { } .padding(.bottom) } + .accessibilityIdentifier("Watch Providers List") } } } diff --git a/Story.xcodeproj/project.pbxproj b/Story.xcodeproj/project.pbxproj index 1c55620a..1fc3cc7f 100644 --- a/Story.xcodeproj/project.pbxproj +++ b/Story.xcodeproj/project.pbxproj @@ -7,6 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + 076233102C92D50200408995 /* HomeUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0762330F2C92D50200408995 /* HomeUITests.swift */; }; + 076E929F2C9324BB00F03CDB /* ItemContentDetailsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 076E929E2C9324BB00F03CDB /* ItemContentDetailsUITests.swift */; }; + 076E92A12C93A40F00F03CDB /* WatchListUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 076E92A02C93A40F00F03CDB /* WatchListUITests.swift */; }; + 076E92A32C93A4A200F03CDB /* SettingsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 076E92A22C93A4A200F03CDB /* SettingsUITests.swift */; }; + 076E92A72C93A8B800F03CDB /* AppNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 076E92A62C93A8B800F03CDB /* AppNavigator.swift */; }; + 07ABD4A02C8E3B8A005E0ECE /* CronicaUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07ABD49F2C8E3B8A005E0ECE /* CronicaUITests.swift */; }; + 07ABD4A22C8E3B8A005E0ECE /* CronicaUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07ABD4A12C8E3B8A005E0ECE /* CronicaUITestsLaunchTests.swift */; }; B801070829F24499008A4551 /* ItemContentCustomListSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8A2C88029CA6ED100FDBB33 /* ItemContentCustomListSelector.swift */; }; B801070929F244DE008A4551 /* NewCustomListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B812C7B3299480620096E806 /* NewCustomListView.swift */; }; B801070D29F255F3008A4551 /* DefaultListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B801070C29F255F3008A4551 /* DefaultListView.swift */; }; @@ -371,6 +378,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 07ABD4A32C8E3B8A005E0ECE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B8110ED3279245D000844FEB /* Project object */; + proxyType = 1; + remoteGlobalIDString = B8110EDE279245D100844FEB; + remoteInfo = "Story (iOS)"; + }; B806C4E92899BE2900A5330E /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = B8110ED3279245D000844FEB /* Project object */; @@ -420,6 +434,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0762330F2C92D50200408995 /* HomeUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeUITests.swift; sourceTree = ""; }; + 076E929E2C9324BB00F03CDB /* ItemContentDetailsUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemContentDetailsUITests.swift; sourceTree = ""; }; + 076E92A02C93A40F00F03CDB /* WatchListUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchListUITests.swift; sourceTree = ""; }; + 076E92A22C93A4A200F03CDB /* SettingsUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsUITests.swift; sourceTree = ""; }; + 076E92A62C93A8B800F03CDB /* AppNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppNavigator.swift; sourceTree = ""; }; + 07ABD49D2C8E3B8A005E0ECE /* CronicaUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CronicaUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 07ABD49F2C8E3B8A005E0ECE /* CronicaUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CronicaUITests.swift; sourceTree = ""; }; + 07ABD4A12C8E3B8A005E0ECE /* CronicaUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CronicaUITestsLaunchTests.swift; sourceTree = ""; }; B801070C29F255F3008A4551 /* DefaultListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultListView.swift; sourceTree = ""; }; B801070E29F25615008A4551 /* EmptyListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyListView.swift; sourceTree = ""; }; B801071029F2562C008A4551 /* WatchlistSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchlistSectionView.swift; sourceTree = ""; }; @@ -652,6 +674,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 07ABD49A2C8E3B8A005E0ECE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; B806C4DB2899BE2800A5330E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -696,6 +725,20 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 07ABD49E2C8E3B8A005E0ECE /* CronicaUITests */ = { + isa = PBXGroup; + children = ( + 07ABD49F2C8E3B8A005E0ECE /* CronicaUITests.swift */, + 07ABD4A12C8E3B8A005E0ECE /* CronicaUITestsLaunchTests.swift */, + 0762330F2C92D50200408995 /* HomeUITests.swift */, + 076E929E2C9324BB00F03CDB /* ItemContentDetailsUITests.swift */, + 076E92A02C93A40F00F03CDB /* WatchListUITests.swift */, + 076E92A22C93A4A200F03CDB /* SettingsUITests.swift */, + 076E92A62C93A8B800F03CDB /* AppNavigator.swift */, + ); + path = CronicaUITests; + sourceTree = ""; + }; B806C4DF2899BE2800A5330E /* AppleWatch */ = { isa = PBXGroup; children = ( @@ -736,6 +779,7 @@ B8D7AEAA28B95F5E0053CE5A /* CronicaWidget */, B88B88FC29DF194A00497058 /* CronicaTests */, B8FCAE902AED9CE300F688FE /* CronicaTests */, + 07ABD49E2C8E3B8A005E0ECE /* CronicaUITests */, B8B271A227A59EF400F6463F /* Frameworks */, B8110EE0279245D100844FEB /* Products */, B8110ED7279245D000844FEB /* Shared */, @@ -774,6 +818,7 @@ B806C4DE2899BE2800A5330E /* Cronica.app */, B8D7AEA728B95F5D0053CE5A /* CronicaWidgetExtension.appex */, B8FCAE8F2AED9CE300F688FE /* CronicaTests.xctest */, + 07ABD49D2C8E3B8A005E0ECE /* CronicaUITests.xctest */, ); name = Products; sourceTree = ""; @@ -1422,6 +1467,24 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 07ABD49C2C8E3B8A005E0ECE /* CronicaUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 07ABD4A72C8E3B8A005E0ECE /* Build configuration list for PBXNativeTarget "CronicaUITests" */; + buildPhases = ( + 07ABD4992C8E3B8A005E0ECE /* Sources */, + 07ABD49A2C8E3B8A005E0ECE /* Frameworks */, + 07ABD49B2C8E3B8A005E0ECE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 07ABD4A42C8E3B8A005E0ECE /* PBXTargetDependency */, + ); + name = CronicaUITests; + productName = CronicaUITests; + productReference = 07ABD49D2C8E3B8A005E0ECE /* CronicaUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; B806C4DD2899BE2800A5330E /* CronicaWatch Watch App */ = { isa = PBXNativeTarget; buildConfigurationList = B806C4EF2899BE2900A5330E /* Build configuration list for PBXNativeTarget "CronicaWatch Watch App" */; @@ -1517,9 +1580,13 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1500; + LastSwiftUpdateCheck = 1520; LastUpgradeCheck = 1500; TargetAttributes = { + 07ABD49C2C8E3B8A005E0ECE = { + CreatedOnToolsVersion = 15.2; + TestTargetID = B8110EDE279245D100844FEB; + }; B806C4DD2899BE2800A5330E = { CreatedOnToolsVersion = 14.0; }; @@ -1564,11 +1631,19 @@ B806C4DD2899BE2800A5330E /* CronicaWatch Watch App */, B8D7AEA628B95F5D0053CE5A /* CronicaWidgetExtension */, B8FCAE8E2AED9CE300F688FE /* CronicaTests */, + 07ABD49C2C8E3B8A005E0ECE /* CronicaUITests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 07ABD49B2C8E3B8A005E0ECE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; B806C4DC2899BE2800A5330E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1616,6 +1691,20 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 07ABD4992C8E3B8A005E0ECE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 076E92A12C93A40F00F03CDB /* WatchListUITests.swift in Sources */, + 076233102C92D50200408995 /* HomeUITests.swift in Sources */, + 076E92A72C93A8B800F03CDB /* AppNavigator.swift in Sources */, + 07ABD4A22C8E3B8A005E0ECE /* CronicaUITestsLaunchTests.swift in Sources */, + 07ABD4A02C8E3B8A005E0ECE /* CronicaUITests.swift in Sources */, + 076E92A32C93A4A200F03CDB /* SettingsUITests.swift in Sources */, + 076E929F2C9324BB00F03CDB /* ItemContentDetailsUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; B806C4DA2899BE2800A5330E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1977,6 +2066,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 07ABD4A42C8E3B8A005E0ECE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B8110EDE279245D100844FEB /* Story (iOS) */; + targetProxy = 07ABD4A32C8E3B8A005E0ECE /* PBXContainerItemProxy */; + }; B806C4EA2899BE2900A5330E /* PBXTargetDependency */ = { isa = PBXTargetDependency; platformFilter = ios; @@ -2000,6 +2094,48 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 07ABD4A52C8E3B8A005E0ECE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = moataz.CronicaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "Story (iOS)"; + }; + name = Debug; + }; + 07ABD4A62C8E3B8A005E0ECE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = moataz.CronicaUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "Story (iOS)"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; B806C4ED2899BE2900A5330E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2418,6 +2554,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 07ABD4A72C8E3B8A005E0ECE /* Build configuration list for PBXNativeTarget "CronicaUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 07ABD4A52C8E3B8A005E0ECE /* Debug */, + 07ABD4A62C8E3B8A005E0ECE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; B806C4EF2899BE2900A5330E /* Build configuration list for PBXNativeTarget "CronicaWatch Watch App" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Story.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Story.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1df46552..3065ebfd 100644 --- a/Story.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Story.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,13 +1,12 @@ { - "originHash" : "10227c8d0a04035986b0d85032e6dc32e07b443a112f640008888465d5bdb5d1", "pins" : [ { "identity" : "aptabase-swift", "kind" : "remoteSourceControl", "location" : "https://github.com/aptabase/aptabase-swift", "state" : { - "revision" : "62e47dad9b6900042a801eafdabb2023d259938e", - "version" : "0.3.8" + "revision" : "73579250784f7040aad28ec376a0ddfcdaa171cf", + "version" : "0.3.10" } }, { @@ -15,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/kean/Nuke", "state" : { - "revision" : "7395c7a9dcd390bbcfad17a731d8d529602702c6", - "version" : "12.7.0" + "revision" : "0ead44350d2737db384908569c012fe67c421e4d", + "version" : "12.8.0" } }, { @@ -38,5 +37,5 @@ } } ], - "version" : 3 + "version" : 2 } diff --git a/Story.xcodeproj/project.xcworkspace/xcuserdata/moatazakram.xcuserdatad/IDEFindNavigatorScopes.plist b/Story.xcodeproj/project.xcworkspace/xcuserdata/moatazakram.xcuserdatad/IDEFindNavigatorScopes.plist new file mode 100644 index 00000000..5dd5da85 --- /dev/null +++ b/Story.xcodeproj/project.xcworkspace/xcuserdata/moatazakram.xcuserdatad/IDEFindNavigatorScopes.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/Story.xcodeproj/project.xcworkspace/xcuserdata/moatazakram.xcuserdatad/UserInterfaceState.xcuserstate b/Story.xcodeproj/project.xcworkspace/xcuserdata/moatazakram.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 00000000..1fce8410 Binary files /dev/null and b/Story.xcodeproj/project.xcworkspace/xcuserdata/moatazakram.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Story.xcodeproj/xcshareddata/xcschemes/Cronica (EN-US).xcscheme b/Story.xcodeproj/xcshareddata/xcschemes/Cronica (EN-US).xcscheme index 036e1b47..b24d5e9a 100644 --- a/Story.xcodeproj/xcshareddata/xcschemes/Cronica (EN-US).xcscheme +++ b/Story.xcodeproj/xcshareddata/xcschemes/Cronica (EN-US).xcscheme @@ -39,6 +39,16 @@ ReferencedContainer = "container:Story.xcodeproj"> + + + + + version = "1.8"> - - - - + version = "1.8">