From e00a0d6a60a08ce8892085736c9b31b259018d43 Mon Sep 17 00:00:00 2001 From: gogolakovaa Date: Sun, 15 Sep 2024 15:14:58 +0200 Subject: [PATCH 01/41] Creates Login Feature first draft --- .../iOS/RocketApp.xcodeproj/project.pbxproj | 7 +++ Solution/iOS/RocketApp/Features/Package.swift | 10 ++++ .../Features/Sources/Login/LoginCore.swift | 49 +++++++++++++++ .../Features/Sources/Login/LoginView.swift | 60 +++++++++++++++++++ Solution/iOS/RocketApp/RocketApp.swift | 15 +++-- 5 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 Solution/iOS/RocketApp/Features/Sources/Login/LoginCore.swift create mode 100644 Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift diff --git a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj index 30a84697a..912c30ad0 100644 --- a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj +++ b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 165B20552999449B0047F70A /* Infrastructure in Resources */ = {isa = PBXBuildFile; fileRef = 165B20532999449B0047F70A /* Infrastructure */; }; 16BC7FFC29CC9AE5002AAE15 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 16BC7FFB29CC9AE5002AAE15 /* Assets.xcassets */; }; 635FE7B12A20CDD40018A585 /* RocketList in Frameworks */ = {isa = PBXBuildFile; productRef = 635FE7B02A20CDD40018A585 /* RocketList */; }; + 6EA6ACB52C96F40A00EA0B8A /* Login in Frameworks */ = {isa = PBXBuildFile; productRef = 6EA6ACB42C96F40A00EA0B8A /* Login */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -31,6 +32,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 6EA6ACB52C96F40A00EA0B8A /* Login in Frameworks */, 635FE7B12A20CDD40018A585 /* RocketList in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -103,6 +105,7 @@ name = RocketApp; packageProductDependencies = ( 635FE7B02A20CDD40018A585 /* RocketList */, + 6EA6ACB42C96F40A00EA0B8A /* Login */, ); productName = RocketApp; productReference = 160CA40D28A506E4004A274A /* RocketApp.app */; @@ -407,6 +410,10 @@ isa = XCSwiftPackageProductDependency; productName = RocketList; }; + 6EA6ACB42C96F40A00EA0B8A /* Login */ = { + isa = XCSwiftPackageProductDependency; + productName = Login; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 160CA40528A506E4004A274A /* Project object */; diff --git a/Solution/iOS/RocketApp/Features/Package.swift b/Solution/iOS/RocketApp/Features/Package.swift index fb40ae486..7690916ac 100644 --- a/Solution/iOS/RocketApp/Features/Package.swift +++ b/Solution/iOS/RocketApp/Features/Package.swift @@ -8,6 +8,10 @@ let package = Package( platforms: [.iOS(.v16), .macOS(.v12)], products: [ + .library( + name: "Login", + targets: ["Login"] + ), .library( name: "RocketDetail", targets: ["RocketDetail"] @@ -35,6 +39,12 @@ let package = Package( ], targets: [ + .target( + name: "Login", + dependencies: [ + .product(name: "ComposableArchitecture", package: "swift-composable-architecture") + ] + ), .target( name: "RocketDetail", dependencies: [ diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/LoginCore.swift b/Solution/iOS/RocketApp/Features/Sources/Login/LoginCore.swift new file mode 100644 index 000000000..3822b23c7 --- /dev/null +++ b/Solution/iOS/RocketApp/Features/Sources/Login/LoginCore.swift @@ -0,0 +1,49 @@ +import ComposableArchitecture +import Foundation + +public struct LoginCore: ReducerProtocol { + public struct State: Equatable { + @BindingState public var username: String + @BindingState public var password: String + public var isAuthFailedMessageDisplayed: Bool + + public init(username: String = "", password: String = "", isAuthFailedMessageDisplayed: Bool = false) { + self.username = username + self.password = password + self.isAuthFailedMessageDisplayed = isAuthFailedMessageDisplayed + } + } + + public enum Action: BindableAction { + case binding(BindingAction) + case loginTapped + case loginSucceeded + } + + public init() {} + + public var body: some ReducerProtocol { + BindingReducer() + + Reduce { state, action in + switch action { + case .loginTapped: + guard isValid(username: state.username, password: state.password) else { + state.isAuthFailedMessageDisplayed = true + return .none + } + + return EffectTask(value: .loginSucceeded) + case .binding: + state.isAuthFailedMessageDisplayed = false + return .none + case .loginSucceeded: + return .none + } + } + } +} + +func isValid(username: String, password: String) -> Bool { + return false +} diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift new file mode 100644 index 000000000..96aee272a --- /dev/null +++ b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift @@ -0,0 +1,60 @@ +import ComposableArchitecture +import SwiftUI + +public struct LoginView: View { + let store: StoreOf + + public init(store: StoreOf) { + self.store = store + } + + public var body: some View { + WithViewStore(self.store) { viewStore in + VStack { + Text("Shiiiiiiiiiit, your credentials are not valid") + .foregroundStyle(.white) + .frame(maxWidth: .infinity) + .padding() + .background(Color.pink) + .transition(.opacity) + .opacity(viewStore.isAuthFailedMessageDisplayed ? 1 : 0) + .accessibilityHidden(!viewStore.isAuthFailedMessageDisplayed) + .animation(.interpolatingSpring, value: viewStore.isAuthFailedMessageDisplayed) + + content + } + } + } + + var content: some View { + WithViewStore(self.store) { viewStore in + VStack { + Text("Login") + .font(.largeTitle) + .fontWeight(.bold) + .padding(.top, 20) + + Spacer() + + TextField("Username", text: viewStore.binding(\.$username)) + .textFieldStyle(.roundedBorder) + + SecureField("Password", text: viewStore.binding(\.$password)) + .textFieldStyle(.roundedBorder) + + Spacer() + + Button("Login") { + viewStore.send(.loginTapped) + } + .buttonStyle(.borderedProminent) + .tint(.pink) + } + .padding(.horizontal, 20) + } + } +} + +//#Preview { +// LoginView() +//} diff --git a/Solution/iOS/RocketApp/RocketApp.swift b/Solution/iOS/RocketApp/RocketApp.swift index ab9c94a4c..d15b82b38 100644 --- a/Solution/iOS/RocketApp/RocketApp.swift +++ b/Solution/iOS/RocketApp/RocketApp.swift @@ -1,15 +1,22 @@ import ComposableArchitecture -import RocketList +//import RocketList +import Login import SwiftUI @main struct RocketApp: App { var body: some Scene { WindowGroup { - RocketListView( +// RocketListView( +// store: Store( +// initialState: RocketListCore.State(), +// reducer: RocketListCore()._printChanges() +// ) +// ) + LoginView( store: Store( - initialState: RocketListCore.State(), - reducer: RocketListCore()._printChanges() + initialState: LoginCore.State(), + reducer: LoginCore() ) ) } From 47ca89d6012ee6237737ed803de7a2cc73d26c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikol=20Duchanova=CC=81?= Date: Sun, 15 Sep 2024 16:12:05 +0200 Subject: [PATCH 02/41] Add UI Tests setup --- .../iOS/RocketApp.xcodeproj/project.pbxproj | 134 +++++++++++++++++- .../xcshareddata/xcschemes/RocketApp.xcscheme | 13 ++ .../Sources/UIToolkit/AccessibilityKeys.swift | 1 + .../RocketAppUITests/Utils/BaseScreen.swift | 5 + .../RocketAppUITests/Utils/BaseTestCase.swift | 18 +++ 5 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/AccessibilityKeys.swift create mode 100644 Solution/iOS/RocketAppUITests/Utils/BaseScreen.swift create mode 100644 Solution/iOS/RocketAppUITests/Utils/BaseTestCase.swift diff --git a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj index 30a84697a..8459553ea 100644 --- a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj +++ b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 55; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -15,6 +15,16 @@ 635FE7B12A20CDD40018A585 /* RocketList in Frameworks */ = {isa = PBXBuildFile; productRef = 635FE7B02A20CDD40018A585 /* RocketList */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + E5A6B6FA2C97216000376970 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 160CA40528A506E4004A274A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 160CA40C28A506E4004A274A; + remoteInfo = RocketApp; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 1607AECC28D21C240030F1DF /* AllTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = AllTests.xctestplan; sourceTree = ""; }; 1607AECD28D21D040030F1DF /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; @@ -24,8 +34,13 @@ 165B20522999449B0047F70A /* Features */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Features; sourceTree = ""; }; 165B20532999449B0047F70A /* Infrastructure */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Infrastructure; sourceTree = ""; }; 16BC7FFB29CC9AE5002AAE15 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + E5A6B6F42C97216000376970 /* RocketAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RocketAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedRootGroup section */ + E5A6B6F52C97216000376970 /* RocketAppUITests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = RocketAppUITests; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 160CA40A28A506E4004A274A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -35,6 +50,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + E5A6B6F12C97216000376970 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -52,6 +74,7 @@ children = ( 1607AECD28D21D040030F1DF /* README.md */, 163E0EBC28C7988100CECFD8 /* RocketApp */, + E5A6B6F52C97216000376970 /* RocketAppUITests */, 160CA40E28A506E4004A274A /* Products */, 163E0F2D28D093BD00CECFD8 /* Frameworks */, ); @@ -61,6 +84,7 @@ isa = PBXGroup; children = ( 160CA40D28A506E4004A274A /* RocketApp.app */, + E5A6B6F42C97216000376970 /* RocketAppUITests.xctest */, ); name = Products; sourceTree = ""; @@ -108,6 +132,29 @@ productReference = 160CA40D28A506E4004A274A /* RocketApp.app */; productType = "com.apple.product-type.application"; }; + E5A6B6F32C97216000376970 /* RocketAppUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = E5A6B6FE2C97216000376970 /* Build configuration list for PBXNativeTarget "RocketAppUITests" */; + buildPhases = ( + E5A6B6F02C97216000376970 /* Sources */, + E5A6B6F12C97216000376970 /* Frameworks */, + E5A6B6F22C97216000376970 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + E5A6B6FB2C97216000376970 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + E5A6B6F52C97216000376970 /* RocketAppUITests */, + ); + name = RocketAppUITests; + packageProductDependencies = ( + ); + productName = RocketAppUITests; + productReference = E5A6B6F42C97216000376970 /* RocketAppUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -115,13 +162,17 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1340; + LastSwiftUpdateCheck = 1600; LastUpgradeCheck = 1420; TargetAttributes = { 160CA40C28A506E4004A274A = { CreatedOnToolsVersion = 13.4.1; LastSwiftMigration = 1340; }; + E5A6B6F32C97216000376970 = { + CreatedOnToolsVersion = 16.0; + TestTargetID = 160CA40C28A506E4004A274A; + }; }; }; buildConfigurationList = 160CA40828A506E4004A274A /* Build configuration list for PBXProject "RocketApp" */; @@ -142,6 +193,7 @@ projectRoot = ""; targets = ( 160CA40C28A506E4004A274A /* RocketApp */, + E5A6B6F32C97216000376970 /* RocketAppUITests */, ); }; /* End PBXProject section */ @@ -158,6 +210,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + E5A6B6F22C97216000376970 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -191,8 +250,23 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + E5A6B6F02C97216000376970 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + E5A6B6FB2C97216000376970 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 160CA40C28A506E4004A274A /* RocketApp */; + targetProxy = E5A6B6FA2C97216000376970 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ 160CA42F28A506E6004A274A /* Debug */ = { isa = XCBuildConfiguration; @@ -379,6 +453,53 @@ }; name = Release; }; + E5A6B6FC2C97216000376970 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = L4GD77D338; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = cz.quanti.RocketAppUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = RocketApp; + }; + name = Debug; + }; + E5A6B6FD2C97216000376970 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = L4GD77D338; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = cz.quanti.RocketAppUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = RocketApp; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -400,6 +521,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + E5A6B6FE2C97216000376970 /* Build configuration list for PBXNativeTarget "RocketAppUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E5A6B6FC2C97216000376970 /* Debug */, + E5A6B6FD2C97216000376970 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ diff --git a/Solution/iOS/RocketApp.xcodeproj/xcshareddata/xcschemes/RocketApp.xcscheme b/Solution/iOS/RocketApp.xcodeproj/xcshareddata/xcschemes/RocketApp.xcscheme index f567a1044..fd95eab32 100644 --- a/Solution/iOS/RocketApp.xcodeproj/xcshareddata/xcschemes/RocketApp.xcscheme +++ b/Solution/iOS/RocketApp.xcodeproj/xcshareddata/xcschemes/RocketApp.xcscheme @@ -51,6 +51,19 @@ default = "YES"> + + + + + + Date: Sun, 15 Sep 2024 17:58:16 +0200 Subject: [PATCH 03/41] Adds AppCore and integrates login and logout --- .../iOS/RocketApp.xcodeproj/project.pbxproj | 7 +++ Solution/iOS/RocketApp/Features/Package.swift | 13 ++++++ .../Features/Sources/App/AppCore.swift | 43 +++++++++++++++++++ .../Features/Sources/App/AppView.swift | 36 ++++++++++++++++ .../Features/Sources/Login/LoginCore.swift | 7 ++- .../Features/Sources/Login/LoginView.swift | 17 +++++--- .../RocketDetail/RocketDetailView.swift | 2 +- .../Sources/RocketList/RocketListCore.swift | 3 +- .../Sources/RocketList/RocketListView.swift | 26 +++++++---- .../quanti.colorset/Contents.json | 20 +++++++++ .../UIToolkit/Resources/Color+quanti.swift | 5 +++ Solution/iOS/RocketApp/RocketApp.swift | 15 ++----- 12 files changed, 167 insertions(+), 27 deletions(-) create mode 100644 Solution/iOS/RocketApp/Features/Sources/App/AppCore.swift create mode 100644 Solution/iOS/RocketApp/Features/Sources/App/AppView.swift create mode 100644 Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/Resources/Assets.xcassets/quanti.colorset/Contents.json create mode 100644 Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/Resources/Color+quanti.swift diff --git a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj index 912c30ad0..7ce634ed5 100644 --- a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj +++ b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 165B20552999449B0047F70A /* Infrastructure in Resources */ = {isa = PBXBuildFile; fileRef = 165B20532999449B0047F70A /* Infrastructure */; }; 16BC7FFC29CC9AE5002AAE15 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 16BC7FFB29CC9AE5002AAE15 /* Assets.xcassets */; }; 635FE7B12A20CDD40018A585 /* RocketList in Frameworks */ = {isa = PBXBuildFile; productRef = 635FE7B02A20CDD40018A585 /* RocketList */; }; + 6E3F7D202C972CFD00554BA3 /* App in Frameworks */ = {isa = PBXBuildFile; productRef = 6E3F7D1F2C972CFD00554BA3 /* App */; }; 6EA6ACB52C96F40A00EA0B8A /* Login in Frameworks */ = {isa = PBXBuildFile; productRef = 6EA6ACB42C96F40A00EA0B8A /* Login */; }; /* End PBXBuildFile section */ @@ -32,6 +33,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 6E3F7D202C972CFD00554BA3 /* App in Frameworks */, 6EA6ACB52C96F40A00EA0B8A /* Login in Frameworks */, 635FE7B12A20CDD40018A585 /* RocketList in Frameworks */, ); @@ -106,6 +108,7 @@ packageProductDependencies = ( 635FE7B02A20CDD40018A585 /* RocketList */, 6EA6ACB42C96F40A00EA0B8A /* Login */, + 6E3F7D1F2C972CFD00554BA3 /* App */, ); productName = RocketApp; productReference = 160CA40D28A506E4004A274A /* RocketApp.app */; @@ -410,6 +413,10 @@ isa = XCSwiftPackageProductDependency; productName = RocketList; }; + 6E3F7D1F2C972CFD00554BA3 /* App */ = { + isa = XCSwiftPackageProductDependency; + productName = App; + }; 6EA6ACB42C96F40A00EA0B8A /* Login */ = { isa = XCSwiftPackageProductDependency; productName = Login; diff --git a/Solution/iOS/RocketApp/Features/Package.swift b/Solution/iOS/RocketApp/Features/Package.swift index 7690916ac..9670a663c 100644 --- a/Solution/iOS/RocketApp/Features/Package.swift +++ b/Solution/iOS/RocketApp/Features/Package.swift @@ -8,6 +8,10 @@ let package = Package( platforms: [.iOS(.v16), .macOS(.v12)], products: [ + .library( + name: "App", + targets: ["App"] + ), .library( name: "Login", targets: ["Login"] @@ -39,9 +43,18 @@ let package = Package( ], targets: [ + .target( + name: "App", + dependencies: [ + "Login", + "RocketList", + .product(name: "ComposableArchitecture", package: "swift-composable-architecture") + ] + ), .target( name: "Login", dependencies: [ + .product(name: "UIToolkit", package: "Infrastructure"), .product(name: "ComposableArchitecture", package: "swift-composable-architecture") ] ), diff --git a/Solution/iOS/RocketApp/Features/Sources/App/AppCore.swift b/Solution/iOS/RocketApp/Features/Sources/App/AppCore.swift new file mode 100644 index 000000000..4c156a604 --- /dev/null +++ b/Solution/iOS/RocketApp/Features/Sources/App/AppCore.swift @@ -0,0 +1,43 @@ +import ComposableArchitecture +import Foundation +import Login +import RocketList + +public struct AppCore: ReducerProtocol { + public enum State: Equatable { + case login(LoginCore.State) + case rocketList(RocketListCore.State) + + public init() { + self = .login(LoginCore.State()) + } + } + + public enum Action { + case login(LoginCore.Action) + case rocketList(RocketListCore.Action) + } + + public init() {} + + public var body: some ReducerProtocol { + Reduce { state, action in + switch action { + case .login(.loginSucceeded): + state = .rocketList(RocketListCore.State()) + return .none + case .rocketList(.logoutTapped): + state = .login(LoginCore.State()) + return .none + default: + return .none + } + } + .ifCaseLet(/State.login, action: /Action.login) { + LoginCore() + } + .ifCaseLet(/State.rocketList, action: /Action.rocketList) { + RocketListCore() + } + } +} diff --git a/Solution/iOS/RocketApp/Features/Sources/App/AppView.swift b/Solution/iOS/RocketApp/Features/Sources/App/AppView.swift new file mode 100644 index 000000000..150e07c3e --- /dev/null +++ b/Solution/iOS/RocketApp/Features/Sources/App/AppView.swift @@ -0,0 +1,36 @@ +import ComposableArchitecture +import Login +import RocketList +import SwiftUI + +public struct AppView: View { + let store: StoreOf + + public init(store: StoreOf) { + self.store = store + } + + public var body: some View { + SwitchStore(store) { + CaseLet( + state: /AppCore.State.login, + action: AppCore.Action.login, + then: LoginView.init + ) + CaseLet( + state: /AppCore.State.rocketList, + action: AppCore.Action.rocketList, + then: RocketListView.init + ) + } + } +} + +#Preview { + AppView( + store: Store( + initialState: AppCore.State(), + reducer: AppCore() + ) + ) +} diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/LoginCore.swift b/Solution/iOS/RocketApp/Features/Sources/Login/LoginCore.swift index 3822b23c7..4b483b5ae 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/LoginCore.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/LoginCore.swift @@ -45,5 +45,10 @@ public struct LoginCore: ReducerProtocol { } func isValid(username: String, password: String) -> Bool { - return false + let validPairs: [String: String] = [ + "username1": "test1234", + "astronaut1": "space" + ] + + return validPairs[username] == password } diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift index 96aee272a..dc37d02c1 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift @@ -1,5 +1,6 @@ import ComposableArchitecture import SwiftUI +import UIToolkit public struct LoginView: View { let store: StoreOf @@ -15,7 +16,7 @@ public struct LoginView: View { .foregroundStyle(.white) .frame(maxWidth: .infinity) .padding() - .background(Color.pink) + .background(Color.quanti) .transition(.opacity) .opacity(viewStore.isAuthFailedMessageDisplayed ? 1 : 0) .accessibilityHidden(!viewStore.isAuthFailedMessageDisplayed) @@ -38,6 +39,7 @@ public struct LoginView: View { TextField("Username", text: viewStore.binding(\.$username)) .textFieldStyle(.roundedBorder) + .autocapitalization(.none) SecureField("Password", text: viewStore.binding(\.$password)) .textFieldStyle(.roundedBorder) @@ -48,13 +50,18 @@ public struct LoginView: View { viewStore.send(.loginTapped) } .buttonStyle(.borderedProminent) - .tint(.pink) + .tint(.quanti) } .padding(.horizontal, 20) } } } -//#Preview { -// LoginView() -//} +#Preview { + LoginView( + store: Store( + initialState: LoginCore.State(), + reducer: LoginCore() + ) + ) +} diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift index d6ebd2b71..603b2d4b6 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift @@ -130,7 +130,7 @@ public struct RocketDetailView: View { } } - private func paramWindow(type: RocketDetail.RocketParameters, backgroundColor: Color = .pink) -> some View { + private func paramWindow(type: RocketDetail.RocketParameters, backgroundColor: Color = .quanti) -> some View { VStack(spacing: 4) { Text( type.detail( diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListCore.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListCore.swift index c3bd49bb0..c64da79b3 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListCore.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListCore.swift @@ -19,6 +19,7 @@ public struct RocketListCore: ReducerProtocol { case fetchData case dataFetched(TaskResult<[RocketDetail]>) case rocketDetail(PresentationAction) + case logoutTapped } public init() {} @@ -58,7 +59,7 @@ public struct RocketListCore: ReducerProtocol { state.loadingStatus = .failure(RocketsClientAsyncError(from: error)) return .none - case .rocketDetail: + case .rocketDetail, .logoutTapped: return .none } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift index 7e9b6e087..09edc206a 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift @@ -26,15 +26,25 @@ public struct RocketListView: View { public var body: some View { NavigationStack { - Group { - switch viewStore.loadingStatus { - case let .success(data): - rocketsListView(rocketData: data) - case let .failure(error): - errorView(error: error) - default: - loadingView + VStack { + Group { + switch viewStore.loadingStatus { + case let .success(data): + rocketsListView(rocketData: data) + case let .failure(error): + errorView(error: error) + default: + loadingView + } } + + Spacer() + + Button("Logout") { + viewStore.send(.logoutTapped) + } + .buttonStyle(.borderedProminent) + .tint(.quanti) } .navigationTitle(.rockets) } diff --git a/Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/Resources/Assets.xcassets/quanti.colorset/Contents.json b/Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/Resources/Assets.xcassets/quanti.colorset/Contents.json new file mode 100644 index 000000000..2dd2fb5cc --- /dev/null +++ b/Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/Resources/Assets.xcassets/quanti.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x87", + "green" : "0x51", + "red" : "0xF1" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/Resources/Color+quanti.swift b/Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/Resources/Color+quanti.swift new file mode 100644 index 000000000..583ae48de --- /dev/null +++ b/Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/Resources/Color+quanti.swift @@ -0,0 +1,5 @@ +import SwiftUI + +public extension Color { + static let quanti = Color("quanti", bundle: .module) +} diff --git a/Solution/iOS/RocketApp/RocketApp.swift b/Solution/iOS/RocketApp/RocketApp.swift index d15b82b38..951007622 100644 --- a/Solution/iOS/RocketApp/RocketApp.swift +++ b/Solution/iOS/RocketApp/RocketApp.swift @@ -1,22 +1,15 @@ import ComposableArchitecture -//import RocketList -import Login +import App import SwiftUI @main struct RocketApp: App { var body: some Scene { WindowGroup { -// RocketListView( -// store: Store( -// initialState: RocketListCore.State(), -// reducer: RocketListCore()._printChanges() -// ) -// ) - LoginView( + AppView( store: Store( - initialState: LoginCore.State(), - reducer: LoginCore() + initialState: AppCore.State(), + reducer: AppCore() ) ) } From 6cda78830ff248a995cba97be02b80b0a0f0b0ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikol=20Duchanova=CC=81?= Date: Sun, 15 Sep 2024 18:10:45 +0200 Subject: [PATCH 04/41] Add UI Tests scheme and format code --- .../iOS/RocketApp.xcodeproj/project.pbxproj | 7 ++ .../xcschemes/RocketAppUITests.xcscheme | 89 +++++++++++++++++++ .../RocketAppUITests/Utils/BaseScreen.swift | 2 +- .../RocketAppUITests/Utils/BaseTestCase.swift | 26 +++--- 4 files changed, 110 insertions(+), 14 deletions(-) create mode 100644 Solution/iOS/RocketApp.xcodeproj/xcshareddata/xcschemes/RocketAppUITests.xcscheme diff --git a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj index 8459553ea..3105228ab 100644 --- a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj +++ b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 165B20552999449B0047F70A /* Infrastructure in Resources */ = {isa = PBXBuildFile; fileRef = 165B20532999449B0047F70A /* Infrastructure */; }; 16BC7FFC29CC9AE5002AAE15 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 16BC7FFB29CC9AE5002AAE15 /* Assets.xcassets */; }; 635FE7B12A20CDD40018A585 /* RocketList in Frameworks */ = {isa = PBXBuildFile; productRef = 635FE7B02A20CDD40018A585 /* RocketList */; }; + E55944B42C97328A00594002 /* UIToolkit in Frameworks */ = {isa = PBXBuildFile; productRef = E55944B32C97328A00594002 /* UIToolkit */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -54,6 +55,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + E55944B42C97328A00594002 /* UIToolkit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -150,6 +152,7 @@ ); name = RocketAppUITests; packageProductDependencies = ( + E55944B32C97328A00594002 /* UIToolkit */, ); productName = RocketAppUITests; productReference = E5A6B6F42C97216000376970 /* RocketAppUITests.xctest */; @@ -537,6 +540,10 @@ isa = XCSwiftPackageProductDependency; productName = RocketList; }; + E55944B32C97328A00594002 /* UIToolkit */ = { + isa = XCSwiftPackageProductDependency; + productName = UIToolkit; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 160CA40528A506E4004A274A /* Project object */; diff --git a/Solution/iOS/RocketApp.xcodeproj/xcshareddata/xcschemes/RocketAppUITests.xcscheme b/Solution/iOS/RocketApp.xcodeproj/xcshareddata/xcschemes/RocketAppUITests.xcscheme new file mode 100644 index 000000000..06cb4e4b4 --- /dev/null +++ b/Solution/iOS/RocketApp.xcodeproj/xcshareddata/xcschemes/RocketAppUITests.xcscheme @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Solution/iOS/RocketAppUITests/Utils/BaseScreen.swift b/Solution/iOS/RocketAppUITests/Utils/BaseScreen.swift index 010d2599c..b4ba44110 100644 --- a/Solution/iOS/RocketAppUITests/Utils/BaseScreen.swift +++ b/Solution/iOS/RocketAppUITests/Utils/BaseScreen.swift @@ -1,5 +1,5 @@ import XCTest protocol Screen { - var app: XCUIApplication { get } + var app: XCUIApplication { get } } diff --git a/Solution/iOS/RocketAppUITests/Utils/BaseTestCase.swift b/Solution/iOS/RocketAppUITests/Utils/BaseTestCase.swift index 137331e4a..902659b4f 100644 --- a/Solution/iOS/RocketAppUITests/Utils/BaseTestCase.swift +++ b/Solution/iOS/RocketAppUITests/Utils/BaseTestCase.swift @@ -1,18 +1,18 @@ import XCTest class BaseTestCase: XCTestCase { - var app: XCUIApplication! + var app: XCUIApplication! + + override func setUp() { + super.setUp() + continueAfterFailure = false - override func setUp() { - super.setUp() - continueAfterFailure = false - - app = XCUIApplication() - app.launchArguments = ["UITestMode", "testsFreshRun"] - app.launch() - } - - override func tearDown() { - super.tearDown() - } + app = XCUIApplication() + app.launchArguments = ["UITestMode", "testsFreshRun"] + app.launch() + } + + override func tearDown() { + super.tearDown() + } } From 7f38fdba34aeade700d888ff14a4cc0c9783892a Mon Sep 17 00:00:00 2001 From: gogolakovaa Date: Sun, 15 Sep 2024 18:57:35 +0200 Subject: [PATCH 05/41] Creates custom button style --- .../Features/Sources/Login/LoginView.swift | 3 +-- .../Sources/RocketList/RocketListView.swift | 3 +-- .../UIToolkit/Components/QuantiButton.swift | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/Components/QuantiButton.swift diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift index dc37d02c1..f70bae6f2 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift @@ -49,8 +49,7 @@ public struct LoginView: View { Button("Login") { viewStore.send(.loginTapped) } - .buttonStyle(.borderedProminent) - .tint(.quanti) + .buttonStyle(QuantiButtonStyle()) } .padding(.horizontal, 20) } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift index 09edc206a..d59d328da 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift @@ -43,8 +43,7 @@ public struct RocketListView: View { Button("Logout") { viewStore.send(.logoutTapped) } - .buttonStyle(.borderedProminent) - .tint(.quanti) + .buttonStyle(QuantiButtonStyle()) } .navigationTitle(.rockets) } diff --git a/Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/Components/QuantiButton.swift b/Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/Components/QuantiButton.swift new file mode 100644 index 000000000..1097dd0ae --- /dev/null +++ b/Solution/iOS/RocketApp/Infrastructure/Sources/UIToolkit/Components/QuantiButton.swift @@ -0,0 +1,15 @@ +import SwiftUI + +public struct QuantiButtonStyle: ButtonStyle { + + public init() {} + + public func makeBody(configuration: Configuration) -> some View { + configuration.label + .frame(maxWidth: .infinity) + .padding() + .background(configuration.isPressed ? Color.quanti.opacity(0.3) : Color.quanti) + .foregroundColor(.white) + .clipShape(RoundedRectangle(cornerRadius: 24)) + } +} From f9e8089d51af9726f0ba81ff7ebbd02f50a3c5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikol=20Duchanova=CC=81?= Date: Sun, 15 Sep 2024 19:41:32 +0200 Subject: [PATCH 06/41] Add setup testing --- .../iOS/RocketApp.xcodeproj/project.pbxproj | 11 ++++++++-- .../AccessibilityKeys+RocketList.swift | 7 +++++++ .../Sources/RocketList/RocketListView.swift | 1 + .../Screens/RocketListScreen.swift | 20 +++++++++++++++++++ .../iOS/RocketAppUITests/Tests/Test.swift | 8 ++++++++ 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+RocketList.swift create mode 100644 Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift create mode 100644 Solution/iOS/RocketAppUITests/Tests/Test.swift diff --git a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj index 3105228ab..e28fd2769 100644 --- a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj +++ b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 165B20552999449B0047F70A /* Infrastructure in Resources */ = {isa = PBXBuildFile; fileRef = 165B20532999449B0047F70A /* Infrastructure */; }; 16BC7FFC29CC9AE5002AAE15 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 16BC7FFB29CC9AE5002AAE15 /* Assets.xcassets */; }; 635FE7B12A20CDD40018A585 /* RocketList in Frameworks */ = {isa = PBXBuildFile; productRef = 635FE7B02A20CDD40018A585 /* RocketList */; }; + E50DF9542C9751A700BE1B5D /* RocketList in Frameworks */ = {isa = PBXBuildFile; productRef = E50DF9532C9751A700BE1B5D /* RocketList */; }; E55944B42C97328A00594002 /* UIToolkit in Frameworks */ = {isa = PBXBuildFile; productRef = E55944B32C97328A00594002 /* UIToolkit */; }; /* End PBXBuildFile section */ @@ -55,6 +56,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + E50DF9542C9751A700BE1B5D /* RocketList in Frameworks */, E55944B42C97328A00594002 /* UIToolkit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -153,6 +155,7 @@ name = RocketAppUITests; packageProductDependencies = ( E55944B32C97328A00594002 /* UIToolkit */, + E50DF9532C9751A700BE1B5D /* RocketList */, ); productName = RocketAppUITests; productReference = E5A6B6F42C97216000376970 /* RocketAppUITests.xctest */; @@ -467,7 +470,7 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = cz.quanti.RocketAppUITests; @@ -491,7 +494,7 @@ ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 18.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = cz.quanti.RocketAppUITests; @@ -540,6 +543,10 @@ isa = XCSwiftPackageProductDependency; productName = RocketList; }; + E50DF9532C9751A700BE1B5D /* RocketList */ = { + isa = XCSwiftPackageProductDependency; + productName = RocketList; + }; E55944B32C97328A00594002 /* UIToolkit */ = { isa = XCSwiftPackageProductDependency; productName = UIToolkit; diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+RocketList.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+RocketList.swift new file mode 100644 index 000000000..388266405 --- /dev/null +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+RocketList.swift @@ -0,0 +1,7 @@ +import UIToolkit + +extension AccessibilityKeys { + public enum RocketList { + public static let list = "rocketListID" + } +} diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift index 7e9b6e087..5139e315a 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift @@ -53,6 +53,7 @@ public struct RocketListView: View { RocketListCellView(store: $0) } } + .accessibilityIdentifier(AccessibilityKeys.RocketList.list) .listStyle(.sidebar) .navigationDestination( store: self.store.scope( diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift new file mode 100644 index 000000000..da614dd02 --- /dev/null +++ b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift @@ -0,0 +1,20 @@ +import UIToolkit +import RocketList +import XCTest + +struct RocketListScreen: Screen { + let app: XCUIApplication + + private let rocketList: XCUIElement + + init(app: XCUIApplication) { + self.app = app + rocketList = app.descendants(matching: .any)[AccessibilityKeys.RocketList.list] + } + + @discardableResult + func checkRocketList() -> Self { + XCTAssert(rocketList.waitForExistence(timeout: 5)) + return self + } +} diff --git a/Solution/iOS/RocketAppUITests/Tests/Test.swift b/Solution/iOS/RocketAppUITests/Tests/Test.swift new file mode 100644 index 000000000..5e3c3b603 --- /dev/null +++ b/Solution/iOS/RocketAppUITests/Tests/Test.swift @@ -0,0 +1,8 @@ +import XCTest + +final class Test: BaseTestCase { + func testTest() { + RocketListScreen(app: app) + .checkRocketList() + } +} From 61873705c30985192b58b795d17a588cf4856531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikol=20Duchanova=CC=81?= Date: Mon, 11 Nov 2024 20:49:59 +0100 Subject: [PATCH 07/41] hihi --- Solution/iOS/RocketApp.xcodeproj/project.pbxproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj index 65e354289..69c38def4 100644 --- a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj +++ b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj @@ -556,6 +556,7 @@ E55944B32C97328A00594002 /* UIToolkit */ = { isa = XCSwiftPackageProductDependency; productName = UIToolkit; + }; 6E3F7D1F2C972CFD00554BA3 /* App */ = { isa = XCSwiftPackageProductDependency; productName = App; From 3fab0ad1dfb55c67008f16e386a2de771012d81a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikol=20Duchanova=CC=81?= Date: Mon, 11 Nov 2024 20:50:42 +0100 Subject: [PATCH 08/41] Change wrong credentials message --- Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift index f70bae6f2..c02e70a49 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift @@ -12,7 +12,7 @@ public struct LoginView: View { public var body: some View { WithViewStore(self.store) { viewStore in VStack { - Text("Shiiiiiiiiiit, your credentials are not valid") + Text("Sorry, your credentials are not valid") .foregroundStyle(.white) .frame(maxWidth: .infinity) .padding() From 41707e244df5daa5b5568310035500160000b1cb Mon Sep 17 00:00:00 2001 From: nikolduchanova Date: Mon, 11 Nov 2024 21:46:40 +0100 Subject: [PATCH 09/41] Change example test --- .../iOS/RocketApp.xcodeproj/project.pbxproj | 27 ++++++++++++------- .../Login/AccessibilityKeys+Login.swift | 7 +++++ .../Features/Sources/Login/LoginView.swift | 1 + .../AccessibilityKeys+RocketList.swift | 7 ----- .../Sources/RocketList/RocketListView.swift | 5 ++-- .../Screens/LoginScreen.swift | 21 +++++++++++++++ .../Screens/RocketListScreen.swift | 20 -------------- .../iOS/RocketAppUITests/Tests/Test.swift | 4 +-- 8 files changed, 50 insertions(+), 42 deletions(-) create mode 100644 Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift delete mode 100644 Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+RocketList.swift create mode 100644 Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift delete mode 100644 Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift diff --git a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj index 69c38def4..d8f924979 100644 --- a/Solution/iOS/RocketApp.xcodeproj/project.pbxproj +++ b/Solution/iOS/RocketApp.xcodeproj/project.pbxproj @@ -13,10 +13,11 @@ 165B20552999449B0047F70A /* Infrastructure in Resources */ = {isa = PBXBuildFile; fileRef = 165B20532999449B0047F70A /* Infrastructure */; }; 16BC7FFC29CC9AE5002AAE15 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 16BC7FFB29CC9AE5002AAE15 /* Assets.xcassets */; }; 635FE7B12A20CDD40018A585 /* RocketList in Frameworks */ = {isa = PBXBuildFile; productRef = 635FE7B02A20CDD40018A585 /* RocketList */; }; - E50DF9542C9751A700BE1B5D /* RocketList in Frameworks */ = {isa = PBXBuildFile; productRef = E50DF9532C9751A700BE1B5D /* RocketList */; }; - E55944B42C97328A00594002 /* UIToolkit in Frameworks */ = {isa = PBXBuildFile; productRef = E55944B32C97328A00594002 /* UIToolkit */; }; 6E3F7D202C972CFD00554BA3 /* App in Frameworks */ = {isa = PBXBuildFile; productRef = 6E3F7D1F2C972CFD00554BA3 /* App */; }; 6EA6ACB52C96F40A00EA0B8A /* Login in Frameworks */ = {isa = PBXBuildFile; productRef = 6EA6ACB42C96F40A00EA0B8A /* Login */; }; + E50818DC2CE2A31A00B9AE68 /* Login in Frameworks */ = {isa = PBXBuildFile; productRef = E50818DB2CE2A31A00B9AE68 /* Login */; }; + E50DF9542C9751A700BE1B5D /* RocketList in Frameworks */ = {isa = PBXBuildFile; productRef = E50DF9532C9751A700BE1B5D /* RocketList */; }; + E55944B42C97328A00594002 /* UIToolkit in Frameworks */ = {isa = PBXBuildFile; productRef = E55944B32C97328A00594002 /* UIToolkit */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -60,6 +61,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + E50818DC2CE2A31A00B9AE68 /* Login in Frameworks */, E50DF9542C9751A700BE1B5D /* RocketList in Frameworks */, E55944B42C97328A00594002 /* UIToolkit in Frameworks */, ); @@ -162,6 +164,7 @@ packageProductDependencies = ( E55944B32C97328A00594002 /* UIToolkit */, E50DF9532C9751A700BE1B5D /* RocketList */, + E50818DB2CE2A31A00B9AE68 /* Login */, ); productName = RocketAppUITests; productReference = E5A6B6F42C97216000376970 /* RocketAppUITests.xctest */; @@ -549,14 +552,6 @@ isa = XCSwiftPackageProductDependency; productName = RocketList; }; - E50DF9532C9751A700BE1B5D /* RocketList */ = { - isa = XCSwiftPackageProductDependency; - productName = RocketList; - }; - E55944B32C97328A00594002 /* UIToolkit */ = { - isa = XCSwiftPackageProductDependency; - productName = UIToolkit; - }; 6E3F7D1F2C972CFD00554BA3 /* App */ = { isa = XCSwiftPackageProductDependency; productName = App; @@ -565,6 +560,18 @@ isa = XCSwiftPackageProductDependency; productName = Login; }; + E50818DB2CE2A31A00B9AE68 /* Login */ = { + isa = XCSwiftPackageProductDependency; + productName = Login; + }; + E50DF9532C9751A700BE1B5D /* RocketList */ = { + isa = XCSwiftPackageProductDependency; + productName = RocketList; + }; + E55944B32C97328A00594002 /* UIToolkit */ = { + isa = XCSwiftPackageProductDependency; + productName = UIToolkit; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 160CA40528A506E4004A274A /* Project object */; diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift b/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift new file mode 100644 index 000000000..bdfe278a2 --- /dev/null +++ b/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift @@ -0,0 +1,7 @@ +import UIToolkit + +extension AccessibilityKeys { + public enum Login { + public static let title = "titleID" + } +} diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift index c02e70a49..41a583be3 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift @@ -34,6 +34,7 @@ public struct LoginView: View { .font(.largeTitle) .fontWeight(.bold) .padding(.top, 20) + .accessibilityIdentifier(AccessibilityKeys.Login.title) Spacer() diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+RocketList.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+RocketList.swift deleted file mode 100644 index 388266405..000000000 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+RocketList.swift +++ /dev/null @@ -1,7 +0,0 @@ -import UIToolkit - -extension AccessibilityKeys { - public enum RocketList { - public static let list = "rocketListID" - } -} diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift index 336efba79..668e09fff 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift @@ -37,9 +37,9 @@ public struct RocketListView: View { loadingView } } - + Spacer() - + Button("Logout") { viewStore.send(.logoutTapped) } @@ -62,7 +62,6 @@ public struct RocketListView: View { RocketListCellView(store: $0) } } - .accessibilityIdentifier(AccessibilityKeys.RocketList.list) .listStyle(.sidebar) .navigationDestination( store: self.store.scope( diff --git a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift new file mode 100644 index 000000000..221cb88b0 --- /dev/null +++ b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift @@ -0,0 +1,21 @@ +import UIToolkit +import RocketList +import Login +import XCTest + +struct LoginScreen: Screen { + let app: XCUIApplication + + private let loginTitle: XCUIElement + + init(app: XCUIApplication) { + self.app = app + loginTitle = app.staticTexts[AccessibilityKeys.Login.title] + } + + @discardableResult + func checkLoginTitle() -> Self { + XCTAssert(loginTitle.waitForExistence(timeout: 5)) + return self + } +} diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift deleted file mode 100644 index da614dd02..000000000 --- a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift +++ /dev/null @@ -1,20 +0,0 @@ -import UIToolkit -import RocketList -import XCTest - -struct RocketListScreen: Screen { - let app: XCUIApplication - - private let rocketList: XCUIElement - - init(app: XCUIApplication) { - self.app = app - rocketList = app.descendants(matching: .any)[AccessibilityKeys.RocketList.list] - } - - @discardableResult - func checkRocketList() -> Self { - XCTAssert(rocketList.waitForExistence(timeout: 5)) - return self - } -} diff --git a/Solution/iOS/RocketAppUITests/Tests/Test.swift b/Solution/iOS/RocketAppUITests/Tests/Test.swift index 5e3c3b603..ed4eafc4d 100644 --- a/Solution/iOS/RocketAppUITests/Tests/Test.swift +++ b/Solution/iOS/RocketAppUITests/Tests/Test.swift @@ -2,7 +2,7 @@ import XCTest final class Test: BaseTestCase { func testTest() { - RocketListScreen(app: app) - .checkRocketList() + LoginScreen(app: app) + .checkLoginTitle() } } From d1fe0ae95e4fc4df7b7f6e173c079f43da42a6d8 Mon Sep 17 00:00:00 2001 From: nikolduchanova Date: Tue, 12 Nov 2024 12:11:41 +0100 Subject: [PATCH 10/41] Cleaning --- .../Features/Sources/RocketList/RocketListView.swift | 4 ++-- .../iOS/RocketAppUITests/Screens/LoginScreen.swift | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift index 668e09fff..881d4184a 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift @@ -37,9 +37,9 @@ public struct RocketListView: View { loadingView } } - + Spacer() - + Button("Logout") { viewStore.send(.logoutTapped) } diff --git a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift index 221cb88b0..7f2bb7808 100644 --- a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift @@ -1,18 +1,18 @@ -import UIToolkit -import RocketList import Login +import RocketList +import UIToolkit import XCTest struct LoginScreen: Screen { let app: XCUIApplication - + private let loginTitle: XCUIElement - + init(app: XCUIApplication) { self.app = app loginTitle = app.staticTexts[AccessibilityKeys.Login.title] } - + @discardableResult func checkLoginTitle() -> Self { XCTAssert(loginTitle.waitForExistence(timeout: 5)) From ec0c0a08e332c8aa8c6c4ef78aa7612166d7b6b7 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Wed, 4 Dec 2024 15:39:00 +0100 Subject: [PATCH 11/41] Add Accessibility Keys for Login Screen Add Accessibility Keys for Rocket List Screen Add Accessibility Keys for Rocket Detail Screen Add Accessibility Keys for Rocket Launch Screen Add Rocket List Screen Add Rocket Rocket Detail Screen Add Rocket Launch screen Add Rocket Launch Test --- .../Login/AccessibilityKeys+Login.swift | 3 ++ .../Features/Sources/Login/LoginView.swift | 3 ++ .../AccessibilityKeys+Detail.swift | 8 +++++ .../RocketDetail/RocketDetailView.swift | 2 ++ .../AccessibilityKeys+Launch.swift | 7 ++++ .../RocketLaunch/RocketLaunchView.swift | 1 + .../RocketList/AccessibilityKeys+List.swift | 8 +++++ .../Sources/RocketList/RocketListView.swift | 3 +- .../Screens/LoginScreen.swift | 27 ++++++++++++++++ .../Screens/RocketDetailScreen.swift | 32 +++++++++++++++++++ .../Screens/RocketLaunchScreen.swift | 22 +++++++++++++ .../Screens/RocketListScreen.swift | 31 ++++++++++++++++++ .../Tests/RocketLaunchTest.swift | 29 +++++++++++++++++ 13 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift create mode 100644 Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift create mode 100644 Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift create mode 100644 Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift create mode 100644 Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift create mode 100644 Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift create mode 100644 Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift b/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift index bdfe278a2..3778d4fd7 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift @@ -3,5 +3,8 @@ import UIToolkit extension AccessibilityKeys { public enum Login { public static let title = "titleID" + public static let username = "usernameField" + public static let password = "passwordField" + public static let loginButton = "loginButton" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift index 41a583be3..e1f004526 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift @@ -41,9 +41,11 @@ public struct LoginView: View { TextField("Username", text: viewStore.binding(\.$username)) .textFieldStyle(.roundedBorder) .autocapitalization(.none) + .accessibilityIdentifier(AccessibilityKeys.Login.username) SecureField("Password", text: viewStore.binding(\.$password)) .textFieldStyle(.roundedBorder) + .accessibilityIdentifier(AccessibilityKeys.Login.password) Spacer() @@ -51,6 +53,7 @@ public struct LoginView: View { viewStore.send(.loginTapped) } .buttonStyle(QuantiButtonStyle()) + .accessibilityIdentifier(AccessibilityKeys.Login.loginButton) } .padding(.horizontal, 20) } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift new file mode 100644 index 000000000..dd874760e --- /dev/null +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift @@ -0,0 +1,8 @@ +import UIToolkit + +extension AccessibilityKeys { + public enum RocketDetail { + public static let rocketTitle = "Falcon 1" + public static let launchButton = "Launch" + } +} diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift index 603b2d4b6..075bbde6d 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift @@ -97,8 +97,10 @@ public struct RocketDetailView: View { } .padding(.horizontal) .navigationTitle(viewStore.rocketData.name) + .accessibilityIdentifier(AccessibilityKeys.RocketDetail.rocketTitle) .onAppear { viewStore.send(.rocketLaunchDismiss) } .navigationBarItems(trailing: Button(.launch) { viewStore.send(.rocketLaunchTapped) }) + .accessibilityIdentifier(AccessibilityKeys.RocketDetail.launchButton) .navigationDestination( store: self.store.scope( state: \.$rocketLaunch, diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift new file mode 100644 index 000000000..97a1f676d --- /dev/null +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift @@ -0,0 +1,7 @@ +import UIToolkit + +extension AccessibilityKeys { + public enum RocketLaunch { + public static let rocketImage = "rocket_idle" + } +} diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift index 797f71108..e30464157 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift @@ -104,6 +104,7 @@ public struct RocketLaunchView: View { private var flyingRocket: some View { Image.rocketFlying + .accessibilityIdentifier(AccessibilityKeys.RocketLaunch.rocketImage) .scaleEffect(rocketScale) .overlay(alignment: .bottom) { Group { diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift new file mode 100644 index 000000000..a90be5b11 --- /dev/null +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift @@ -0,0 +1,8 @@ +import UIToolkit + +extension AccessibilityKeys { + public enum RocketList { + public static let logoutButton = "logoutButton" + public static let rocketCell = "rocketCell" + } +} diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift index 881d4184a..c347b4017 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift @@ -39,11 +39,11 @@ public struct RocketListView: View { } Spacer() - Button("Logout") { viewStore.send(.logoutTapped) } .buttonStyle(QuantiButtonStyle()) + .accessibilityIdentifier(AccessibilityKeys.RocketList.logoutButton) } .navigationTitle(.rockets) } @@ -60,6 +60,7 @@ public struct RocketListView: View { ) ) { RocketListCellView(store: $0) + .accessibilityIdentifier(AccessibilityKeys.RocketList.rocketCell) } } .listStyle(.sidebar) diff --git a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift index 7f2bb7808..41db4ec73 100644 --- a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift @@ -7,10 +7,16 @@ struct LoginScreen: Screen { let app: XCUIApplication private let loginTitle: XCUIElement + private let usernameField: XCUIElement + private let passwordField: XCUIElement + private let loginButton: XCUIElement init(app: XCUIApplication) { self.app = app loginTitle = app.staticTexts[AccessibilityKeys.Login.title] + usernameField = app.textFields[AccessibilityKeys.Login.username] + passwordField = app.secureTextFields[AccessibilityKeys.Login.password] + loginButton = app.buttons[AccessibilityKeys.Login.loginButton] } @discardableResult @@ -18,4 +24,25 @@ struct LoginScreen: Screen { XCTAssert(loginTitle.waitForExistence(timeout: 5)) return self } + + @discardableResult + func enterUsername(_ username: String) -> Self { + usernameField.tap() + usernameField.typeText(username) + return self + } + + @discardableResult + func enterPassword(_ password: String) -> Self { + passwordField.tap() + passwordField.typeText(password) + return self + } + + @discardableResult + func tapLoginButton() -> Self { + loginButton.tap() + return self + } + } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift new file mode 100644 index 000000000..8cf774470 --- /dev/null +++ b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift @@ -0,0 +1,32 @@ +import Login +import RocketList +import UIToolkit +import XCTest + +struct RocketDetailScreen: Screen { + let app: XCUIApplication + + private let rocketTitle: XCUIElement + private let launchButton: XCUIElement + + init(app: XCUIApplication) { + self.app = app + rocketTitle = app.navigationBars.staticTexts[AccessibilityKeys.RocketDetail.rocketTitle] + launchButton = app.buttons[AccessibilityKeys.RocketDetail.launchButton] + } + + @discardableResult + func checkRocketTitle() -> Self { + XCTAssert(rocketTitle.waitForExistence(timeout: 5)) + return self + } + @discardableResult + func tapLaunchButton() -> Self { + launchButton.tap() + return self + } + + + + +} diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift new file mode 100644 index 000000000..d03d48ae7 --- /dev/null +++ b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift @@ -0,0 +1,22 @@ +import Login +import RocketList +import UIToolkit +import XCTest + +struct RocketLaunchScreen: Screen { + let app: XCUIApplication + + private let rocketImage: XCUIElement + + init(app: XCUIApplication) { + self.app = app + rocketImage = app.images[AccessibilityKeys.RocketLaunch.rocketImage] + + } + + @discardableResult + func checkRocketImage() -> Self { + XCTAssert(rocketImage.waitForExistence(timeout: 5)) + return self + } +} diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift new file mode 100644 index 000000000..83b0c97bf --- /dev/null +++ b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift @@ -0,0 +1,31 @@ +import Login +import RocketList +import UIToolkit +import XCTest + +struct RocketListScreen: Screen { + let app: XCUIApplication + + private let logoutButton: XCUIElement + private let firstRocketCell: XCUIElement + + init(app: XCUIApplication) { + self.app = app + logoutButton = app.buttons[AccessibilityKeys.RocketList.logoutButton] + firstRocketCell = app.cells[AccessibilityKeys.RocketList.rocketCell] + } + + @discardableResult + func checkLogoutButton() -> Self { + XCTAssert(logoutButton.waitForExistence(timeout: 5)) + return self + } + + @discardableResult + func tapFirstRocketCell() -> Self { + let rocketCells = app.buttons.matching(identifier: "rocketCell") + let firstRocketCell = rocketCells.element(boundBy: 0) + firstRocketCell.tap() + return self + } +} diff --git a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift b/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift new file mode 100644 index 000000000..7483800dc --- /dev/null +++ b/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift @@ -0,0 +1,29 @@ +// +// Test 2.swift +// RocketApp +// +// Created by Cyril Colombier on 30.11.2024. +// + +import XCTest + +final class TestRocketLaunch: BaseTestCase { + func testRocketLaunchTest() { + let username = "astronaut1" + let password = "space" + + LoginScreen(app: app) + .checkLoginTitle() + .enterUsername(username) + .enterPassword(password) + .tapLoginButton() + RocketListScreen(app: app) + .checkLogoutButton() + .tapFirstRocketCell() + RocketDetailScreen(app: app) + .checkRocketTitle() + .tapLaunchButton() + RocketLaunchScreen(app: app) + .checkRocketImage() + } +} From a39ff8435f7099ee6282416048d54ad37a3f4982 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Sun, 8 Dec 2024 15:05:49 +0100 Subject: [PATCH 12/41] Add Accessibility Identifiers --- .../Features/Sources/Login/AccessibilityKeys+Login.swift | 6 +++--- .../Sources/RocketDetail/AccessibilityKeys+Detail.swift | 4 ++-- .../Sources/RocketLaunch/AccessibilityKeys+Launch.swift | 2 +- .../Features/Sources/RocketLaunch/RocketLaunchView.swift | 3 ++- .../Sources/RocketList/AccessibilityKeys+List.swift | 3 +-- .../Features/Sources/RocketList/RocketListView.swift | 3 +-- .../Sources/RocketListCell/AccessibilityKeys+List.swift | 7 +++++++ .../Sources/RocketListCell/RocketListCellView.swift | 1 + 8 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift b/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift index 3778d4fd7..6c1422e4d 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift @@ -3,8 +3,8 @@ import UIToolkit extension AccessibilityKeys { public enum Login { public static let title = "titleID" - public static let username = "usernameField" - public static let password = "passwordField" - public static let loginButton = "loginButton" + public static let username = "usernameFieldID" + public static let password = "passwordFieldID" + public static let loginButton = "loginButtonID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift index dd874760e..31d49cbbc 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift @@ -2,7 +2,7 @@ import UIToolkit extension AccessibilityKeys { public enum RocketDetail { - public static let rocketTitle = "Falcon 1" - public static let launchButton = "Launch" + public static let rocketTitle = "rocketTitleID" + public static let launchButton = "launchButtonID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift index 97a1f676d..e0e5b3c21 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift @@ -2,6 +2,6 @@ import UIToolkit extension AccessibilityKeys { public enum RocketLaunch { - public static let rocketImage = "rocket_idle" + public static let rocketImage = "rocketImageID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift index e30464157..44f78265b 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift @@ -57,6 +57,8 @@ public struct RocketLaunchView: View { flyingRocket } else { Image.rocketIdle + .accessibilityIdentifier(AccessibilityKeys.RocketLaunch.rocketImage) + } Spacer() @@ -104,7 +106,6 @@ public struct RocketLaunchView: View { private var flyingRocket: some View { Image.rocketFlying - .accessibilityIdentifier(AccessibilityKeys.RocketLaunch.rocketImage) .scaleEffect(rocketScale) .overlay(alignment: .bottom) { Group { diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift index a90be5b11..2a78cb7a4 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift @@ -2,7 +2,6 @@ import UIToolkit extension AccessibilityKeys { public enum RocketList { - public static let logoutButton = "logoutButton" - public static let rocketCell = "rocketCell" + public static let rocketCells = "rocketCellsID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift index c347b4017..3d1bb9d97 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift @@ -43,7 +43,6 @@ public struct RocketListView: View { viewStore.send(.logoutTapped) } .buttonStyle(QuantiButtonStyle()) - .accessibilityIdentifier(AccessibilityKeys.RocketList.logoutButton) } .navigationTitle(.rockets) } @@ -60,7 +59,7 @@ public struct RocketListView: View { ) ) { RocketListCellView(store: $0) - .accessibilityIdentifier(AccessibilityKeys.RocketList.rocketCell) + .accessibilityIdentifier(AccessibilityKeys.RocketList.rocketCells) } } .listStyle(.sidebar) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift new file mode 100644 index 000000000..41848a625 --- /dev/null +++ b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift @@ -0,0 +1,7 @@ +import UIToolkit + +extension AccessibilityKeys { + public enum RocketListCell { + public static let rocketCellArrow = "rocketCellArrowID" + } +} diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift index ed8b23612..a37703b7e 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift @@ -34,6 +34,7 @@ public struct RocketListCellView: View { Image.linkArrow .resizable() .frame(width: 32, height: 32) + .accessibilityIdentifier(AccessibilityKeys.RocketListCell.rocketCellArrow) } } } From 36997f23ea74cba30c99e17e75f32846c4eee47d Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Sun, 8 Dec 2024 15:09:18 +0100 Subject: [PATCH 13/41] Add Accessibility Identifiers for RocketDetailView --- .../Sources/RocketDetail/RocketDetailView.swift | 10 +++++++--- .../RocketAppUITests/Screens/RocketDetailScreen.swift | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift index 075bbde6d..5b2d79480 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift @@ -57,6 +57,7 @@ public struct RocketDetailView: View { section(.overview) { Text(viewStore.rocketData.overview) .font(.body) + .accessibilityIdentifier(AccessibilityKeys.RocketDetail.rocketTitle) } .padding(.bottom) @@ -97,10 +98,13 @@ public struct RocketDetailView: View { } .padding(.horizontal) .navigationTitle(viewStore.rocketData.name) - .accessibilityIdentifier(AccessibilityKeys.RocketDetail.rocketTitle) .onAppear { viewStore.send(.rocketLaunchDismiss) } - .navigationBarItems(trailing: Button(.launch) { viewStore.send(.rocketLaunchTapped) }) - .accessibilityIdentifier(AccessibilityKeys.RocketDetail.launchButton) + .navigationBarItems( + trailing: Button(.launch) { + viewStore.send(.rocketLaunchTapped) + } + .accessibilityIdentifier(AccessibilityKeys.RocketDetail.launchButton) + ) .navigationDestination( store: self.store.scope( state: \.$rocketLaunch, diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift index 8cf774470..c54be455c 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift @@ -11,8 +11,8 @@ struct RocketDetailScreen: Screen { init(app: XCUIApplication) { self.app = app - rocketTitle = app.navigationBars.staticTexts[AccessibilityKeys.RocketDetail.rocketTitle] - launchButton = app.buttons[AccessibilityKeys.RocketDetail.launchButton] + rocketTitle = app.staticTexts[AccessibilityKeys.RocketDetail.rocketTitle] + launchButton = app.navigationBars.buttons[AccessibilityKeys.RocketDetail.launchButton] } @discardableResult From 57d2737b1bfa5bdc5712382b73b27eba5c67488a Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Sun, 8 Dec 2024 15:16:02 +0100 Subject: [PATCH 14/41] Add test to count rocket cells and improve test to tap arrow on first rocket cell. --- .../Screens/RocketListScreen.swift | 34 ++++++++++++------- .../Tests/RocketLaunchTest.swift | 9 +---- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift index 83b0c97bf..e3a9adcbb 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift @@ -5,27 +5,37 @@ import XCTest struct RocketListScreen: Screen { let app: XCUIApplication - - private let logoutButton: XCUIElement - private let firstRocketCell: XCUIElement + + private let rocketCells: XCUIElementQuery + private let rocketCellArrow: XCUIElementQuery init(app: XCUIApplication) { self.app = app - logoutButton = app.buttons[AccessibilityKeys.RocketList.logoutButton] - firstRocketCell = app.cells[AccessibilityKeys.RocketList.rocketCell] + + rocketCells = app.buttons.matching(identifier: "rocketCellsID") + rocketCellArrow = app.images.matching(identifier: "rocketCellArrowID") + } - + @discardableResult - func checkLogoutButton() -> Self { - XCTAssert(logoutButton.waitForExistence(timeout: 5)) - return self - } + func countRocketCells() -> Self { + let exists = rocketCells.firstMatch.waitForExistence(timeout: 5) + XCTAssertTrue(exists, "Rocket cells did not appear within the timeout.") + + let cellCount = rocketCells.count + print("Number of rocket cells displayed: \(cellCount)") + + XCTAssertEqual(cellCount, 4, "Expected exactly 4 rocket cells to be displayed.") + + return self + } @discardableResult func tapFirstRocketCell() -> Self { - let rocketCells = app.buttons.matching(identifier: "rocketCell") - let firstRocketCell = rocketCells.element(boundBy: 0) + let firstRocketCell = rocketCellArrow.element(boundBy: 0) + firstRocketCell.tap() + return self } } diff --git a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift b/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift index 7483800dc..2e37ad599 100644 --- a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift +++ b/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift @@ -1,10 +1,3 @@ -// -// Test 2.swift -// RocketApp -// -// Created by Cyril Colombier on 30.11.2024. -// - import XCTest final class TestRocketLaunch: BaseTestCase { @@ -18,7 +11,7 @@ final class TestRocketLaunch: BaseTestCase { .enterPassword(password) .tapLoginButton() RocketListScreen(app: app) - .checkLogoutButton() + .countRocketCells() .tapFirstRocketCell() RocketDetailScreen(app: app) .checkRocketTitle() From a0b74b4aac52a1b8a4ebf82279fc9b60ef7a70cf Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Sun, 8 Dec 2024 15:17:03 +0100 Subject: [PATCH 15/41] Add failure messages --- Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift | 2 +- Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift | 2 +- Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift index 41db4ec73..4eeedc94f 100644 --- a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift @@ -21,7 +21,7 @@ struct LoginScreen: Screen { @discardableResult func checkLoginTitle() -> Self { - XCTAssert(loginTitle.waitForExistence(timeout: 5)) + XCTAssert(loginTitle.waitForExistence(timeout: 5), "Login title did not appear within the timeout.") return self } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift index c54be455c..d18b7d4e0 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift @@ -17,7 +17,7 @@ struct RocketDetailScreen: Screen { @discardableResult func checkRocketTitle() -> Self { - XCTAssert(rocketTitle.waitForExistence(timeout: 5)) + XCTAssert(rocketTitle.waitForExistence(timeout: 5), "Rocket title did not appear within the timeout.") return self } @discardableResult diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift index d03d48ae7..ff81d5d5b 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift @@ -16,7 +16,7 @@ struct RocketLaunchScreen: Screen { @discardableResult func checkRocketImage() -> Self { - XCTAssert(rocketImage.waitForExistence(timeout: 5)) + XCTAssert(rocketImage.waitForExistence(timeout: 5), "Rocket image did not appear within the timeout.") return self } } From e49a101f5fb9e42b95b170601b4b94ef1ae45559 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Tue, 10 Dec 2024 17:59:27 +0100 Subject: [PATCH 16/41] Fix countRocketCells func and naming --- .../RocketList/AccessibilityKeys+List.swift | 2 +- .../Screens/RocketListScreen.swift | 28 ++++++++----------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift index 2a78cb7a4..5560eb44d 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift @@ -2,6 +2,6 @@ import UIToolkit extension AccessibilityKeys { public enum RocketList { - public static let rocketCells = "rocketCellsID" + public static let rocketCell = "rocketCellID" } } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift index e3a9adcbb..cbbeb309e 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift @@ -7,33 +7,29 @@ struct RocketListScreen: Screen { let app: XCUIApplication private let rocketCells: XCUIElementQuery - private let rocketCellArrow: XCUIElementQuery + private let rocketCellArrows: XCUIElementQuery init(app: XCUIApplication) { self.app = app - rocketCells = app.buttons.matching(identifier: "rocketCellsID") - rocketCellArrow = app.images.matching(identifier: "rocketCellArrowID") - + rocketCells = app.buttons.matching(identifier: AccessibilityKeys.RocketList.rocketCell) + rocketCellArrows = app.images.matching(identifier: AccessibilityKeys.RocketListCell.rocketCellArrow) } @discardableResult - func countRocketCells() -> Self { - let exists = rocketCells.firstMatch.waitForExistence(timeout: 5) - XCTAssertTrue(exists, "Rocket cells did not appear within the timeout.") - - let cellCount = rocketCells.count - print("Number of rocket cells displayed: \(cellCount)") - - XCTAssertEqual(cellCount, 4, "Expected exactly 4 rocket cells to be displayed.") - - return self + func countRocketCells() -> Self { + let cellCount = rocketCells.count + + XCTAssert(rocketCells.firstMatch.waitForExistence(timeout: 5), "Rocket cells did not appear within the timeout.") + print("Number of rocket cells displayed: \(cellCount)") + XCTAssertEqual(cellCount, 4, "Expected exactly 4 rocket cells to be displayed.") + + return self } @discardableResult func tapFirstRocketCell() -> Self { - let firstRocketCell = rocketCellArrow.element(boundBy: 0) - + let firstRocketCell = rocketCellArrows.element(boundBy: 0) firstRocketCell.tap() return self From 688eec0d01861f6b5fe574ab51d286cae3ff319f Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Tue, 10 Dec 2024 18:00:07 +0100 Subject: [PATCH 17/41] Fix whitespace trailing --- Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift | 1 - .../iOS/RocketAppUITests/Screens/RocketDetailScreen.swift | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift index 4eeedc94f..d902744fb 100644 --- a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift @@ -44,5 +44,4 @@ struct LoginScreen: Screen { loginButton.tap() return self } - } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift index d18b7d4e0..31ed1cf0b 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift @@ -20,13 +20,10 @@ struct RocketDetailScreen: Screen { XCTAssert(rocketTitle.waitForExistence(timeout: 5), "Rocket title did not appear within the timeout.") return self } + @discardableResult func tapLaunchButton() -> Self { launchButton.tap() return self } - - - - } From 010a8847f7968f96d983183d9beca1a79c305049 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Wed, 11 Dec 2024 16:12:16 +0100 Subject: [PATCH 18/41] Add checkRocketNames test --- .../AccessibilityKeys+List.swift | 1 + .../RocketListCell/RocketListCellView.swift | 1 + .../Screens/RocketListScreen.swift | 24 +++++++++++++------ .../Tests/RocketLaunchTest.swift | 1 + 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift index 41848a625..70cf1276a 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift @@ -3,5 +3,6 @@ import UIToolkit extension AccessibilityKeys { public enum RocketListCell { public static let rocketCellArrow = "rocketCellArrowID" + public static let rocketNameLabel = "rocketNameLabelID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift index a37703b7e..b677f210a 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift @@ -23,6 +23,7 @@ public struct RocketListCellView: View { VStack(alignment: .leading, spacing: 4) { Text(viewStore.rocketData.name) .font(.title2.bold()) + .accessibilityIdentifier(AccessibilityKeys.RocketListCell.rocketNameLabel) Text("First flight: \(viewStore.rocketData.firstFlight)") .font(.callout) diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift index cbbeb309e..8216a86cd 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift @@ -7,31 +7,41 @@ struct RocketListScreen: Screen { let app: XCUIApplication private let rocketCells: XCUIElementQuery + private let rocketNameLabels: XCUIElementQuery private let rocketCellArrows: XCUIElementQuery init(app: XCUIApplication) { self.app = app - + rocketCells = app.buttons.matching(identifier: AccessibilityKeys.RocketList.rocketCell) + rocketNameLabels = app.staticTexts.matching(identifier: AccessibilityKeys.RocketListCell.rocketNameLabel) rocketCellArrows = app.images.matching(identifier: AccessibilityKeys.RocketListCell.rocketCellArrow) } @discardableResult func countRocketCells() -> Self { - let cellCount = rocketCells.count - XCTAssert(rocketCells.firstMatch.waitForExistence(timeout: 5), "Rocket cells did not appear within the timeout.") - print("Number of rocket cells displayed: \(cellCount)") - XCTAssertEqual(cellCount, 4, "Expected exactly 4 rocket cells to be displayed.") - + print("Number of rocket cells displayed: \(rocketCells.count)") + XCTAssert(rocketCells.count == 4, "Expected exactly 4 rocket cells to be displayed.") return self } + @discardableResult + func checkRocketNames() -> Self { + let expectedRocketNames = ["Falcon 1", "Falcon 9", "Falcon Heavy", "Starship"] + for index in 0.. Self { let firstRocketCell = rocketCellArrows.element(boundBy: 0) firstRocketCell.tap() - return self } } diff --git a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift b/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift index 2e37ad599..ce5df50ed 100644 --- a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift +++ b/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift @@ -12,6 +12,7 @@ final class TestRocketLaunch: BaseTestCase { .tapLoginButton() RocketListScreen(app: app) .countRocketCells() + .checkRocketNames() .tapFirstRocketCell() RocketDetailScreen(app: app) .checkRocketTitle() From ab20d51a11100050af9dd83f0fe5a6e4846ff9f6 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Wed, 11 Dec 2024 16:12:39 +0100 Subject: [PATCH 19/41] Fix naming and spacing --- .../RocketApp/Features/Sources/RocketList/RocketListView.swift | 2 +- Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift index 3d1bb9d97..f7d4bcf69 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift @@ -59,7 +59,7 @@ public struct RocketListView: View { ) ) { RocketListCellView(store: $0) - .accessibilityIdentifier(AccessibilityKeys.RocketList.rocketCells) + .accessibilityIdentifier(AccessibilityKeys.RocketList.rocketCell) } } .listStyle(.sidebar) diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift index ff81d5d5b..166c2b37d 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift @@ -11,7 +11,6 @@ struct RocketLaunchScreen: Screen { init(app: XCUIApplication) { self.app = app rocketImage = app.images[AccessibilityKeys.RocketLaunch.rocketImage] - } @discardableResult From c50c57bc2845265bb699a1da1239e7633b10ba69 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Tue, 17 Dec 2024 15:04:35 +0100 Subject: [PATCH 20/41] Add separate test constants file --- .../iOS/RocketAppUITests/Tests/RocketLaunchTest.swift | 10 ++++------ .../iOS/RocketAppUITests/Utils/TestConstants.swift | 11 +++++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 Solution/iOS/RocketAppUITests/Utils/TestConstants.swift diff --git a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift b/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift index ce5df50ed..30dee42d4 100644 --- a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift +++ b/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift @@ -2,17 +2,15 @@ import XCTest final class TestRocketLaunch: BaseTestCase { func testRocketLaunchTest() { - let username = "astronaut1" - let password = "space" - + LoginScreen(app: app) .checkLoginTitle() - .enterUsername(username) - .enterPassword(password) + .enterUsername(TestConstants.LoginDetails.username) + .enterPassword(TestConstants.LoginDetails.password) .tapLoginButton() RocketListScreen(app: app) .countRocketCells() - .checkRocketNames() + .checkRocketNames(TestConstants.Rockets.rocketNames) .tapFirstRocketCell() RocketDetailScreen(app: app) .checkRocketTitle() diff --git a/Solution/iOS/RocketAppUITests/Utils/TestConstants.swift b/Solution/iOS/RocketAppUITests/Utils/TestConstants.swift new file mode 100644 index 000000000..da679f551 --- /dev/null +++ b/Solution/iOS/RocketAppUITests/Utils/TestConstants.swift @@ -0,0 +1,11 @@ +import Foundation + +enum TestConstants { + enum LoginDetails{ + public static let username = "astronaut1" + public static let password = "space" + } + enum Rockets { + public static let rocketNames = ["Falcon 1", "Falcon 9", "Falcon Heavy", "Starship"] + } +} From f1efe9b54fd569963dbceb813aba00fb66639b19 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Tue, 17 Dec 2024 15:05:48 +0100 Subject: [PATCH 21/41] Fix checkRocketNames test --- .../Screens/RocketListScreen.swift | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift index 8216a86cd..d764164ac 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift @@ -7,14 +7,14 @@ struct RocketListScreen: Screen { let app: XCUIApplication private let rocketCells: XCUIElementQuery - private let rocketNameLabels: XCUIElementQuery + private let rocketNames: XCUIElementQuery private let rocketCellArrows: XCUIElementQuery init(app: XCUIApplication) { self.app = app rocketCells = app.buttons.matching(identifier: AccessibilityKeys.RocketList.rocketCell) - rocketNameLabels = app.staticTexts.matching(identifier: AccessibilityKeys.RocketListCell.rocketNameLabel) + rocketNames = app.staticTexts.matching(identifier: AccessibilityKeys.RocketListCell.rocketNameLabel) rocketCellArrows = app.images.matching(identifier: AccessibilityKeys.RocketListCell.rocketCellArrow) } @@ -24,16 +24,17 @@ struct RocketListScreen: Screen { print("Number of rocket cells displayed: \(rocketCells.count)") XCTAssert(rocketCells.count == 4, "Expected exactly 4 rocket cells to be displayed.") return self - } + } @discardableResult - func checkRocketNames() -> Self { - let expectedRocketNames = ["Falcon 1", "Falcon 9", "Falcon Heavy", "Starship"] - for index in 0.. Self { + for index in 0.. Date: Mon, 30 Dec 2024 13:47:43 +0100 Subject: [PATCH 22/41] Fix naming and whitespaces --- .../Features/Sources/Login/AccessibilityKeys+Login.swift | 6 +++--- .../iOS/RocketApp/Features/Sources/Login/LoginView.swift | 7 +++---- .../Sources/RocketDetail/AccessibilityKeys+Detail.swift | 2 +- .../Features/Sources/RocketDetail/RocketDetailCore.swift | 6 +++--- .../Features/Sources/RocketDetail/RocketDetailView.swift | 2 +- .../Features/Sources/RocketList/RocketListCore.swift | 4 ++-- .../Sources/RocketListCell/AccessibilityKeys+List.swift | 3 +-- .../Sources/RocketListCell/RocketListCellView.swift | 3 +-- 8 files changed, 15 insertions(+), 18 deletions(-) diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift b/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift index 6c1422e4d..910194235 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift @@ -2,9 +2,9 @@ import UIToolkit extension AccessibilityKeys { public enum Login { - public static let title = "titleID" - public static let username = "usernameFieldID" - public static let password = "passwordFieldID" + public static let titleText = "titleTextID" + public static let usernameTextField = "usernameTextFieldID" + public static let passwordSecureField = "passwordSecureFieldID" public static let loginButton = "loginButtonID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift index e1f004526..60a3d99a6 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift @@ -34,19 +34,18 @@ public struct LoginView: View { .font(.largeTitle) .fontWeight(.bold) .padding(.top, 20) - .accessibilityIdentifier(AccessibilityKeys.Login.title) + .accessibilityIdentifier(AccessibilityKeys.Login.titleText) Spacer() TextField("Username", text: viewStore.binding(\.$username)) .textFieldStyle(.roundedBorder) .autocapitalization(.none) - .accessibilityIdentifier(AccessibilityKeys.Login.username) + .accessibilityIdentifier(AccessibilityKeys.Login.usernameTextField) SecureField("Password", text: viewStore.binding(\.$password)) .textFieldStyle(.roundedBorder) - .accessibilityIdentifier(AccessibilityKeys.Login.password) - + .accessibilityIdentifier(AccessibilityKeys.Login.passwordSecureField) Spacer() Button("Login") { diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift index 31d49cbbc..aff6cf206 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift @@ -2,7 +2,7 @@ import UIToolkit extension AccessibilityKeys { public enum RocketDetail { - public static let rocketTitle = "rocketTitleID" + public static let rocketTitleText = "rocketTitleTextID" public static let launchButton = "launchButtonID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailCore.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailCore.swift index 35df83961..21eaae8ca 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailCore.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailCore.swift @@ -33,15 +33,15 @@ public struct RocketDetailCore: ReducerProtocol { case .setToUSMetrics: state.isUSMetrics.toggle() return .none - + case .rocketLaunch: return .none - + case .rocketLaunchDismiss: if state.rocketLaunch != nil { return .send(.rocketLaunch(.presented(.onDisappear))) } - + return .none } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift index 5b2d79480..158e27084 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift @@ -57,7 +57,7 @@ public struct RocketDetailView: View { section(.overview) { Text(viewStore.rocketData.overview) .font(.body) - .accessibilityIdentifier(AccessibilityKeys.RocketDetail.rocketTitle) + .accessibilityIdentifier(AccessibilityKeys.RocketDetail.rocketTitleText) } .padding(.bottom) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListCore.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListCore.swift index c64da79b3..594e3e6bc 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListCore.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListCore.swift @@ -10,7 +10,7 @@ public struct RocketListCore: ReducerProtocol { public struct State: Equatable { var loadingStatus: Loadable, RocketsClientAsyncError> = .notRequested @PresentationState var rocketDetail: RocketDetailCore.State? - + public init() {} } @@ -58,7 +58,7 @@ public struct RocketListCore: ReducerProtocol { case let .dataFetched(.failure(error)): state.loadingStatus = .failure(RocketsClientAsyncError(from: error)) return .none - + case .rocketDetail, .logoutTapped: return .none } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift index 70cf1276a..945a43148 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift @@ -2,7 +2,6 @@ import UIToolkit extension AccessibilityKeys { public enum RocketListCell { - public static let rocketCellArrow = "rocketCellArrowID" - public static let rocketNameLabel = "rocketNameLabelID" + public static let rocketArrowImage = "rocketArrowImageID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift index b677f210a..529cd1b69 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift @@ -23,7 +23,6 @@ public struct RocketListCellView: View { VStack(alignment: .leading, spacing: 4) { Text(viewStore.rocketData.name) .font(.title2.bold()) - .accessibilityIdentifier(AccessibilityKeys.RocketListCell.rocketNameLabel) Text("First flight: \(viewStore.rocketData.firstFlight)") .font(.callout) @@ -35,7 +34,7 @@ public struct RocketListCellView: View { Image.linkArrow .resizable() .frame(width: 32, height: 32) - .accessibilityIdentifier(AccessibilityKeys.RocketListCell.rocketCellArrow) + .accessibilityIdentifier(AccessibilityKeys.RocketListCell.rocketArrowImage) } } } From 8bc60cd0f83c436da54207e0093aff64c9fae391 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 30 Dec 2024 13:52:55 +0100 Subject: [PATCH 23/41] Fix rocket launch test --- .../RocketLaunch/AccessibilityKeys+Launch.swift | 1 + .../Sources/RocketLaunch/RocketLaunchView.swift | 3 +-- .../Screens/RocketLaunchScreen.swift | 15 ++++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift index e0e5b3c21..2d0d081ea 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift @@ -3,5 +3,6 @@ import UIToolkit extension AccessibilityKeys { public enum RocketLaunch { public static let rocketImage = "rocketImageID" + public static let rocketLaunchText = "RocketLaunchTextID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift index 44f78265b..dda9333c5 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift @@ -57,8 +57,6 @@ public struct RocketLaunchView: View { flyingRocket } else { Image.rocketIdle - .accessibilityIdentifier(AccessibilityKeys.RocketLaunch.rocketImage) - } Spacer() @@ -218,6 +216,7 @@ public struct RocketLaunchView: View { .font(.headline) .bold() .foregroundColor(viewStore.textColor) + .accessibilityIdentifier(AccessibilityKeys.RocketLaunch.rocketLaunchText) } private var flash: some View { diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift index 166c2b37d..cf000a541 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift @@ -5,17 +5,18 @@ import XCTest struct RocketLaunchScreen: Screen { let app: XCUIApplication - - private let rocketImage: XCUIElement - + + private let rocketLaunchText: XCUIElement + init(app: XCUIApplication) { self.app = app - rocketImage = app.images[AccessibilityKeys.RocketLaunch.rocketImage] + + rocketLaunchText = app.staticTexts[AccessibilityKeys.RocketLaunch.rocketLaunchText] } - + @discardableResult - func checkRocketImage() -> Self { - XCTAssert(rocketImage.waitForExistence(timeout: 5), "Rocket image did not appear within the timeout.") + func checkRocketLaunchText() -> Self { + XCTAssert(rocketLaunchText.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) return self } } From 7d2af4ffa6e9f87e800c19a201400a3113e54c1a Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 30 Dec 2024 13:55:38 +0100 Subject: [PATCH 24/41] Fix login screen tests --- .../Screens/LoginScreen.swift | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift index d902744fb..b9a431bc8 100644 --- a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift @@ -13,34 +13,37 @@ struct LoginScreen: Screen { init(app: XCUIApplication) { self.app = app - loginTitle = app.staticTexts[AccessibilityKeys.Login.title] - usernameField = app.textFields[AccessibilityKeys.Login.username] - passwordField = app.secureTextFields[AccessibilityKeys.Login.password] + loginTitle = app.staticTexts[AccessibilityKeys.Login.titleText] + usernameField = app.textFields[AccessibilityKeys.Login.usernameTextField] + passwordField = app.secureTextFields[AccessibilityKeys.Login.passwordSecureField] loginButton = app.buttons[AccessibilityKeys.Login.loginButton] } @discardableResult func checkLoginTitle() -> Self { - XCTAssert(loginTitle.waitForExistence(timeout: 5), "Login title did not appear within the timeout.") + XCTAssert(loginTitle.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) return self } - + @discardableResult - func enterUsername(_ username: String) -> Self { + func enter(username: String) -> Self { + XCTAssert(usernameField.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) usernameField.tap() usernameField.typeText(username) return self } - + @discardableResult - func enterPassword(_ password: String) -> Self { + func enter(password: String) -> Self { + XCTAssert(passwordField.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) passwordField.tap() passwordField.typeText(password) return self } - + @discardableResult func tapLoginButton() -> Self { + XCTAssert(loginButton.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) loginButton.tap() return self } From 6768d631976a9a0d861e71bb962d1173ebd78198 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 30 Dec 2024 13:56:58 +0100 Subject: [PATCH 25/41] Fix Rocket Detail tests --- .../Screens/RocketDetailScreen.swift | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift index 31ed1cf0b..e0ceaef05 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift @@ -5,24 +5,25 @@ import XCTest struct RocketDetailScreen: Screen { let app: XCUIApplication - + private let rocketTitle: XCUIElement private let launchButton: XCUIElement - + init(app: XCUIApplication) { self.app = app - rocketTitle = app.staticTexts[AccessibilityKeys.RocketDetail.rocketTitle] + rocketTitle = app.staticTexts[AccessibilityKeys.RocketDetail.rocketTitleText] launchButton = app.navigationBars.buttons[AccessibilityKeys.RocketDetail.launchButton] } - + @discardableResult func checkRocketTitle() -> Self { - XCTAssert(rocketTitle.waitForExistence(timeout: 5), "Rocket title did not appear within the timeout.") + XCTAssert(rocketTitle.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) return self } - + @discardableResult func tapLaunchButton() -> Self { + XCTAssert(launchButton.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) launchButton.tap() return self } From ecf4574da5fe0a8439d5077db2f2570ed22b4550 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 30 Dec 2024 13:59:33 +0100 Subject: [PATCH 26/41] Remove check rocket count and names functions Fix goToRocket function --- .../Screens/RocketListScreen.swift | 36 ++++--------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift index d764164ac..790c59d3f 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift @@ -6,42 +6,18 @@ import XCTest struct RocketListScreen: Screen { let app: XCUIApplication - private let rocketCells: XCUIElementQuery - private let rocketNames: XCUIElementQuery private let rocketCellArrows: XCUIElementQuery - + init(app: XCUIApplication) { self.app = app - - rocketCells = app.buttons.matching(identifier: AccessibilityKeys.RocketList.rocketCell) - rocketNames = app.staticTexts.matching(identifier: AccessibilityKeys.RocketListCell.rocketNameLabel) - rocketCellArrows = app.images.matching(identifier: AccessibilityKeys.RocketListCell.rocketCellArrow) - } - @discardableResult - func countRocketCells() -> Self { - XCTAssert(rocketCells.firstMatch.waitForExistence(timeout: 5), "Rocket cells did not appear within the timeout.") - print("Number of rocket cells displayed: \(rocketCells.count)") - XCTAssert(rocketCells.count == 4, "Expected exactly 4 rocket cells to be displayed.") - return self + rocketCellArrows = app.images.matching(identifier: AccessibilityKeys.RocketListCell.rocketArrowImage) } - - @discardableResult - func checkRocketNames(_ expectedRocketNames: [String]) -> Self { - for index in 0.. Self { - let firstRocketCell = rocketCellArrows.element(boundBy: 0) + func goToRocket(atIndex index: Int) -> Self { + let firstRocketCell = rocketCellArrows.element(boundBy: index) + XCTAssert(firstRocketCell.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) firstRocketCell.tap() return self } From e31abba2929d2041e6847c17eb5f9826a3f2d6b6 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 30 Dec 2024 14:00:18 +0100 Subject: [PATCH 27/41] Fix rocket launch test --- .../iOS/RocketAppUITests/Tests/RocketLaunchTest.swift | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift b/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift index 30dee42d4..238309ffd 100644 --- a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift +++ b/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift @@ -5,17 +5,15 @@ final class TestRocketLaunch: BaseTestCase { LoginScreen(app: app) .checkLoginTitle() - .enterUsername(TestConstants.LoginDetails.username) - .enterPassword(TestConstants.LoginDetails.password) + .enter(username: TestConstants.LoginCredentials.username) + .enter(password: TestConstants.LoginCredentials.password) .tapLoginButton() RocketListScreen(app: app) - .countRocketCells() - .checkRocketNames(TestConstants.Rockets.rocketNames) - .tapFirstRocketCell() + .goToRocket(atIndex: 0) RocketDetailScreen(app: app) .checkRocketTitle() .tapLaunchButton() RocketLaunchScreen(app: app) - .checkRocketImage() + .checkRocketLaunchText() } } From aa0c6d3a687ec56b21f7b27418cf9837f253862e Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 30 Dec 2024 14:01:19 +0100 Subject: [PATCH 28/41] Fix test constants Add timeout constant --- .../iOS/RocketAppUITests/Utils/TestConstants.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Solution/iOS/RocketAppUITests/Utils/TestConstants.swift b/Solution/iOS/RocketAppUITests/Utils/TestConstants.swift index da679f551..9d523ab0d 100644 --- a/Solution/iOS/RocketAppUITests/Utils/TestConstants.swift +++ b/Solution/iOS/RocketAppUITests/Utils/TestConstants.swift @@ -1,11 +1,12 @@ import Foundation enum TestConstants { - enum LoginDetails{ - public static let username = "astronaut1" - public static let password = "space" + enum LoginCredentials { + static let username = "astronaut1" + static let password = "space" } - enum Rockets { - public static let rocketNames = ["Falcon 1", "Falcon 9", "Falcon Heavy", "Starship"] + + enum Timeouts { + static let defaultTimeout: Double = 5 } } From d921436a74dc34ad9b370f55c99f89b0d62a0c7a Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Tue, 7 Jan 2025 22:20:43 +0100 Subject: [PATCH 29/41] REFACTOR: change naming and remove uneccessary code --- .../Features/Sources/Login/AccessibilityKeys+Login.swift | 2 +- .../iOS/RocketApp/Features/Sources/Login/LoginView.swift | 2 +- .../Sources/RocketLaunch/AccessibilityKeys+Launch.swift | 1 - .../Sources/RocketList/AccessibilityKeys+List.swift | 7 ------- .../Features/Sources/RocketList/RocketListView.swift | 1 - .../iOS/RocketAppUITests/Screens/RocketListScreen.swift | 8 ++++---- .../iOS/RocketAppUITests/Tests/RocketLaunchTest.swift | 2 +- 7 files changed, 7 insertions(+), 16 deletions(-) delete mode 100644 Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift b/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift index 910194235..7cd062c48 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/AccessibilityKeys+Login.swift @@ -2,7 +2,7 @@ import UIToolkit extension AccessibilityKeys { public enum Login { - public static let titleText = "titleTextID" + public static let titleStaticText = "titleStaticTextID" public static let usernameTextField = "usernameTextFieldID" public static let passwordSecureField = "passwordSecureFieldID" public static let loginButton = "loginButtonID" diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift index 60a3d99a6..95fc31516 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift @@ -34,7 +34,7 @@ public struct LoginView: View { .font(.largeTitle) .fontWeight(.bold) .padding(.top, 20) - .accessibilityIdentifier(AccessibilityKeys.Login.titleText) + .accessibilityIdentifier(AccessibilityKeys.Login.titleStaticText) Spacer() diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift index 2d0d081ea..cc2d191e3 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift @@ -2,7 +2,6 @@ import UIToolkit extension AccessibilityKeys { public enum RocketLaunch { - public static let rocketImage = "rocketImageID" public static let rocketLaunchText = "RocketLaunchTextID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift deleted file mode 100644 index 5560eb44d..000000000 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/AccessibilityKeys+List.swift +++ /dev/null @@ -1,7 +0,0 @@ -import UIToolkit - -extension AccessibilityKeys { - public enum RocketList { - public static let rocketCell = "rocketCellID" - } -} diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift index f7d4bcf69..7a7011bc2 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift @@ -59,7 +59,6 @@ public struct RocketListView: View { ) ) { RocketListCellView(store: $0) - .accessibilityIdentifier(AccessibilityKeys.RocketList.rocketCell) } } .listStyle(.sidebar) diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift index 790c59d3f..da047fab6 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift @@ -15,10 +15,10 @@ struct RocketListScreen: Screen { } @discardableResult - func goToRocket(atIndex index: Int) -> Self { - let firstRocketCell = rocketCellArrows.element(boundBy: index) - XCTAssert(firstRocketCell.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) - firstRocketCell.tap() + func goToRocketDetail(index: Int) -> Self { + let rocketArrow = rocketCellArrows.element(boundBy: index) + XCTAssert(rocketArrow.waitForExistence(timeout: Timeouts.defaultTimeout)) + rocketArrow.tap() return self } } diff --git a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift b/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift index 238309ffd..c09b76bb1 100644 --- a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift +++ b/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift @@ -9,7 +9,7 @@ final class TestRocketLaunch: BaseTestCase { .enter(password: TestConstants.LoginCredentials.password) .tapLoginButton() RocketListScreen(app: app) - .goToRocket(atIndex: 0) + .goToRocketDetail(index: 0) RocketDetailScreen(app: app) .checkRocketTitle() .tapLaunchButton() From e8dc9abc33347e82e876bceaf0466548cade78ff Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Tue, 7 Jan 2025 22:23:11 +0100 Subject: [PATCH 30/41] REFACTOR: Move timeouts from TestConstants to dedicated Timeouts file --- .../Screens/LoginScreen.swift | 26 +++++++++---------- .../Screens/RocketDetailScreen.swift | 4 +-- .../Screens/RocketLaunchScreen.swift | 4 +-- .../Utils/TestConstants.swift | 4 --- .../iOS/RocketAppUITests/Utils/Timeouts.swift | 5 ++++ 5 files changed, 22 insertions(+), 21 deletions(-) create mode 100644 Solution/iOS/RocketAppUITests/Utils/Timeouts.swift diff --git a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift index b9a431bc8..882125a18 100644 --- a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift @@ -7,43 +7,43 @@ struct LoginScreen: Screen { let app: XCUIApplication private let loginTitle: XCUIElement - private let usernameField: XCUIElement - private let passwordField: XCUIElement + private let usernameTextField: XCUIElement + private let passwordSecureTextField: XCUIElement private let loginButton: XCUIElement init(app: XCUIApplication) { self.app = app - loginTitle = app.staticTexts[AccessibilityKeys.Login.titleText] - usernameField = app.textFields[AccessibilityKeys.Login.usernameTextField] - passwordField = app.secureTextFields[AccessibilityKeys.Login.passwordSecureField] + loginTitle = app.staticTexts[AccessibilityKeys.Login.titleStaticText] + usernameTextField = app.textFields[AccessibilityKeys.Login.usernameTextField] + passwordSecureTextField = app.secureTextFields[AccessibilityKeys.Login.passwordSecureField] loginButton = app.buttons[AccessibilityKeys.Login.loginButton] } @discardableResult func checkLoginTitle() -> Self { - XCTAssert(loginTitle.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) + XCTAssert(loginTitle.waitForExistence(timeout: Timeouts.defaultTimeout)) return self } @discardableResult func enter(username: String) -> Self { - XCTAssert(usernameField.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) - usernameField.tap() - usernameField.typeText(username) + XCTAssert(usernameTextField.waitForExistence(timeout: Timeouts.defaultTimeout)) + usernameTextField.tap() + usernameTextField.typeText(username) return self } @discardableResult func enter(password: String) -> Self { - XCTAssert(passwordField.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) - passwordField.tap() - passwordField.typeText(password) + XCTAssert(passwordSecureTextField.waitForExistence(timeout: Timeouts.defaultTimeout)) + passwordSecureTextField.tap() + passwordSecureTextField.typeText(password) return self } @discardableResult func tapLoginButton() -> Self { - XCTAssert(loginButton.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) + XCTAssert(loginButton.waitForExistence(timeout: Timeouts.defaultTimeout)) loginButton.tap() return self } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift index e0ceaef05..1c9811e8f 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift @@ -17,13 +17,13 @@ struct RocketDetailScreen: Screen { @discardableResult func checkRocketTitle() -> Self { - XCTAssert(rocketTitle.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) + XCTAssert(rocketTitle.waitForExistence(timeout: Timeouts.defaultTimeout)) return self } @discardableResult func tapLaunchButton() -> Self { - XCTAssert(launchButton.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) + XCTAssert(launchButton.waitForExistence(timeout: Timeouts.defaultTimeout)) launchButton.tap() return self } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift index cf000a541..63e56335f 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift @@ -10,13 +10,13 @@ struct RocketLaunchScreen: Screen { init(app: XCUIApplication) { self.app = app - + rocketLaunchText = app.staticTexts[AccessibilityKeys.RocketLaunch.rocketLaunchText] } @discardableResult func checkRocketLaunchText() -> Self { - XCTAssert(rocketLaunchText.waitForExistence(timeout: TestConstants.Timeouts.defaultTimeout)) + XCTAssert(rocketLaunchText.waitForExistence(timeout: Timeouts.defaultTimeout)) return self } } diff --git a/Solution/iOS/RocketAppUITests/Utils/TestConstants.swift b/Solution/iOS/RocketAppUITests/Utils/TestConstants.swift index 9d523ab0d..c55605827 100644 --- a/Solution/iOS/RocketAppUITests/Utils/TestConstants.swift +++ b/Solution/iOS/RocketAppUITests/Utils/TestConstants.swift @@ -5,8 +5,4 @@ enum TestConstants { static let username = "astronaut1" static let password = "space" } - - enum Timeouts { - static let defaultTimeout: Double = 5 - } } diff --git a/Solution/iOS/RocketAppUITests/Utils/Timeouts.swift b/Solution/iOS/RocketAppUITests/Utils/Timeouts.swift new file mode 100644 index 000000000..d339a27c1 --- /dev/null +++ b/Solution/iOS/RocketAppUITests/Utils/Timeouts.swift @@ -0,0 +1,5 @@ +import XCTest + +enum Timeouts { + static let defaultTimeout: Double = 5 +} From a0c026f4695b38a16542daf1244b92b0c35caadc Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Wed, 8 Jan 2025 11:01:07 +0100 Subject: [PATCH 31/41] REFACTOR: Change name of file, class and func --- .../Tests/{RocketLaunchTest.swift => RocketLaunch.swift} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename Solution/iOS/RocketAppUITests/Tests/{RocketLaunchTest.swift => RocketLaunch.swift} (85%) diff --git a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift b/Solution/iOS/RocketAppUITests/Tests/RocketLaunch.swift similarity index 85% rename from Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift rename to Solution/iOS/RocketAppUITests/Tests/RocketLaunch.swift index c09b76bb1..385b515cb 100644 --- a/Solution/iOS/RocketAppUITests/Tests/RocketLaunchTest.swift +++ b/Solution/iOS/RocketAppUITests/Tests/RocketLaunch.swift @@ -1,7 +1,7 @@ import XCTest -final class TestRocketLaunch: BaseTestCase { - func testRocketLaunchTest() { +final class RocketLaunch: BaseTestCase { + func testRocketLaunch() { LoginScreen(app: app) .checkLoginTitle() From 8006ab1ba4102b56909e477816be707e411a90ce Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 13 Jan 2025 18:29:26 +0100 Subject: [PATCH 32/41] REFACTOR change naming --- .../Sources/RocketDetail/RocketDetailView.swift | 2 +- .../AccessibilityKeys+RocketLaunch.swift | 7 +++++++ .../Sources/RocketLaunch/RocketLaunchView.swift | 2 +- .../AccessibilityKeys+RocketListCell.swift | 7 +++++++ .../RocketListCell/RocketListCellView.swift | 2 +- .../RocketAppUITests/Screens/LoginScreen.swift | 16 ++++++++-------- .../Screens/RocketDetailScreen.swift | 7 ++++--- .../Screens/RocketLaunchScreen.swift | 7 ++++--- .../Screens/RocketListScreen.swift | 10 +++++----- .../RocketAppUITests/Tests/RocketLaunch.swift | 1 - 10 files changed, 38 insertions(+), 23 deletions(-) create mode 100644 Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+RocketLaunch.swift create mode 100644 Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+RocketListCell.swift diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift index 158e27084..195d243e7 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift @@ -57,7 +57,7 @@ public struct RocketDetailView: View { section(.overview) { Text(viewStore.rocketData.overview) .font(.body) - .accessibilityIdentifier(AccessibilityKeys.RocketDetail.rocketTitleText) + .accessibilityIdentifier(AccessibilityKeys.RocketDetail.titleStaticText) } .padding(.bottom) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+RocketLaunch.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+RocketLaunch.swift new file mode 100644 index 000000000..74735fc75 --- /dev/null +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+RocketLaunch.swift @@ -0,0 +1,7 @@ +import UIToolkit + +extension AccessibilityKeys { + public enum RocketLaunch { + public static let titleStaticText = "titleStaticTextID" + } +} diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift index dda9333c5..ef90bcd42 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift @@ -216,7 +216,7 @@ public struct RocketLaunchView: View { .font(.headline) .bold() .foregroundColor(viewStore.textColor) - .accessibilityIdentifier(AccessibilityKeys.RocketLaunch.rocketLaunchText) + .accessibilityIdentifier(AccessibilityKeys.RocketLaunch.titleStaticText) } private var flash: some View { diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+RocketListCell.swift b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+RocketListCell.swift new file mode 100644 index 000000000..603702329 --- /dev/null +++ b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+RocketListCell.swift @@ -0,0 +1,7 @@ +import UIToolkit + +extension AccessibilityKeys { + public enum RocketListCell { + public static let arrowImage = "arrowImageID" + } +} diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift index 529cd1b69..3d601666c 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift @@ -34,7 +34,7 @@ public struct RocketListCellView: View { Image.linkArrow .resizable() .frame(width: 32, height: 32) - .accessibilityIdentifier(AccessibilityKeys.RocketListCell.rocketArrowImage) + .accessibilityIdentifier(AccessibilityKeys.RocketListCell.arrowImage) } } } diff --git a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift index 882125a18..69dbfd983 100644 --- a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift @@ -6,22 +6,22 @@ import XCTest struct LoginScreen: Screen { let app: XCUIApplication - private let loginTitle: XCUIElement + private let titleStaticText: XCUIElement private let usernameTextField: XCUIElement - private let passwordSecureTextField: XCUIElement + private let passwordSecureField: XCUIElement private let loginButton: XCUIElement init(app: XCUIApplication) { self.app = app - loginTitle = app.staticTexts[AccessibilityKeys.Login.titleStaticText] + titleStaticText = app.staticTexts[AccessibilityKeys.Login.titleStaticText] usernameTextField = app.textFields[AccessibilityKeys.Login.usernameTextField] - passwordSecureTextField = app.secureTextFields[AccessibilityKeys.Login.passwordSecureField] + passwordSecureField = app.secureTextFields[AccessibilityKeys.Login.passwordSecureField] loginButton = app.buttons[AccessibilityKeys.Login.loginButton] } @discardableResult func checkLoginTitle() -> Self { - XCTAssert(loginTitle.waitForExistence(timeout: Timeouts.defaultTimeout)) + XCTAssert(titleStaticText.waitForExistence(timeout: Timeouts.defaultTimeout)) return self } @@ -35,9 +35,9 @@ struct LoginScreen: Screen { @discardableResult func enter(password: String) -> Self { - XCTAssert(passwordSecureTextField.waitForExistence(timeout: Timeouts.defaultTimeout)) - passwordSecureTextField.tap() - passwordSecureTextField.typeText(password) + XCTAssert(passwordSecureField.waitForExistence(timeout: Timeouts.defaultTimeout)) + passwordSecureField.tap() + passwordSecureField.typeText(password) return self } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift index 1c9811e8f..5a4f49c0b 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift @@ -6,18 +6,19 @@ import XCTest struct RocketDetailScreen: Screen { let app: XCUIApplication - private let rocketTitle: XCUIElement + private let titleStaticText: XCUIElement private let launchButton: XCUIElement init(app: XCUIApplication) { self.app = app - rocketTitle = app.staticTexts[AccessibilityKeys.RocketDetail.rocketTitleText] + titleStaticText = app.staticTexts[AccessibilityKeys.RocketDetail.titleStaticText] launchButton = app.navigationBars.buttons[AccessibilityKeys.RocketDetail.launchButton] } @discardableResult func checkRocketTitle() -> Self { - XCTAssert(rocketTitle.waitForExistence(timeout: Timeouts.defaultTimeout)) + XCTAssert(titleStaticText.waitForExistence(timeout: Timeouts.defaultTimeout)) + print(app.debugDescription) return self } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift index 63e56335f..95f46880d 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift @@ -6,17 +6,18 @@ import XCTest struct RocketLaunchScreen: Screen { let app: XCUIApplication - private let rocketLaunchText: XCUIElement + private let titleStaticText: XCUIElement init(app: XCUIApplication) { self.app = app - rocketLaunchText = app.staticTexts[AccessibilityKeys.RocketLaunch.rocketLaunchText] + titleStaticText = app.staticTexts[AccessibilityKeys.RocketLaunch.titleStaticText] } @discardableResult func checkRocketLaunchText() -> Self { - XCTAssert(rocketLaunchText.waitForExistence(timeout: Timeouts.defaultTimeout)) + XCTAssert(titleStaticText.waitForExistence(timeout: Timeouts.defaultTimeout)) + print(app.debugDescription) return self } } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift index da047fab6..9beb14223 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift @@ -6,19 +6,19 @@ import XCTest struct RocketListScreen: Screen { let app: XCUIApplication - private let rocketCellArrows: XCUIElementQuery + private let arrowImage: XCUIElementQuery init(app: XCUIApplication) { self.app = app - rocketCellArrows = app.images.matching(identifier: AccessibilityKeys.RocketListCell.rocketArrowImage) + arrowImage = app.images.matching(identifier: AccessibilityKeys.RocketListCell.arrowImage) } @discardableResult func goToRocketDetail(index: Int) -> Self { - let rocketArrow = rocketCellArrows.element(boundBy: index) - XCTAssert(rocketArrow.waitForExistence(timeout: Timeouts.defaultTimeout)) - rocketArrow.tap() + let rocket = arrowImage.element(boundBy: index) + XCTAssert(rocket.waitForExistence(timeout: Timeouts.defaultTimeout)) + rocket.tap() return self } } diff --git a/Solution/iOS/RocketAppUITests/Tests/RocketLaunch.swift b/Solution/iOS/RocketAppUITests/Tests/RocketLaunch.swift index 385b515cb..f67f7cef6 100644 --- a/Solution/iOS/RocketAppUITests/Tests/RocketLaunch.swift +++ b/Solution/iOS/RocketAppUITests/Tests/RocketLaunch.swift @@ -2,7 +2,6 @@ import XCTest final class RocketLaunch: BaseTestCase { func testRocketLaunch() { - LoginScreen(app: app) .checkLoginTitle() .enter(username: TestConstants.LoginCredentials.username) From 73731e8c9c0aeead7dd5950c6d43e2fb9dca6253 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 13 Jan 2025 18:30:10 +0100 Subject: [PATCH 33/41] REFACTOR renaming files --- .../Sources/RocketLaunch/AccessibilityKeys+Launch.swift | 7 ------- .../Sources/RocketListCell/AccessibilityKeys+List.swift | 7 ------- 2 files changed, 14 deletions(-) delete mode 100644 Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift delete mode 100644 Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift deleted file mode 100644 index cc2d191e3..000000000 --- a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+Launch.swift +++ /dev/null @@ -1,7 +0,0 @@ -import UIToolkit - -extension AccessibilityKeys { - public enum RocketLaunch { - public static let rocketLaunchText = "RocketLaunchTextID" - } -} diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift deleted file mode 100644 index 945a43148..000000000 --- a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/AccessibilityKeys+List.swift +++ /dev/null @@ -1,7 +0,0 @@ -import UIToolkit - -extension AccessibilityKeys { - public enum RocketListCell { - public static let rocketArrowImage = "rocketArrowImageID" - } -} From b73e89b0541aea16a0db5e141960ba252354158d Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 13 Jan 2025 18:30:42 +0100 Subject: [PATCH 34/41] REFACTOR rename file --- ...tyKeys+Detail.swift => AccessibilityKeys+RocketDetail.swift} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Solution/iOS/RocketApp/Features/Sources/RocketDetail/{AccessibilityKeys+Detail.swift => AccessibilityKeys+RocketDetail.swift} (69%) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+RocketDetail.swift similarity index 69% rename from Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift rename to Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+RocketDetail.swift index aff6cf206..e8d51c40f 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+Detail.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+RocketDetail.swift @@ -2,7 +2,7 @@ import UIToolkit extension AccessibilityKeys { public enum RocketDetail { - public static let rocketTitleText = "rocketTitleTextID" + public static let titleStaticText = "titleStaticTextID" public static let launchButton = "launchButtonID" } } From f38af48c73455c0964e1710af675313f136db267 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 13 Jan 2025 19:16:36 +0100 Subject: [PATCH 35/41] REMOVE prints --- Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift | 1 - Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift index 5a4f49c0b..f095ff553 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift @@ -18,7 +18,6 @@ struct RocketDetailScreen: Screen { @discardableResult func checkRocketTitle() -> Self { XCTAssert(titleStaticText.waitForExistence(timeout: Timeouts.defaultTimeout)) - print(app.debugDescription) return self } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift index 95f46880d..da70a2d1f 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift @@ -17,7 +17,6 @@ struct RocketLaunchScreen: Screen { @discardableResult func checkRocketLaunchText() -> Self { XCTAssert(titleStaticText.waitForExistence(timeout: Timeouts.defaultTimeout)) - print(app.debugDescription) return self } } From eb9e10ae09bf7fba687a8a49665be9da8e6089ea Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Tue, 14 Jan 2025 15:49:53 +0100 Subject: [PATCH 36/41] REFACTOR rename constants --- .../Screens/LoginScreen.swift | 26 +++++++++---------- .../Screens/RocketDetailScreen.swift | 6 ++--- .../Screens/RocketLaunchScreen.swift | 6 ++--- .../Screens/RocketListScreen.swift | 6 ++--- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift index 69dbfd983..640f45d4b 100644 --- a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift @@ -6,38 +6,38 @@ import XCTest struct LoginScreen: Screen { let app: XCUIApplication - private let titleStaticText: XCUIElement - private let usernameTextField: XCUIElement - private let passwordSecureField: XCUIElement + private let title: XCUIElement + private let usernameField: XCUIElement + private let passwordField: XCUIElement private let loginButton: XCUIElement init(app: XCUIApplication) { self.app = app - titleStaticText = app.staticTexts[AccessibilityKeys.Login.titleStaticText] - usernameTextField = app.textFields[AccessibilityKeys.Login.usernameTextField] - passwordSecureField = app.secureTextFields[AccessibilityKeys.Login.passwordSecureField] + title = app.staticTexts[AccessibilityKeys.Login.titleStaticText] + usernameField = app.textFields[AccessibilityKeys.Login.usernameTextField] + passwordField = app.secureTextFields[AccessibilityKeys.Login.passwordSecureField] loginButton = app.buttons[AccessibilityKeys.Login.loginButton] } @discardableResult func checkLoginTitle() -> Self { - XCTAssert(titleStaticText.waitForExistence(timeout: Timeouts.defaultTimeout)) + XCTAssert(title.waitForExistence(timeout: Timeouts.defaultTimeout)) return self } @discardableResult func enter(username: String) -> Self { - XCTAssert(usernameTextField.waitForExistence(timeout: Timeouts.defaultTimeout)) - usernameTextField.tap() - usernameTextField.typeText(username) + XCTAssert(usernameField.waitForExistence(timeout: Timeouts.defaultTimeout)) + usernameField.tap() + usernameField.typeText(username) return self } @discardableResult func enter(password: String) -> Self { - XCTAssert(passwordSecureField.waitForExistence(timeout: Timeouts.defaultTimeout)) - passwordSecureField.tap() - passwordSecureField.typeText(password) + XCTAssert(passwordField.waitForExistence(timeout: Timeouts.defaultTimeout)) + passwordField.tap() + passwordField.typeText(password) return self } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift index f095ff553..653705773 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift @@ -6,18 +6,18 @@ import XCTest struct RocketDetailScreen: Screen { let app: XCUIApplication - private let titleStaticText: XCUIElement + private let title: XCUIElement private let launchButton: XCUIElement init(app: XCUIApplication) { self.app = app - titleStaticText = app.staticTexts[AccessibilityKeys.RocketDetail.titleStaticText] + title = app.staticTexts[AccessibilityKeys.RocketDetail.titleStaticText] launchButton = app.navigationBars.buttons[AccessibilityKeys.RocketDetail.launchButton] } @discardableResult func checkRocketTitle() -> Self { - XCTAssert(titleStaticText.waitForExistence(timeout: Timeouts.defaultTimeout)) + XCTAssert(title.waitForExistence(timeout: Timeouts.defaultTimeout)) return self } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift index da70a2d1f..3228a2f82 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift @@ -6,17 +6,17 @@ import XCTest struct RocketLaunchScreen: Screen { let app: XCUIApplication - private let titleStaticText: XCUIElement + private let title: XCUIElement init(app: XCUIApplication) { self.app = app - titleStaticText = app.staticTexts[AccessibilityKeys.RocketLaunch.titleStaticText] + title = app.staticTexts[AccessibilityKeys.RocketLaunch.titleStaticText] } @discardableResult func checkRocketLaunchText() -> Self { - XCTAssert(titleStaticText.waitForExistence(timeout: Timeouts.defaultTimeout)) + XCTAssert(title.waitForExistence(timeout: Timeouts.defaultTimeout)) return self } } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift index 9beb14223..849f57d55 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketListScreen.swift @@ -6,17 +6,17 @@ import XCTest struct RocketListScreen: Screen { let app: XCUIApplication - private let arrowImage: XCUIElementQuery + private let arrowImages: XCUIElementQuery init(app: XCUIApplication) { self.app = app - arrowImage = app.images.matching(identifier: AccessibilityKeys.RocketListCell.arrowImage) + arrowImages = app.images.matching(identifier: AccessibilityKeys.RocketListCell.arrowImage) } @discardableResult func goToRocketDetail(index: Int) -> Self { - let rocket = arrowImage.element(boundBy: index) + let rocket = arrowImages.element(boundBy: index) XCTAssert(rocket.waitForExistence(timeout: Timeouts.defaultTimeout)) rocket.tap() return self From 16dae6e8f24e58d5d338cd3182bc51e81dc9bcfc Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 20 Jan 2025 12:23:28 +0100 Subject: [PATCH 37/41] REFACTOR: rename and delete whitespace --- .../RocketLaunch/AccessibilityKeys+RocketLaunch.swift | 2 +- .../Features/Sources/RocketLaunch/RocketLaunchView.swift | 2 +- Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift | 1 + .../iOS/RocketAppUITests/Screens/RocketDetailScreen.swift | 1 + .../iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift | 6 +++--- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+RocketLaunch.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+RocketLaunch.swift index 74735fc75..cdfda8b48 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+RocketLaunch.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+RocketLaunch.swift @@ -2,6 +2,6 @@ import UIToolkit extension AccessibilityKeys { public enum RocketLaunch { - public static let titleStaticText = "titleStaticTextID" + public static let distanceToLaunchStaticText = "distanceToLaunchStaticTextID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift index ef90bcd42..3cd7f9fb0 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift @@ -216,7 +216,7 @@ public struct RocketLaunchView: View { .font(.headline) .bold() .foregroundColor(viewStore.textColor) - .accessibilityIdentifier(AccessibilityKeys.RocketLaunch.titleStaticText) + .accessibilityIdentifier(AccessibilityKeys.RocketLaunch.distanceToLaunchStaticText) } private var flash: some View { diff --git a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift index 640f45d4b..0b138ae21 100644 --- a/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/LoginScreen.swift @@ -13,6 +13,7 @@ struct LoginScreen: Screen { init(app: XCUIApplication) { self.app = app + title = app.staticTexts[AccessibilityKeys.Login.titleStaticText] usernameField = app.textFields[AccessibilityKeys.Login.usernameTextField] passwordField = app.secureTextFields[AccessibilityKeys.Login.passwordSecureField] diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift index 653705773..74cf1c75e 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift @@ -11,6 +11,7 @@ struct RocketDetailScreen: Screen { init(app: XCUIApplication) { self.app = app + title = app.staticTexts[AccessibilityKeys.RocketDetail.titleStaticText] launchButton = app.navigationBars.buttons[AccessibilityKeys.RocketDetail.launchButton] } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift index 3228a2f82..ae4bcd7e9 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift @@ -6,17 +6,17 @@ import XCTest struct RocketLaunchScreen: Screen { let app: XCUIApplication - private let title: XCUIElement + private let distanceToLaunchText: XCUIElement init(app: XCUIApplication) { self.app = app - title = app.staticTexts[AccessibilityKeys.RocketLaunch.titleStaticText] + distanceToLaunchText = app.staticTexts[AccessibilityKeys.RocketLaunch.distanceToLaunchStaticText] } @discardableResult func checkRocketLaunchText() -> Self { - XCTAssert(title.waitForExistence(timeout: Timeouts.defaultTimeout)) + XCTAssert(distanceToLaunchText.waitForExistence(timeout: Timeouts.defaultTimeout)) return self } } From 4d8e20a38dc70014f469bf0c6fcf5b6755d3bb79 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 20 Jan 2025 12:24:15 +0100 Subject: [PATCH 38/41] DELETE: example test --- Solution/iOS/RocketAppUITests/Tests/Test.swift | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 Solution/iOS/RocketAppUITests/Tests/Test.swift diff --git a/Solution/iOS/RocketAppUITests/Tests/Test.swift b/Solution/iOS/RocketAppUITests/Tests/Test.swift deleted file mode 100644 index ed4eafc4d..000000000 --- a/Solution/iOS/RocketAppUITests/Tests/Test.swift +++ /dev/null @@ -1,8 +0,0 @@ -import XCTest - -final class Test: BaseTestCase { - func testTest() { - LoginScreen(app: app) - .checkLoginTitle() - } -} From 9a2b5e6486638a6204bb2acabcd2383bd5417c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikol=20Duchanova=CC=81?= Date: Mon, 20 Jan 2025 14:04:09 +0100 Subject: [PATCH 39/41] Add blocking the inheritance of identifiers --- .../Features/Sources/RocketDetail/RocketDetailView.swift | 3 ++- .../Features/Sources/RocketListCell/RocketListCellView.swift | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift index 195d243e7..c902b427a 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift @@ -104,6 +104,7 @@ public struct RocketDetailView: View { viewStore.send(.rocketLaunchTapped) } .accessibilityIdentifier(AccessibilityKeys.RocketDetail.launchButton) + .accessibilityElement(children: .contain) ) .navigationDestination( store: self.store.scope( @@ -114,7 +115,7 @@ public struct RocketDetailView: View { RocketLaunchView(store: store) } } - + @ViewBuilder private func section(_ caption: LocalizedStringKey, _ content: @escaping () -> V) -> some View { VStack(alignment: .leading, spacing: 8) { diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift index 3d601666c..5d8f322cd 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketListCell/RocketListCellView.swift @@ -35,6 +35,7 @@ public struct RocketListCellView: View { .resizable() .frame(width: 32, height: 32) .accessibilityIdentifier(AccessibilityKeys.RocketListCell.arrowImage) + .accessibilityElement(children: .contain) } } } From bffe8dd5b759c75c246878bde1cc0d81cc71fe26 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Mon, 20 Jan 2025 16:48:36 +0100 Subject: [PATCH 40/41] REFACTOR: rename --- .../RocketDetail/AccessibilityKeys+RocketDetail.swift | 2 +- .../Features/Sources/RocketDetail/RocketDetailView.swift | 2 +- .../RocketLaunch/AccessibilityKeys+RocketLaunch.swift | 2 +- .../Features/Sources/RocketLaunch/RocketLaunchView.swift | 2 +- .../iOS/RocketAppUITests/Screens/RocketDetailScreen.swift | 8 ++++---- .../iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift | 8 ++++---- Solution/iOS/RocketAppUITests/Tests/RocketLaunch.swift | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+RocketDetail.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+RocketDetail.swift index e8d51c40f..3f9ad40a2 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+RocketDetail.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/AccessibilityKeys+RocketDetail.swift @@ -2,7 +2,7 @@ import UIToolkit extension AccessibilityKeys { public enum RocketDetail { - public static let titleStaticText = "titleStaticTextID" + public static let overviewStaticText = "overviewStaticTextID" public static let launchButton = "launchButtonID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift index c902b427a..74989207c 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift @@ -57,7 +57,7 @@ public struct RocketDetailView: View { section(.overview) { Text(viewStore.rocketData.overview) .font(.body) - .accessibilityIdentifier(AccessibilityKeys.RocketDetail.titleStaticText) + .accessibilityIdentifier(AccessibilityKeys.RocketDetail.overviewStaticText) } .padding(.bottom) diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+RocketLaunch.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+RocketLaunch.swift index cdfda8b48..716d3d7d6 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+RocketLaunch.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/AccessibilityKeys+RocketLaunch.swift @@ -2,6 +2,6 @@ import UIToolkit extension AccessibilityKeys { public enum RocketLaunch { - public static let distanceToLaunchStaticText = "distanceToLaunchStaticTextID" + public static let launchDistanceStaticText = "launchDistanceStaticTextID" } } diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift index 3cd7f9fb0..b15f44ef8 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketLaunch/RocketLaunchView.swift @@ -216,7 +216,7 @@ public struct RocketLaunchView: View { .font(.headline) .bold() .foregroundColor(viewStore.textColor) - .accessibilityIdentifier(AccessibilityKeys.RocketLaunch.distanceToLaunchStaticText) + .accessibilityIdentifier(AccessibilityKeys.RocketLaunch.launchDistanceStaticText) } private var flash: some View { diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift index 74cf1c75e..7bb855528 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketDetailScreen.swift @@ -6,19 +6,19 @@ import XCTest struct RocketDetailScreen: Screen { let app: XCUIApplication - private let title: XCUIElement + private let rocketOverview: XCUIElement private let launchButton: XCUIElement init(app: XCUIApplication) { self.app = app - title = app.staticTexts[AccessibilityKeys.RocketDetail.titleStaticText] + rocketOverview = app.staticTexts[AccessibilityKeys.RocketDetail.overviewStaticText] launchButton = app.navigationBars.buttons[AccessibilityKeys.RocketDetail.launchButton] } @discardableResult - func checkRocketTitle() -> Self { - XCTAssert(title.waitForExistence(timeout: Timeouts.defaultTimeout)) + func checkRocketOverview() -> Self { + XCTAssert(rocketOverview.waitForExistence(timeout: Timeouts.defaultTimeout)) return self } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift index ae4bcd7e9..8c95dc796 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift @@ -6,17 +6,17 @@ import XCTest struct RocketLaunchScreen: Screen { let app: XCUIApplication - private let distanceToLaunchText: XCUIElement + private let launchDistanceText: XCUIElement init(app: XCUIApplication) { self.app = app - distanceToLaunchText = app.staticTexts[AccessibilityKeys.RocketLaunch.distanceToLaunchStaticText] + launchDistanceText = app.staticTexts[AccessibilityKeys.RocketLaunch.launchDistanceStaticText] } @discardableResult - func checkRocketLaunchText() -> Self { - XCTAssert(distanceToLaunchText.waitForExistence(timeout: Timeouts.defaultTimeout)) + func checkLaunchDistanceText() -> Self { + XCTAssert(launchDistanceText.waitForExistence(timeout: Timeouts.defaultTimeout)) return self } } diff --git a/Solution/iOS/RocketAppUITests/Tests/RocketLaunch.swift b/Solution/iOS/RocketAppUITests/Tests/RocketLaunch.swift index f67f7cef6..2cb61c29e 100644 --- a/Solution/iOS/RocketAppUITests/Tests/RocketLaunch.swift +++ b/Solution/iOS/RocketAppUITests/Tests/RocketLaunch.swift @@ -10,9 +10,9 @@ final class RocketLaunch: BaseTestCase { RocketListScreen(app: app) .goToRocketDetail(index: 0) RocketDetailScreen(app: app) - .checkRocketTitle() + .checkRocketOverview() .tapLaunchButton() RocketLaunchScreen(app: app) - .checkRocketLaunchText() + .checkLaunchDistanceText() } } From 3b6fd71723cab0278ff0ad38547cca8f4a12a087 Mon Sep 17 00:00:00 2001 From: Cyril Colombier <> Date: Wed, 22 Jan 2025 09:17:17 +0100 Subject: [PATCH 41/41] REFACTOR: Wrong spacing --- .../RocketApp/Features/Sources/Login/LoginView.swift | 1 + .../Sources/RocketDetail/RocketDetailView.swift | 12 ++++++------ .../Features/Sources/RocketList/RocketListView.swift | 1 + .../Screens/RocketLaunchScreen.swift | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift index 95fc31516..458a345fe 100644 --- a/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/Login/LoginView.swift @@ -46,6 +46,7 @@ public struct LoginView: View { SecureField("Password", text: viewStore.binding(\.$password)) .textFieldStyle(.roundedBorder) .accessibilityIdentifier(AccessibilityKeys.Login.passwordSecureField) + Spacer() Button("Login") { diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift index 74989207c..3c1bed909 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketDetail/RocketDetailView.swift @@ -100,12 +100,12 @@ public struct RocketDetailView: View { .navigationTitle(viewStore.rocketData.name) .onAppear { viewStore.send(.rocketLaunchDismiss) } .navigationBarItems( - trailing: Button(.launch) { - viewStore.send(.rocketLaunchTapped) - } - .accessibilityIdentifier(AccessibilityKeys.RocketDetail.launchButton) - .accessibilityElement(children: .contain) - ) + trailing: Button(.launch) { + viewStore.send(.rocketLaunchTapped) + } + .accessibilityIdentifier(AccessibilityKeys.RocketDetail.launchButton) + .accessibilityElement(children: .contain) + ) .navigationDestination( store: self.store.scope( state: \.$rocketLaunch, diff --git a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift index 7a7011bc2..881d4184a 100644 --- a/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift +++ b/Solution/iOS/RocketApp/Features/Sources/RocketList/RocketListView.swift @@ -39,6 +39,7 @@ public struct RocketListView: View { } Spacer() + Button("Logout") { viewStore.send(.logoutTapped) } diff --git a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift index 8c95dc796..773592dd9 100644 --- a/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift +++ b/Solution/iOS/RocketAppUITests/Screens/RocketLaunchScreen.swift @@ -6,7 +6,7 @@ import XCTest struct RocketLaunchScreen: Screen { let app: XCUIApplication - private let launchDistanceText: XCUIElement + private let launchDistanceText: XCUIElement init(app: XCUIApplication) { self.app = app