{"id":20244316,"url":"https://github.com/protonmail/apple-fusion","last_synced_at":"2025-04-10T20:43:57.912Z","repository":{"id":183426922,"uuid":"668339701","full_name":"ProtonMail/apple-fusion","owner":"ProtonMail","description":"fusion is a lightweight and easy-to-use UI testing framework built on top of Apple XCTest that supports testing on iOS and macOS platforms. Developed with readability and reliability in mind.","archived":false,"fork":false,"pushed_at":"2025-04-07T08:25:34.000Z","size":8941,"stargazers_count":14,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-07T09:23:51.542Z","etag":null,"topics":["ios","ios-app","ios-swift","macos","test","testautomation","testautomationframework","uitest","uitesting","xctest","xcuitest"],"latest_commit_sha":null,"homepage":"https://proton.me/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ProtonMail.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-19T15:12:50.000Z","updated_at":"2025-04-07T08:08:23.000Z","dependencies_parsed_at":"2024-05-21T12:54:53.802Z","dependency_job_id":"2198a02d-8f45-4abb-bd37-d9de2aec60f2","html_url":"https://github.com/ProtonMail/apple-fusion","commit_stats":null,"previous_names":["protonmail/apple-fusion"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProtonMail%2Fapple-fusion","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProtonMail%2Fapple-fusion/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProtonMail%2Fapple-fusion/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProtonMail%2Fapple-fusion/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ProtonMail","download_url":"https://codeload.github.com/ProtonMail/apple-fusion/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248294186,"owners_count":21079799,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ios","ios-app","ios-swift","macos","test","testautomation","testautomationframework","uitest","uitesting","xctest","xcuitest"],"created_at":"2024-11-14T09:14:25.106Z","updated_at":"2025-04-10T20:43:57.904Z","avatar_url":"https://github.com/ProtonMail.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"## fusion\n\n**fusion** is a lightweight and easy-to-use UI testing framework built on top of Apple XCTest. Developed with readability and reliability in mind, it brings the following features:\n\n- Builder-like syntax reduces test code boilerplate.\n- Multiple XCUIElement matchers can be applied in a single line of code to locate the desired element.\n- Built-in explicit waits ensure that the XCUIElement is in the desired state before performing an action or assertion on it.\n- Pre-implemented functions like `swipeDownUntilVisible()`, `swipeUpUntilVisible()`, and `waitUntilGone()` eliminate the need to implement them on your own.\n- Easy-to-use `onChild()` and `onDescendant()` functions allow you to target actions or assertions to desired XCUIElement(s) that belong to the located ancestor.\n- A simplified UI interruption mechanism makes it easier to add UI interruption monitors for a single element or a group of elements of the same type.\n\n## Contributing\nIf you would like to contribute, please keep in mind the following rules:\n- Try to stick to the project's existing code style and naming conventions\n\nBy making a contribution to this project you agree to the following:\n- [x] I assign any and all copyright related to the contribution to Proton Technologies AG;\n- [x] I certify that the contribution was created in whole by me;\n- [x] I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it) is maintained indefinitely and may be redistributed with this project or the open source license(s) involved.\n\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Getting Started](#getting-started)\n  - [Getting Started on macOS](#getting-started-on-macos)\n  - [Locating the Element](#locating-the-element)\n  - [Performing Actions on Element](#performing-actions-on-element)\n  - [Checking Element States](#checking-element-states)\n  - [Waiting for Element States](#waiting-for-element-states)\n  - [Working with UI Interruption Monitor](#working-with-ui-interruption-monitor)\n\n### Installation \u003ca name=\"installation\"\u003e\u003c/a\u003e\n#### CocoaPods\n\n1) `fusion` is available through CocoaPods. To install it, simply add the following line to your Podfile:\n\n```ruby\n   pod 'fusion'\n```\nThen run `pod install` in the project directory to install.\n\n#### Swift Package Manager\n\n2) `fusion` is available through SPM. To install it, simply add this github url to your project under the package dependencies. \n\n### Usage \u003ca name=\"usage\"\u003e\u003c/a\u003e\n\n#### Getting started \u003ca name=\"getting-started\"\u003e\u003c/a\u003e\n\nTo start using `fusion`, you have two options:\n\nExtend your test class with `CoreTestCase`, which extends XCTestCase. This allows you to access any element by its type.\n\n```swift\nimport fusion\n\nclass SampleTestCase: CoreTestCase {\n\n    override class func setUp() {\n        super.setUp()\n        FusionConfig.Waits.defaultTimeout = 2.0\n        FusionConfig.Waits.watchInterval = 0.1\n    }\n\n   func testLoginSample() {\n      // Type text into TextField.\n      textField(usernameTextFieldLocator).typeText(\"username\")\n\n      // Tap, clear text, and then type text into SecureTextField.\n      secureTextField(passwordSecureTextFieldIdentifier).tap().clearText().typeText(\"password\")\n\n      // Tap the login button and wait until it is gone.\n      button(loginButtonidentifier).tap().waitUntilGone()\n\n      // Check that the login successful Static text exists.\n      staticText(loggedInStaticTextidentifier).checkExists()\n   }\n}\n\n```\nFollow the page object test design pattern\n2) If you follow the page object test design pattern, extend your page class with `CoreElements`. Afterward, you will have direct access to all element types.\n\n\n```swift\nimport fusion\n\nclass LoginRobot: CoreElements {\n\n   func typePassword(_ password: String) -\u003e LoginRobot {\n      secureTextField(passwordTextFieldIdentifier).tap().typeText(password)\n      return self\n   }\n \n   func typeUsername(_ username: String) -\u003e LoginRobot {\n      textField(loginTextFieldIdentifier).tap().typeText(username)\n      return self\n   }\n \n   func signIn() -\u003e MainRobot {\n      button(signInButtonIdentifier).tap()\n      return MainRobot()\n   }\n}\n\n```\n#### Getting Started on macOS \u003ca name=\"getting-started-on-macos\"\u003e\u003c/a\u003e\n\n\nTo start using `fusion` on macOS, follow the same options as for iOS.\n\n##### Locating the element \u003ca name=\"locating-the-element\"\u003e\u003c/a\u003e\nDifferent strategies can be applied to locate the UI element depending on the layout hierarchy complexity.\n\nLocating element by index:\n```swift\nbutton().byIndex(1).tap()\n```\nLocating element by label, accessibilityIdentifier, or predicate:\n```swift\nbutton(signInButtonLabel).tap()\n\ntextField(loginTextFieldAccessibilityIdentifier).typeText(username)\n```\nApplying multiple matchers to locate the element:\n\n```swift\nbutton(\"nonUniqueButtonIdentifier\").isEnabled().hasLabel(\"buttonLabel\").tap()\n```\nLocating the child element:\nIdentify the parent element and then provide a child UiElement instance as a parameter to onChild() function:\n\n```swift\ncell(cellIdentifier).onChild(button(childButtonIdentifier)).tap()\n```\nLocating the descendant element:\nSimilar to onChild() function, you can locate the ancestor's descendant using the onDescendant() function:\n\n```swift\ncell(cellIdentifier).onDescendant(button(childButtonIdentifier)).tap()\n```\n\n#### Performing Actions on Element \u003ca name=\"performing-actions-on-element\"\u003e\u003c/a\u003e\n\n`fusion` supports the majority of actions available in XCTest framework. Actions should follow the element locator and can be bundled one after another. For example:\n\n```swift\nsecureTextField(passwordSecureTextFieldIdentifier).tap().clearText().typeText(\"password\")\n```\nActions will be executed in the same order as they are applied to the element. Before each action, fusion framework explicitly waits for element existence with a timeout of 10 seconds (adjustable). If the element does not exist when the timeout is reached, an attempt to interact with the non-existing element will be made, and the test will fail. If the element exists, the test will proceed immediately.\n\n#### Checking Element States \u003ca name=\"checking-element-states\"\u003e\u003c/a\u003e\n\n`fusion` provides various check functions to validate the state of an element.\n\n```swift\ncell(cellIdentifier).checkExists()\n\ncell(cellIdentifier).checkIsHittable()\n\ncell(cellIdentifier).checkDoesNotExist()\n\ncell(cellIdentifier).checkHasChild(button(buttonIdentifier))\n\ncell(cellIdentifier).checkHasDescendant(button(childButtonIdentifier))\n```\n\n#### Waiting for Element States \u003ca name=\"waiting-for-element-states\"\u003e\u003c/a\u003e\nWaits can be used for validation purposes or when waiting for a specific element state before performing an action or checking the non-existence of an element.\n\n```swift\nbutton(buttonIdentifier).waitForHittable().tap()\n\ncell(cellIdentifier).waitUntilGone(5)\n\nstaticText(staticTextIdentifier).waitUntilExists().checkExists()\n```\n\n#### Working with UI Interruption Monitor \u003ca name=\"working-with-ui-interruption-monitor\"\u003e\u003c/a\u003e\n\n`fusion` simplifies the process of registering a UI interruption monitor, which allows you to handle elements that unexpectedly block access to other elements during UI testing.\n\nYou can register a UI interruption monitor in two ways:\n\nRegistering a single element to monitor:\n```swift\naddUIMonitor(elementToTap: XCUIApplication().buttons[\"OK\"])\n```\nThis registers a single element to monitor and taps on it when it blocks other elements.\n\nRegistering a group of elements to monitor:\n```swift\naddUIMonitor(elementsQuery: XCUIApplication().buttons, identifiers: [\"OK\", \"Allow\"])\n```\nThis registers the type of elements to monitor (e.g., buttons) together with an array of their locators (labels, accessibility identifiers) and taps on the monitored element when it blocks the test code execution.\n\nRemember to remove the UI interruption monitors in the tearDown() method of your test class to clean up after the test.\n\n```swift\nremoveUIInterruptionMonitor(okButtonMonitor!)\n\nremoveUIInterruptionMonitor(buttonsToMonitor!)\n```\n\n\n```swift\n\n    override func setUp() {\n       ... setup multiple things\n       handleInterruption()\n    }\n    \n    func handleInterruption() {\n        let labels = [\"Allow\", \"Don’t Allow\"]\n        /// Adds UI interruption monitor that queries all buttons and clicks if identifier is in the labels array. It is triggered when system alert interrupts the test execution.\n        addUIMonitor(elementQueryToTap: XCUIApplication(bundleIdentifier: \"com.apple.springboard\").buttons, identifiers: labels)\n    }\n```\nThese examples provide an overview of how to use fusion for UI testing.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprotonmail%2Fapple-fusion","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprotonmail%2Fapple-fusion","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprotonmail%2Fapple-fusion/lists"}