diff --git a/.gitignore b/.gitignore index 312d1f6..42a0098 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,6 @@ fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output + +# fastlane snapshot +screenshots/ diff --git a/TallestTowers.xcodeproj/project.pbxproj b/TallestTowers.xcodeproj/project.pbxproj index aa1e7df..7e3f3f4 100644 --- a/TallestTowers.xcodeproj/project.pbxproj +++ b/TallestTowers.xcodeproj/project.pbxproj @@ -18,6 +18,8 @@ 5519C171234E33440022E332 /* TowerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5519C170234E33440022E332 /* TowerTests.swift */; }; 5519C17C234E33440022E332 /* TallestTowersUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5519C17B234E33440022E332 /* TallestTowersUITests.swift */; }; 5519C18A234E35880022E332 /* Tower.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5519C189234E35880022E332 /* Tower.swift */; }; + 5535C79A2382FF7B00E65B03 /* TallestTowersScreenshots.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5535C7992382FF7B00E65B03 /* TallestTowersScreenshots.swift */; }; + 5535C7A22382FFD400E65B03 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5535C7A12382FFD400E65B03 /* SnapshotHelper.swift */; }; 55CFADD6235F44390067DBB7 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 55CFADD5235F44380067DBB7 /* README.md */; }; 55CFADD8235F860E0067DBB7 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 55CFADD7235F860E0067DBB7 /* LICENSE */; }; /* End PBXBuildFile section */ @@ -37,6 +39,13 @@ remoteGlobalIDString = 5519C155234E33420022E332; remoteInfo = TallestTowers; }; + 5535C79C2382FF7B00E65B03 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5519C14E234E33420022E332 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5519C155234E33420022E332; + remoteInfo = TallestTowers; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -60,6 +69,11 @@ 552B707D236B5D9300ACC210 /* Pluginfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Pluginfile; sourceTree = ""; }; 552B707E236B5D9300ACC210 /* Appfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Appfile; sourceTree = ""; }; 552B707F236B5D9300ACC210 /* Fastfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Fastfile; sourceTree = ""; }; + 5535C7972382FF7B00E65B03 /* TallestTowersScreenshots.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TallestTowersScreenshots.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 5535C7992382FF7B00E65B03 /* TallestTowersScreenshots.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TallestTowersScreenshots.swift; sourceTree = ""; }; + 5535C79B2382FF7B00E65B03 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5535C7A12382FFD400E65B03 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = ""; }; + 5535C7A32383038C00E65B03 /* Snapfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Snapfile; sourceTree = ""; }; 55AE65672360A0D60064B1AB /* semaphore.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = semaphore.yml; sourceTree = ""; }; 55CFADD5235F44380067DBB7 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 55CFADD7235F860E0067DBB7 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; @@ -87,6 +101,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 5535C7942382FF7B00E65B03 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -128,6 +149,7 @@ 5519C158234E33420022E332 /* TallestTowers */, 5519C16F234E33440022E332 /* TallestTowersTests */, 5519C17A234E33440022E332 /* TallestTowersUITests */, + 5535C7982382FF7B00E65B03 /* TallestTowersScreenshots */, 5519C157234E33420022E332 /* Products */, ); sourceTree = ""; @@ -138,6 +160,7 @@ 5519C156234E33420022E332 /* TallestTowers.app */, 5519C16C234E33440022E332 /* TallestTowersTests.xctest */, 5519C177234E33440022E332 /* TallestTowersUITests.xctest */, + 5535C7972382FF7B00E65B03 /* TallestTowersScreenshots.xctest */, ); name = Products; sourceTree = ""; @@ -178,11 +201,22 @@ children = ( 552B707E236B5D9300ACC210 /* Appfile */, 552B707F236B5D9300ACC210 /* Fastfile */, + 5535C7A32383038C00E65B03 /* Snapfile */, 552B707D236B5D9300ACC210 /* Pluginfile */, ); path = fastlane; sourceTree = ""; }; + 5535C7982382FF7B00E65B03 /* TallestTowersScreenshots */ = { + isa = PBXGroup; + children = ( + 5535C7A12382FFD400E65B03 /* SnapshotHelper.swift */, + 5535C7992382FF7B00E65B03 /* TallestTowersScreenshots.swift */, + 5535C79B2382FF7B00E65B03 /* Info.plist */, + ); + path = TallestTowersScreenshots; + sourceTree = ""; + }; 55AE65662360A0D60064B1AB /* .semaphore */ = { isa = PBXGroup; children = ( @@ -247,13 +281,31 @@ productReference = 5519C177234E33440022E332 /* TallestTowersUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; + 5535C7962382FF7B00E65B03 /* TallestTowersScreenshots */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5535C7A02382FF7B00E65B03 /* Build configuration list for PBXNativeTarget "TallestTowersScreenshots" */; + buildPhases = ( + 5535C7932382FF7B00E65B03 /* Sources */, + 5535C7942382FF7B00E65B03 /* Frameworks */, + 5535C7952382FF7B00E65B03 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 5535C79D2382FF7B00E65B03 /* PBXTargetDependency */, + ); + name = TallestTowersScreenshots; + productName = TallestTowersScreenshots; + productReference = 5535C7972382FF7B00E65B03 /* TallestTowersScreenshots.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 5519C14E234E33420022E332 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1110; + LastSwiftUpdateCheck = 1120; LastUpgradeCheck = 1110; ORGANIZATIONNAME = "Semaphore CI"; TargetAttributes = { @@ -268,6 +320,10 @@ CreatedOnToolsVersion = 11.1; TestTargetID = 5519C155234E33420022E332; }; + 5535C7962382FF7B00E65B03 = { + CreatedOnToolsVersion = 11.2.1; + TestTargetID = 5519C155234E33420022E332; + }; }; }; buildConfigurationList = 5519C151234E33420022E332 /* Build configuration list for PBXProject "TallestTowers" */; @@ -286,6 +342,7 @@ 5519C155234E33420022E332 /* TallestTowers */, 5519C16B234E33440022E332 /* TallestTowersTests */, 5519C176234E33440022E332 /* TallestTowersUITests */, + 5535C7962382FF7B00E65B03 /* TallestTowersScreenshots */, ); }; /* End PBXProject section */ @@ -316,6 +373,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 5535C7952382FF7B00E65B03 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -349,6 +413,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 5535C7932382FF7B00E65B03 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5535C79A2382FF7B00E65B03 /* TallestTowersScreenshots.swift in Sources */, + 5535C7A22382FFD400E65B03 /* SnapshotHelper.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -362,6 +435,11 @@ target = 5519C155234E33420022E332 /* TallestTowers */; targetProxy = 5519C178234E33440022E332 /* PBXContainerItemProxy */; }; + 5535C79D2382FF7B00E65B03 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5519C155234E33420022E332 /* TallestTowers */; + targetProxy = 5535C79C2382FF7B00E65B03 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -608,6 +686,44 @@ }; name = Release; }; + 5535C79E2382FF7B00E65B03 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = TallestTowersScreenshots/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.semaphoreci.TallestTowersScreenshots; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = TallestTowers; + }; + name = Debug; + }; + 5535C79F2382FF7B00E65B03 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = TallestTowersScreenshots/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.semaphoreci.TallestTowersScreenshots; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = TallestTowers; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -647,6 +763,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 5535C7A02382FF7B00E65B03 /* Build configuration list for PBXNativeTarget "TallestTowersScreenshots" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5535C79E2382FF7B00E65B03 /* Debug */, + 5535C79F2382FF7B00E65B03 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 5519C14E234E33420022E332 /* Project object */; diff --git a/TallestTowers.xcodeproj/xcshareddata/xcschemes/TakeScreenshots.xcscheme b/TallestTowers.xcodeproj/xcshareddata/xcschemes/TakeScreenshots.xcscheme new file mode 100644 index 0000000..813c2fb --- /dev/null +++ b/TallestTowers.xcodeproj/xcshareddata/xcschemes/TakeScreenshots.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TallestTowersScreenshots/Info.plist b/TallestTowersScreenshots/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/TallestTowersScreenshots/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/TallestTowersScreenshots/SnapshotHelper.swift b/TallestTowersScreenshots/SnapshotHelper.swift new file mode 100644 index 0000000..aaa2a9a --- /dev/null +++ b/TallestTowersScreenshots/SnapshotHelper.swift @@ -0,0 +1,303 @@ +// +// SnapshotHelper.swift +// Example +// +// Created by Felix Krause on 10/8/15. +// + +// ----------------------------------------------------- +// IMPORTANT: When modifying this file, make sure to +// increment the version number at the very +// bottom of the file to notify users about +// the new SnapshotHelper.swift +// ----------------------------------------------------- + +import Foundation +import XCTest + +var deviceLanguage = "" +var locale = "" + +func setupSnapshot(_ app: XCUIApplication, waitForAnimations: Bool = true) { + Snapshot.setupSnapshot(app, waitForAnimations: waitForAnimations) +} + +func snapshot(_ name: String, waitForLoadingIndicator: Bool) { + if waitForLoadingIndicator { + Snapshot.snapshot(name) + } else { + Snapshot.snapshot(name, timeWaitingForIdle: 0) + } +} + +/// - Parameters: +/// - name: The name of the snapshot +/// - timeout: Amount of seconds to wait until the network loading indicator disappears. Pass `0` if you don't want to wait. +func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) { + Snapshot.snapshot(name, timeWaitingForIdle: timeout) +} + +enum SnapshotError: Error, CustomDebugStringConvertible { + case cannotDetectUser + case cannotFindHomeDirectory + case cannotFindSimulatorHomeDirectory + case cannotAccessSimulatorHomeDirectory(String) + case cannotRunOnPhysicalDevice + + var debugDescription: String { + switch self { + case .cannotDetectUser: + return "Couldn't find Snapshot configuration files - can't detect current user " + case .cannotFindHomeDirectory: + return "Couldn't find Snapshot configuration files - can't detect `Users` dir" + case .cannotFindSimulatorHomeDirectory: + return "Couldn't find simulator home location. Please, check SIMULATOR_HOST_HOME env variable." + case .cannotAccessSimulatorHomeDirectory(let simulatorHostHome): + return "Can't prepare environment. Simulator home location is inaccessible. Does \(simulatorHostHome) exist?" + case .cannotRunOnPhysicalDevice: + return "Can't use Snapshot on a physical device." + } + } +} + +@objcMembers +open class Snapshot: NSObject { + static var app: XCUIApplication? + static var waitForAnimations = true + static var cacheDirectory: URL? + static var screenshotsDirectory: URL? { + return cacheDirectory?.appendingPathComponent("screenshots", isDirectory: true) + } + + open class func setupSnapshot(_ app: XCUIApplication, waitForAnimations: Bool = true) { + + Snapshot.app = app + Snapshot.waitForAnimations = waitForAnimations + + do { + let cacheDir = try pathPrefix() + Snapshot.cacheDirectory = cacheDir + setLanguage(app) + setLocale(app) + setLaunchArguments(app) + } catch let error { + NSLog(error.localizedDescription) + } + } + + class func setLanguage(_ app: XCUIApplication) { + guard let cacheDirectory = self.cacheDirectory else { + NSLog("CacheDirectory is not set - probably running on a physical device?") + return + } + + let path = cacheDirectory.appendingPathComponent("language.txt") + + do { + let trimCharacterSet = CharacterSet.whitespacesAndNewlines + deviceLanguage = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet) + app.launchArguments += ["-AppleLanguages", "(\(deviceLanguage))"] + } catch { + NSLog("Couldn't detect/set language...") + } + } + + class func setLocale(_ app: XCUIApplication) { + guard let cacheDirectory = self.cacheDirectory else { + NSLog("CacheDirectory is not set - probably running on a physical device?") + return + } + + let path = cacheDirectory.appendingPathComponent("locale.txt") + + do { + let trimCharacterSet = CharacterSet.whitespacesAndNewlines + locale = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet) + } catch { + NSLog("Couldn't detect/set locale...") + } + + if locale.isEmpty && !deviceLanguage.isEmpty { + locale = Locale(identifier: deviceLanguage).identifier + } + + if !locale.isEmpty { + app.launchArguments += ["-AppleLocale", "\"\(locale)\""] + } + } + + class func setLaunchArguments(_ app: XCUIApplication) { + guard let cacheDirectory = self.cacheDirectory else { + NSLog("CacheDirectory is not set - probably running on a physical device?") + return + } + + let path = cacheDirectory.appendingPathComponent("snapshot-launch_arguments.txt") + app.launchArguments += ["-FASTLANE_SNAPSHOT", "YES", "-ui_testing"] + + do { + let launchArguments = try String(contentsOf: path, encoding: String.Encoding.utf8) + let regex = try NSRegularExpression(pattern: "(\\\".+?\\\"|\\S+)", options: []) + let matches = regex.matches(in: launchArguments, options: [], range: NSRange(location: 0, length: launchArguments.count)) + let results = matches.map { result -> String in + (launchArguments as NSString).substring(with: result.range) + } + app.launchArguments += results + } catch { + NSLog("Couldn't detect/set launch_arguments...") + } + } + + open class func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) { + if timeout > 0 { + waitForLoadingIndicatorToDisappear(within: timeout) + } + + NSLog("snapshot: \(name)") // more information about this, check out https://docs.fastlane.tools/actions/snapshot/#how-does-it-work + + if Snapshot.waitForAnimations { + sleep(1) // Waiting for the animation to be finished (kind of) + } + + #if os(OSX) + guard let app = self.app else { + NSLog("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().") + return + } + + app.typeKey(XCUIKeyboardKeySecondaryFn, modifierFlags: []) + #else + + guard self.app != nil else { + NSLog("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().") + return + } + + let screenshot = XCUIScreen.main.screenshot() + guard var simulator = ProcessInfo().environment["SIMULATOR_DEVICE_NAME"], let screenshotsDir = screenshotsDirectory else { return } + + do { + // The simulator name contains "Clone X of " inside the screenshot file when running parallelized UI Tests on concurrent devices + let regex = try NSRegularExpression(pattern: "Clone [0-9]+ of ") + let range = NSRange(location: 0, length: simulator.count) + simulator = regex.stringByReplacingMatches(in: simulator, range: range, withTemplate: "") + + let path = screenshotsDir.appendingPathComponent("\(simulator)-\(name).png") + try screenshot.pngRepresentation.write(to: path) + } catch let error { + NSLog("Problem writing screenshot: \(name) to \(screenshotsDir)/\(simulator)-\(name).png") + NSLog(error.localizedDescription) + } + #endif + } + + class func waitForLoadingIndicatorToDisappear(within timeout: TimeInterval) { + #if os(tvOS) + return + #endif + + guard let app = self.app else { + NSLog("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().") + return + } + + let networkLoadingIndicator = app.otherElements.deviceStatusBars.networkLoadingIndicators.element + let networkLoadingIndicatorDisappeared = XCTNSPredicateExpectation(predicate: NSPredicate(format: "exists == false"), object: networkLoadingIndicator) + _ = XCTWaiter.wait(for: [networkLoadingIndicatorDisappeared], timeout: timeout) + } + + class func pathPrefix() throws -> URL? { + let homeDir: URL + // on OSX config is stored in /Users//Library + // and on iOS/tvOS/WatchOS it's in simulator's home dir + #if os(OSX) + guard let user = ProcessInfo().environment["USER"] else { + throw SnapshotError.cannotDetectUser + } + + guard let usersDir = FileManager.default.urls(for: .userDirectory, in: .localDomainMask).first else { + throw SnapshotError.cannotFindHomeDirectory + } + + homeDir = usersDir.appendingPathComponent(user) + #else + #if arch(i386) || arch(x86_64) + guard let simulatorHostHome = ProcessInfo().environment["SIMULATOR_HOST_HOME"] else { + throw SnapshotError.cannotFindSimulatorHomeDirectory + } + guard let homeDirUrl = URL(string: simulatorHostHome) else { + throw SnapshotError.cannotAccessSimulatorHomeDirectory(simulatorHostHome) + } + homeDir = URL(fileURLWithPath: homeDirUrl.path) + #else + throw SnapshotError.cannotRunOnPhysicalDevice + #endif + #endif + return homeDir.appendingPathComponent("Library/Caches/tools.fastlane") + } +} + +private extension XCUIElementAttributes { + var isNetworkLoadingIndicator: Bool { + if hasWhiteListedIdentifier { return false } + + let hasOldLoadingIndicatorSize = frame.size == CGSize(width: 10, height: 20) + let hasNewLoadingIndicatorSize = frame.size.width.isBetween(46, and: 47) && frame.size.height.isBetween(2, and: 3) + + return hasOldLoadingIndicatorSize || hasNewLoadingIndicatorSize + } + + var hasWhiteListedIdentifier: Bool { + let whiteListedIdentifiers = ["GeofenceLocationTrackingOn", "StandardLocationTrackingOn"] + + return whiteListedIdentifiers.contains(identifier) + } + + func isStatusBar(_ deviceWidth: CGFloat) -> Bool { + if elementType == .statusBar { return true } + guard frame.origin == .zero else { return false } + + let oldStatusBarSize = CGSize(width: deviceWidth, height: 20) + let newStatusBarSize = CGSize(width: deviceWidth, height: 44) + + return [oldStatusBarSize, newStatusBarSize].contains(frame.size) + } +} + +private extension XCUIElementQuery { + var networkLoadingIndicators: XCUIElementQuery { + let isNetworkLoadingIndicator = NSPredicate { (evaluatedObject, _) in + guard let element = evaluatedObject as? XCUIElementAttributes else { return false } + + return element.isNetworkLoadingIndicator + } + + return self.containing(isNetworkLoadingIndicator) + } + + var deviceStatusBars: XCUIElementQuery { + guard let app = Snapshot.app else { + fatalError("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().") + } + + let deviceWidth = app.windows.firstMatch.frame.width + + let isStatusBar = NSPredicate { (evaluatedObject, _) in + guard let element = evaluatedObject as? XCUIElementAttributes else { return false } + + return element.isStatusBar(deviceWidth) + } + + return self.containing(isStatusBar) + } +} + +private extension CGFloat { + func isBetween(_ numberA: CGFloat, and numberB: CGFloat) -> Bool { + return numberA...numberB ~= self + } +} + +// Please don't remove the lines below +// They are used to detect outdated configuration files +// SnapshotHelperVersion [1.21] diff --git a/TallestTowersScreenshots/TallestTowersScreenshots.swift b/TallestTowersScreenshots/TallestTowersScreenshots.swift new file mode 100644 index 0000000..4ff04de --- /dev/null +++ b/TallestTowersScreenshots/TallestTowersScreenshots.swift @@ -0,0 +1,22 @@ +import XCTest + +class TallestTowersScreenshots: XCTestCase { + override func setUp() { + } + + override func tearDown() { + } + + func testTakeScreenshots() { + let app = XCUIApplication() + setupSnapshot(app) + app.launch() + + snapshot("01-ListOfTowers") + + let burjKhalifaPredicate = NSPredicate(format: "label beginswith 'Burj Khalifa'") + app.tables.buttons.element(matching: burjKhalifaPredicate).tap() + + snapshot("02-TowerDetail") + } +} diff --git a/fastlane/Appfile b/fastlane/Appfile index 75f095e..79d4cf6 100644 --- a/fastlane/Appfile +++ b/fastlane/Appfile @@ -1,4 +1,5 @@ # For more information about the Appfile, see: # https://docs.fastlane.tools/advanced/#appfile +app_name 'TallestTowers' app_identifier 'com.semaphoreci.TallestTowers' diff --git a/fastlane/Fastfile b/fastlane/Fastfile index b2a2cee..b1bf6c8 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -29,4 +29,8 @@ platform :ios do run_tests(scheme: 'TallestTowers', devices: ['iPhone 8', 'iPhone 11 Pro']) end + + lane :screenshots do + snapshot + end end diff --git a/fastlane/README.md b/fastlane/README.md index 6a0d096..f81cde0 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -26,6 +26,11 @@ fastlane ios build fastlane ios test ``` +### ios screenshots +``` +fastlane ios screenshots +``` + ---- diff --git a/fastlane/Snapfile b/fastlane/Snapfile new file mode 100644 index 0000000..a3f4e84 --- /dev/null +++ b/fastlane/Snapfile @@ -0,0 +1,9 @@ +scheme("TakeScreenshots") + +devices([ + "iPhone 8", + "iPhone 11 Pro" +]) + +clear_previous_screenshots(true) +output_directory("screenshots")