diff --git a/.developer/dev.sh b/.developer/dev.sh new file mode 100644 index 0000000..3d0bd2e --- /dev/null +++ b/.developer/dev.sh @@ -0,0 +1,10 @@ +### Initialize Developer Environment + +# Set up the environment +python3 -m venv ./venv + +# Use venv +source ./venv/bin/activate + +# Download Python packages +pip install -r requirements.txt diff --git a/README.md b/README.md index 012838e..46b4e1c 100644 --- a/README.md +++ b/README.md @@ -661,6 +661,12 @@ $ ideviceinstaller -l ## Tests 测试的用例放在`tests/`目录下,使用iphone SE作为测试机型,系统语言应用。调度框架`pytest` +## WDA Benchmark E2E Tests +[E2E Tests](e2e_benchmarks) +Latest WDA Version Testing Report: +- [WDA Version 7.1.1](e2e_benchmarks/reports/WDA_V711.md) +- [WDA Version 6.1.1](e2e_benchmarks/reports/WDA_V611.md) + ## Reference Source code diff --git a/e2e_benchmarks/README.md b/e2e_benchmarks/README.md new file mode 100644 index 0000000..1223024 --- /dev/null +++ b/e2e_benchmarks/README.md @@ -0,0 +1,9 @@ +# E2E Testing + +## Overview +This directory contains end-to-end tests for the operator. +This will involve benchmark testing across different [WDA service versions](https://github.com/appium/WebDriverAgent/releases). + +## Running the tests +The tests can be run using the `pytest -v -rsx '/Users/youngfreefjs/Desktop/code/github/facebook-wda/e2e_benchmarks/'` script. +This will install the WDA service versions, ensuring a swift identification of whether the client remains compatible and functional after an upgrade in the WDA service. diff --git a/e2e_benchmarks/__init__.py b/e2e_benchmarks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/project.pbxproj b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/project.pbxproj new file mode 100644 index 0000000..8aa50d1 --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/project.pbxproj @@ -0,0 +1,598 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 88FD625B2BAADAB7003459E2 /* facebookwdae2eApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88FD625A2BAADAB7003459E2 /* facebookwdae2eApp.swift */; }; + 88FD625D2BAADAB7003459E2 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88FD625C2BAADAB7003459E2 /* ContentView.swift */; }; + 88FD625F2BAADAB9003459E2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 88FD625E2BAADAB9003459E2 /* Assets.xcassets */; }; + 88FD62622BAADAB9003459E2 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 88FD62612BAADAB9003459E2 /* Preview Assets.xcassets */; }; + 88FD626C2BAADAB9003459E2 /* facebookwdae2eTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88FD626B2BAADAB9003459E2 /* facebookwdae2eTests.swift */; }; + 88FD62762BAADAB9003459E2 /* facebookwdae2eUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88FD62752BAADAB9003459E2 /* facebookwdae2eUITests.swift */; }; + 88FD62782BAADAB9003459E2 /* facebookwdae2eUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88FD62772BAADAB9003459E2 /* facebookwdae2eUITestsLaunchTests.swift */; }; + 88FD62852BAC3F63003459E2 /* ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88FD62842BAC3F63003459E2 /* ListView.swift */; }; + 88FD62872BAC4510003459E2 /* DragView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88FD62862BAC4510003459E2 /* DragView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 88FD62682BAADAB9003459E2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 88FD624F2BAADAB7003459E2 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 88FD62562BAADAB7003459E2; + remoteInfo = facebookwdae2e; + }; + 88FD62722BAADAB9003459E2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 88FD624F2BAADAB7003459E2 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 88FD62562BAADAB7003459E2; + remoteInfo = facebookwdae2e; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 88FD62572BAADAB7003459E2 /* facebookwdae2e.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = facebookwdae2e.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 88FD625A2BAADAB7003459E2 /* facebookwdae2eApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = facebookwdae2eApp.swift; sourceTree = ""; }; + 88FD625C2BAADAB7003459E2 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 88FD625E2BAADAB9003459E2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 88FD62612BAADAB9003459E2 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 88FD62672BAADAB9003459E2 /* facebookwdae2eTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = facebookwdae2eTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 88FD626B2BAADAB9003459E2 /* facebookwdae2eTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = facebookwdae2eTests.swift; sourceTree = ""; }; + 88FD62712BAADAB9003459E2 /* facebookwdae2eUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = facebookwdae2eUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 88FD62752BAADAB9003459E2 /* facebookwdae2eUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = facebookwdae2eUITests.swift; sourceTree = ""; }; + 88FD62772BAADAB9003459E2 /* facebookwdae2eUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = facebookwdae2eUITestsLaunchTests.swift; sourceTree = ""; }; + 88FD62842BAC3F63003459E2 /* ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListView.swift; sourceTree = ""; }; + 88FD62862BAC4510003459E2 /* DragView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DragView.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 88FD62542BAADAB7003459E2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 88FD62642BAADAB9003459E2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 88FD626E2BAADAB9003459E2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 88FD624E2BAADAB7003459E2 = { + isa = PBXGroup; + children = ( + 88FD62592BAADAB7003459E2 /* facebookwdae2e */, + 88FD626A2BAADAB9003459E2 /* facebookwdae2eTests */, + 88FD62742BAADAB9003459E2 /* facebookwdae2eUITests */, + 88FD62582BAADAB7003459E2 /* Products */, + ); + sourceTree = ""; + }; + 88FD62582BAADAB7003459E2 /* Products */ = { + isa = PBXGroup; + children = ( + 88FD62572BAADAB7003459E2 /* facebookwdae2e.app */, + 88FD62672BAADAB9003459E2 /* facebookwdae2eTests.xctest */, + 88FD62712BAADAB9003459E2 /* facebookwdae2eUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 88FD62592BAADAB7003459E2 /* facebookwdae2e */ = { + isa = PBXGroup; + children = ( + 88FD625A2BAADAB7003459E2 /* facebookwdae2eApp.swift */, + 88FD625C2BAADAB7003459E2 /* ContentView.swift */, + 88FD625E2BAADAB9003459E2 /* Assets.xcassets */, + 88FD62602BAADAB9003459E2 /* Preview Content */, + 88FD62842BAC3F63003459E2 /* ListView.swift */, + 88FD62862BAC4510003459E2 /* DragView.swift */, + ); + path = facebookwdae2e; + sourceTree = ""; + }; + 88FD62602BAADAB9003459E2 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 88FD62612BAADAB9003459E2 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 88FD626A2BAADAB9003459E2 /* facebookwdae2eTests */ = { + isa = PBXGroup; + children = ( + 88FD626B2BAADAB9003459E2 /* facebookwdae2eTests.swift */, + ); + path = facebookwdae2eTests; + sourceTree = ""; + }; + 88FD62742BAADAB9003459E2 /* facebookwdae2eUITests */ = { + isa = PBXGroup; + children = ( + 88FD62752BAADAB9003459E2 /* facebookwdae2eUITests.swift */, + 88FD62772BAADAB9003459E2 /* facebookwdae2eUITestsLaunchTests.swift */, + ); + path = facebookwdae2eUITests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 88FD62562BAADAB7003459E2 /* facebookwdae2e */ = { + isa = PBXNativeTarget; + buildConfigurationList = 88FD627B2BAADAB9003459E2 /* Build configuration list for PBXNativeTarget "facebookwdae2e" */; + buildPhases = ( + 88FD62532BAADAB7003459E2 /* Sources */, + 88FD62542BAADAB7003459E2 /* Frameworks */, + 88FD62552BAADAB7003459E2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = facebookwdae2e; + productName = facebookwdae2e; + productReference = 88FD62572BAADAB7003459E2 /* facebookwdae2e.app */; + productType = "com.apple.product-type.application"; + }; + 88FD62662BAADAB9003459E2 /* facebookwdae2eTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 88FD627E2BAADAB9003459E2 /* Build configuration list for PBXNativeTarget "facebookwdae2eTests" */; + buildPhases = ( + 88FD62632BAADAB9003459E2 /* Sources */, + 88FD62642BAADAB9003459E2 /* Frameworks */, + 88FD62652BAADAB9003459E2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 88FD62692BAADAB9003459E2 /* PBXTargetDependency */, + ); + name = facebookwdae2eTests; + productName = facebookwdae2eTests; + productReference = 88FD62672BAADAB9003459E2 /* facebookwdae2eTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 88FD62702BAADAB9003459E2 /* facebookwdae2eUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 88FD62812BAADAB9003459E2 /* Build configuration list for PBXNativeTarget "facebookwdae2eUITests" */; + buildPhases = ( + 88FD626D2BAADAB9003459E2 /* Sources */, + 88FD626E2BAADAB9003459E2 /* Frameworks */, + 88FD626F2BAADAB9003459E2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 88FD62732BAADAB9003459E2 /* PBXTargetDependency */, + ); + name = facebookwdae2eUITests; + productName = facebookwdae2eUITests; + productReference = 88FD62712BAADAB9003459E2 /* facebookwdae2eUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 88FD624F2BAADAB7003459E2 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + 88FD62562BAADAB7003459E2 = { + CreatedOnToolsVersion = 14.3; + }; + 88FD62662BAADAB9003459E2 = { + CreatedOnToolsVersion = 14.3; + TestTargetID = 88FD62562BAADAB7003459E2; + }; + 88FD62702BAADAB9003459E2 = { + CreatedOnToolsVersion = 14.3; + TestTargetID = 88FD62562BAADAB7003459E2; + }; + }; + }; + buildConfigurationList = 88FD62522BAADAB7003459E2 /* Build configuration list for PBXProject "facebookwdae2e" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 88FD624E2BAADAB7003459E2; + productRefGroup = 88FD62582BAADAB7003459E2 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 88FD62562BAADAB7003459E2 /* facebookwdae2e */, + 88FD62662BAADAB9003459E2 /* facebookwdae2eTests */, + 88FD62702BAADAB9003459E2 /* facebookwdae2eUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 88FD62552BAADAB7003459E2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 88FD62622BAADAB9003459E2 /* Preview Assets.xcassets in Resources */, + 88FD625F2BAADAB9003459E2 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 88FD62652BAADAB9003459E2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 88FD626F2BAADAB9003459E2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 88FD62532BAADAB7003459E2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 88FD625D2BAADAB7003459E2 /* ContentView.swift in Sources */, + 88FD625B2BAADAB7003459E2 /* facebookwdae2eApp.swift in Sources */, + 88FD62872BAC4510003459E2 /* DragView.swift in Sources */, + 88FD62852BAC3F63003459E2 /* ListView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 88FD62632BAADAB9003459E2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 88FD626C2BAADAB9003459E2 /* facebookwdae2eTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 88FD626D2BAADAB9003459E2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 88FD62782BAADAB9003459E2 /* facebookwdae2eUITestsLaunchTests.swift in Sources */, + 88FD62762BAADAB9003459E2 /* facebookwdae2eUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 88FD62692BAADAB9003459E2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 88FD62562BAADAB7003459E2 /* facebookwdae2e */; + targetProxy = 88FD62682BAADAB9003459E2 /* PBXContainerItemProxy */; + }; + 88FD62732BAADAB9003459E2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 88FD62562BAADAB7003459E2 /* facebookwdae2e */; + targetProxy = 88FD62722BAADAB9003459E2 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 88FD62792BAADAB9003459E2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 88FD627A2BAADAB9003459E2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 88FD627C2BAADAB9003459E2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"facebookwdae2e/Preview Content\""; + DEVELOPMENT_TEAM = WK7SZCY93A; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.test.cert.TestCert; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 88FD627D2BAADAB9003459E2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"facebookwdae2e/Preview Content\""; + DEVELOPMENT_TEAM = WK7SZCY93A; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.test.cert.TestCert; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 88FD627F2BAADAB9003459E2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = V82S4U9CM9; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.facebookwda.e2e.facebookwdae2eTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/facebookwdae2e.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/facebookwdae2e"; + }; + name = Debug; + }; + 88FD62802BAADAB9003459E2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = V82S4U9CM9; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.facebookwda.e2e.facebookwdae2eTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/facebookwdae2e.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/facebookwdae2e"; + }; + name = Release; + }; + 88FD62822BAADAB9003459E2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = WK7SZCY93A; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.facebookwda.e2e.facebookwdae2eUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = facebookwdae2e; + }; + name = Debug; + }; + 88FD62832BAADAB9003459E2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = WK7SZCY93A; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.facebookwda.e2e.facebookwdae2eUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = facebookwdae2e; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 88FD62522BAADAB7003459E2 /* Build configuration list for PBXProject "facebookwdae2e" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 88FD62792BAADAB9003459E2 /* Debug */, + 88FD627A2BAADAB9003459E2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 88FD627B2BAADAB9003459E2 /* Build configuration list for PBXNativeTarget "facebookwdae2e" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 88FD627C2BAADAB9003459E2 /* Debug */, + 88FD627D2BAADAB9003459E2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 88FD627E2BAADAB9003459E2 /* Build configuration list for PBXNativeTarget "facebookwdae2eTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 88FD627F2BAADAB9003459E2 /* Debug */, + 88FD62802BAADAB9003459E2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 88FD62812BAADAB9003459E2 /* Build configuration list for PBXNativeTarget "facebookwdae2eUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 88FD62822BAADAB9003459E2 /* Debug */, + 88FD62832BAADAB9003459E2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 88FD624F2BAADAB7003459E2 /* Project object */; +} diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/project.xcworkspace/xcuserdata/youngfreefjs.xcuserdatad/UserInterfaceState.xcuserstate b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/project.xcworkspace/xcuserdata/youngfreefjs.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..5d86683 Binary files /dev/null and b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/project.xcworkspace/xcuserdata/youngfreefjs.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/xcuserdata/youngfreefjs.xcuserdatad/xcschemes/xcschememanagement.plist b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/xcuserdata/youngfreefjs.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..68881f9 --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e.xcodeproj/xcuserdata/youngfreefjs.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + facebookwdae2e.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/AccentColor.colorset/Contents.json b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/AppIcon.appiconset/Contents.json b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..3f1ec8c --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "e2e (2).png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/AppIcon.appiconset/e2e (2).png b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/AppIcon.appiconset/e2e (2).png new file mode 100644 index 0000000..64d135c Binary files /dev/null and b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/AppIcon.appiconset/e2e (2).png differ diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/Contents.json b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/applogo.imageset/Contents.json b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/applogo.imageset/Contents.json new file mode 100644 index 0000000..4f74b77 --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/applogo.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "applogo.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "applogo 1.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "applogo 2.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/applogo.imageset/applogo 1.png b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/applogo.imageset/applogo 1.png new file mode 100644 index 0000000..64d135c Binary files /dev/null and b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/applogo.imageset/applogo 1.png differ diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/applogo.imageset/applogo 2.png b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/applogo.imageset/applogo 2.png new file mode 100644 index 0000000..64d135c Binary files /dev/null and b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/applogo.imageset/applogo 2.png differ diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/applogo.imageset/applogo.png b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/applogo.imageset/applogo.png new file mode 100644 index 0000000..64d135c Binary files /dev/null and b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Assets.xcassets/applogo.imageset/applogo.png differ diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/ContentView.swift b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/ContentView.swift new file mode 100644 index 0000000..43c250b --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/ContentView.swift @@ -0,0 +1,363 @@ +// +// ContentView.swift +// facebookwdae2e +// +// Created by youngfreefjs on 2024/3/20. +// + +import SwiftUI + +// Custom struct to encapsulate the confirmation alert logic with a button +struct ConfirmationAlertButton: View { + @Binding var isPresented: Bool + let id: String + + var body: some View { + Button(id) { + isPresented = true + } + .id(id) + .accessibilityLabel(id) + .accessibilityIdentifier(id) + .alert(isPresented: $isPresented) { + Alert( + title: Text("Confirmation"), + message: Text("Do you accept?"), + primaryButton: .default(Text("Accept").accessibilityLabel("Accept")) { + // Handle acceptance + print("Accepted") + }, + secondaryButton: .cancel(Text("Reject").accessibilityLabel("Reject")) { + // Handle rejection + print("Rejected") + } + ) + } + .padding() + } +} + +// Custom struct to encapsulate the text input alert logic +struct TextInputAlert: View { + @Binding var isPresented: Bool + @Binding var text: String + let onAccept: (String) -> Void + + var body: some View { + if isPresented { + VStack(spacing: 20) { + Text("Enter your input") + TextField("Enter text", text: $text) + .textFieldStyle(RoundedBorderTextFieldStyle()) + HStack(spacing: 20) { + Button("Cancel") { + isPresented = false + } + Button("Accept") { + isPresented = false + onAccept(text) + } + } + } + .padding() + .background(Color.white) + .cornerRadius(10) + .shadow(radius: 5) + .frame(maxWidth: 300) + .transition(.scale) + } + } +} + +// View extension to present the custom text input alert +extension View { + func textInputAlert(isPresented: Binding, text: Binding, onAccept: @escaping (String) -> Void) -> some View { + ZStack { + self + TextInputAlert(isPresented: isPresented, text: text, onAccept: onAccept) + .animation(.default, value: isPresented.wrappedValue) + .opacity(isPresented.wrappedValue ? 1 : 0) + } + } +} + +// ContentView using both ConfirmationAlertButton and TextInputAlert +struct ContentView: View { + @State private var showConfirmationAlert = false + @State private var showTextInputAlert = false + @State private var userInput = "this value" + @State private var isButtonHidden = true + @State private var isChecked: Bool = false + @State private var inputText: String = "" + @State private var longTapShowAlert = false + @State private var DoubleTapShowAlert = false + @State private var showAlert = false + @State private var alertText = "" + @State private var orientation: UIDeviceOrientation = UIDevice.current.orientation + + + let acceptOrRejectAlertID = "ACCEPT_OR_REJECT_ALERT" + let inputAlertID = "INPUT_ALERT" + + + var orientationText: String { + switch orientation { + case .landscapeLeft, .landscapeRight: + return "LANDSCAPE" + case .portrait, .portraitUpsideDown: + return "PORTRAIT" + default: + return "UNKNOW" + } + } + + var body: some View { + NavigationView { + + VStack { + + HStack { + ConfirmationAlertButton(isPresented: $showConfirmationAlert, id: acceptOrRejectAlertID) + + Button(inputAlertID) { + showTextInputAlert = true + }.id(inputAlertID) + .accessibilityLabel(inputAlertID) + .accessibilityIdentifier(inputAlertID) + .padding() + + + } + + // 新的并排按钮 + HStack { + // 启用的按钮 + Button("ENABLED_BTN") { + // 这里处理按钮点击事件 + print("Enabled button tapped") + } + .background(Color.black) // 背景颜色 + .accessibilityLabel("ENABLED_BTN") + .accessibilityIdentifier("ENABLED_BTN") + + .padding() + + // 禁用的按钮 + Button("DISABLED_BTN") { + // 由于按钮被禁用,这里的代码不会被执行 + } + .accessibilityLabel("DISABLED_BTN") + .accessibilityIdentifier("DISABLED_BTN") + .disabled(true) // 禁用按钮 + .padding() + + // 添加本地图像 + Image("applogo") + .resizable() + .scaledToFit() + .frame(width: 100, height: 50) + .accessibilityIdentifier("IMG_BTN") + + // 隐藏按钮 + Button("HIDDEN_BTN") { + // 按钮的动作 + } + .opacity(isButtonHidden ? 0 : 1) // 根据条件设置透明度 + // 或者 + // .hidden(isButtonHidden) // 根据条件隐藏按钮,但这需要自定义扩展 + } + + // select + HStack { + // 没有被选中的勾选框 + Button(action: { + isChecked = false + }) { + Image(systemName: isChecked ? "circle" : "checkmark.circle.fill") + }.accessibilityIdentifier("CHECKED_BTN") + + // 被选中的勾选框 + Button(action: { + isChecked = true + }) { + Image(systemName: isChecked ? "checkmark.circle.fill" : "circle") + }.accessibilityIdentifier("UNCHECKED_BTN") + } + // input + HStack { + TextField("INPUT_FIELD", text: $inputText) + .accessibilityLabel("INPUT_FIELD") + .textFieldStyle(RoundedBorderTextFieldStyle()) + .padding() + + Button(action: { + self.inputText = "" // 清空输入框 + }) { + Text("CLEAR INPUT") + }.accessibilityLabel("CLEAR_INPUT_BTN") + .padding() + } + + // accessibilityContainer COMBINED_TEXT_CONTAINER + VStack { + Text("First line of text") + Text("Second line of text") + Text("Third line of text") + } + .accessibilityElement(children: .combine) + .accessibilityIdentifier("COMBINED_TEXT_CONTAINER") + + HStack { + Text("LONG_TAP_ALERT") + .padding() + .background(Color.blue) + .foregroundColor(Color.white) + .cornerRadius(5) + .onLongPressGesture(minimumDuration: 1.0) { + self.longTapShowAlert = true + } + .alert(isPresented: $longTapShowAlert) { + Alert( + title: Text("Long Tap Alert"), + message: Text("Long Tap Alert"), + dismissButton: .default(Text("LONG_TAP_ALERT_OK")) + ) + } + + Text("DOUBLE_TAP_ALERT") + .padding() + .background(Color.blue) + .foregroundColor(Color.white) + .cornerRadius(5) + .onTapGesture(count: 2) { + self.DoubleTapShowAlert = true + } + .alert(isPresented: $DoubleTapShowAlert) { + Alert( + title: Text("DOUBLE Tap Alert"), + message: Text("DOUBLE Tap Alert"), + dismissButton: .default(Text("DOUBLE_TAP_ALERT_OK")) + ) + } + + + } + + // Return Origatation Text + Text(orientationText) + .accessibilityIdentifier("ORIGATATION_TEXT") + .onAppear { + // Listener + NotificationCenter.default.addObserver( + forName: UIDevice.orientationDidChangeNotification, + object: nil, + queue: .main + ) { _ in + // Update + orientation = UIDevice.current.orientation + } + } + + + + // List View + HStack { + // GO OTHER + NavigationLink(destination: ListView()) { + Text("ListView") + } + .padding() + .background(Color.blue) + .foregroundColor(Color.white) + .cornerRadius(8) + + // GO LIST + NavigationLink(destination: DragView()) { + Text("DragView") + } + .padding() + .background(Color.blue) + .foregroundColor(Color.white) + .cornerRadius(8) + + // 其他内容 + } + .navigationBarTitle("Home", displayMode: .inline) + + } + .textInputAlert(isPresented: $showTextInputAlert, text: $userInput) { enteredText in + // Handle the text input acceptance here + print("The user entered: \(enteredText)") + } + } + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} + + +// SwiftUI 没有内置的带文本输入的弹出对话框,所以我们需要自定义一个 +extension View { + func textFieldAlert(isPresented: Binding, text: Binding) -> some View { + TextFieldAlert(isPresented: isPresented, text: text, presentingView: self) + } +} + +struct TextFieldAlert: UIViewControllerRepresentable { + @Binding var isPresented: Bool + @Binding var text: String + let presentingView: PresentingView + + func makeUIViewController(context: Context) -> UIViewController { + UIViewController() + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) { + guard context.coordinator.alert == nil else { return } + + if isPresented { + let alert = UIAlertController(title: "Alert", message: "Type something:", preferredStyle: .alert) + alert.addTextField { textField in + textField.placeholder = "Enter text here" + textField.text = text + } + + alert.addAction(UIAlertAction(title: "Submit", style: .default) { _ in + if let textField = alert.textFields?.first, let text = textField.text { + self.text = text + isPresented = false + } + }) + + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in + isPresented = false + }) + + context.coordinator.alert = alert + + DispatchQueue.main.async { // Must be presented in the main thread + uiViewController.present(alert, animated: true, completion: { + self.isPresented = false + context.coordinator.alert = nil + }) + } + } + } + + func makeCoordinator() -> Coordinator { + Coordinator(self) + } + + class Coordinator { + var alert: UIAlertController? + var textFieldAlert: TextFieldAlert + + init(_ textFieldAlert: TextFieldAlert) { + self.textFieldAlert = textFieldAlert + } + } +} diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/DragView.swift b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/DragView.swift new file mode 100644 index 0000000..98dc7c5 --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/DragView.swift @@ -0,0 +1,52 @@ +// +// DragView.swift +// facebookwdae2e +// +// Created by youngfreefjs on 2024/3/21. +// + +import Foundation +import SwiftUI + +struct DragView: View { + @State private var showingAlert = false + @State private var isLongPressing = false + + // 按钮B的布局边界 + @State private var buttonBFrame: CGRect = .zero + + var body: some View { + VStack { + // 按钮A + Button("按钮 A (长按并拖到B)") { + // 不在这里处理点击事件 + } + .simultaneousGesture(LongPressGesture(minimumDuration: 0.5).onEnded { _ in + self.isLongPressing = true + }) + .gesture(DragGesture(minimumDistance: 0) + .onEnded { value in + if self.isLongPressing && self.buttonBFrame.contains(value.location) { + self.showingAlert = true + } + self.isLongPressing = false + } + ) + + Spacer().frame(height: 50) + + // 按钮B + Button("按钮 B") { + // 不在这里处理点击事件 + } + .background(GeometryReader { geometry in + Color.clear.onAppear { self.buttonBFrame = geometry.frame(in: .global) } + }) + + Spacer() + } + .alert(isPresented: $showingAlert) { + Alert(title: Text("成功"), message: Text("你已经成功长按并拖动到按钮 B!"), dismissButton: .default(Text("好的"))) + } + } +} diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/ListView.swift b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/ListView.swift new file mode 100644 index 0000000..20acbb0 --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/ListView.swift @@ -0,0 +1,19 @@ +// +// ListView.swift +// facebookwdae2e +// +// Created by youngfreefjs on 2024/3/21. +// + +import Foundation +import SwiftUI + +// ListView.swift +struct ListView: View { + var body: some View { + List(1...100, id: \.self) { number in + Text("Row\(number)") + }.accessibilityLabel("LIST_CONTAINER") + .navigationBarTitle("Numbers List", displayMode: .inline) + } +} diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Preview Content/Preview Assets.xcassets/Contents.json b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/facebookwdae2eApp.swift b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/facebookwdae2eApp.swift new file mode 100644 index 0000000..2ff7985 --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2e/facebookwdae2eApp.swift @@ -0,0 +1,17 @@ +// +// facebookwdae2eApp.swift +// facebookwdae2e +// +// Created by youngfreefjs on 2024/3/20. +// + +import SwiftUI + +@main +struct facebookwdae2eApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2eTests/facebookwdae2eTests.swift b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2eTests/facebookwdae2eTests.swift new file mode 100644 index 0000000..c0c058f --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2eTests/facebookwdae2eTests.swift @@ -0,0 +1,36 @@ +// +// facebookwdae2eTests.swift +// facebookwdae2eTests +// +// Created by youngfreefjs on 2024/3/20. +// + +import XCTest +@testable import facebookwdae2e + +final class facebookwdae2eTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2eUITests/facebookwdae2eUITests.swift b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2eUITests/facebookwdae2eUITests.swift new file mode 100644 index 0000000..f37aa05 --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2eUITests/facebookwdae2eUITests.swift @@ -0,0 +1,41 @@ +// +// facebookwdae2eUITests.swift +// facebookwdae2eUITests +// +// Created by youngfreefjs on 2024/3/20. +// + +import XCTest + +final class facebookwdae2eUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/e2e_benchmarks/app/facebookwdae2e/facebookwdae2eUITests/facebookwdae2eUITestsLaunchTests.swift b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2eUITests/facebookwdae2eUITestsLaunchTests.swift new file mode 100644 index 0000000..1e955d2 --- /dev/null +++ b/e2e_benchmarks/app/facebookwdae2e/facebookwdae2eUITests/facebookwdae2eUITestsLaunchTests.swift @@ -0,0 +1,32 @@ +// +// facebookwdae2eUITestsLaunchTests.swift +// facebookwdae2eUITests +// +// Created by youngfreefjs on 2024/3/20. +// + +import XCTest + +final class facebookwdae2eUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} diff --git a/e2e_benchmarks/constant.py b/e2e_benchmarks/constant.py new file mode 100644 index 0000000..8d47fd2 --- /dev/null +++ b/e2e_benchmarks/constant.py @@ -0,0 +1,14 @@ +from enum import Enum + + +UNDER_TEST_BUNDLE_ID = "com.test.cert.TestCert" + +UNKNOW_BUNDLE_ID = "com.apple.unknown" + +HOME_SCREEN_BUNDLE_ID = "com.apple.springboard" + +class ALERT_ENUMS(Enum): + CONFIRM = "ACCEPT_OR_REJECT_ALERT" + INPUT = "INPUT_ALERT" + + diff --git a/e2e_benchmarks/reports/WDA_V611.md b/e2e_benchmarks/reports/WDA_V611.md new file mode 100644 index 0000000..652a9ab --- /dev/null +++ b/e2e_benchmarks/reports/WDA_V611.md @@ -0,0 +1,117 @@ +# WDA Server And Client Benchmark E2E Testing Report (V7.1.1) + +## Overview +61 passed, 17 skipped, 1 warning in 172.92s (0:02:52) + +## 1. Test Environments +- WDA Version: [version 6.1.1 release by Appium.](https://github.com/appium/WebDriverAgent/releases/tag/v6.1.1) +- Platform: iPhone 13 Pro (iOS 16.3.1) + +## 2. Test Results +platform darwin -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0 +collected 78 items + +e2e_benchmarks/test_alert_commands.py::TestAlert::test_alert_text_accept_endpoint PASSED [ 1%] +e2e_benchmarks/test_alert_commands.py::TestAlert::test_alert_text_button_endpoint_value PASSED [ 2%] +e2e_benchmarks/test_alert_commands.py::TestAlert::test_alert_text_dimiss_endpoint PASSED [ 3%] +e2e_benchmarks/test_alert_commands.py::TestAlert::test_alert_text_endpoint_confirmation PASSED [ 5%] +e2e_benchmarks/test_alert_commands.py::TestAlert::test_alert_text_endpoint_when_no_alert PASSED [ 6%] +e2e_benchmarks/test_alert_commands.py::TestAlert::test_alert_text_input PASSED [ 7%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_active_app_info PASSED [ 8%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_battery_info PASSED [ 10%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_deactivate_app PASSED [ 11%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_device_info PASSED [ 12%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_get_paste_board SKIPPED (WDA API NOT USEFUL: {{baseURL}}/session/{{sessionId}}/wda/getPasteboard) [ 14%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_homescreen PASSED [ 15%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_keybord_dismiss PASSED [ 16%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_keybord_lock_and_unlock PASSED [ 17%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_locked PASSED [ 19%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_scale PASSED [ 20%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_set_paste_board PASSED [ 21%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_timeouts PASSED [ 23%] +e2e_benchmarks/test_debug_commands.py::TestDebug::test_accessible_source PASSED [ 24%] +e2e_benchmarks/test_debug_commands.py::TestDebug::test_source PASSED [ 25%] +e2e_benchmarks/test_element_commands.py::TestElement::test_app_drag_from_to_for_duration PASSED [ 26%] +e2e_benchmarks/test_element_commands.py::TestElement::test_attribute_btn_displayed PASSED [ 28%] +e2e_benchmarks/test_element_commands.py::TestElement::test_attribute_btn_not_displayed PASSED [ 29%] +e2e_benchmarks/test_element_commands.py::TestElement::test_attribute_btn_text PASSED [ 30%] +e2e_benchmarks/test_element_commands.py::TestElement::test_attribute_image_text PASSED [ 32%] +e2e_benchmarks/test_element_commands.py::TestElement::test_attribute_invild PASSED [ 33%] +e2e_benchmarks/test_element_commands.py::TestElement::test_attribute_label PASSED [ 34%] +e2e_benchmarks/test_element_commands.py::TestElement::test_btn_enable PASSED [ 35%] +e2e_benchmarks/test_element_commands.py::TestElement::test_btn_name PASSED [ 37%] +e2e_benchmarks/test_element_commands.py::TestElement::test_btn_selected PASSED [ 38%] +e2e_benchmarks/test_element_commands.py::TestElement::test_drag_from_to_for_duration SKIPPED (NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/dragfromtoforduration) [ 39%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_accessibilityContainer_is_false PASSED [ 41%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_accessibilityContainer_is_true SKIPPED (WDA API NOT USEFUL: Not use in SwiftUI.) [ 42%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_accessible_is_false PASSED [ 43%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_accessible_is_true PASSED [ 44%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_force_touch SKIPPED (NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/forceTouch) [ 46%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_select SKIPPED (NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/pickerwheel/{{uuid}}/select) [ 47%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_swipe_top SKIPPED (NOT IMPLEMENTED) [ 48%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_touble_tap PASSED [ 50%] +e2e_benchmarks/test_element_commands.py::TestElement::test_img_name PASSED [ 51%] +e2e_benchmarks/test_element_commands.py::TestElement::test_input_clear PASSED [ 52%] +e2e_benchmarks/test_element_commands.py::TestElement::test_input_click PASSED [ 53%] +e2e_benchmarks/test_element_commands.py::TestElement::test_input_text PASSED [ 55%] +e2e_benchmarks/test_element_commands.py::TestElement::test_pinch SKIPPED (NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/pinch) [ 56%] +e2e_benchmarks/test_element_commands.py::TestElement::test_rect PASSED [ 57%] +e2e_benchmarks/test_element_commands.py::TestElement::test_screenshot PASSED [ 58%] +e2e_benchmarks/test_element_commands.py::TestElement::test_screenshot_element SKIPPED (NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/screenshot/{{uuid}}) [ 60%] +e2e_benchmarks/test_element_commands.py::TestElement::test_touch_and_hold PASSED [ 61%] +e2e_benchmarks/test_element_commands.py::TestElement::test_wda_keys PASSED [ 62%] +e2e_benchmarks/test_element_commands.py::TestElement::test_wda_touch_and_hold PASSED [ 64%] +e2e_benchmarks/test_element_commands.py::TestElement::test_window_size PASSED [ 65%] +e2e_benchmarks/test_find_element_commands.py::TestFindElement::test_ele_elements SKIPPED (NOT IMPLEMENTED: [GET] /session/{{sessionId}}/element/{{uuid}}/elements) [ 66%] +e2e_benchmarks/test_find_element_commands.py::TestFindElement::test_element_ids PASSED [ 67%] +e2e_benchmarks/test_find_element_commands.py::TestFindElement::test_elements PASSED [ 69%] +e2e_benchmarks/test_find_element_commands.py::TestFindElement::test_wda_active SKIPPED (NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/element/active) [ 70%] +e2e_benchmarks/test_find_element_commands.py::TestFindElement::test_wda_element SKIPPED (NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/getVisibleCells) [ 71%] +e2e_benchmarks/test_orientation_commands.py::TestOrientation::test_get_rotation SKIPPED (NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/rotation) [ 73%] +e2e_benchmarks/test_orientation_commands.py::TestOrientation::test_orientation PASSED [ 74%] +e2e_benchmarks/test_orientation_commands.py::TestOrientation::test_set_orientation PASSED [ 75%] +e2e_benchmarks/test_orientation_commands.py::TestOrientation::test_set_rotation SKIPPED (NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/rotation) [ 76%] +e2e_benchmarks/test_screenshot_commands.py::TestScreenshot::test_screenshot PASSED [ 78%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_app_activate PASSED [ 79%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_app_list PASSED [ 80%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_app_state PASSED [ 82%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_app_terminate PASSED [ 83%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_appium_settings PASSED [ 84%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_create_session_id PASSED [ 85%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_delete_session PASSED [ 87%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_health SKIPPED (NOT IMPLEMENTED: [GET] {{baseURL}}/health) [ 88%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_health_check PASSED [ 89%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_health_check_has_session_id PASSED [ 91%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_launch_app PASSED [ 92%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_session_info SKIPPED (NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}) [ 93%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_status_command_return_schema PASSED [ 94%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_status_command_state_and_ready PASSED [ 96%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_unknow_app_state PASSED [ 97%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_url SKIPPED (UNKNOW HOW TO TEST: [POST] {{baseURL}}/session/{{sessionId}}/url) [ 98%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_wda_shutdown SKIPPED (NOT IMPLEMENTED: [GET] {{baseURL}}/wda/shutdown) [100%] + +=========================================================================================================== warnings summary ============================================================================================================ +e2e_benchmarks/test_debug_commands.py::TestDebug::test_source + /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py:703: FutureWarning: The behavior of this method will change in future versions. Use specific 'len(elem)' or 'elem is not None' test instead. + if not expr: + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +======================================================================================================== short test summary info ======================================================================================================== +SKIPPED [1] e2e_benchmarks/test_custom_commands.py:127: WDA API NOT USEFUL: {{baseURL}}/session/{{sessionId}}/wda/getPasteboard +SKIPPED [1] e2e_benchmarks/test_element_commands.py:256: NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/dragfromtoforduration +SKIPPED [1] e2e_benchmarks/test_element_commands.py:212: WDA API NOT USEFUL: Not use in SwiftUI. +SKIPPED [1] e2e_benchmarks/test_element_commands.py:299: NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/forceTouch +SKIPPED [1] e2e_benchmarks/test_element_commands.py:280: NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/pickerwheel/{{uuid}}/select +SKIPPED [1] e2e_benchmarks/test_element_commands.py:224: NOT IMPLEMENTED +SKIPPED [1] e2e_benchmarks/test_element_commands.py:234: NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/pinch +SKIPPED [1] e2e_benchmarks/test_element_commands.py:168: NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/screenshot/{{uuid}} +SKIPPED [1] e2e_benchmarks/test_find_element_commands.py:51: NOT IMPLEMENTED: [GET] /session/{{sessionId}}/element/{{uuid}}/elements +SKIPPED [1] e2e_benchmarks/test_find_element_commands.py:87: NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/element/active +SKIPPED [1] e2e_benchmarks/test_find_element_commands.py:76: NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/getVisibleCells +SKIPPED [1] e2e_benchmarks/test_orientation_commands.py:54: NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/rotation +SKIPPED [1] e2e_benchmarks/test_orientation_commands.py:63: NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/rotation +SKIPPED [1] e2e_benchmarks/test_session_commands.py:256: NOT IMPLEMENTED: [GET] {{baseURL}}/health +SKIPPED [1] e2e_benchmarks/test_session_commands.py:140: NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}} +SKIPPED [1] e2e_benchmarks/test_session_commands.py:33: UNKNOW HOW TO TEST: [POST] {{baseURL}}/session/{{sessionId}}/url +SKIPPED [1] e2e_benchmarks/test_session_commands.py:247: NOT IMPLEMENTED: [GET] {{baseURL}}/wda/shutdown +========================================================================================= 61 passed, 17 skipped, 1 warning in 172.92s (0:02:52) ========================================================================================= \ No newline at end of file diff --git a/e2e_benchmarks/reports/WDA_V711.md b/e2e_benchmarks/reports/WDA_V711.md new file mode 100644 index 0000000..37b40a1 --- /dev/null +++ b/e2e_benchmarks/reports/WDA_V711.md @@ -0,0 +1,118 @@ +# WDA Server And Client Benchmark E2E Testing Report (V7.1.1) + +## Overview +61 passed, 17 skipped, 1 warning in 173.98s (0:02:53) + +## 1. Test Environments +- WDA Version: [version 7.1.1 release by Appium.](https://github.com/appium/WebDriverAgent/releases/tag/v7.1.1) +- Platform: iPhone 13 Pro (iOS 16.3.1) + +## 2. Test Results +platform darwin -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0 + +collected 78 items + +e2e_benchmarks/test_alert_commands.py::TestAlert::test_alert_text_accept_endpoint PASSED [ 1%] +e2e_benchmarks/test_alert_commands.py::TestAlert::test_alert_text_button_endpoint_value PASSED [ 2%] +e2e_benchmarks/test_alert_commands.py::TestAlert::test_alert_text_dimiss_endpoint PASSED [ 3%] +e2e_benchmarks/test_alert_commands.py::TestAlert::test_alert_text_endpoint_confirmation PASSED [ 5%] +e2e_benchmarks/test_alert_commands.py::TestAlert::test_alert_text_endpoint_when_no_alert PASSED [ 6%] +e2e_benchmarks/test_alert_commands.py::TestAlert::test_alert_text_input PASSED [ 7%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_active_app_info PASSED [ 8%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_battery_info PASSED [ 10%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_deactivate_app PASSED [ 11%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_device_info PASSED [ 12%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_get_paste_board SKIPPED (WDA API NOT USEFUL: {{baseURL}}/session/{{sessionId}}/wda/getPasteboard) [ 14%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_homescreen PASSED [ 15%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_keybord_dismiss PASSED [ 16%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_keybord_lock_and_unlock PASSED [ 17%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_locked PASSED [ 19%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_scale PASSED [ 20%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_set_paste_board PASSED [ 21%] +e2e_benchmarks/test_custom_commands.py::TestDevice::test_timeouts PASSED [ 23%] +e2e_benchmarks/test_debug_commands.py::TestDebug::test_accessible_source PASSED [ 24%] +e2e_benchmarks/test_debug_commands.py::TestDebug::test_source PASSED [ 25%] +e2e_benchmarks/test_element_commands.py::TestElement::test_app_drag_from_to_for_duration PASSED [ 26%] +e2e_benchmarks/test_element_commands.py::TestElement::test_attribute_btn_displayed PASSED [ 28%] +e2e_benchmarks/test_element_commands.py::TestElement::test_attribute_btn_not_displayed PASSED [ 29%] +e2e_benchmarks/test_element_commands.py::TestElement::test_attribute_btn_text PASSED [ 30%] +e2e_benchmarks/test_element_commands.py::TestElement::test_attribute_image_text PASSED [ 32%] +e2e_benchmarks/test_element_commands.py::TestElement::test_attribute_invild PASSED [ 33%] +e2e_benchmarks/test_element_commands.py::TestElement::test_attribute_label PASSED [ 34%] +e2e_benchmarks/test_element_commands.py::TestElement::test_btn_enable PASSED [ 35%] +e2e_benchmarks/test_element_commands.py::TestElement::test_btn_name PASSED [ 37%] +e2e_benchmarks/test_element_commands.py::TestElement::test_btn_selected PASSED [ 38%] +e2e_benchmarks/test_element_commands.py::TestElement::test_drag_from_to_for_duration SKIPPED (NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}...) [ 39%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_accessibilityContainer_is_false PASSED [ 41%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_accessibilityContainer_is_true SKIPPED (WDA API NOT USEFUL: Not use in SwiftUI.) [ 42%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_accessible_is_false PASSED [ 43%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_accessible_is_true PASSED [ 44%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_force_touch SKIPPED (NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/forceTouch) [ 46%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_select SKIPPED (NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/pickerwheel/{{uuid}}/select) [ 47%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_swipe_top SKIPPED (NOT IMPLEMENTED) [ 48%] +e2e_benchmarks/test_element_commands.py::TestElement::test_ele_touble_tap PASSED [ 50%] +e2e_benchmarks/test_element_commands.py::TestElement::test_img_name PASSED [ 51%] +e2e_benchmarks/test_element_commands.py::TestElement::test_input_clear PASSED [ 52%] +e2e_benchmarks/test_element_commands.py::TestElement::test_input_click PASSED [ 53%] +e2e_benchmarks/test_element_commands.py::TestElement::test_input_text PASSED [ 55%] +e2e_benchmarks/test_element_commands.py::TestElement::test_pinch SKIPPED (NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/pinch) [ 56%] +e2e_benchmarks/test_element_commands.py::TestElement::test_rect PASSED [ 57%] +e2e_benchmarks/test_element_commands.py::TestElement::test_screenshot PASSED [ 58%] +e2e_benchmarks/test_element_commands.py::TestElement::test_screenshot_element SKIPPED (NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/screenshot/{{uuid}}) [ 60%] +e2e_benchmarks/test_element_commands.py::TestElement::test_touch_and_hold PASSED [ 61%] +e2e_benchmarks/test_element_commands.py::TestElement::test_wda_keys PASSED [ 62%] +e2e_benchmarks/test_element_commands.py::TestElement::test_wda_touch_and_hold PASSED [ 64%] +e2e_benchmarks/test_element_commands.py::TestElement::test_window_size PASSED [ 65%] +e2e_benchmarks/test_find_element_commands.py::TestFindElement::test_ele_elements SKIPPED (NOT IMPLEMENTED: [GET] /session/{{sessionId}}/element/{{uuid}}/elements) [ 66%] +e2e_benchmarks/test_find_element_commands.py::TestFindElement::test_element_ids PASSED [ 67%] +e2e_benchmarks/test_find_element_commands.py::TestFindElement::test_elements PASSED [ 69%] +e2e_benchmarks/test_find_element_commands.py::TestFindElement::test_wda_active SKIPPED (NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/element/active) [ 70%] +e2e_benchmarks/test_find_element_commands.py::TestFindElement::test_wda_element SKIPPED (NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/getVi...) [ 71%] +e2e_benchmarks/test_orientation_commands.py::TestOrientation::test_get_rotation SKIPPED (NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/rotation) [ 73%] +e2e_benchmarks/test_orientation_commands.py::TestOrientation::test_orientation PASSED [ 74%] +e2e_benchmarks/test_orientation_commands.py::TestOrientation::test_set_orientation PASSED [ 75%] +e2e_benchmarks/test_orientation_commands.py::TestOrientation::test_set_rotation SKIPPED (NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/rotation) [ 76%] +e2e_benchmarks/test_screenshot_commands.py::TestScreenshot::test_screenshot PASSED [ 78%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_app_activate PASSED [ 79%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_app_list PASSED [ 80%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_app_state PASSED [ 82%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_app_terminate PASSED [ 83%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_appium_settings PASSED [ 84%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_create_session_id PASSED [ 85%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_delete_session PASSED [ 87%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_health SKIPPED (NOT IMPLEMENTED: [GET] {{baseURL}}/health) [ 88%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_health_check PASSED [ 89%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_health_check_has_session_id PASSED [ 91%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_launch_app PASSED [ 92%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_session_info SKIPPED (NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}) [ 93%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_status_command_return_schema PASSED [ 94%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_status_command_state_and_ready PASSED [ 96%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_unknow_app_state PASSED [ 97%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_url SKIPPED (UNKNOW HOW TO TEST: [POST] {{baseURL}}/session/{{sessionId}}/url) [ 98%] +e2e_benchmarks/test_session_commands.py::TestSessionCommands::test_wda_shutdown SKIPPED (NOT IMPLEMENTED: [GET] {{baseURL}}/wda/shutdown) [100%] + +=================================================================================== warnings summary =================================================================================== +e2e_benchmarks/test_debug_commands.py::TestDebug::test_source + /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/case.py:703: FutureWarning: The behavior of this method will change in future versions. Use specific 'len(elem)' or 'elem is not None' test instead. + if not expr: + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +=============================================================================== short test summary info ================================================================================ +SKIPPED [1] e2e_benchmarks/test_custom_commands.py:127: WDA API NOT USEFUL: {{baseURL}}/session/{{sessionId}}/wda/getPasteboard +SKIPPED [1] e2e_benchmarks/test_element_commands.py:256: NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/dragfromtoforduration +SKIPPED [1] e2e_benchmarks/test_element_commands.py:212: WDA API NOT USEFUL: Not use in SwiftUI. +SKIPPED [1] e2e_benchmarks/test_element_commands.py:299: NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/forceTouch +SKIPPED [1] e2e_benchmarks/test_element_commands.py:280: NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/pickerwheel/{{uuid}}/select +SKIPPED [1] e2e_benchmarks/test_element_commands.py:224: NOT IMPLEMENTED +SKIPPED [1] e2e_benchmarks/test_element_commands.py:234: NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/pinch +SKIPPED [1] e2e_benchmarks/test_element_commands.py:168: NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/screenshot/{{uuid}} +SKIPPED [1] e2e_benchmarks/test_find_element_commands.py:51: NOT IMPLEMENTED: [GET] /session/{{sessionId}}/element/{{uuid}}/elements +SKIPPED [1] e2e_benchmarks/test_find_element_commands.py:87: NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/element/active +SKIPPED [1] e2e_benchmarks/test_find_element_commands.py:76: NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/getVisibleCells +SKIPPED [1] e2e_benchmarks/test_orientation_commands.py:54: NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/rotation +SKIPPED [1] e2e_benchmarks/test_orientation_commands.py:63: NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/rotation +SKIPPED [1] e2e_benchmarks/test_session_commands.py:256: NOT IMPLEMENTED: [GET] {{baseURL}}/health +SKIPPED [1] e2e_benchmarks/test_session_commands.py:140: NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}} +SKIPPED [1] e2e_benchmarks/test_session_commands.py:33: UNKNOW HOW TO TEST: [POST] {{baseURL}}/session/{{sessionId}}/url +SKIPPED [1] e2e_benchmarks/test_session_commands.py:247: NOT IMPLEMENTED: [GET] {{baseURL}}/wda/shutdown +================================================================ 61 passed, 17 skipped, 1 warning in 173.98s (0:02:53) ================================================================= \ No newline at end of file diff --git a/e2e_benchmarks/requirement.txt b/e2e_benchmarks/requirement.txt new file mode 100644 index 0000000..56255c6 --- /dev/null +++ b/e2e_benchmarks/requirement.txt @@ -0,0 +1,3 @@ +pytest==7.4.4 +jsonschema==4.17.3 +lxml==5.1.0 \ No newline at end of file diff --git a/e2e_benchmarks/test_alert_commands.py b/e2e_benchmarks/test_alert_commands.py new file mode 100644 index 0000000..93e0364 --- /dev/null +++ b/e2e_benchmarks/test_alert_commands.py @@ -0,0 +1,91 @@ +''' +WDA ALERT Command Testing +source code here: https://github.com/appium/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBAlertViewCommands.m +WDA API document and example(not offical): https://documenter.getpostman.com/view/1837823/TVmMhJNB#c6d397f5-8ece-4f2c-8bfd-26e798f83cf2 +''' +import os +import pytest +import unittest +import wda +from .constant import * + +curPath = os.path.abspath(os.path.dirname(__file__)) + + +class TestAlert(unittest.TestCase): + + def setUp(self): + self.under_test_bundle_id = UNDER_TEST_BUNDLE_ID + self.wda_client: wda.Client = wda.Client() + self.app = self.wda_client.session(bundle_id=self.under_test_bundle_id) + + def tearDown(self): + self.wda_client.close() + + + ''' + Method: GET + Endpoint: {{baseURL}}/alert/text + Description: Get the content of the Alert (only the content, not including options), + an exception(wda.exceptions.WDARequestError) will be thrown if no Alert is present. + ''' + + def test_alert_text_endpoint_confirmation(self): + self.app(label='ACCEPT_OR_REJECT_ALERT').click() + self.assertEqual('Confirmation\nDo you accept?', self.wda_client.alert.text) + + def test_alert_text_endpoint_when_no_alert(self): + with pytest.raises(wda.exceptions.WDARequestError, match="status=110, value={'error': \'no such alert', "\ + "'message': 'An attempt was made to operate on a modal dialog when one was not open'}"): + self.wda_client.alert.text + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/alert/text + ''' + def test_alert_text_input(self): + with pytest.raises(wda.exceptions.WDARequestError, match="status=110, value={'error': \'no such alert', "\ + "'message': 'An attempt was made to operate on a modal dialog when one was not open'}"): + self.wda_client.alert.set_text('hello world') + + + '''1 + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/alert/buttons + Description: Get buttons for all prompt alert buttons. + ''' + def test_alert_text_button_endpoint_value(self): + self.app(label=ALERT_ENUMS.CONFIRM.value).click() + self.assertEqual(['Reject', 'Accept'], self.wda_client.alert.buttons()) + + + ''' + Method: POST + Endpoint: {{baseURL}}/alert/dismiss + Description: Dimiss the alert which display. + ''' + def test_alert_text_dimiss_endpoint(self): + self.app(label=ALERT_ENUMS.CONFIRM.value).click() + self.app(text='Reject').get(timeout=1) + self.wda_client.alert.dismiss() + try: + self.app(text='Reject').get(timeout=1) + except wda.exceptions.WDAElementNotFoundError: + return + raise AssertionError('Alert not dismissed') + + + ''' + Method: POST + Endpoint: {{baseURL}}/alert/accept + Description: Accept the alert which display. + ''' + def test_alert_text_accept_endpoint(self): + self.app(label=ALERT_ENUMS.CONFIRM.value).click() + self.app.alert.accept() + try: + self.app(text='Accept').get(timeout=1) + except wda.exceptions.WDAElementNotFoundError: + return + raise AssertionError('Alert not dismissed') diff --git a/e2e_benchmarks/test_custom_commands.py b/e2e_benchmarks/test_custom_commands.py new file mode 100644 index 0000000..98f5a68 --- /dev/null +++ b/e2e_benchmarks/test_custom_commands.py @@ -0,0 +1,176 @@ +''' +WDA Custom Command Testing +source code here: https://github.com/appium/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBCustomCommands.m#L38 +WDA API document and example(not offical): https://documenter.getpostman.com/view/1837823/TVmMhJNB#573d11c5-c434-4753-b845-4a3afcc4b614 +''' +import os +import pytest +import unittest +import jsonschema +import wda +from .constant import * + +curPath = os.path.abspath(os.path.dirname(__file__)) + + +class TestDevice(unittest.TestCase): + + def setUp(self): + self.under_test_bundle_id = UNDER_TEST_BUNDLE_ID + self.wda_client: wda.Client = wda.Client() + self.app = self.wda_client.session(bundle_id=self.under_test_bundle_id) + + def tearDown(self): + self.wda_client.close() + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/deactivateApp + Description: Put app into background and than put it back. + ''' + def test_deactivate_app(self): + self.app.deactivate(duration=1) + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/keyboard/dismiss + Description: Put the keyboard into the background. + ''' + def test_keybord_dismiss(self): + with pytest.raises(RuntimeError) as e: + self.app.keyboard_dismiss() + + + ''' + Method: POST + Endpoint: {{baseURL}}/wda/lock + Endpoint: {{baseURL}}/wda/unlock + Description: Lock the device. + ''' + def test_keybord_lock_and_unlock(self): + self.app.lock() + self.app.unlock() + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/screen + Description: UIKit scale factor + Refs: + https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html + There is another way to get scale + self._session_http.get("/wda/screen").value returns {"statusBarSize": {'width': 320, 'height': 20}, 'scale': 2} + ''' + def test_scale(self): + self.assertIsInstance(self.app.scale, int) + + + ''' + Method: GET + Endpoint: {{baseURL}}/wda/activeAppInfo + Description: Return bundleId pid and etc. like: + {'processArguments': {'env': {}, 'args': []}, 'name': '', 'pid': 19052, 'bundleId': 'com.test.cert.TestCert'} + ''' + def test_active_app_info(self): + except_json_schema = {"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties": + {"processArguments":{"type":"object","properties":{"env":{"type":"object", + "additionalProperties":False},"args":{"type":"array"}},"additionalProperties":False, + "required":["env","args"]},"name":{"type":"string"},"pid":{"type":"integer"}, + "bundleId":{"type":"string"}},"additionalProperties":False,"required": + ["processArguments","name","pid","bundleId"]} + self.assertTrue(jsonschema.Draft7Validator(except_json_schema).is_valid(self.app.app_current())) + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/setPasteboard + Description: Set paste board board. + ''' + def test_set_paste_board(self): + self.app.set_clipboard('test') + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/getPasteboard + #NOTE: IS NOT USED. RETURN NULL. + e.g: + curl http://127.0.0.1:8100/session/3D28C745-5290-4787-948C-43C7A27E6146/wda/getPasteboard -X POST -v + * Trying 127.0.0.1:8100... + * Connected to 127.0.0.1 (127.0.0.1) port 8100 (#0) + > POST /session/3D28C745-5290-4787-948C-43C7A27E6146/wda/getPasteboard HTTP/1.1 + > Host: 127.0.0.1:8100 + > User-Agent: curl/7.86.0 + > Accept: */* + > + * Mark bundle as not supporting multiuse + < HTTP/1.1 400 Bad Request + < Server: WebDriverAgent/1.0 + < Access-Control-Allow-Origin: * + < Date: Thu, 21 Mar 2024 02:18:52 GMT + < Access-Control-Allow-Headers: Content-Type, X-Requested-With + < Accept-Ranges: bytes + < Content-Length: 0 + < Connection: close + < + * Closing connection 0 + ''' + @pytest.mark.skip('WDA API NOT USEFUL: {{baseURL}}/session/{{sessionId}}/wda/getPasteboard') + def test_get_paste_board(self): + '''Wait to PR merge: https://github.com/openatx/facebook-wda/pull/133/files''' + ... + + + ''' + Method: GET + Endpoint: {{baseURL}}/wda/device/info + Description: Return device info. + Example Return: + {"timeZone": "GMT+0800", "currentLocale": "zh_CN", "model": "iPhone", "uuid": + "25E3142B-303E-41FE-9F6A-2C303CB66FBC", "thermalState": 1, "userInterfaceIdiom": 0, + "userInterfaceStyle": "light", "name": "iPhone", "isSimulator": False} + ''' + def test_device_info(self): + expect_schema = {"$schema":"http://json-schema.org/draft-07/schema#","type":"object", + "properties":{"timeZone":{"type":"string"},"currentLocale":{"type":"string"}, + "model":{"type":"string"},"uuid":{"type":"string"},"thermalState":{"type":"integer"}, + "userInterfaceIdiom":{"type":"integer"},"userInterfaceStyle":{"type":"string"}, + "name":{"type":"string"},"isSimulator":{"type":"boolean"}},"additionalProperties":False, + "required":["timeZone","currentLocale","model","uuid","thermalState","userInterfaceIdiom", + "userInterfaceStyle","name","isSimulator"]} + self.assertTrue(jsonschema.Draft7Validator(expect_schema).is_valid(self.app.device_info())) + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/batteryInfo + Description: Return device battery info. + Example Return: + {"level": 0.5799999833106995, "state": 2} + ''' + def test_battery_info(self): + import json + except_json_schema = {"$schema":"http://json-schema.org/draft-07/schema#","type":"object", + "properties":{"level":{"type":"number"},"state":{"type":"integer"}}, + "additionalProperties":False,"required":["level","state"]} + self.assertTrue(jsonschema.Draft7Validator(except_json_schema).is_valid(self.app.battery_info())) + + + ''' + Method: GET + Endpoint: {{baseURL}}/wda/homescreen + Description: back to home screen + ''' + def test_homescreen(self): + self.assertEqual(self.app.app_current().get('bundleId'), UNDER_TEST_BUNDLE_ID) + self.app.home() + self.assertEqual(self.app.app_current().get('bundleId'), HOME_SCREEN_BUNDLE_ID) + + + ''' + Method: GET + Endpoint: {{baseURL}}/wda/locked + Description: check device is locked or not. + ''' + def test_locked(self): + self.assertFalse(self.app.locked()) diff --git a/e2e_benchmarks/test_debug_commands.py b/e2e_benchmarks/test_debug_commands.py new file mode 100644 index 0000000..2941055 --- /dev/null +++ b/e2e_benchmarks/test_debug_commands.py @@ -0,0 +1,51 @@ +''' +WDA DEBUG Command Testing +source code here: https://github.com/appium/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBDebugCommands.m +WDA API document and example(not offical): https://documenter.getpostman.com/view/1837823/TVmMhJNB#a17feaf2-237e-4fe7-8e2f-75f132420d26 +''' +import os +import pytest +import unittest +import jsonschema +import wda +from lxml import etree +from .constant import * + +curPath = os.path.abspath(os.path.dirname(__file__)) + +#TODO : 更新断言在apprelease后 + +class TestDebug(unittest.TestCase): + + def setUp(self): + self.under_test_bundle_id = UNDER_TEST_BUNDLE_ID + self.wda_client: wda.Client = wda.Client() + self.app = self.wda_client.session(bundle_id=self.under_test_bundle_id) + + def tearDown(self): + self.wda_client.close() + + + ''' + Method: GET + Endpoint: {{baseURL}}/source + Description: Fetch the source tree (DOM) of the current page. + ''' + def test_source(self): + self.app(text='ListView').click() + source_tree_xml_str: str = self.app.source() + xml_bytes = source_tree_xml_str.encode('utf-8') + self.assertTrue(etree.fromstring(xml_bytes)) + + + ''' + Method: GET + Endpoint: {{baseURL}}/wda/accessibleSource + ''' + def test_accessible_source(self): + self.app(text='ListView').click() + source = self.app.source(accessible=True) + self.assertIsInstance(source, dict) + assert 'name' in source, "'name' field is missing" + assert 'type' in source, "'type' field is missing" + assert 'children' in source, "'children' field is missing" \ No newline at end of file diff --git a/e2e_benchmarks/test_element_commands.py b/e2e_benchmarks/test_element_commands.py new file mode 100644 index 0000000..01da814 --- /dev/null +++ b/e2e_benchmarks/test_element_commands.py @@ -0,0 +1,325 @@ +''' +WDA Element Command Testing +source code here: https://github.com/appium/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBElementCommands.m +WDA API document and example(not offical): https://documenter.getpostman.com/view/1837823/TVmMhJNB#f68f7fd9-3a08-4a0b-9253-f1bedf0ae926 +''' +import os +import pytest +import unittest +import wda +from .constant import * + +curPath = os.path.abspath(os.path.dirname(__file__)) + + +class TestElement(unittest.TestCase): + + def setUp(self): + self.under_test_bundle_id = UNDER_TEST_BUNDLE_ID + self.wda_client: wda.Client = wda.Client() + self.app = self.wda_client.session(bundle_id=self.under_test_bundle_id) + self.temp_file_pic = os.path.join(curPath, 'temp_file_pic.png') + + def tearDown(self): + self.wda_client.close() + [os.remove(temp_file) for temp_file in [self.temp_file_pic] if os.path.exists(temp_file)] + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/window/size + Description: Fetch device window size. + ''' + def test_window_size(self): + assert hasattr(self.app.window_size(), 'width') and type(self.app.window_size().width) == int + assert hasattr(self.app.window_size(), 'height') and type(self.app.window_size().height) == int + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/element/{{uuid}}/enabled + Description: Check element is enbaled or not. + ''' + def test_btn_enable(self): + assert self.app(text='ENABLED_BTN').enabled == True + + def test_btn_enable(self): + assert self.app(text='DISABLED_BTN').enabled == False + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/element/{{uuid}}/rect + ''' + def test_rect(self): + rect = self.app(text='ENABLED_BTN').bounds + assert all([ + hasattr(rect, 'x') and type(rect.x) == int, + hasattr(rect, 'y') and type(rect.y) == int, + hasattr(rect, 'width') and type(rect.width) == int, + hasattr(rect, 'height') and type(rect.height) == int, + ]) + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/element/{{uuid}}/attribute/{{attribute_name}} + ''' + def test_attribute_label(self): + self.assertEqual(self.app(text='ENABLED_BTN').label, 'ENABLED_BTN') + + def test_attribute_invild(self): + with pytest.raises(AttributeError, match="'Element' object has no attribute 'attribute'"): + self.app(text='ENABLED_BTN').attribute('invalid_attribute_name') + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/element/{{uuid}}/text + ''' + def test_attribute_btn_text(self): + self.assertEqual(self.app(text='ENABLED_BTN').text, 'ENABLED_BTN') + + def test_attribute_image_text(self): + self.assertEqual(self.app(id='IMG_BTN').text, 'applogo') + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/element/{{uuid}}/displayed + ''' + def test_attribute_btn_displayed(self): + self.assertEqual(self.app(text='ENABLED_BTN').displayed, True) + + def test_attribute_btn_not_displayed(self): + self.assertEqual(self.app(text='HIDDEN_BTN').displayed, False) + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/element/{{uuid}}/selected + ''' + def test_btn_selected(self): + self.assertEqual(self.app(text='CHECKED_BTN').selected(), True) + + def test_attribute_btn_not_displayed(self): + self.assertEqual(self.app(text='UNCHECKED_BTN').selected(), False) + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/element/{{uuid}}/name + NOTE: Return element type. + ''' + def test_btn_name(self): + self.assertEqual(self.app(text='CHECKED_BTN').name, 'XCUIElementTypeButton') + + def test_img_name(self): + self.assertEqual(self.app(text='IMG_BTN').name, 'XCUIElementTypeImage') + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/element/{{uuid}}/value + ''' + def test_input_text(self): + self.app(id='INPUT_FIELD').set_text('test') + self.assertEqual(self.app(id='INPUT_FIELD').value, 'test') + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/element/{{uuid}}/click + ''' + def test_input_click(self): + input_text_val = 'test' + self.app(id='INPUT_FIELD').set_text(input_text_val) + assert self.app(id='INPUT_FIELD').value == input_text_val + self.app(id='CLEAR_INPUT_BTN').click() + assert self.app(id='INPUT_FIELD').value != input_text_val + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/element/{{uuid}}/clear + ''' + def test_input_clear(self): + input_text_val = 'test' + self.app(id='INPUT_FIELD').set_text(input_text_val) + assert self.app(id='INPUT_FIELD').value == input_text_val + self.app(id='INPUT_FIELD').clear_text() + assert self.app(id='INPUT_FIELD').value != input_text_val + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/element/{{uuid}}/screenshot + ''' + def test_screenshot(self): + self.app.screenshot(png_filename=self.temp_file_pic) + assert os.path.exists(self.temp_file_pic) + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/screenshot/{{uuid}} + Description: screenshot for target element. + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/screenshot/{{uuid}}') + def test_screenshot_element(self): + pass + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/accessible + ''' + def test_ele_accessible_is_false(self): + self.assertFalse(self.app(text='HIDDEN_BTN').accessible) + + def test_ele_accessible_is_true(self): + self.assertTrue(self.app(text='ENABLED_BTN').accessible) + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/accessibilityContainer + NOTE Swift accessibility Container is not useful, always return false. + + Example: + ``` + import SwiftUI + + struct ContentView: View { + var body: some View { + HStack { + Text("First Item") + Divider() + Text("Second Item") + } + .accessibilityElement(children: .combine) + .accessibility(label: Text("Combined Items")) + } + } + + struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } + } + ``` + ''' + @pytest.mark.skip('WDA API NOT USEFUL: Not use in SwiftUI.') + def test_ele_accessibilityContainer_is_true(self): + self.assertFalse(self.app(id='Combined Items').accessibility_container) + + def test_ele_accessibilityContainer_is_false(self): + self.assertFalse(self.app(id='ENABLED_BTN').accessibility_container) + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/swipe + ''' + @pytest.mark.skip('NOT IMPLEMENTED') + def test_ele_swipe_top(self): + self.app(text="Go to List").click() + self.app(text="Row1").click() + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/pinch + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/pinch') + def test_pinch(self): + ... + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/touchAndHold + ''' + def test_touch_and_hold(self): + try: + self.app(text='OK').get(timeout=1) + except wda.exceptions.WDAElementNotFoundError: + pass + self.app(text='LONG_TAP_ALERT').tap_hold(duration=2) + self.assertTrue(self.app(text='LONG_TAP_ALERT_OK').get(timeout=1).displayed) + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/dragfromtoforduration + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/dragfromtoforduration') + def test_drag_from_to_for_duration(self): + pass + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/dragfromtoforduration + ''' + def test_app_drag_from_to_for_duration(self): + self.app(text='ListView').click() + self.assertTrue(self.app(text='Row1').get(timeout=1).displayed) + try: + self.app(text='Row30').get(timeout=1) + except wda.exceptions.WDAElementNotFoundError: + pass + self.app.swipe(500, 800, 500, 200, duration=0.5) + self.assertTrue(self.app(text='Row30').get(timeout=1).displayed) + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/pickerwheel/{{uuid}}/select + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/pickerwheel/{{uuid}}/select') + def test_ele_select(self): + pass + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/keys + ''' + def test_wda_keys(self): + self.app(text='INPUT_FIELD').click() + self.app.send_keys('hello world') + self.assertEqual(self.app(text='INPUT_FIELD').value, 'hello world') + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/forceTouch + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/forceTouch') + def test_ele_force_touch(self): + pass + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/doubleTap + ''' + def test_ele_touble_tap(self): + bounds = self.app(text='DOUBLE_TAP_ALERT').bounds + x = int(bounds.x + bounds.width * 0.5) + y = int(bounds.y + bounds.height * 0.5) + self.app.double_tap(x, y) + self.assertTrue(self.app(text='DOUBLE_TAP_ALERT_OK').get(timeout=1).displayed) + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/touchAndHold + ''' + def test_wda_touch_and_hold(self): + bounds = self.app(text='LONG_TAP_ALERT').bounds + x = int(bounds.x + bounds.width * 0.5) + y = int(bounds.y + bounds.height * 0.5) + self.app.tap_hold(x, y, duration=2) + self.assertTrue(self.app(text='LONG_TAP_ALERT_OK').get(timeout=1).displayed) diff --git a/e2e_benchmarks/test_find_element_commands.py b/e2e_benchmarks/test_find_element_commands.py new file mode 100644 index 0000000..009577e --- /dev/null +++ b/e2e_benchmarks/test_find_element_commands.py @@ -0,0 +1,89 @@ +''' +WDA Find Element Command Testing +source code here: https://github.com/appium/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBElementCommands.m +WDA API document and example(not offical): https://documenter.getpostman.com/view/1837823/TVmMhJNB#56e19c88-8571-48d3-a9f1-8e9bd0cbae0d +''' +import os +import pytest +import unittest +from typing import List +from collections.abc import Iterable +import wda +from .constant import * + +curPath = os.path.abspath(os.path.dirname(__file__)) + + +class TestFindElement(unittest.TestCase): + + def setUp(self): + self.under_test_bundle_id = UNDER_TEST_BUNDLE_ID + self.wda_client: wda.Client = wda.Client() + self.app = self.wda_client.session(bundle_id=self.under_test_bundle_id) + self.temp_file_pic = os.path.join(curPath, 'temp_file_pic.png') + + def tearDown(self): + self.wda_client.close() + [os.remove(temp_file) for temp_file in [self.temp_file_pic] if os.path.exists(temp_file)] + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/elements + ''' + def test_elements(self): + elements = self.app(text='IMG_BTN').find_elements() + self.assertTrue(isinstance(elements, Iterable)) # 检查是否可迭代 + for element in elements: + self.assertIsInstance(element, wda.Element) + + def test_element_ids(self): + elements = self.app(text='IMG_BTN').find_element_ids() + self.assertTrue(isinstance(elements, Iterable)) # 检查是否可迭代 + for element in elements: + self.assertIsInstance(element, str) + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/element/{{uuid}}/elements + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [GET] /session/{{sessionId}}/element/{{uuid}}/elements') + def test_ele_elements(self): + pass + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/element + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/element') + def test_wda_element(self): + pass + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/getVisibleCells + + Return Example: + ``` + {'value': [{'ELEMENT': '31000000-0000-0000-996B-000000000000', + 'element-6066-11e4-a52e-4f735466cecf': '31000000-0000-0000-996B-000000000000'}], + 'sessionId': 'BC8DE836-6F48-4F7B-B540-FCEC97C06068', 'status': 0} + ``` + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/wda/element/{{uuid}}/getVisibleCells') + def test_wda_element(self): + self.app(text='ListView').click() + ele: wda.Element = self.app(label='LIST_CONTAINER').get(timeout=1) + visible_cells_response = ele.http.get(f'/wda/element/{ele.id}/getVisibleCells') + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/element/active + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/element/active') + def test_wda_active(self): + pass diff --git a/e2e_benchmarks/test_orientation_commands.py b/e2e_benchmarks/test_orientation_commands.py new file mode 100644 index 0000000..879a293 --- /dev/null +++ b/e2e_benchmarks/test_orientation_commands.py @@ -0,0 +1,66 @@ +''' +WDA Orientation Command Testing +source code here: https://github.com/appium/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBOrientationCommands.m +WDA API document and example(not offical): https://documenter.getpostman.com/view/1837823/TVmMhJNB#1ca27827-9931-4315-a6aa-141b6015ac04 +''' +import os +import time +import pytest +import unittest +from typing import List +from collections.abc import Iterable +import wda +from .constant import * + +curPath = os.path.abspath(os.path.dirname(__file__)) + + +class TestOrientation(unittest.TestCase): + + def setUp(self): + self.under_test_bundle_id = UNDER_TEST_BUNDLE_ID + self.wda_client: wda.Client = wda.Client() + self.app = self.wda_client.session(bundle_id=self.under_test_bundle_id) + self.temp_file_pic = os.path.join(curPath, 'temp_file_pic.png') + + def tearDown(self): + self.app.orientation = wda.PORTRAIT + self.wda_client.close() + [os.remove(temp_file) for temp_file in [self.temp_file_pic] if os.path.exists(temp_file)] + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/orientation + ''' + def test_orientation(self): + assert self.app.orientation in ['PORTRAIT', 'LANDSCAPE'] + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/orientation + ''' + def test_set_orientation(self): + self.app.orientation = wda.LANDSCAPE + time.sleep(1) + assert self.app.orientation == 'LANDSCAPE' + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/rotation + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}/rotation') + def test_get_rotation(self): + pass + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/rotation + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [POST] {{baseURL}}/session/{{sessionId}}/rotation') + def test_set_rotation(self): + pass + diff --git a/e2e_benchmarks/test_screenshot_commands.py b/e2e_benchmarks/test_screenshot_commands.py new file mode 100644 index 0000000..9befb4c --- /dev/null +++ b/e2e_benchmarks/test_screenshot_commands.py @@ -0,0 +1,35 @@ +''' +WDA Screenshot Command Testing +source code here: https://github.com/appium/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBScreenshotCommands.m +WDA API document and example(not offical): https://documenter.getpostman.com/view/1837823/TVmMhJNB#bf7cb0a1-cc3b-4eb5-a9ee-1e7e63b55df1 +''' +import os +import unittest +from typing import List +import wda +from .constant import * + +curPath = os.path.abspath(os.path.dirname(__file__)) + + +class TestScreenshot(unittest.TestCase): + + def setUp(self): + self.under_test_bundle_id = UNDER_TEST_BUNDLE_ID + self.wda_client: wda.Client = wda.Client() + self.app = self.wda_client.session(bundle_id=self.under_test_bundle_id) + self.temp_file_pic = os.path.join(curPath, 'temp_file_pic.png') + + def tearDown(self): + self.app.orientation = wda.PORTRAIT + self.wda_client.close() + [os.remove(temp_file) for temp_file in [self.temp_file_pic] if os.path.exists(temp_file)] + + + ''' + Method: POST + Endpoint: {{baseURL}}/screenshot + ''' + def test_screenshot(self): + self.app.screenshot(self.temp_file_pic) + assert os.path.exists(self.temp_file_pic) diff --git a/e2e_benchmarks/test_session_commands.py b/e2e_benchmarks/test_session_commands.py new file mode 100644 index 0000000..32d1f45 --- /dev/null +++ b/e2e_benchmarks/test_session_commands.py @@ -0,0 +1,258 @@ +''' +WDA Session Command Testing +API FBSessionCommands.m +source code here: https://github.com/appium/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBSessionCommands.m +WDA API document and example(not offical): https://documenter.getpostman.com/view/1837823/TVmMhJNB#f94ebe16-ae2a-4098-879e-570a0138c7f4 +''' +import os +import pytest +import unittest +import jsonschema +import wda +from .constant import * + +curPath = os.path.abspath(os.path.dirname(__file__)) + + +class TestSessionCommands(unittest.TestCase): + + def setUp(self): + self.wda_client: wda.Client = wda.Client() + # self.app = self.wda_client.session(bundle_id=self.under_test_bundle_id) + self.temp_file_pic = os.path.join(curPath, 'temp_file_pic.png') + + def tearDown(self): + self.wda_client.close() + [os.remove(temp_file) for temp_file in [self.temp_file_pic] if os.path.exists(temp_file)] + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/url + ''' + @pytest.mark.skip("UNKNOW HOW TO TEST: [POST] {{baseURL}}/session/{{sessionId}}/url") + def test_url(self): + pass + + + ''' + Method: POST + Endpoint: {{baseURL}}/session + ''' + def test_create_session_id(self): + before_create_session_id = self.wda_client.session_id + self.assertIsNot(self.wda_client.session().session_id, before_create_session_id, None) + client: wda.Client = wda.Client().session(bundle_id=UNDER_TEST_BUNDLE_ID) + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/apps/launch + ''' + def test_launch_app(self): + self.wda_client.app_activate(UNDER_TEST_BUNDLE_ID) + self.wda_client.app_current().get('bundle_id') == UNDER_TEST_BUNDLE_ID + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/apps/list + ''' + def test_app_list(self): + self.wda_client.session(UNDER_TEST_BUNDLE_ID) + self.assertIsInstance(self.wda_client.app_list(), list) + self.assertEqual(self.wda_client.app_list()[0].get('bundleId'), UNDER_TEST_BUNDLE_ID) + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/apps/state + ''' + def test_app_state(self): + self.wda_client.session(UNDER_TEST_BUNDLE_ID) + self.assertIsInstance(self.wda_client.app_state(UNDER_TEST_BUNDLE_ID), dict) + self.assertEqual(self.wda_client.app_state(UNDER_TEST_BUNDLE_ID).get('value'), 4) + + def test_unknow_app_state(self): + self.assertEqual(self.wda_client.app_state(UNKNOW_BUNDLE_ID).get('value'), 1) + + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/apps/terminate + ''' + def test_app_terminate(self): + self.wda_client.app_terminate(UNDER_TEST_BUNDLE_ID) + self.assertTrue(self.wda_client.app_state(UNDER_TEST_BUNDLE_ID).get('value') == 1) + + + ''' + Method: GET + Endpoint: {{baseURL}}/status + ''' + def test_status_command_return_schema(self): + ''' + Current Origin Status Return: + { + "build": { + "time": "Mar 18 2024 11:29:21", + "productBundleIdentifier": "com.facebook.WebDriverAgentRunner" + }, + "os": { + "testmanagerdVersion": 28, + "name": "iOS", + "sdkVersion": "16.4", + "version": "16.3.1" + }, + "device": "iphone", + "ios": { + "ip": "XX.XX.XX.XX" + }, + "message": "WebDriverAgent is ready to accept commands", + "state": "success", + "ready": true, + "sessionId": "XXXXX" + } + ''' + self.wda_client.session(UNDER_TEST_BUNDLE_ID) + expect_schema = {"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"build":\ + {"type":"object","properties":{"time":{"type":"string"},"productBundleIdentifier":\ + {"type":"string"}},"additionalProperties":"false","required":["time",\ + "productBundleIdentifier"]},"os":{"type":"object","properties":{"testmanagerdVersion"\ + :{"type":"integer"},"name":{"type":"string"},"sdkVersion":{"type":"string"},"version":\ + {"type":"string"}},"additionalProperties":"false","required":["testmanagerdVersion","name",\ + "sdkVersion","version"]},"device":{"type":"string"},"ios":{"type":"object","properties":\ + {"ip":{"type":"string"}},"additionalProperties":"false","required":["ip"]},"message":\ + {"type":"string"},"state":{"type":"string"},"ready":{"type":"boolean"},"sessionId":\ + {"type":"string"}},"additionalProperties":"false","required":["build","os","device","ios",\ + "message","state","ready", "sessionId"]} + + self.assertTrue(jsonschema.Draft7Validator(expect_schema).is_valid(self.wda_client.status())) + + def test_status_command_state_and_ready(self): + status = self.wda_client.status() + assert all([status['state'] == 'success', status['ready'] is True]) + + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}} + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [GET] {{baseURL}}/session/{{sessionId}}') + def test_session_info(self): + pass + + ''' + Method: DELETE + Endpoint: {{baseURL}}/session/{{sessionId}} + ''' + def test_delete_session(self): + self.wda_client.session(UNDER_TEST_BUNDLE_ID) + assert self.wda_client.session_id is not None + print(self.wda_client.close()) + + ''' + Method: POST + Endpoint: {{baseURL}}/session/{{sessionId}}/wda/apps/activate + ''' + def test_app_activate(self): + self.wda_client.session() + self.wda_client.app_activate(UNDER_TEST_BUNDLE_ID) + self.assertEqual(self.wda_client.app_current().get('bundleId'), UNDER_TEST_BUNDLE_ID) + + + ''' + Method: GET + Endpoint: {{baseURL}}/wda/healthcheck + ''' + def test_health_check(self): + health_check = self.wda_client.healthcheck() + assert all([ + hasattr(health_check, 'value'), + hasattr(health_check, 'sessionId'), + health_check.get('value') == None, + health_check.get('sessionId') == None + ]) + + def test_health_check_has_session_id(self): + self.wda_client.session(UNDER_TEST_BUNDLE_ID) + health_check = self.wda_client.healthcheck() + assert all([ + hasattr(health_check, 'value'), + hasattr(health_check, 'sessionId'), + health_check.get('value') == None, + health_check.get('sessionId') == self.wda_client.session_id + ]) + + ''' + Method: GET + Endpoint: {{baseURL}}/session/{{sessionId}}/appium/settings + Return Example: + + ``` + { + "mjpegFixOrientation": false, + "boundElementsByIndex": false, + "mjpegServerFramerate": 10, + "screenshotOrientation": "auto", + "reduceMotion": false, + "elementResponseAttributes": "type,label", + "screenshotQuality": 3, + "mjpegScalingFactor": 100, + "keyboardPrediction": 0, + "defaultActiveApplication": "auto", + "mjpegServerScreenshotQuality": 25, + "defaultAlertAction": "", + "keyboardAutocorrection": 0, + "useFirstMatch": false, + "shouldUseCompactResponses": true, + "customSnapshotTimeout": 15, + "dismissAlertButtonSelector": "", + "activeAppDetectionPoint": "64.00,64.00", + "snapshotMaxDepth": 50, + "waitForIdleTimeout": 10, + "includeNonModalElements": false, + "acceptAlertButtonSelector": "", + "animationCoolOffTimeout": 2 + } + ``` + ''' + def test_appium_settings(self): + appium_settings = self.wda_client.appium_settings() + expect_schema = {"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties": + {"mjpegFixOrientation":{"type":"boolean"},"boundElementsByIndex":{"type":"boolean"}, + "mjpegServerFramerate":{"type":"integer"},"screenshotOrientation":{"type":"string"}, + "reduceMotion":{"type":"boolean"},"elementResponseAttributes":{"type":"string"}, + "screenshotQuality":{"type":"integer"},"mjpegScalingFactor":{"type":"integer"}, + "keyboardPrediction":{"type":"integer"},"defaultActiveApplication":{"type":"string"}, + "mjpegServerScreenshotQuality":{"type":"integer"},"defaultAlertAction":{"type":"string"}, + "keyboardAutocorrection":{"type":"integer"},"useFirstMatch":{"type":"boolean"}, + "shouldUseCompactResponses":{"type":"boolean"},"customSnapshotTimeout":{"type":"integer"}, + "dismissAlertButtonSelector":{"type":"string"},"activeAppDetectionPoint":{"type":"string"}, + "snapshotMaxDepth":{"type":"integer"},"waitForIdleTimeout":{"type":"integer"}, + "includeNonModalElements":{"type":"boolean"},"acceptAlertButtonSelector":{"type":"string"}, + "animationCoolOffTimeout":{"type":"integer"}},"additionalProperties":False, + "required":["mjpegFixOrientation","boundElementsByIndex","mjpegServerFramerate", + "screenshotOrientation","reduceMotion","elementResponseAttributes","screenshotQuality", + "mjpegScalingFactor","keyboardPrediction","defaultActiveApplication","mjpegServerScreenshotQuality", + "defaultAlertAction","keyboardAutocorrection","useFirstMatch","shouldUseCompactResponses", + "customSnapshotTimeout","dismissAlertButtonSelector","activeAppDetectionPoint","snapshotMaxDepth", + "waitForIdleTimeout","includeNonModalElements","acceptAlertButtonSelector","animationCoolOffTimeout"]} + self.assertTrue(jsonschema.Draft7Validator(expect_schema).is_valid(appium_settings)) + + + ''' + Method: GET + Endpoint: {{baseURL}}/wda/shutdown + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [GET] {{baseURL}}/wda/shutdown') + def test_wda_shutdown(self): + pass + + + ''' + Method: GET + Endpoint: {{baseURL}}/health + ''' + @pytest.mark.skip('NOT IMPLEMENTED: [GET] {{baseURL}}/health') + def test_health(self): + pass diff --git a/wda/__init__.py b/wda/__init__.py index 0c4a1bb..14604a0 100644 --- a/wda/__init__.py +++ b/wda/__init__.py @@ -184,7 +184,6 @@ def _unsafe_httpdo(url, method='GET', data=None, timeout=None): for errCls in (WDAInvalidSessionIdError, WDAPossiblyCrashedError, WDAKeyboardNotPresentError, WDAUnknownError, WDAStaleElementReferenceError): if errCls.check(value): raise errCls(status, value) - raise WDARequestError(status, value) return r except JSONDecodeError: @@ -645,7 +644,6 @@ def session(self, # when device is Locked, it is unable to start app if self.locked(): self.unlock() - try: res = self.http.post('session', payload) except WDAEmptyResponseError: @@ -660,7 +658,13 @@ def session(self, client.__callbacks = self.__callbacks return client - def close(self): # close session + + ''' + TODO: Should the ctx of the client be written back after this code is executed,\ + as the session ID is already empty when delete session api trigger. + ''' + def close(self): + '''Close created session which session id saved in class ctx.''' try: return self._session_http.delete('/') except WDARequestError as e: @@ -895,7 +899,13 @@ def tap(self, x, y): "TMQ_ORIGIN") == "civita": # in TMQ and belong to MDS return self._session_http.post("/mds/touchAndHold", dict(x=x, y=y, duration=0.02)) - return self._session_http.post('/wda/tap/0', dict(x=x, y=y)) + + # Support WDA `BREAKING CHANGES` + # More see: https://github.com/appium/WebDriverAgent/blob/master/CHANGELOG.md#600-2024-01-31 + try: + return self._session_http.post('/wda/tap', dict(x=x, y=y)) + except: + return self._session_http.post('/wda/tap/0', dict(x=x, y=y)) def _percent2pos(self, x, y, window_size=None): if any(isinstance(v, float) for v in [x, y]): @@ -1167,6 +1177,16 @@ def exists(self): @property def text(self): return self.http.get('/alert/text').value + + def set_text(self, text: str): + '''Set text to alert. + Except return example: + ``` + wda.exceptions.WDARequestError: WDARequestError(status=110, + value={'error': 'no such alert', 'message': 'An attempt was + made to operate on a modal dialog when one was not open'})``` + ''' + return self.http.post('/alert/text', data={'value': text}) def wait(self, timeout=20.0): start_time = time.time() @@ -1790,6 +1810,12 @@ def clear_text(self): # todo lot of other operations # tap_hold + def selected(self): + ''' Element has been selected. + Returns: bool + ''' + return self._req('get', '/selected').value + class USBClient(Client): """ connect device through unix:/var/run/usbmuxd """