├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md ├── images │ ├── MSUITest-Coordinator-Flow.jpg │ └── target_membership_aip.png └── raw │ └── MSUITest-Coordinator-Flow.xmind ├── .gitignore ├── .jazzy.yaml ├── .swiftlint.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Documentation └── Advanced.md ├── Example ├── Examples.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Examples.xcscheme ├── Source │ ├── Infrastructure │ │ ├── Identifiable │ │ │ └── Identifiable.swift │ │ └── Routing │ │ │ └── Coordinator.swift │ ├── Main │ │ ├── AppDelegate.swift │ │ └── MainCoordinator │ │ │ ├── MainCoordinator.swift │ │ │ └── UITestCoordinator.swift │ ├── Resources │ │ └── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ └── minion.imageset │ │ │ ├── 48659a38ae687fabe7fa101bf400d1ad.jpg │ │ │ └── Contents.json │ ├── Scenarios │ │ ├── Button │ │ │ ├── AIP │ │ │ │ └── ButtonAIP.swift │ │ │ ├── Coordinator │ │ │ │ └── ButtonCoordinator.swift │ │ │ └── View │ │ │ │ ├── ButtonViewController.swift │ │ │ │ └── ButtonViewController.xib │ │ ├── Home │ │ │ ├── AIP │ │ │ │ └── HomeAIP.swift │ │ │ ├── Coordinator │ │ │ │ └── HomeCoordinator.swift │ │ │ └── View │ │ │ │ ├── HomeViewController.swift │ │ │ │ └── HomeViewController.xib │ │ ├── Image │ │ │ ├── AIP │ │ │ │ └── ImageAIP.swift │ │ │ ├── Coordinator │ │ │ │ └── ImageCoordinator.swift │ │ │ └── View │ │ │ │ ├── ImageViewController.swift │ │ │ │ └── ImageViewController.xib │ │ ├── Label │ │ │ ├── AIP │ │ │ │ └── LabelAIP.swift │ │ │ ├── Coordinator │ │ │ │ └── LabelCoordinator.swift │ │ │ └── View │ │ │ │ ├── LabelViewController.swift │ │ │ │ └── LabelViewController.xib │ │ ├── LaunchScreen │ │ │ └── View │ │ │ │ └── Base.lproj │ │ │ │ └── LaunchScreen.storyboard │ │ └── TextField │ │ │ ├── AIP │ │ │ └── TextFieldAIP.swift │ │ │ ├── Coordinator │ │ │ └── TextFieldCoordinator.swift │ │ │ └── View │ │ │ ├── TextFieldViewController.swift │ │ │ └── TextFieldViewController.xib │ └── SupportingFiles │ │ └── Info.plist └── Tests │ └── UITests │ ├── Info.plist │ ├── PageObjects │ ├── ButtonPage.swift │ ├── HomePage.swift │ ├── ImagePage.swift │ ├── LabelPage.swift │ └── TextFieldPage.swift │ └── Scenario │ ├── ButtonTests.swift │ ├── HomeTests.swift │ ├── ImageTests.swift │ ├── LabelTests.swift │ └── TextFieldTests.swift ├── LICENSE ├── MSUITest.podspec ├── MSUITest.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── MSUITest.xcscheme ├── MSUITest.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── README.md ├── Source ├── AIP │ ├── Extensions │ │ └── NSObject+Accessibility.swift │ └── Provider │ │ └── AccessibilityIdentifierProvider.swift ├── Core │ ├── Extensions │ │ ├── XCUIApplication+Lifecycle.swift │ │ ├── XCUIElement+TextField.swift │ │ └── XCUIElement+Timeout.swift │ ├── LaunchArguments │ │ └── UITestLaunchArguments.swift │ └── PageObjects │ │ ├── ElementProvider │ │ └── PageObjectUIElementProvider.swift │ │ ├── Helpers │ │ └── HittableElementFinder.swift │ │ ├── Models │ │ └── SwipeAction.swift │ │ ├── PageObject.swift │ │ ├── PageObjectShould.swift │ │ └── PageObjectWhen.swift └── SupportingFiles │ ├── Info.plist │ └── MSUITest.h └── docs ├── Enums.html ├── Enums └── UITestLaunchArgument.html ├── Extensions.html ├── Extensions ├── UIAccessibilityIdentification.html └── XCUIApplication.html ├── Protocols.html ├── Protocols ├── AccessibilityIdentifierProvider.html ├── PageObject.html ├── PageObjectShould.html ├── PageObjectUIElementProvider.html └── PageObjectWhen.html ├── Structs.html ├── Structs ├── SwipeAction.html └── SwipeAction │ └── Direction.html ├── Typealiases.html ├── _config.yml ├── badge.svg ├── css ├── highlight.css └── jazzy.css ├── docsets ├── MSUITest.docset │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ ├── Documents │ │ ├── Enums.html │ │ ├── Enums │ │ │ └── UITestLaunchArgument.html │ │ ├── Extensions.html │ │ ├── Extensions │ │ │ ├── UIAccessibilityIdentification.html │ │ │ └── XCUIApplication.html │ │ ├── Protocols.html │ │ ├── Protocols │ │ │ ├── AccessibilityIdentifierProvider.html │ │ │ ├── PageObject.html │ │ │ ├── PageObjectShould.html │ │ │ ├── PageObjectUIElementProvider.html │ │ │ └── PageObjectWhen.html │ │ ├── Structs.html │ │ ├── Structs │ │ │ ├── SwipeAction.html │ │ │ └── SwipeAction │ │ │ │ └── Direction.html │ │ ├── Typealiases.html │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ ├── img │ │ │ ├── carat.png │ │ │ ├── dash.png │ │ │ ├── gh.png │ │ │ └── spinner.gif │ │ ├── index.html │ │ ├── js │ │ │ ├── jazzy.js │ │ │ ├── jazzy.search.js │ │ │ ├── jquery.min.js │ │ │ ├── lunr.min.js │ │ │ └── typeahead.jquery.js │ │ └── search.json │ │ └── docSet.dsidx ├── MSUITest.tgz └── MSUITest.xml ├── img ├── carat.png ├── dash.png ├── gh.png └── spinner.gif ├── index.html ├── js ├── jazzy.js ├── jazzy.search.js ├── jquery.min.js ├── lunr.min.js └── typeahead.jquery.js ├── search.json └── undocumented.json /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @MarcoSantarossa -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Version of the library affected 2 | 3 | ## *What* is affected by this bug 4 | 5 | ## *When* does this occur 6 | 7 | ## *How* do we replicate the issue 8 | 9 | ## Expected behavior (i.e. solution) 10 | 11 | ## Other Comments 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | ## Motivation and Context 5 | 6 | 7 | 8 | ## Types of changes 9 | 10 | - [ ] Bug fix (non-breaking change which fixes an issue) 11 | - [ ] New feature (non-breaking change which adds functionality) 12 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 13 | 14 | # Checklist 15 | 16 | 17 | - [ ] My code follows the code style of this project. 18 | - [ ] My change requires a change to the documentation. 19 | - [ ] I have updated the documentation accordingly. 20 | - [ ] I have updated the changelog accordingly. 21 | - [ ] My change doesn't break the current UI tests 22 | -------------------------------------------------------------------------------- /.github/images/MSUITest-Coordinator-Flow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/.github/images/MSUITest-Coordinator-Flow.jpg -------------------------------------------------------------------------------- /.github/images/target_membership_aip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/.github/images/target_membership_aip.png -------------------------------------------------------------------------------- /.github/raw/MSUITest-Coordinator-Flow.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/.github/raw/MSUITest-Coordinator-Flow.xmind -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | author: Marco Santarossa 2 | author_url: http://marcosantadev.com/ 3 | github_url: https://github.com/MarcoSantarossa/MSUITest 4 | root_url: https://marcosantarossa.github.io/MSUITest/ 5 | module: MSUITest 6 | output: docs 7 | theme: fullwidth 8 | xcodebuild_arguments: [-workspace, 'MSUITest.xcworkspace', -scheme, 'MSUITest'] -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | opt_in_rules: 2 | - anyobject_protocol 3 | - array_init 4 | - attributes 5 | - closure_body_length 6 | - closure_end_indentation 7 | - closure_spacing 8 | - collection_alignment 9 | - contains_over_first_not_nil 10 | - empty_count 11 | - empty_string 12 | - empty_xctest_method 13 | - explicit_init 14 | - fallthrough 15 | - first_where 16 | - identical_operands 17 | - joined_default_parameter 18 | - let_var_whitespace 19 | - lower_acl_than_parent 20 | - last_where 21 | - legacy_random 22 | - modifier_order 23 | - operator_usage_whitespace 24 | - overridden_super_call 25 | - pattern_matching_keywords 26 | - private_action 27 | - private_outlet 28 | - prohibited_super_call 29 | - redundant_nil_coalescing 30 | - redundant_type_annotation 31 | - single_test_class 32 | - sorted_first_last 33 | - sorted_imports 34 | - static_operator 35 | - strong_iboutlet 36 | - unneeded_parentheses_in_closure_argument 37 | - untyped_error_in_catch 38 | - unused_import 39 | - unused_private_declaration 40 | - vertical_parameter_alignment_on_call 41 | - void_return 42 | - yoda_condition 43 | 44 | 45 | disabled_rules: 46 | - identifier_name 47 | - force_try 48 | - line_length 49 | - file_length 50 | - force_cast 51 | - function_body_length 52 | - type_body_length 53 | - xctfail_message 54 | - cyclomatic_complexity 55 | 56 | function_parameter_count: 7 57 | 58 | empty_count: 59 | severity: warning 60 | 61 | nesting: 62 | type_level: 63 | warning: 2 64 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ### Added 11 | 12 | * [#28](https://github.com/MarcoSantarossa/MSUITest/pull/27) - Static jazzy website 13 | * [#27](https://github.com/MarcoSantarossa/MSUITest/pull/27) - Badges in readme 14 | 15 | ## [1.0.1] - 2019-07-21 16 | 17 | ### Added 18 | 19 | * [#25](https://github.com/MarcoSantarossa/MSUITest/pull/25) - New changelog 20 | 21 | ### Fixed 22 | 23 | * [#24](https://github.com/MarcoSantarossa/MSUITest/pull/24) - Podspec lint 24 | 25 | ## [1.0.0] - 2019-07-21 26 | 27 | ### Added 28 | 29 | * First Version of library 30 | * Documentation 31 | 32 | [Unreleased]: https://github.com/MarcoSantarossa/MSUITest/compare/v1.0.1...HEAD 33 | [1.0.1]: https://github.com/MarcoSantarossa/MSUITest/compare/v1.0.0...v1.0.1 34 | [1.0.0]: https://github.com/MarcoSantarossa/MSUITest/releases/tag/v1.0.0 -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to MSUITest 2 | 3 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: 4 | 5 | - Reporting a bug 6 | - Discussing the current state of the code 7 | - Submitting a fix 8 | - Proposing new features 9 | - Becoming a maintainer 10 | 11 | ## We Develop with Github 12 | 13 | We use github to host code, to track issues and feature requests, as well as accept pull requests. 14 | 15 | ## Any contributions you make will be under the MIT Software License 16 | 17 | In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. 18 | 19 | ## Report bugs using Github's [issues](https://github.com/MarcoSantarossa/MSUITest/issues) 20 | 21 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/MarcoSantarossa/MSUITest/issues/new) and following the Issue template. 22 | 23 | ## Submit a change using Github's [pull request](https://github.com/MarcoSantarossa/MSUITest/pulls) 24 | 25 | We update the library using a pull request which allows the maintainer to review the changes and have an open discussion about it. [Create a new pull request](https://github.com/MarcoSantarossa/MSUITest/compare) and follow the PR template. 26 | 27 | ## License 28 | 29 | By contributing, you agree that your contributions will be licensed under its MIT License. 30 | 31 | [*Template reference*](https://gist.github.com/briandk/3d2e8b3ec8daf5a27a62) 32 | -------------------------------------------------------------------------------- /Example/Examples.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Examples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Examples.xcodeproj/xcshareddata/xcschemes/Examples.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /Example/Source/Infrastructure/Identifiable/Identifiable.swift: -------------------------------------------------------------------------------- 1 | protocol Identifiable { 2 | static var identifier: String { get } 3 | } 4 | -------------------------------------------------------------------------------- /Example/Source/Infrastructure/Routing/Coordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | protocol Coordinator: AnyObject, Identifiable { 24 | func start() 25 | } 26 | 27 | extension Coordinator { 28 | static var identifier: String { 29 | return String(describing: self) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Example/Source/Main/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import MSUITest 24 | import UIKit 25 | 26 | @UIApplicationMain 27 | class AppDelegate: UIResponder, UIApplicationDelegate { 28 | 29 | var window: UIWindow? 30 | private var mainCoordinator: Coordinator! 31 | 32 | func application(_ application: UIApplication, 33 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 34 | 35 | let window = UIWindow() 36 | let rootVC = UINavigationController() 37 | window.rootViewController = rootVC 38 | window.makeKeyAndVisible() 39 | 40 | self.window = window 41 | 42 | mainCoordinator = createMainCoordinator(with: rootVC) 43 | mainCoordinator.start() 44 | 45 | disableAnimationIfNeeded() 46 | 47 | return true 48 | } 49 | 50 | private func createMainCoordinator(with rootViewController: UINavigationController) -> Coordinator { 51 | if let coordinatorUnderUITest = UserDefaults.standard.string(forKey: "coordinatorUnderUITest") { 52 | return UITestCoordinator(rootViewController: rootViewController, 53 | coordinatorUnderUITest: coordinatorUnderUITest) 54 | } else { 55 | return MainCoordinator(rootViewController: rootViewController) 56 | } 57 | } 58 | 59 | private func disableAnimationIfNeeded() { 60 | guard UITestLaunchArgument.animationsDisabled.isActive else { return } 61 | UIView.setAnimationsEnabled(false) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Example/Source/Main/MainCoordinator/MainCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | final class MainCoordinator: Coordinator { 26 | 27 | private var coords = [Coordinator]() 28 | 29 | private unowned let rootViewController: UINavigationController 30 | 31 | init(rootViewController: UINavigationController) { 32 | self.rootViewController = rootViewController 33 | } 34 | 35 | func start() { 36 | let homeCoord = HomeCoordinator(navigationController: rootViewController) 37 | homeCoord.start() 38 | 39 | coords.append(homeCoord) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Example/Source/Main/MainCoordinator/UITestCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | final class UITestCoordinator: Coordinator { 26 | 27 | private var coords = [Coordinator]() 28 | 29 | private unowned let rootViewController: UINavigationController 30 | private let coordinatorUnderUITest: String 31 | 32 | init(rootViewController: UINavigationController, coordinatorUnderUITest: String) { 33 | self.rootViewController = rootViewController 34 | self.coordinatorUnderUITest = coordinatorUnderUITest 35 | } 36 | 37 | func start() { 38 | let coordinator = createCoordinator() 39 | coordinator.start() 40 | 41 | coords.append(coordinator) 42 | } 43 | 44 | private func createCoordinator() -> Coordinator { 45 | switch coordinatorUnderUITest { 46 | case HomeCoordinator.identifier: 47 | return HomeCoordinator(navigationController: rootViewController) 48 | case LabelCoordinator.identifier: 49 | return LabelCoordinator(navigationController: rootViewController) 50 | case ButtonCoordinator.identifier: 51 | return ButtonCoordinator(navigationController: rootViewController) 52 | case TextFieldCoordinator.identifier: 53 | return TextFieldCoordinator(navigationController: rootViewController) 54 | case ImageCoordinator.identifier: 55 | return ImageCoordinator(navigationController: rootViewController) 56 | default: 57 | fatalError("Coordinator under UI tests not found") 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Example/Source/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Example/Source/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Source/Resources/Assets.xcassets/minion.imageset/48659a38ae687fabe7fa101bf400d1ad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/Example/Source/Resources/Assets.xcassets/minion.imageset/48659a38ae687fabe7fa101bf400d1ad.jpg -------------------------------------------------------------------------------- /Example/Source/Resources/Assets.xcassets/minion.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "48659a38ae687fabe7fa101bf400d1ad.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Source/Scenarios/Button/AIP/ButtonAIP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import MSUITest 24 | 25 | final class ButtonAIP: AIP { 26 | static var mainIdentifier: String = "button" 27 | } 28 | 29 | extension ButtonAIP { 30 | enum Element: String { 31 | case mainView 32 | case button 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Button/Coordinator/ButtonCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | final class ButtonCoordinator: Coordinator { 26 | 27 | private unowned let navigationController: UINavigationController 28 | 29 | init(navigationController: UINavigationController) { 30 | self.navigationController = navigationController 31 | } 32 | 33 | func start() { 34 | let view = ButtonViewController() 35 | navigationController.pushViewController(view, animated: true) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Button/View/ButtonViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | class ButtonViewController: UIViewController { 26 | 27 | @IBOutlet private var button: UIButton! 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | 32 | title = "Button" 33 | 34 | addAccessibility() 35 | } 36 | 37 | private func addAccessibility() { 38 | let aip = ButtonAIP.self 39 | view.addAccessibility(aip: aip, element: .mainView) 40 | button.addAccessibility(aip: aip, element: .button) 41 | } 42 | 43 | @IBAction private func onTap(_ sender: Any) { 44 | print("let's add something when I'm in easter egg mood 🤫") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Button/View/ButtonViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Home/AIP/HomeAIP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import MSUITest 24 | 25 | final class HomeAIP: AIP { 26 | static var mainIdentifier: String = "home" 27 | } 28 | 29 | extension HomeAIP { 30 | enum Element: String { 31 | case mainView 32 | case tableView 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Home/Coordinator/HomeCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | final class HomeCoordinator: Coordinator { 26 | 27 | private unowned let navigationController: UINavigationController 28 | 29 | private var coords = [String: Coordinator]() 30 | 31 | init(navigationController: UINavigationController) { 32 | self.navigationController = navigationController 33 | } 34 | 35 | func start() { 36 | let onCellSeleted: (HomeCell) -> Void = { [weak self] in 37 | guard let self = self else { return } 38 | let coordinator = self.makeCoordinator(for: $0) 39 | coordinator.start() 40 | self.coords[type(of: coordinator).identifier] = coordinator 41 | } 42 | 43 | let view = HomeViewController(onCellSelected: onCellSeleted) 44 | navigationController.setViewControllers([view], animated: false) 45 | } 46 | 47 | private func makeCoordinator(for cell: HomeCell) -> Coordinator { 48 | switch cell { 49 | case .label: 50 | return LabelCoordinator(navigationController: navigationController) 51 | case .button: 52 | return ButtonCoordinator(navigationController: navigationController) 53 | case .textField: 54 | return TextFieldCoordinator(navigationController: navigationController) 55 | case .image: 56 | return ImageCoordinator(navigationController: navigationController) 57 | default: 58 | fatalError() 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Home/View/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | enum HomeCell: String, CaseIterable { 26 | case label 27 | case button 28 | case textField 29 | case image 30 | case alert 31 | } 32 | 33 | class HomeViewController: UIViewController { 34 | 35 | @IBOutlet private var tableView: UITableView! 36 | 37 | private let dataSource = HomeCell.allCases 38 | private let cellId = "HomeCell" 39 | 40 | private let onCellSelected: (HomeCell) -> Void 41 | 42 | init(onCellSelected: @escaping (HomeCell) -> Void) { 43 | self.onCellSelected = onCellSelected 44 | 45 | super.init(nibName: nil, bundle: nil) 46 | } 47 | 48 | required init?(coder aDecoder: NSCoder) { 49 | fatalError("init(coder:) has not been implemented") 50 | } 51 | 52 | override func viewDidLoad() { 53 | super.viewDidLoad() 54 | 55 | title = "Home" 56 | 57 | addAccessibility() 58 | } 59 | 60 | private func addAccessibility() { 61 | let aip = HomeAIP.self 62 | view.addAccessibility(aip: aip, element: .mainView) 63 | tableView.addAccessibility(aip: aip, element: .tableView) 64 | } 65 | } 66 | 67 | extension HomeViewController: UITableViewDataSource { 68 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 69 | return dataSource.count 70 | } 71 | 72 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 73 | let cell = tableView.dequeueReusableCell(withIdentifier: cellId) ?? 74 | UITableViewCell(style: .default, reuseIdentifier: cellId) 75 | 76 | cell.textLabel?.text = dataSource[indexPath.row].rawValue 77 | 78 | return cell 79 | } 80 | } 81 | 82 | extension HomeViewController: UITableViewDelegate { 83 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 84 | let homeCell = dataSource[indexPath.row] 85 | if homeCell == .alert { 86 | showAlert() 87 | } else { 88 | onCellSelected(homeCell) 89 | } 90 | 91 | tableView.deselectRow(at: indexPath, animated: true) 92 | } 93 | 94 | private func showAlert() { 95 | let alertController = UIAlertController(title: "🚨 Alert 🚨", 96 | message: "[ Add here your test ]", 97 | preferredStyle: .alert) 98 | let cancelAction = UIAlertAction(title: "Close", style: .cancel) 99 | alertController.addAction(cancelAction) 100 | 101 | present(alertController, animated: true, completion: nil) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Home/View/HomeViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Image/AIP/ImageAIP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import MSUITest 24 | 25 | final class ImageAIP: AIP { 26 | static var mainIdentifier: String = "image" 27 | } 28 | 29 | extension ImageAIP { 30 | enum Element: RawRepresentable { 31 | case mainView 32 | case imageView(_ imageName: String) 33 | 34 | init?(rawValue: String) { 35 | return nil 36 | } 37 | 38 | var rawValue: String { 39 | switch self { 40 | case .mainView: 41 | return "mainView" 42 | case .imageView(let name): 43 | return "imageView_image:\(name)" 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Image/Coordinator/ImageCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | final class ImageCoordinator: Coordinator { 26 | 27 | private unowned let navigationController: UINavigationController 28 | 29 | init(navigationController: UINavigationController) { 30 | self.navigationController = navigationController 31 | } 32 | 33 | func start() { 34 | let view = ImageViewController() 35 | navigationController.pushViewController(view, animated: true) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Image/View/ImageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | class ImageViewController: UIViewController { 26 | 27 | @IBOutlet private var imageView: UIImageView! 28 | 29 | private let imageName: String = "minion" 30 | 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | 34 | title = "Image" 35 | 36 | imageView.image = UIImage(named: imageName) 37 | 38 | addAccessibility() 39 | } 40 | 41 | private func addAccessibility() { 42 | let aip = ImageAIP.self 43 | view.addAccessibility(aip: aip, element: .mainView) 44 | imageView.addAccessibility(aip: aip, element: .imageView(imageName)) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Image/View/ImageViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Label/AIP/LabelAIP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import MSUITest 24 | 25 | final class LabelAIP: AIP { 26 | static var mainIdentifier: String = "label" 27 | } 28 | 29 | extension LabelAIP { 30 | enum Element: String { 31 | case mainView 32 | case label 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Label/Coordinator/LabelCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | final class LabelCoordinator: Coordinator { 26 | 27 | private unowned let navigationController: UINavigationController 28 | 29 | init(navigationController: UINavigationController) { 30 | self.navigationController = navigationController 31 | } 32 | 33 | func start() { 34 | let view = LabelViewController() 35 | navigationController.pushViewController(view, animated: true) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Label/View/LabelViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | class LabelViewController: UIViewController { 26 | 27 | @IBOutlet private var label: UILabel! 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | 32 | title = "Label" 33 | 34 | addAccessibility() 35 | } 36 | 37 | private func addAccessibility() { 38 | let aip = LabelAIP.self 39 | view.addAccessibility(aip: aip, element: .mainView) 40 | label.addAccessibility(aip: aip, element: .label) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/Label/View/LabelViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/LaunchScreen/View/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/TextField/AIP/TextFieldAIP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import MSUITest 24 | 25 | final class TextFieldAIP: AIP { 26 | static var mainIdentifier: String = "textField" 27 | } 28 | 29 | extension TextFieldAIP { 30 | enum Element: String { 31 | case mainView 32 | case textField 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/TextField/Coordinator/TextFieldCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | final class TextFieldCoordinator: Coordinator { 26 | 27 | private unowned let navigationController: UINavigationController 28 | 29 | init(navigationController: UINavigationController) { 30 | self.navigationController = navigationController 31 | } 32 | 33 | func start() { 34 | let view = TextFieldViewController() 35 | navigationController.pushViewController(view, animated: true) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/TextField/View/TextFieldViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | class TextFieldViewController: UIViewController { 26 | 27 | @IBOutlet private var textField: UITextField! 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | 32 | title = "TextField" 33 | 34 | addAccessibility() 35 | } 36 | 37 | override func viewWillDisappear(_ animated: Bool) { 38 | super.viewWillDisappear(animated) 39 | 40 | textField.resignFirstResponder() 41 | } 42 | 43 | private func addAccessibility() { 44 | let aip = TextFieldAIP.self 45 | view.addAccessibility(aip: aip, element: .mainView) 46 | textField.addAccessibility(aip: aip, element: .textField) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Example/Source/Scenarios/TextField/View/TextFieldViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Example/Source/SupportingFiles/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0.1 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Example/Tests/UITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Tests/UITests/PageObjects/ButtonPage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import MSUITest 24 | import XCTest 25 | 26 | final class ButtonPage { 27 | typealias Element = ButtonAIP.Element 28 | } 29 | 30 | extension ButtonPage: PageObjectUIElementProvider, PageObject { 31 | func uiElement(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElement { 32 | let query = self.query(for: element, in: queryProvider) 33 | 34 | let identifier = ButtonAIP.elementIdentifier(for: element) 35 | return query[identifier] 36 | } 37 | 38 | private func query(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElementQuery { 39 | switch element { 40 | case .mainView: 41 | return queryProvider.otherElements 42 | case .button: 43 | return queryProvider.buttons 44 | } 45 | } 46 | } 47 | 48 | // MARK: - Given 49 | extension ButtonPage { 50 | func givenPage() -> ButtonPage { 51 | XCUIApplication().launchTestMode(customArguments: [ 52 | "-coordinatorUnderUITest", "ButtonCoordinator" 53 | ]) 54 | 55 | return self 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Example/Tests/UITests/PageObjects/HomePage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import MSUITest 24 | import XCTest 25 | 26 | final class HomePage { 27 | typealias Element = HomeAIP.Element 28 | } 29 | 30 | extension HomePage: PageObjectUIElementProvider, PageObject { 31 | func uiElement(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElement { 32 | let query = self.query(for: element, in: queryProvider) 33 | 34 | let identifier = HomeAIP.elementIdentifier(for: element) 35 | return query[identifier] 36 | } 37 | 38 | private func query(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElementQuery { 39 | switch element { 40 | case .mainView: 41 | return queryProvider.otherElements 42 | case .tableView: 43 | return queryProvider.tables 44 | } 45 | } 46 | } 47 | 48 | // MARK: - Given 49 | extension HomePage { 50 | func givenPage() -> HomePage { 51 | XCUIApplication().launchTestMode(customArguments: [ 52 | "-coordinatorUnderUITest", "HomeCoordinator" 53 | ]) 54 | 55 | return self 56 | } 57 | } 58 | 59 | // MARK: - When 60 | extension HomePage { 61 | func whenFocusTextField() -> HomePage { 62 | TextFieldPage() 63 | .whenTap(element: .textField) 64 | 65 | return self 66 | } 67 | } 68 | 69 | // MARK: - Should 70 | extension HomePage { 71 | @discardableResult 72 | func shouldSeeLabelPage() -> HomePage { 73 | 74 | LabelPage() 75 | .thenIShouldSee(element: .mainView) 76 | 77 | return self 78 | } 79 | 80 | @discardableResult 81 | func shouldSeeButtonPage() -> HomePage { 82 | 83 | ButtonPage() 84 | .thenIShouldSee(element: .mainView) 85 | 86 | return self 87 | } 88 | 89 | @discardableResult 90 | func shouldSeeTextFieldPage() -> HomePage { 91 | 92 | TextFieldPage() 93 | .thenIShouldSee(element: .mainView) 94 | 95 | return self 96 | } 97 | 98 | @discardableResult 99 | func shouldSeeImagePage() -> HomePage { 100 | 101 | ImagePage() 102 | .thenIShouldSee(element: .mainView) 103 | 104 | return self 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Example/Tests/UITests/PageObjects/ImagePage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import MSUITest 24 | import XCTest 25 | 26 | final class ImagePage { 27 | typealias Element = ImageAIP.Element 28 | } 29 | 30 | extension ImagePage: PageObjectUIElementProvider, PageObject { 31 | func uiElement(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElement { 32 | let query = self.query(for: element, in: queryProvider) 33 | 34 | let identifier = ImageAIP.elementIdentifier(for: element) 35 | return query[identifier] 36 | } 37 | 38 | private func query(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElementQuery { 39 | switch element { 40 | case .mainView: 41 | return queryProvider.otherElements 42 | case .imageView: 43 | return queryProvider.images 44 | } 45 | } 46 | } 47 | 48 | // MARK: - Given 49 | extension ImagePage { 50 | func givenPage() -> ImagePage { 51 | XCUIApplication().launchTestMode(customArguments: [ 52 | "-coordinatorUnderUITest", "ImageCoordinator" 53 | ]) 54 | 55 | return self 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Example/Tests/UITests/PageObjects/LabelPage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import MSUITest 24 | import XCTest 25 | 26 | final class LabelPage { 27 | typealias Element = LabelAIP.Element 28 | } 29 | 30 | extension LabelPage: PageObjectUIElementProvider, PageObject { 31 | func uiElement(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElement { 32 | let query = self.query(for: element, in: queryProvider) 33 | 34 | let identifier = LabelAIP.elementIdentifier(for: element) 35 | return query[identifier] 36 | } 37 | 38 | private func query(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElementQuery { 39 | switch element { 40 | case .mainView: 41 | return queryProvider.otherElements 42 | case .label: 43 | return queryProvider.staticTexts 44 | } 45 | } 46 | } 47 | 48 | // MARK: - Given 49 | extension LabelPage { 50 | func givenPage() -> LabelPage { 51 | XCUIApplication().launchTestMode(customArguments: [ 52 | "-coordinatorUnderUITest", "LabelCoordinator" 53 | ]) 54 | 55 | return self 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Example/Tests/UITests/PageObjects/TextFieldPage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import MSUITest 24 | import XCTest 25 | 26 | final class TextFieldPage { 27 | typealias Element = TextFieldAIP.Element 28 | } 29 | 30 | extension TextFieldPage: PageObjectUIElementProvider, PageObject { 31 | func uiElement(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElement { 32 | let query = self.query(for: element, in: queryProvider) 33 | 34 | let identifier = TextFieldAIP.elementIdentifier(for: element) 35 | return query[identifier] 36 | } 37 | 38 | private func query(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElementQuery { 39 | switch element { 40 | case .mainView: 41 | return queryProvider.otherElements 42 | case .textField: 43 | return queryProvider.textFields 44 | } 45 | } 46 | } 47 | 48 | // MARK: - Given 49 | extension TextFieldPage { 50 | func givenPage() -> TextFieldPage { 51 | XCUIApplication().launchTestMode(customArguments: [ 52 | "-coordinatorUnderUITest", "TextFieldCoordinator" 53 | ]) 54 | 55 | return self 56 | } 57 | } 58 | 59 | // MARK: - Then 60 | extension TextFieldPage { 61 | @discardableResult 62 | func thenIShouldSeeEmptyTextFieldWithPlaceholder() -> TextFieldPage { 63 | if #available(iOS 11.0, *) { 64 | return thenIShouldSee(element: .textField, text: "Try me 🤓") 65 | } else { 66 | return thenIShouldSee(element: .textField, text: "") 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Example/Tests/UITests/Scenario/ButtonTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import XCTest 24 | 25 | class ButtonTests: XCTestCase { 26 | 27 | func test_whenLoadView_seeExpectedElements() { 28 | ButtonPage() 29 | .givenPage() 30 | 31 | .thenIShouldSee(element: .mainView, timeout: 0.3) 32 | .thenIShouldSeeNavigationBar(text: "Button") 33 | .thenIShouldSee(element: .button, text: "Yes, I'm a button") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Example/Tests/UITests/Scenario/HomeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import XCTest 24 | 25 | class HomeTests: XCTestCase { 26 | 27 | func test_whenLoadView_seeExpectedElements() { 28 | HomePage() 29 | .givenPage() 30 | 31 | .thenIShouldSee(element: .mainView, timeout: 0.3) 32 | .thenIShouldSeeNavigationBar(text: "Home") 33 | .thenIShouldSee(element: .tableView) 34 | .thenIShouldSee(in: .tableView, at: 0, text: "label") 35 | .thenIShouldSee(in: .tableView, at: 1, text: "button") 36 | .thenIShouldSee(in: .tableView, at: 2, text: "textField") 37 | .thenIShouldSee(in: .tableView, at: 3, text: "image") 38 | .thenIShouldSee(in: .tableView, at: 4, text: "alert") 39 | } 40 | 41 | func test_whenTapLabelCell_seeLabelView() { 42 | HomePage() 43 | .givenPage() 44 | 45 | .whenTapCell(in: .tableView, at: 0) 46 | 47 | .shouldSeeLabelPage() 48 | } 49 | 50 | func test_whenTapLabelCellAndBackButton_seeMainView() { 51 | HomePage() 52 | .givenPage() 53 | 54 | .whenTapCell(in: .tableView, at: 0) 55 | .whenTapBackButton() 56 | 57 | .thenIShouldSee(element: .mainView) 58 | } 59 | 60 | func test_whenTapButtonCell_seeButtonView() { 61 | HomePage() 62 | .givenPage() 63 | 64 | .whenTapCell(in: .tableView, at: 1) 65 | 66 | .shouldSeeButtonPage() 67 | } 68 | 69 | func test_whenTapButtonCellAndBackButton_seeMainView() { 70 | HomePage() 71 | .givenPage() 72 | 73 | .whenTapCell(in: .tableView, at: 1) 74 | .whenTapBackButton() 75 | 76 | .thenIShouldSee(element: .mainView) 77 | } 78 | 79 | func test_whenTapTextFieldCell_seeTextFieldView() { 80 | HomePage() 81 | .givenPage() 82 | 83 | .whenTapCell(in: .tableView, at: 2) 84 | 85 | .shouldSeeTextFieldPage() 86 | } 87 | 88 | func test_whenTapTextFieldCellAndBackButton_seeMainView() { 89 | HomePage() 90 | .givenPage() 91 | 92 | .whenTapCell(in: .tableView, at: 2) 93 | .whenTapBackButton() 94 | 95 | .thenIShouldSee(element: .mainView) 96 | } 97 | 98 | func test_whenTapTextFieldCellAndFocusAndBackButton_doNotSeeKeyboard() { 99 | HomePage() 100 | .givenPage() 101 | 102 | .whenTapCell(in: .tableView, at: 2) 103 | .whenFocusTextField() 104 | 105 | .delay(0.2) 106 | 107 | .whenTapBackButton() 108 | 109 | .thenIShouldNotSeeKeyboard() 110 | } 111 | 112 | func test_whenTapImageCell_seeImageView() { 113 | HomePage() 114 | .givenPage() 115 | 116 | .whenTapCell(in: .tableView, at: 3) 117 | 118 | .shouldSeeImagePage() 119 | } 120 | 121 | func test_whenTapImageCellAndBackButton_seeMainView() { 122 | HomePage() 123 | .givenPage() 124 | 125 | .whenTapCell(in: .tableView, at: 3) 126 | .whenTapBackButton() 127 | 128 | .thenIShouldSee(element: .mainView) 129 | } 130 | 131 | func test_whenTapAlertCell_seeAlert() { 132 | HomePage() 133 | .givenPage() 134 | 135 | .whenTapCell(in: .tableView, at: 4) 136 | 137 | .thenIShouldSeeAlert(title: "🚨 Alert 🚨", message: "[ Add here your test ]") 138 | } 139 | 140 | func test_whenTapAlertCellAndTapCancel_doNotSeeAlert() { 141 | HomePage() 142 | .givenPage() 143 | 144 | .whenTapCell(in: .tableView, at: 4) 145 | .whenTapAlertButton(at: 0) 146 | 147 | .thenIShouldNotSeeAlert() 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /Example/Tests/UITests/Scenario/ImageTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import XCTest 24 | 25 | class ImageTests: XCTestCase { 26 | 27 | func test_whenLoadView_seeExpectedElements() { 28 | ImagePage() 29 | .givenPage() 30 | 31 | .thenIShouldSee(element: .mainView, timeout: 0.3) 32 | .thenIShouldSeeNavigationBar(text: "Image") 33 | .thenIShouldSee(element: .imageView("minion")) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Example/Tests/UITests/Scenario/LabelTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import XCTest 24 | 25 | class LabelTests: XCTestCase { 26 | 27 | func test_whenLoadView_seeExpectedElements() { 28 | LabelPage() 29 | .givenPage() 30 | 31 | .thenIShouldSee(element: .mainView, timeout: 0.3) 32 | .thenIShouldSeeNavigationBar(text: "Label") 33 | .thenIShouldSee(element: .label, text: "Welcome ✌🏻") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Example/Tests/UITests/Scenario/TextFieldTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import XCTest 24 | 25 | class TextFieldTests: XCTestCase { 26 | 27 | func test_whenLoadView_seeExpectedElements() { 28 | TextFieldPage() 29 | .givenPage() 30 | 31 | .thenIShouldSee(element: .mainView, timeout: 0.3) 32 | .thenIShouldSeeNavigationBar(text: "TextField") 33 | .thenIShouldSeeEmptyTextFieldWithPlaceholder() 34 | } 35 | 36 | func test_whenTapTextField_seeKeyboard() { 37 | TextFieldPage() 38 | .givenPage() 39 | 40 | .whenTap(element: .textField) 41 | 42 | .thenIShouldSeeKeyboard() 43 | } 44 | 45 | func test_whenTypeOnTextField_doNotSeePlaceholder() { 46 | TextFieldPage() 47 | .givenPage() 48 | 49 | .whenType("a", in: .textField) 50 | 51 | .thenIShouldSee(element: .textField, text: "a") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Marco Santarossa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MSUITest.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | 3 | spec.name = "MSUITest" 4 | spec.version = "1.0.1" 5 | spec.summary = "A Swift XCUITest wrapper inspired by Gherkin syntax." 6 | 7 | spec.description = <<-DESC 8 | A XCUITest wrapper written in Swift inspired by the Gherkin syntax to allow everyone to write human readable UI tests in a clean way. 9 | DESC 10 | 11 | spec.homepage = "https://github.com/MarcoSantarossa/MSUITest" 12 | 13 | spec.license = { :type => "MIT", :file => "LICENSE" } 14 | 15 | spec.author = "Marco Santarossa" 16 | spec.social_media_url = "https://twitter.com/MarcoSantaDev" 17 | 18 | spec.source = { :git => "https://github.com/MarcoSantarossa/MSUITest.git", :tag => "v#{spec.version}" } 19 | 20 | spec.swift_version = '5.0' 21 | spec.platform = :ios, '10.0' 22 | 23 | spec.subspec "Core" do |ss| 24 | ss.source_files = "Source/Core/**/*.swift" 25 | ss.framework = "XCTest" 26 | end 27 | 28 | spec.subspec "App" do |ss| 29 | ss.source_files = ["Source/AIP/**/*.swift", "Source/Core/LaunchArguments/UITestLaunchArguments.swift"] 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /MSUITest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MSUITest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MSUITest.xcodeproj/xcshareddata/xcschemes/MSUITest.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /MSUITest.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /MSUITest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Source/AIP/Extensions/NSObject+Accessibility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | 25 | extension UIAccessibilityIdentification where Self: NSObject { 26 | /// Add an accessibility identifier to a UIKit element. 27 | /// 28 | /// - Parameters: 29 | /// - aip: AIP to provide the Element. 30 | /// - element: Element of AIP to assign to the UIKit element. 31 | public func addAccessibility(aip: T.Type, element: T.Element) { 32 | let id = T.elementIdentifier(for: element) 33 | accessibilityIdentifier = id 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/AIP/Provider/AccessibilityIdentifierProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import Foundation 24 | 25 | /// Type alias of AccessibilityIdentifierProvider to use as shorter name alternative. 26 | public typealias AIP = AccessibilityIdentifierProvider 27 | 28 | /** 29 | This object handles the identifer of the UI elements to test. 30 | 31 | 32 | Example: 33 | ```` 34 | final class HomeAIP: AIP { 35 | static var mainIdentifier: String = "home" 36 | } 37 | 38 | extension HomeAIP { 39 | enum Element: String { 40 | case mainView 41 | case tableView 42 | } 43 | } 44 | 45 | ```` 46 | */ 47 | public protocol AccessibilityIdentifierProvider: AnyObject { 48 | associatedtype Element 49 | 50 | /// The name of a specific container of UI elements. It could be a good idea using the name of the view of the UI elements like "home". 51 | static var mainIdentifier: String { get } 52 | 53 | /// Helper method to provide the complete identifier of an UI element. You don't need to implement it. An internal extension provides the expected behaviour. 54 | /// 55 | /// - Parameter element: UI Element which needs an identifier. 56 | /// - Returns: The complete identifier of the UI element. 57 | static func elementIdentifier(for element: Element) -> String 58 | } 59 | 60 | extension AccessibilityIdentifierProvider where Element: RawRepresentable, Element.RawValue == String { 61 | public static func elementIdentifier(for element: Element) -> String { 62 | return mainIdentifier + "." + element.rawValue 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Source/Core/Extensions/XCUIApplication+Lifecycle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import XCTest 24 | 25 | extension XCUIApplication { 26 | /// Launches a UI test with specific arguments. 27 | /// 28 | /// It sets some arguments by default. See the README.md for more details. 29 | /// 30 | /// - Parameter customArguments: Custom arguments to add. 31 | public func launchTestMode(customArguments: [String]? = nil) { 32 | let hiddenArgs = [ 33 | "-FIRDebugDisabled" 34 | ] 35 | let defaultArgs: [String] = UITestLaunchArgument.allCases.flatMap { 36 | return ["-\($0.rawValue)", "1"] 37 | } 38 | var args = hiddenArgs + defaultArgs 39 | if let customArguments = customArguments { 40 | args.append(contentsOf: customArguments) 41 | } 42 | launchArguments = args 43 | 44 | launch() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Source/Core/Extensions/XCUIElement+TextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import XCTest 24 | 25 | extension XCUIElement { 26 | func clearTextIfNeeded() { 27 | guard let stringValue = self.value as? String else { 28 | return 29 | } 30 | 31 | let deleteString: String = stringValue.lazy.map { _ in XCUIKeyboardKey.delete.rawValue }.joined() 32 | typeText(deleteString) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/Core/Extensions/XCUIElement+Timeout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import XCTest 24 | 25 | extension XCUIElement { 26 | func waitIfNeeded(timeout: TimeInterval?) { 27 | guard let timeout = timeout else { return } 28 | _ = waitForExistence(timeout: timeout) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Source/Core/LaunchArguments/UITestLaunchArguments.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import Foundation 24 | 25 | /// List of internal launch arguments used from MSUITest and available for the client. 26 | public enum UITestLaunchArgument: String, CaseIterable { 27 | /// Request to disable the animation. Animations can create flicky tests. 28 | case animationsDisabled 29 | 30 | /** 31 | Checks if a launch argument has been set from MSUITest. 32 | 33 | Example: 34 | ```` 35 | guard UITestLaunchArgument.animationsDisabled.isActive else { return } 36 | UIView.setAnimationsEnabled(false) 37 | ```` 38 | */ 39 | public var isActive: Bool { 40 | return UserDefaults.standard.object(forKey: self.rawValue) != nil 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Source/Core/PageObjects/ElementProvider/PageObjectUIElementProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import XCTest 24 | 25 | /** Abstract object to provide a XCUIElement given a specific AIP element. 26 | 27 | Example: 28 | ```` 29 | extension HomePage: PageObjectUIElementProvider, PageObject { 30 | func uiElement(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElement { 31 | let query = self.query(for: element, in: queryProvider) 32 | 33 | let identifier = HomeAIP.elementIdentifier(for: element) 34 | return query[identifier] 35 | } 36 | 37 | private func query(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElementQuery { 38 | switch element { 39 | case .mainView: 40 | return queryProvider.otherElements 41 | case .tableView: 42 | return queryProvider.tables 43 | } 44 | } 45 | 46 | ```` 47 | */ 48 | public protocol PageObjectUIElementProvider: AnyObject { 49 | associatedtype Element 50 | 51 | /// Method to provide a XCUIElement given a specific AIP element. 52 | /// 53 | /// - Parameters: 54 | /// - element: AIP element to convert. 55 | /// - queryProvider: Provider where to find the XCUIElement element. Default: XCUIApplication(). 56 | /// - Returns: the XCUIElement found. 57 | func uiElement(for element: Element, in queryProvider: XCUIElementTypeQueryProvider) -> XCUIElement 58 | } 59 | -------------------------------------------------------------------------------- /Source/Core/PageObjects/Helpers/HittableElementFinder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import XCTest 24 | 25 | final class HittableElementFinder { 26 | static func findSwipingIfNeeded(element: XCUIElement, swipeAction: SwipeAction?) { 27 | guard let swipeAction = swipeAction else { return } 28 | var numSwipes = 0 29 | while (!element.exists || element.frame.isEmpty || !element.isHittable) && numSwipes < swipeAction.maxSwipes { 30 | switch swipeAction.direction { 31 | case .up: 32 | swipeAction.target.swipeUp() 33 | case .down: 34 | swipeAction.target.swipeDown() 35 | case .left: 36 | swipeAction.target.swipeLeft() 37 | case .right: 38 | swipeAction.target.swipeRight() 39 | } 40 | 41 | numSwipes += 1 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Source/Core/PageObjects/Models/SwipeAction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import XCTest 24 | 25 | /// Paylod object to perform a swipe action. 26 | public struct SwipeAction { 27 | /// Where to perform the swipe. 28 | let target: XCUIElement 29 | 30 | /// The direction of the swipe. 31 | let direction: Direction 32 | 33 | /// Limit of swipes before stopping. 34 | let maxSwipes: Int 35 | } 36 | 37 | extension SwipeAction { 38 | public enum Direction { 39 | case up 40 | case right 41 | case down 42 | case left 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Source/Core/PageObjects/PageObject.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | import XCTest 24 | 25 | /// Abstract Page object to provide the default functionality for a concrete Page object. 26 | /// * Attention: **None** of these methods require a custom implementation. Everything is provided with internal extensions. 27 | public protocol PageObject: PageObjectWhen, PageObjectShould { 28 | 29 | /// Add a delay before continuing with the UI test flow. 30 | /// 31 | /// - Parameter delay: Seconds to add as delay 32 | /// - Returns: itself to use in a chain of calls. 33 | @discardableResult 34 | func delay(_ delay: TimeInterval) -> Self 35 | 36 | /// Terminates an UI test 37 | static func terminate() 38 | } 39 | 40 | extension PageObject { 41 | @discardableResult 42 | public func delay(_ delay: TimeInterval) -> Self { 43 | Thread.sleep(forTimeInterval: delay) 44 | 45 | return self 46 | } 47 | } 48 | 49 | extension PageObject { 50 | public static func terminate() { 51 | XCUIApplication().terminate() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Source/SupportingFiles/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.1 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Source/SupportingFiles/MSUITest.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Marco Santarossa 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | // 22 | 23 | #import 24 | 25 | //! Project version number for MSUITest. 26 | FOUNDATION_EXPORT double MSUITestVersionNumber; 27 | 28 | //! Project version string for MSUITest. 29 | FOUNDATION_EXPORT const unsigned char MSUITestVersionString[]; 30 | 31 | // In this header, you should import all the public headers of your framework using statements like #import 32 | 33 | 34 | -------------------------------------------------------------------------------- /docs/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | MSUITest Docs 25 | 26 | (88% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |

