diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..1357ce0 Binary files /dev/null and b/.DS_Store differ diff --git a/Wiggles-iOS.xcodeproj/project.pbxproj b/Wiggles-iOS.xcodeproj/project.pbxproj index 52ef292..03c8870 100644 --- a/Wiggles-iOS.xcodeproj/project.pbxproj +++ b/Wiggles-iOS.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -27,6 +27,16 @@ 73E4B4D925EE75B10020C282 /* DetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E4B4D825EE75B10020C282 /* DetailsViewModel.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + D667F6782DB1425500FB6437 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 73CEC9D825EE0A8F008362CE /* Project object */; + proxyType = 1; + remoteGlobalIDString = 73CEC9DF25EE0A8F008362CE; + remoteInfo = "Wiggles-iOS"; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 73CEC9E025EE0A8F008362CE /* Wiggles-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Wiggles-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 73CEC9E325EE0A8F008362CE /* Wiggles_iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wiggles_iOSApp.swift; sourceTree = ""; }; @@ -48,8 +58,13 @@ 73CECA2F25EE3A76008362CE /* DogData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DogData.swift; sourceTree = ""; }; 73CECA3425EE416C008362CE /* DetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsView.swift; sourceTree = ""; }; 73E4B4D825EE75B10020C282 /* DetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailsViewModel.swift; sourceTree = ""; }; + D667F6722DB1425500FB6437 /* Wiggles-iOSUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Wiggles-iOSUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedRootGroup section */ + D667F6732DB1425500FB6437 /* Wiggles-iOSUITests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = "Wiggles-iOSUITests"; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 73CEC9DD25EE0A8F008362CE /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -58,6 +73,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D667F66F2DB1425500FB6437 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -65,6 +87,7 @@ isa = PBXGroup; children = ( 73CEC9E225EE0A8F008362CE /* Wiggles-iOS */, + D667F6732DB1425500FB6437 /* Wiggles-iOSUITests */, 73CEC9E125EE0A8F008362CE /* Products */, ); sourceTree = ""; @@ -73,6 +96,7 @@ isa = PBXGroup; children = ( 73CEC9E025EE0A8F008362CE /* Wiggles-iOS.app */, + D667F6722DB1425500FB6437 /* Wiggles-iOSUITests.xctest */, ); name = Products; sourceTree = ""; @@ -179,18 +203,46 @@ productReference = 73CEC9E025EE0A8F008362CE /* Wiggles-iOS.app */; productType = "com.apple.product-type.application"; }; + D667F6712DB1425500FB6437 /* Wiggles-iOSUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = D667F67C2DB1425500FB6437 /* Build configuration list for PBXNativeTarget "Wiggles-iOSUITests" */; + buildPhases = ( + D667F66E2DB1425500FB6437 /* Sources */, + D667F66F2DB1425500FB6437 /* Frameworks */, + D667F6702DB1425500FB6437 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + D667F6792DB1425500FB6437 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + D667F6732DB1425500FB6437 /* Wiggles-iOSUITests */, + ); + name = "Wiggles-iOSUITests"; + packageProductDependencies = ( + ); + productName = "Wiggles-iOSUITests"; + productReference = D667F6722DB1425500FB6437 /* Wiggles-iOSUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 73CEC9D825EE0A8F008362CE /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1240; + LastSwiftUpdateCheck = 1630; LastUpgradeCheck = 1240; TargetAttributes = { 73CEC9DF25EE0A8F008362CE = { CreatedOnToolsVersion = 12.4; }; + D667F6712DB1425500FB6437 = { + CreatedOnToolsVersion = 16.3; + LastSwiftMigration = 1630; + TestTargetID = 73CEC9DF25EE0A8F008362CE; + }; }; }; buildConfigurationList = 73CEC9DB25EE0A8F008362CE /* Build configuration list for PBXProject "Wiggles-iOS" */; @@ -207,6 +259,7 @@ projectRoot = ""; targets = ( 73CEC9DF25EE0A8F008362CE /* Wiggles-iOS */, + D667F6712DB1425500FB6437 /* Wiggles-iOSUITests */, ); }; /* End PBXProject section */ @@ -224,6 +277,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D667F6702DB1425500FB6437 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -247,8 +307,23 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + D667F66E2DB1425500FB6437 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + D667F6792DB1425500FB6437 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 73CEC9DF25EE0A8F008362CE /* Wiggles-iOS */; + targetProxy = D667F6782DB1425500FB6437 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ 73CEC9ED25EE0A92008362CE /* Debug */ = { isa = XCBuildConfiguration; @@ -410,6 +485,54 @@ }; name = Release; }; + D667F67A2DB1425500FB6437 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.4; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "PimySoft.Wiggles-iOSUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "Wiggles-iOS"; + }; + name = Debug; + }; + D667F67B2DB1425500FB6437 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.4; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "PimySoft.Wiggles-iOSUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "Wiggles-iOS"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -431,6 +554,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + D667F67C2DB1425500FB6437 /* Build configuration list for PBXNativeTarget "Wiggles-iOSUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D667F67A2DB1425500FB6437 /* Debug */, + D667F67B2DB1425500FB6437 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 73CEC9D825EE0A8F008362CE /* Project object */; diff --git a/Wiggles-iOS.xcodeproj/project.xcworkspace/xcuserdata/pimysoft.xcuserdatad/UserInterfaceState.xcuserstate b/Wiggles-iOS.xcodeproj/project.xcworkspace/xcuserdata/pimysoft.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..20f78ed Binary files /dev/null and b/Wiggles-iOS.xcodeproj/project.xcworkspace/xcuserdata/pimysoft.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Wiggles-iOS.xcodeproj/xcuserdata/pimysoft.xcuserdatad/xcschemes/xcschememanagement.plist b/Wiggles-iOS.xcodeproj/xcuserdata/pimysoft.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..463e98e --- /dev/null +++ b/Wiggles-iOS.xcodeproj/xcuserdata/pimysoft.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + Wiggles-iOS.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Wiggles-iOS/Details/DetailsView.swift b/Wiggles-iOS/Details/DetailsView.swift index f52ded4..63939c1 100644 --- a/Wiggles-iOS/Details/DetailsView.swift +++ b/Wiggles-iOS/Details/DetailsView.swift @@ -26,27 +26,45 @@ struct DetailsView: View { ZStack(alignment: .top) { Image(viewModel.model.image).resizable() .frame(height: 400).frame(maxWidth: .infinity) + .accessibilityIdentifier("Dog Picture") + .accessibilityLabel("Dog Picture") HStack { Button(action: { self.presentationMode.wrappedValue.dismiss() }, label: { Image(IMAGE_BACK_ICON).resizable().frame(width: 34, height: 34) }) + .accessibilityIdentifier("Back") + .accessibilityLabel("List Page") Spacer() Button(action: { viewModel.favouriteMethod() }, label: { Image(IMAGE_FAV_ICON).resizable().frame(width: 26, height: 26) }) + .accessibilityIdentifier("Fav Button") + .accessibilityLabel("Add To Favourites") }.padding(.horizontal, 24).padding(.top, 46) + + } Group { HStack { Text(viewModel.model.name).modifier(SailecFont(.bold, size: 24)).lineLimit(1) .foregroundColor(Color.text_primary_color) + .accessibilityIdentifier("Name") + .accessibilityValue(viewModel.model.name) Spacer() GenderView(isMale: viewModel.model.gender == "male") + .accessibilityIdentifier("Gender") + .accessibilityLabel("Gender") + .accessibilityValue(viewModel.model.gender) }.padding(.vertical, 8) + + HStack(alignment: .center) { HStack(alignment: .center, spacing: 2) { Image(IMAGE_LOC_ICON).resizable().frame(width: 24, height: 24) Text("\(viewModel.model.location) away").modifier(SailecFont(.regular, size: 14)) .foregroundColor(Color.text_primary_color).padding(.top, 2) + .accessibilityIdentifier("Distance") + .accessibilityLabel("Distance") + .accessibilityValue(viewModel.model.location) } Spacer() Text("\(viewModel.model.age) yrs | \(viewModel.model.about)").modifier(SailecFont(.regular, size: 14)) @@ -58,6 +76,9 @@ struct DetailsView: View { .foregroundColor(Color.text_primary_color) Spacer() }.padding(.leading, 6).padding(.top, 2) + .accessibilityIdentifier("Time") + .accessibilityLabel("Post date") + .accessibilityValue("12 min ago") VStack(spacing: 16) { HStack { @@ -68,6 +89,7 @@ struct DetailsView: View { Text(viewModel.story).modifier(SailecFont(.regular, size: 16)) .foregroundColor(Color.text_primary_color) }.padding(.vertical, 16) + .accessibilityIdentifier("Dog Story") VStack(spacing: 16) { HStack { @@ -85,7 +107,6 @@ struct DetailsView: View { VStack(spacing: 16) { HStack { Text("Owner Info").modifier(SailecFont(.bold, size: 18)) - .foregroundColor(Color.text_primary_color) Spacer() } DetailsOwnerView(image: viewModel.model.owner.image, name: viewModel.model.owner.name, bio: viewModel.model.owner.bio, messageMethod: viewModel.messageMethod) @@ -96,6 +117,8 @@ struct DetailsView: View { .frame(height: 50).frame(maxWidth: .infinity) .background(Color.main_color).cornerRadius(8) .padding(.vertical, 24) + .accessibilityIdentifier("Adopt Me") + .accessibilityLabel("Adopt Me") }.padding(.horizontal, 16).padding(.top, 8) }.background(Color.primary_color) @@ -119,16 +142,23 @@ struct DetailsOwnerView: View { var body: some View { HStack { Image(image).resizable().scaledToFill().frame(width: 60, height: 60).cornerRadius(30) + .foregroundColor(Color.text_primary_color) + .accessibilityIdentifier("Owner Picture") + .accessibilityLabel("Owner Picture") VStack(alignment: .leading, spacing: 8) { Text(name).modifier(SailecFont(.medium, size: 16)) .foregroundColor(Color.text_primary_color) + .accessibilityIdentifier("Owner Name") Text(bio).modifier(SailecFont(.regular, size: 14)) .foregroundColor(Color(hex: "828282")) + .accessibilityIdentifier("Owner Bio") }.padding(.leading, 8) Spacer() Button(action: { self.messageMethod() }, label: { Image(IMAGE_MSG_ICON).resizable().frame(width: 20, height: 20) }) .frame(width: 45, height: 45).background(Color.main_color).cornerRadius(25) + .accessibilityIdentifier("Contact Owner") + .accessibilityLabel("Contact Owner") } } } diff --git a/Wiggles-iOS/Home/HomeView.swift b/Wiggles-iOS/Home/HomeView.swift index e069271..14a1286 100644 --- a/Wiggles-iOS/Home/HomeView.swift +++ b/Wiggles-iOS/Home/HomeView.swift @@ -21,6 +21,8 @@ struct HomeView: View { VStack(alignment: .leading) { Text("Hey Sameer,").modifier(SailecFont(.bold, size: 24)) .foregroundColor(Color.text_primary_color).padding(.top, 16) + .accessibilityIdentifier("User Name") + Text("Adopt a new friend near you!").modifier(SailecFont(.regular, size: 18)) .foregroundColor(Color.text_primary_color).padding(.top, 4) } @@ -34,6 +36,7 @@ struct HomeView: View { HomeListModelView(image: model.image, name: model.name, age: model.age, about: model.about, location: model.location, gender: model.gender).padding(.bottom, 4) }) + .accessibilityIdentifier("Dog Card") } } Spacer() diff --git a/Wiggles-iOS/Home/HomeViewModel.swift b/Wiggles-iOS/Home/HomeViewModel.swift index 78d4022..4d3b597 100644 --- a/Wiggles-iOS/Home/HomeViewModel.swift +++ b/Wiggles-iOS/Home/HomeViewModel.swift @@ -14,4 +14,5 @@ class HomeViewModel: ObservableObject { init() { fetchDogsList() } func fetchDogsList() { dogsList = DogData.dogs } + } diff --git a/Wiggles-iOSUITests/PageObjects/PuppyDetailsPage.swift b/Wiggles-iOSUITests/PageObjects/PuppyDetailsPage.swift new file mode 100644 index 0000000..059cd8f --- /dev/null +++ b/Wiggles-iOSUITests/PageObjects/PuppyDetailsPage.swift @@ -0,0 +1,56 @@ +import XCTest + +class PuppyDetailsPage { + let app: XCUIApplication + + // MARK: - Init + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - UI Elements + + var backButton: XCUIElement { app.buttons["Back"] } + var favoriteButton: XCUIElement { app.buttons["Fav Button"] } + var dogPicture: XCUIElement { app.images["Dog Picture"] } + var gender: XCUIElement { app.staticTexts["Gender"] } + var distance: XCUIElement { app.staticTexts["Distance"] } + var time: XCUIElement { app.staticTexts["Time"] } + var dogStory: XCUIElement { app.staticTexts["Dog Story"] } + var ownerPicture: XCUIElement { app.images["Owner Picture"] } + var ownerName: XCUIElement { app.staticTexts["Owner Name"] } + var ownerBio: XCUIElement { app.staticTexts["Owner Bio"] } + var contactButton: XCUIElement { app.buttons["Contact Owner"] } + var adoptMeButton: XCUIElement { app.buttons["Adopt Me"] } + var puppyName: XCUIElement { app.staticTexts["Name"] } + + // MARK: - State + + var cardDetails: Bool { + puppyName.exists && + dogPicture.exists && + gender.exists && + distance.exists && + time.exists && + ownerPicture.exists && + ownerName.exists && + ownerBio.exists && + contactButton.exists && + adoptMeButton.exists + } + + // MARK: - Actions + + func tapFavorite() { + favoriteButton.tap() + } + + func tapAdoptMe() { + adoptMeButton.tap() + } + + func goBack() { + backButton.tap() + } +} diff --git a/Wiggles-iOSUITests/PageObjects/PuppyListingPage.swift b/Wiggles-iOSUITests/PageObjects/PuppyListingPage.swift new file mode 100644 index 0000000..7d6072a --- /dev/null +++ b/Wiggles-iOSUITests/PageObjects/PuppyListingPage.swift @@ -0,0 +1,33 @@ +import XCTest + +class PuppyListingPage { + let app: XCUIApplication + + // MARK: - Init + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - UI Elements + + var userName: XCUIElement { app.staticTexts["User Name"] } + var puppyCards: XCUIElementQuery { app.buttons.matching(identifier: "Dog Card") } + + // MARK: - State + + var hasUserName: Bool { userName.exists } + var hasPuppyCards: Bool { + let firstCard = puppyCards.element(boundBy: 0) + return firstCard.waitForExistence(timeout: 5) + } + + // MARK: - Actions + + func tapLastPuppy() { + let lastIndex = puppyCards.count - 1 + guard lastIndex >= 0 else { return } + + puppyCards.element(boundBy: lastIndex).tap() + } +} diff --git a/Wiggles-iOSUITests/WiggleUITests.swift b/Wiggles-iOSUITests/WiggleUITests.swift new file mode 100644 index 0000000..5eaebf4 --- /dev/null +++ b/Wiggles-iOSUITests/WiggleUITests.swift @@ -0,0 +1,25 @@ +import XCTest + +class WigglesUITests: XCTestCase { + + let app = XCUIApplication() + + override func setUp() { + super.setUp() + continueAfterFailure = false + app.launch() + } + + func testNavigationToPDPAndBack() { + let plp = PuppyListingPage(app: app) + let pdp = PuppyDetailsPage(app: app) + + XCTAssertTrue(plp.hasPuppyCards, "Expected at least one puppy card on listing page.") + plp.tapLastPuppy() + XCTAssertTrue(pdp.cardDetails, "Expected PDP to load successfully.") + pdp.tapFavorite() + pdp.goBack() + XCTAssertTrue(plp.hasPuppyCards, "Expected to return to the listing page.") + } +} +