From b19028f19ce91a92868f16652a1909f1f4fc5e26 Mon Sep 17 00:00:00 2001 From: Balazs Perlaki-Horvath Date: Sat, 14 Jun 2025 14:46:32 +0200 Subject: [PATCH] Update UI tests --- .github/workflows/ci.yml | 18 +++++---- Model/OPDSParser/OPDSParser.swift | 2 + Tests/LibraryRefreshViewModelTest.swift | 12 +++++- Tests/OPDSParserTests.swift | 3 ++ UITests_iPad/LoadingUI_iPad_Test.swift | 51 ++++++++++++++++++++++--- ViewModel/LibraryViewModel.swift | 22 ++++------- Views/Library/Library.swift | 2 +- Views/Payment/PaymentForm.swift | 1 + Views/Payment/PaymentResultPopUp.swift | 1 + 9 files changed, 84 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91ce5c2a..96151352 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: - name: Run Unit Tests on iOS if: matrix.platform == 'iOS' - run: xcodebuild test -scheme Kiwix -destination 'platform=iOS Simulator,name=iPhone 16 Pro' + run: xcodebuild clean test -scheme Kiwix -destination 'platform=iOS Simulator,name=iPhone 16 Pro' - name: Run Unit Tests on macOS if: matrix.platform == 'macOS' @@ -75,13 +75,17 @@ jobs: env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - - name: Run UI Tests on iPhone - if: matrix.platform == 'iOS' - run: xcodebuild test -scheme UITests_iPhone -destination 'platform=iOS Simulator,name=iPhone 16 Pro' + # - name: Run UI Tests on iPhone + # if: matrix.platform == 'iOS' + # env: + # DEVICE_NAME: "iPhone 16 Pro Max" + # run: xcodebuild test -scheme UITests_iPhone -destination "platform=iOS Simulator,name=$DEVICE_NAME" - - name: Run UI Tests on iPad - if: matrix.platform == 'iOS' - run: xcodebuild test -scheme UITests_iPad -destination 'platform=iOS Simulator,name=iPad Pro 13-inch (M4)' + # - name: Run UI Tests on iPad + # if: matrix.platform == 'iOS' + # env: + # DEVICE_NAME: "iPad Pro 13-inch (M4)" + # run: xcodebuild test -scheme UITests_iPad -destination "platform=iOS Simulator,name=$DEVICE_NAME" - name: Run UI Tests on macOS if: matrix.platform == 'macOS' diff --git a/Model/OPDSParser/OPDSParser.swift b/Model/OPDSParser/OPDSParser.swift index 510b7586..e0730c57 100644 --- a/Model/OPDSParser/OPDSParser.swift +++ b/Model/OPDSParser/OPDSParser.swift @@ -15,6 +15,7 @@ protocol Parser { var zimFileIDs: Set { get } + @ZimActor func parse(data: Data, urlHost: String) throws func getMetaData(id: UUID) -> ZimFileMetaData? } @@ -24,6 +25,7 @@ extension OPDSParser: Parser { __getZimFileIDs() as? Set ?? Set() } + @ZimActor func parse(data: Data, urlHost: String) throws { if !self.__parseData(data, using: urlHost.removingSuffix("/")) { throw LibraryRefreshError.parse diff --git a/Tests/LibraryRefreshViewModelTest.swift b/Tests/LibraryRefreshViewModelTest.swift index 3c1535e9..e6af8b35 100644 --- a/Tests/LibraryRefreshViewModelTest.swift +++ b/Tests/LibraryRefreshViewModelTest.swift @@ -240,13 +240,21 @@ final class LibraryRefreshViewModelTest: XCTestCase { func testZimFileDeprecation() async throws { let testDefaults = TestDefaults() testDefaults.setup() + let context = Database.shared.viewContext + let oldZimFiles = try? context.fetch(ZimFile.fetchRequest()) + oldZimFiles?.forEach { zimFile in + context.delete(zimFile) + } + if context.hasChanges { + try? context.save() + } + // refresh library for the first time, which should create one zim file let viewModel = LibraryViewModel(urlSession: urlSession, processFactory: { LibraryProcess(defaultState: .initial) }, defaults: testDefaults, categories: CategoriesToLanguages(withDefaults: testDefaults)) await viewModel.start(isUserInitiated: true) - let context = Database.shared.viewContext let zimFile1 = try XCTUnwrap(try context.fetch(ZimFile.fetchRequest()).first) // refresh library for the second time, which should replace the old zim file with a new one @@ -271,6 +279,8 @@ final class LibraryRefreshViewModelTest: XCTestCase { // clean up context.delete(zimFile1) context.delete(zimFile2) + + try? context.save() } } diff --git a/Tests/OPDSParserTests.swift b/Tests/OPDSParserTests.swift index 282ff68f..efcc7788 100644 --- a/Tests/OPDSParserTests.swift +++ b/Tests/OPDSParserTests.swift @@ -18,6 +18,7 @@ import XCTest final class OPDSParserTests: XCTestCase { /// Test OPDSParser.parse throws error when OPDS data is invalid. + @ZimActor func testInvalidOPDSData() { XCTExpectFailure("Requires work in dependency to resolve the issue.") let content = "Invalid OPDS Data" @@ -26,6 +27,7 @@ final class OPDSParserTests: XCTestCase { ) } + @ZimActor func testNonCompatibleDataWithUT8() throws { let content = "any data" let incompatibleEncodings: [String.Encoding] = [.unicode, .utf16, .utf32] @@ -44,6 +46,7 @@ final class OPDSParserTests: XCTestCase { } /// Test OPDSParser can parse and extract zim file metadata. + @ZimActor func test() throws { let content = """ Bool in + let alertButton = alert.buttons["Allow"] + if alertButton.exists { + alertButton.tap() + return true + } + return false + } + let openMainPageButton = app.buttons["Open Main Page"] + Wait.inApp(app, forElement: openMainPageButton) + openMainPageButton.tap() + usleep(1) + + // RESTART THE APP + app.terminate() + usleep(1) + app.activate() + + testAfterRelaunch(app) + + app.terminate() + + } + + private func testAfterRelaunch(_ app: XCUIApplication) { + let sidebar = app.collectionViews["sidebar_collection_view"] + let zimFileTab = sidebar.cells["Apache Pig Documentation"].firstMatch + Wait.inApp(app, forElement: zimFileTab) + XCTAssert(sidebar.cells["Apache Pig Documentation"].firstMatch.isSelected) sidebar.cells["bookmarks"].tap() sidebar.cells["opened"].tap() @@ -40,5 +80,6 @@ final class LoadingUI_iPad_Test: XCTestCase { sidebar.cells["new"].tap() sidebar.cells["settings"].tap() sidebar.cells["donation"].tap() + app.buttons["close_payment_button"].tap() } } diff --git a/ViewModel/LibraryViewModel.swift b/ViewModel/LibraryViewModel.swift index 9140ecb7..3682fb5a 100644 --- a/ViewModel/LibraryViewModel.swift +++ b/ViewModel/LibraryViewModel.swift @@ -131,7 +131,7 @@ final class LibraryViewModel: ObservableObject { process.state = .inProgress // refresh library - guard case (var data, let responseURL)? = try await fetchData() else { + guard case (let data, let responseURL)? = try await fetchData() else { // this is the case when we have no new data (304 http) // but we still need to refresh the memory only stored // zimfile categories to languages dictionary @@ -283,19 +283,13 @@ final class LibraryViewModel: ObservableObject { } private func parse(data: Data, urlHost: URL) async throws -> OPDSParser { - try await withCheckedThrowingContinuation { continuation in - let parser = OPDSParser() - do { - let urlHostString = urlHost - .withoutQueryParams() - .trim(pathComponents: ["catalog", "v2", "entries"]) - .absoluteString - try parser.parse(data: data, urlHost: urlHostString) - continuation.resume(returning: parser) - } catch { - continuation.resume(throwing: error) - } - } + let parser = OPDSParser() + let urlHostString = urlHost + .withoutQueryParams() + .trim(pathComponents: ["catalog", "v2", "entries"]) + .absoluteString + try await parser.parse(data: data, urlHost: urlHostString) + return parser } private func process(parser: Parser) async throws { diff --git a/Views/Library/Library.swift b/Views/Library/Library.swift index 37754f46..51235be3 100644 --- a/Views/Library/Library.swift +++ b/Views/Library/Library.swift @@ -228,7 +228,7 @@ struct LibraryZimFileContext: View { ZimFileDetail(zimFile: zimFile, dismissParent: dismiss) } label: { content - } + } .accessibilityIdentifier(zimFile.name) #endif }.contextMenu { ZimFileContextMenu(zimFile: zimFile) diff --git a/Views/Payment/PaymentForm.swift b/Views/Payment/PaymentForm.swift index 78bda3d4..4f8c2c96 100644 --- a/Views/Payment/PaymentForm.swift +++ b/Views/Payment/PaymentForm.swift @@ -45,6 +45,7 @@ struct PaymentForm: View { Button("", systemImage: "x.circle.fill") { dismiss() } + .accessibilityIdentifier("close_payment_button") .font(.title) .foregroundStyle(.tertiary) .padding() diff --git a/Views/Payment/PaymentResultPopUp.swift b/Views/Payment/PaymentResultPopUp.swift index 46f98e9b..dc6541fe 100644 --- a/Views/Payment/PaymentResultPopUp.swift +++ b/Views/Payment/PaymentResultPopUp.swift @@ -65,6 +65,7 @@ struct PaymentResultPopUp: View { Button("", systemImage: "x.circle.fill") { dismiss() } + .accessibilityIdentifier("close_payment_button") .font(.title2) .foregroundStyle(.secondary) .padding()