43 | 44 | 45 | Install in Dash 46 | 47 |

48 |
49 | 50 | 55 | 56 |
57 | 119 |
120 | 121 |
122 |
123 |

Enumerations

124 |

The following enumerations are available globally.

125 | 126 |
127 |
128 | 129 |
130 |
131 |
132 |
    133 |
  • 134 |
    135 | 136 | 137 | 138 | UITestLaunchArgument 139 | 140 |
    141 |
    142 |
    143 |
    144 |
    145 |
    146 |

    List of internal launch arguments used from MSUITest and available for the client.

    147 | 148 | See more 149 |
    150 |
    151 |

    Declaration

    152 |
    153 |

    Swift

    154 |
    public enum UITestLaunchArgument : String, CaseIterable
    155 | 156 |
    157 |
    158 |
    159 |
    160 |
  • 161 |
162 |
163 |
164 |
165 | 166 |
167 |
168 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/Structs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Structures Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | MSUITest Docs 25 | 26 | (88% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |

43 | 44 | 45 | Install in Dash 46 | 47 |

48 |
49 | 50 | 55 | 56 |
57 | 119 |
120 | 121 |
122 |
123 |

Structures

124 |

The following structures are available globally.

125 | 126 |
127 |
128 | 129 |
130 |
131 |
132 |
    133 |
  • 134 |
    135 | 136 | 137 | 138 | SwipeAction 139 | 140 |
    141 |
    142 |
    143 |
    144 |
    145 |
    146 |

    Paylod object to perform a swipe action.

    147 | 148 | See more 149 |
    150 |
    151 |

    Declaration

    152 |
    153 |

    Swift

    154 |
    public struct SwipeAction
    155 | 156 |
    157 |
    158 |
    159 |
    160 |
  • 161 |
162 |
163 |
164 |
165 | 166 |
167 |
168 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/Structs/SwipeAction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SwipeAction Structure Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | MSUITest Docs 25 | 26 | (88% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |

43 | 44 | 45 | Install in Dash 46 | 47 |

48 |
49 | 50 | 55 | 56 |
57 | 119 |
120 | 121 |
122 |
123 |

SwipeAction

124 |
125 |
126 |
public struct SwipeAction
127 | 128 |
129 |
130 |

Paylod object to perform a swipe action.

131 | 132 |
133 |
134 | 135 |
136 |
137 |
138 |
    139 |
  • 140 |
    141 | 142 | 143 | 144 | Direction 145 | 146 |
    147 |
    148 |
    149 |
    150 |
    151 |
    152 |

    Undocumented

    153 | 154 | See more 155 |
    156 |
    157 |

    Declaration

    158 |
    159 |

    Swift

    160 |
    public enum Direction
    161 | 162 |
    163 |
    164 |
    165 |
    166 |
  • 167 |
168 |
169 |
170 |
171 | 172 |
173 |
174 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /docs/Typealiases.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Type Aliases Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | MSUITest Docs 25 | 26 | (88% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |

43 | 44 | 45 | Install in Dash 46 | 47 |

48 |
49 | 50 | 55 | 56 |
57 | 119 |
120 | 121 |
122 |
123 |

Type Aliases

124 |

The following type aliases are available globally.

125 | 126 |
127 |
128 | 129 |
130 |
131 |
132 |
    133 |
  • 134 |
    135 | 136 | 137 | 138 | AIP 139 | 140 |
    141 |
    142 |
    143 |
    144 |
    145 |
    146 |

    Type alias of AccessibilityIdentifierProvider to use as shorter name alternative.

    147 | 148 |
    149 |
    150 |

    Declaration

    151 |
    152 |

    Swift

    153 |
    public typealias AIP = AccessibilityIdentifierProvider
    154 | 155 |
    156 |
    157 |
    158 |
    159 |
  • 160 |
161 |
162 |
163 |
164 | 165 |
166 |
167 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 88% 23 | 24 | 25 | 88% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.msuitest 7 | CFBundleName 8 | MSUITest 9 | DocSetPlatformFamily 10 | msuitest 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Resources/Documents/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | MSUITest Docs 25 | 26 | (88% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |

43 | 44 | 45 | Install in Dash 46 | 47 |

48 |
49 | 50 | 55 | 56 |
57 | 119 |
120 | 121 |
122 |
123 |

Enumerations

124 |

The following enumerations are available globally.

125 | 126 |
127 |
128 | 129 |
130 |
131 |
132 |
    133 |
  • 134 |
    135 | 136 | 137 | 138 | UITestLaunchArgument 139 | 140 |
    141 |
    142 |
    143 |
    144 |
    145 |
    146 |

    List of internal launch arguments used from MSUITest and available for the client.

    147 | 148 | See more 149 |
    150 |
    151 |

    Declaration

    152 |
    153 |

    Swift

    154 |
    public enum UITestLaunchArgument : String, CaseIterable
    155 | 156 |
    157 |
    158 |
    159 |
    160 |
  • 161 |
162 |
163 |
164 |
165 | 166 |
167 |
168 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Resources/Documents/Structs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Structures Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | MSUITest Docs 25 | 26 | (88% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |

43 | 44 | 45 | Install in Dash 46 | 47 |

48 |
49 | 50 | 55 | 56 |
57 | 119 |
120 | 121 |
122 |
123 |

Structures

124 |

The following structures are available globally.

125 | 126 |
127 |
128 | 129 |
130 |
131 |
132 |
    133 |
  • 134 |
    135 | 136 | 137 | 138 | SwipeAction 139 | 140 |
    141 |
    142 |
    143 |
    144 |
    145 |
    146 |

    Paylod object to perform a swipe action.

    147 | 148 | See more 149 |
    150 |
    151 |

    Declaration

    152 |
    153 |

    Swift

    154 |
    public struct SwipeAction
    155 | 156 |
    157 |
    158 |
    159 |
    160 |
  • 161 |
162 |
163 |
164 |
165 | 166 |
167 |
168 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Resources/Documents/Structs/SwipeAction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SwipeAction Structure Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | MSUITest Docs 25 | 26 | (88% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |

43 | 44 | 45 | Install in Dash 46 | 47 |

48 |
49 | 50 | 55 | 56 |
57 | 119 |
120 | 121 |
122 |
123 |

SwipeAction

124 |
125 |
126 |
public struct SwipeAction
127 | 128 |
129 |
130 |

Paylod object to perform a swipe action.

131 | 132 |
133 |
134 | 135 |
136 |
137 |
138 |
    139 |
  • 140 |
    141 | 142 | 143 | 144 | Direction 145 | 146 |
    147 |
    148 |
    149 |
    150 |
    151 |
    152 |

    Undocumented

    153 | 154 | See more 155 |
    156 |
    157 |

    Declaration

    158 |
    159 |

    Swift

    160 |
    public enum Direction
    161 | 162 |
    163 |
    164 |
    165 |
    166 |
  • 167 |
168 |
169 |
170 |
171 | 172 |
173 |
174 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Resources/Documents/Typealiases.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Type Aliases Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |

23 | 24 | MSUITest Docs 25 | 26 | (88% documented) 27 |

28 | 29 |

30 |

31 | 32 |
33 |

34 | 35 |

36 | 37 | 38 | View on GitHub 39 | 40 |

41 | 42 |

43 | 44 | 45 | Install in Dash 46 | 47 |

48 |
49 | 50 | 55 | 56 |
57 | 119 |
120 | 121 |
122 |
123 |

Type Aliases

124 |

The following type aliases are available globally.

125 | 126 |
127 |
128 | 129 |
130 |
131 |
132 |
    133 |
  • 134 |
    135 | 136 | 137 | 138 | AIP 139 | 140 |
    141 |
    142 |
    143 |
    144 |
    145 |
    146 |

    Type alias of AccessibilityIdentifierProvider to use as shorter name alternative.

    147 | 148 |
    149 |
    150 |

    Declaration

    151 |
    152 |

    Swift

    153 |
    public typealias AIP = AccessibilityIdentifierProvider
    154 | 155 |
    156 |
    157 |
    158 |
    159 |
  • 160 |
161 |
162 |
163 |
164 | 165 |
166 |
167 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/docs/docsets/MSUITest.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/docs/docsets/MSUITest.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/docs/docsets/MSUITest.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Resources/Documents/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/docs/docsets/MSUITest.docset/Contents/Resources/Documents/img/spinner.gif -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`.token[href="${location.hash}"]`); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Resources/Documents/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var $typeahead = $('[data-typeahead]'); 3 | var $form = $typeahead.parents('form'); 4 | var searchURL = $form.attr('action'); 5 | 6 | function displayTemplate(result) { 7 | return result.name; 8 | } 9 | 10 | function suggestionTemplate(result) { 11 | var t = '
'; 12 | t += '' + result.name + ''; 13 | if (result.parent_name) { 14 | t += '' + result.parent_name + ''; 15 | } 16 | t += '
'; 17 | return t; 18 | } 19 | 20 | $typeahead.one('focus', function() { 21 | $form.addClass('loading'); 22 | 23 | $.getJSON(searchURL).then(function(searchData) { 24 | const searchIndex = lunr(function() { 25 | this.ref('url'); 26 | this.field('name'); 27 | this.field('abstract'); 28 | for (const [url, doc] of Object.entries(searchData)) { 29 | this.add({url: url, name: doc.name, abstract: doc.abstract}); 30 | } 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3, 37 | autoselect: true 38 | }, 39 | { 40 | limit: 10, 41 | display: displayTemplate, 42 | templates: { suggestion: suggestionTemplate }, 43 | source: function(query, sync) { 44 | const lcSearch = query.toLowerCase(); 45 | const results = searchIndex.query(function(q) { 46 | q.term(lcSearch, { boost: 100 }); 47 | q.term(lcSearch, { 48 | boost: 10, 49 | wildcard: lunr.Query.wildcard.TRAILING 50 | }); 51 | }).map(function(result) { 52 | var doc = searchData[result.ref]; 53 | doc.url = result.ref; 54 | return doc; 55 | }); 56 | sync(results); 57 | } 58 | } 59 | ); 60 | $form.removeClass('loading'); 61 | $typeahead.trigger('focus'); 62 | }); 63 | }); 64 | 65 | var baseURL = searchURL.slice(0, -"search.json".length); 66 | 67 | $typeahead.on('typeahead:select', function(e, result) { 68 | window.location = baseURL + result.url; 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /docs/docsets/MSUITest.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/docs/docsets/MSUITest.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/MSUITest.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/docs/docsets/MSUITest.tgz -------------------------------------------------------------------------------- /docs/docsets/MSUITest.xml: -------------------------------------------------------------------------------- 1 | 1.0.1https://marcosantarossa.github.io/MSUITest/docsets/MSUITest.tgz 2 | -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/docs/img/gh.png -------------------------------------------------------------------------------- /docs/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoSantarossa/MSUITest/30038418ee3baee0fe3d9075cc228f8a6da7cc7d/docs/img/spinner.gif -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`.token[href="${location.hash}"]`); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /docs/js/jazzy.search.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | var $typeahead = $('[data-typeahead]'); 3 | var $form = $typeahead.parents('form'); 4 | var searchURL = $form.attr('action'); 5 | 6 | function displayTemplate(result) { 7 | return result.name; 8 | } 9 | 10 | function suggestionTemplate(result) { 11 | var t = '
'; 12 | t += '' + result.name + ''; 13 | if (result.parent_name) { 14 | t += '' + result.parent_name + ''; 15 | } 16 | t += '
'; 17 | return t; 18 | } 19 | 20 | $typeahead.one('focus', function() { 21 | $form.addClass('loading'); 22 | 23 | $.getJSON(searchURL).then(function(searchData) { 24 | const searchIndex = lunr(function() { 25 | this.ref('url'); 26 | this.field('name'); 27 | this.field('abstract'); 28 | for (const [url, doc] of Object.entries(searchData)) { 29 | this.add({url: url, name: doc.name, abstract: doc.abstract}); 30 | } 31 | }); 32 | 33 | $typeahead.typeahead( 34 | { 35 | highlight: true, 36 | minLength: 3, 37 | autoselect: true 38 | }, 39 | { 40 | limit: 10, 41 | display: displayTemplate, 42 | templates: { suggestion: suggestionTemplate }, 43 | source: function(query, sync) { 44 | const lcSearch = query.toLowerCase(); 45 | const results = searchIndex.query(function(q) { 46 | q.term(lcSearch, { boost: 100 }); 47 | q.term(lcSearch, { 48 | boost: 10, 49 | wildcard: lunr.Query.wildcard.TRAILING 50 | }); 51 | }).map(function(result) { 52 | var doc = searchData[result.ref]; 53 | doc.url = result.ref; 54 | return doc; 55 | }); 56 | sync(results); 57 | } 58 | } 59 | ); 60 | $form.removeClass('loading'); 61 | $typeahead.trigger('focus'); 62 | }); 63 | }); 64 | 65 | var baseURL = searchURL.slice(0, -"search.json".length); 66 | 67 | $typeahead.on('typeahead:select', function(e, result) { 68 | window.location = baseURL + result.url; 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | { 4 | "file": "/Users/MarcoSantaDev/Projects/MSUITest/Source/AIP/Provider/AccessibilityIdentifierProvider.swift", 5 | "line": 48, 6 | "symbol": "AccessibilityIdentifierProvider.Element", 7 | "symbol_kind": "source.lang.swift.decl.associatedtype", 8 | "warning": "undocumented" 9 | }, 10 | { 11 | "file": "/Users/MarcoSantaDev/Projects/MSUITest/Source/Core/PageObjects/ElementProvider/PageObjectUIElementProvider.swift", 12 | "line": 49, 13 | "symbol": "PageObjectUIElementProvider.Element", 14 | "symbol_kind": "source.lang.swift.decl.associatedtype", 15 | "warning": "undocumented" 16 | }, 17 | { 18 | "file": "/Users/MarcoSantaDev/Projects/MSUITest/Source/Core/PageObjects/Models/SwipeAction.swift", 19 | "line": 38, 20 | "symbol": "SwipeAction.Direction", 21 | "symbol_kind": "source.lang.swift.decl.enum", 22 | "warning": "undocumented" 23 | }, 24 | { 25 | "file": "/Users/MarcoSantaDev/Projects/MSUITest/Source/Core/PageObjects/Models/SwipeAction.swift", 26 | "line": 39, 27 | "symbol": "SwipeAction.Direction.up", 28 | "symbol_kind": "source.lang.swift.decl.enumelement", 29 | "warning": "undocumented" 30 | }, 31 | { 32 | "file": "/Users/MarcoSantaDev/Projects/MSUITest/Source/Core/PageObjects/Models/SwipeAction.swift", 33 | "line": 40, 34 | "symbol": "SwipeAction.Direction.right", 35 | "symbol_kind": "source.lang.swift.decl.enumelement", 36 | "warning": "undocumented" 37 | }, 38 | { 39 | "file": "/Users/MarcoSantaDev/Projects/MSUITest/Source/Core/PageObjects/Models/SwipeAction.swift", 40 | "line": 41, 41 | "symbol": "SwipeAction.Direction.down", 42 | "symbol_kind": "source.lang.swift.decl.enumelement", 43 | "warning": "undocumented" 44 | }, 45 | { 46 | "file": "/Users/MarcoSantaDev/Projects/MSUITest/Source/Core/PageObjects/Models/SwipeAction.swift", 47 | "line": 42, 48 | "symbol": "SwipeAction.Direction.left", 49 | "symbol_kind": "source.lang.swift.decl.enumelement", 50 | "warning": "undocumented" 51 | }, 52 | { 53 | "file": "/Users/MarcoSantaDev/Projects/MSUITest/Source/Core/PageObjects/PageObjectShould.swift", 54 | "line": 28, 55 | "symbol": "PageObjectShould.Element", 56 | "symbol_kind": "source.lang.swift.decl.associatedtype", 57 | "warning": "undocumented" 58 | }, 59 | { 60 | "file": "/Users/MarcoSantaDev/Projects/MSUITest/Source/Core/PageObjects/PageObjectWhen.swift", 61 | "line": 28, 62 | "symbol": "PageObjectWhen.Element", 63 | "symbol_kind": "source.lang.swift.decl.associatedtype", 64 | "warning": "undocumented" 65 | } 66 | ], 67 | "source_directory": "/Users/MarcoSantaDev/Projects/MSUITest" 68 | } --------------------------------------------------------------------------------