From 66e9a852a131437e3707ead4cd1b54530558c91b Mon Sep 17 00:00:00 2001 From: rachelmcr Date: Tue, 29 Sep 2020 10:52:34 +0100 Subject: [PATCH 1/5] Add support for dark mode screenshots --- Scripts/fastlane/ScreenshotFastfile | 15 ++++++----- .../WordPressScreenshotGeneration.swift | 25 +++++++++++++------ .../Utils/XCTest+Extensions.swift | 8 ++++++ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/Scripts/fastlane/ScreenshotFastfile b/Scripts/fastlane/ScreenshotFastfile index f065aa335cda..a0534132e191 100644 --- a/Scripts/fastlane/ScreenshotFastfile +++ b/Scripts/fastlane/ScreenshotFastfile @@ -54,12 +54,15 @@ platform :ios do puts languages - capture_ios_screenshots( - test_without_building: true, - derived_data_path: derived_data_path, - languages: languages, - clear_previous_screenshots: should_clear_previous_screenshots, - ) + [true, false].each { | dark_mode_enabled | + capture_ios_screenshots( + test_without_building: true, + derived_data_path: derived_data_path, + languages: languages, + clear_previous_screenshots: should_clear_previous_screenshots, + dark_mode: dark_mode_enabled + ) + } end ##################################################################################### diff --git a/WordPress/WordPressScreenshotGeneration/WordPressScreenshotGeneration.swift b/WordPress/WordPressScreenshotGeneration/WordPressScreenshotGeneration.swift index 614cb13143a7..afedd4274541 100644 --- a/WordPress/WordPressScreenshotGeneration/WordPressScreenshotGeneration.swift +++ b/WordPress/WordPressScreenshotGeneration/WordPressScreenshotGeneration.swift @@ -46,10 +46,10 @@ class WordPressScreenshotGeneration: XCTestCase { let postEditorScreenshot = postList.selectPost(withSlug: "our-services") sleep(imagesWaitTime) // wait for post images to load if isIpad { - snapshot("1-Editor") + thenTakeScreenshot(1, named: "Editor") } else { BlockEditorScreen().openBlockPicker() - snapshot("1-Editor-With-BlockPicker") + thenTakeScreenshot(1, named: "Editor-With-BlockPicker") BlockEditorScreen().closeBlockPicker() } postEditorScreenshot.close() @@ -64,7 +64,7 @@ class WordPressScreenshotGeneration: XCTestCase { .selectPost(withSlug: "easy-blueberry-muffins") BlockEditorScreen().selectBlock(containingText: "Ingredients") sleep(imagesWaitTime) // wait for post images to load - snapshot("7-Editor-With-Keyboard") + thenTakeScreenshot(7, named: "Editor-With-Keyboard") ipadScreenshot.close() } else { postList.pop() @@ -74,12 +74,12 @@ class WordPressScreenshotGeneration: XCTestCase { let mySite = MySiteScreen() .showSiteSwitcher() .switchToSite(withTitle: "tricountyrealestate.wordpress.com") - snapshot("4-MySite") + thenTakeScreenshot(4, named: "MySite") // Get Media screenshot _ = mySite.gotoMediaScreen() sleep(imagesWaitTime) // wait for post images to load - snapshot("6-Media") + thenTakeScreenshot(6, named: "Media") if !isIpad { postList.pop() @@ -90,14 +90,14 @@ class WordPressScreenshotGeneration: XCTestCase { statsScreen .dismissCustomizeInsightsNotice() .switchTo(mode: .months) - snapshot("3-Stats") + thenTakeScreenshot(3, named: "Stats") // Get Discover screenshot // Currently, the view includes the "You Might Like" section TabNavComponent() .gotoReaderScreen() .openDiscover() - snapshot("2-Discover") + thenTakeScreenshot(2, named: "Discover") // Get Notifications screenshot let notificationList = TabNavComponent() @@ -107,6 +107,15 @@ class WordPressScreenshotGeneration: XCTestCase { notificationList.openNotification(withText: "Reyansh Pawar commented on My Top 10 Pastry Recipes") .replyToNotification() } - snapshot("5-Notifications") + thenTakeScreenshot(5, named: "Notifications") + } +} + +extension XCTestCase { + func thenTakeScreenshot(_ index: Int, named title: String) { + let mode = isDarkMode ? "dark" : "light" + let filename = "\(index)-\(mode)-\(title)" + + snapshot(filename) } } diff --git a/WordPress/WordPressUITests/Utils/XCTest+Extensions.swift b/WordPress/WordPressUITests/Utils/XCTest+Extensions.swift index 248ede608b16..70206e23f59d 100644 --- a/WordPress/WordPressUITests/Utils/XCTest+Extensions.swift +++ b/WordPress/WordPressUITests/Utils/XCTest+Extensions.swift @@ -8,6 +8,14 @@ var isIpad: Bool { return UIDevice.current.userInterfaceIdiom == .pad } +var isDarkMode: Bool { + if #available(iOS 12.0, *) { + return UIViewController().traitCollection.userInterfaceStyle == .dark + } else { + return false + } +} + let navBackButton = XCUIApplication().navigationBars.element(boundBy: 0).buttons.element(boundBy: 0) extension XCUIElement { From 226689f7979015f84fc6e5fe292a5d5a0469e2a6 Mon Sep 17 00:00:00 2001 From: rachelmcr Date: Tue, 29 Sep 2020 10:53:53 +0100 Subject: [PATCH 2/5] Update fastlane snapshothelper --- .../SnapshotHelper.swift | 70 +++++++++---------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/WordPress/WordPressScreenshotGeneration/SnapshotHelper.swift b/WordPress/WordPressScreenshotGeneration/SnapshotHelper.swift index aaa2a9a9234f..1f12573cbc01 100644 --- a/WordPress/WordPressScreenshotGeneration/SnapshotHelper.swift +++ b/WordPress/WordPressScreenshotGeneration/SnapshotHelper.swift @@ -38,22 +38,13 @@ func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) { } 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." } @@ -75,7 +66,7 @@ open class Snapshot: NSObject { Snapshot.waitForAnimations = waitForAnimations do { - let cacheDir = try pathPrefix() + let cacheDir = try getCacheDirectory() Snapshot.cacheDirectory = cacheDir setLanguage(app) setLocale(app) @@ -174,6 +165,8 @@ open class Snapshot: NSObject { } let screenshot = XCUIScreen.main.screenshot() + let image = XCUIDevice.shared.orientation.isLandscape ? fixLandscapeOrientation(image: screenshot.image) : screenshot.image + guard var simulator = ProcessInfo().environment["SIMULATOR_DEVICE_NAME"], let screenshotsDir = screenshotsDirectory else { return } do { @@ -183,7 +176,7 @@ open class Snapshot: NSObject { simulator = regex.stringByReplacingMatches(in: simulator, range: range, withTemplate: "") let path = screenshotsDir.appendingPathComponent("\(simulator)-\(name).png") - try screenshot.pngRepresentation.write(to: path) + try image.pngData()?.write(to: path, options: .atomic) } catch let error { NSLog("Problem writing screenshot: \(name) to \(screenshotsDir)/\(simulator)-\(name).png") NSLog(error.localizedDescription) @@ -191,6 +184,19 @@ open class Snapshot: NSObject { #endif } + class func fixLandscapeOrientation(image: UIImage) -> UIImage { + if #available(iOS 10.0, *) { + let format = UIGraphicsImageRendererFormat() + format.scale = image.scale + let renderer = UIGraphicsImageRenderer(size: image.size, format: format) + return renderer.image { context in + image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)) + } + } else { + return image + } + } + class func waitForLoadingIndicatorToDisappear(within timeout: TimeInterval) { #if os(tvOS) return @@ -206,40 +212,28 @@ open class Snapshot: NSObject { _ = XCTWaiter.wait(for: [networkLoadingIndicatorDisappeared], timeout: timeout) } - class func pathPrefix() throws -> URL? { - let homeDir: URL + class func getCacheDirectory() throws -> URL { + let cachePath = "Library/Caches/tools.fastlane" // 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 + let homeDir = URL(fileURLWithPath: NSHomeDirectory()) + return homeDir.appendingPathComponent(cachePath) + #elseif arch(i386) || arch(x86_64) + guard let simulatorHostHome = ProcessInfo().environment["SIMULATOR_HOST_HOME"] else { + throw SnapshotError.cannotFindSimulatorHomeDirectory } - - homeDir = usersDir.appendingPathComponent(user) + let homeDir = URL(fileURLWithPath: simulatorHostHome) + return homeDir.appendingPathComponent(cachePath) #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 + throw SnapshotError.cannotRunOnPhysicalDevice #endif - return homeDir.appendingPathComponent("Library/Caches/tools.fastlane") } } private extension XCUIElementAttributes { var isNetworkLoadingIndicator: Bool { - if hasWhiteListedIdentifier { return false } + if hasAllowListedIdentifier { 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) @@ -247,10 +241,10 @@ private extension XCUIElementAttributes { return hasOldLoadingIndicatorSize || hasNewLoadingIndicatorSize } - var hasWhiteListedIdentifier: Bool { - let whiteListedIdentifiers = ["GeofenceLocationTrackingOn", "StandardLocationTrackingOn"] + var hasAllowListedIdentifier: Bool { + let allowListedIdentifiers = ["GeofenceLocationTrackingOn", "StandardLocationTrackingOn"] - return whiteListedIdentifiers.contains(identifier) + return allowListedIdentifiers.contains(identifier) } func isStatusBar(_ deviceWidth: CGFloat) -> Bool { @@ -300,4 +294,4 @@ private extension CGFloat { // Please don't remove the lines below // They are used to detect outdated configuration files -// SnapshotHelperVersion [1.21] +// SnapshotHelperVersion [1.24] From 0aba1cf5ee55f3eddacf491417b795787cca345c Mon Sep 17 00:00:00 2001 From: rachelmcr Date: Tue, 6 Oct 2020 14:10:54 +0100 Subject: [PATCH 3/5] Make screenshot method an extension of BaseScreen --- .../WordPressScreenshotGeneration.swift | 36 +++++++++++-------- .../Screens/ReaderScreen.swift | 4 ++- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/WordPress/WordPressScreenshotGeneration/WordPressScreenshotGeneration.swift b/WordPress/WordPressScreenshotGeneration/WordPressScreenshotGeneration.swift index afedd4274541..9dc0eaed79e0 100644 --- a/WordPress/WordPressScreenshotGeneration/WordPressScreenshotGeneration.swift +++ b/WordPress/WordPressScreenshotGeneration/WordPressScreenshotGeneration.swift @@ -46,11 +46,13 @@ class WordPressScreenshotGeneration: XCTestCase { let postEditorScreenshot = postList.selectPost(withSlug: "our-services") sleep(imagesWaitTime) // wait for post images to load if isIpad { - thenTakeScreenshot(1, named: "Editor") + BlockEditorScreen() + .thenTakeScreenshot(1, named: "Editor") } else { - BlockEditorScreen().openBlockPicker() - thenTakeScreenshot(1, named: "Editor-With-BlockPicker") - BlockEditorScreen().closeBlockPicker() + BlockEditorScreen() + .openBlockPicker() + .thenTakeScreenshot(1, named: "Editor-With-BlockPicker") + .closeBlockPicker() } postEditorScreenshot.close() @@ -62,9 +64,9 @@ class WordPressScreenshotGeneration: XCTestCase { .gotoPostsScreen() .showOnly(.drafts) .selectPost(withSlug: "easy-blueberry-muffins") - BlockEditorScreen().selectBlock(containingText: "Ingredients") + BlockEditorScreen().selectBlock(containingText: "Ingredients") sleep(imagesWaitTime) // wait for post images to load - thenTakeScreenshot(7, named: "Editor-With-Keyboard") + BlockEditorScreen().thenTakeScreenshot(7, named: "Editor-With-Keyboard") ipadScreenshot.close() } else { postList.pop() @@ -74,12 +76,12 @@ class WordPressScreenshotGeneration: XCTestCase { let mySite = MySiteScreen() .showSiteSwitcher() .switchToSite(withTitle: "tricountyrealestate.wordpress.com") - thenTakeScreenshot(4, named: "MySite") + .thenTakeScreenshot(4, named: "MySite") // Get Media screenshot _ = mySite.gotoMediaScreen() sleep(imagesWaitTime) // wait for post images to load - thenTakeScreenshot(6, named: "Media") + mySite.thenTakeScreenshot(6, named: "Media") if !isIpad { postList.pop() @@ -90,32 +92,36 @@ class WordPressScreenshotGeneration: XCTestCase { statsScreen .dismissCustomizeInsightsNotice() .switchTo(mode: .months) - thenTakeScreenshot(3, named: "Stats") + .thenTakeScreenshot(3, named: "Stats") // Get Discover screenshot // Currently, the view includes the "You Might Like" section TabNavComponent() .gotoReaderScreen() .openDiscover() - thenTakeScreenshot(2, named: "Discover") + .thenTakeScreenshot(2, named: "Discover") // Get Notifications screenshot let notificationList = TabNavComponent() .gotoNotificationsScreen() .dismissNotificationAlertIfNeeded() if isIpad { - notificationList.openNotification(withText: "Reyansh Pawar commented on My Top 10 Pastry Recipes") - .replyToNotification() + notificationList + .openNotification(withText: "Reyansh Pawar commented on My Top 10 Pastry Recipes") + .replyToNotification() } - thenTakeScreenshot(5, named: "Notifications") + notificationList.thenTakeScreenshot(5, named: "Notifications") } } -extension XCTestCase { - func thenTakeScreenshot(_ index: Int, named title: String) { +extension BaseScreen { + @discardableResult + func thenTakeScreenshot(_ index: Int, named title: String) -> Self { let mode = isDarkMode ? "dark" : "light" let filename = "\(index)-\(mode)-\(title)" snapshot(filename) + + return self } } diff --git a/WordPress/WordPressUITests/Screens/ReaderScreen.swift b/WordPress/WordPressUITests/Screens/ReaderScreen.swift index 8598865eb21c..211a3875446c 100644 --- a/WordPress/WordPressUITests/Screens/ReaderScreen.swift +++ b/WordPress/WordPressUITests/Screens/ReaderScreen.swift @@ -22,7 +22,9 @@ class ReaderScreen: BaseScreen { return XCUIApplication().tables[ElementStringIDs.readerTable].exists } - func openDiscover() { + func openDiscover() -> ReaderScreen { discoverButton.tap() + + return self } } From 145976a0c101bb9f98b8e5e98d60d778f6bb8e24 Mon Sep 17 00:00:00 2001 From: rachelmcr Date: Fri, 9 Oct 2020 11:10:30 +0100 Subject: [PATCH 4/5] Leave simulator in en-US when screenshot run finishes --- Scripts/fastlane/ScreenshotFastfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/fastlane/ScreenshotFastfile b/Scripts/fastlane/ScreenshotFastfile index a0534132e191..db6db441e043 100644 --- a/Scripts/fastlane/ScreenshotFastfile +++ b/Scripts/fastlane/ScreenshotFastfile @@ -40,7 +40,7 @@ platform :ios do # By default, clear previous screenshots should_clear_previous_screenshots = true - languages = "da de-DE en-AU en-CA en-GB en-US es-ES fr-FR id it ja ko no nl-NL pt-BR pt-PT ru sv th tr zh-Hans zh-Hant".split(" ") + languages = "da de-DE en-AU en-CA en-GB es-ES fr-FR id it ja ko no nl-NL pt-BR pt-PT ru sv th tr zh-Hans zh-Hant en-US".split(" ") # Allow creating screenshots for just one languages if options[:language] != nil From 09d5eb26c6be3769e0966b6108d1c708a59739f5 Mon Sep 17 00:00:00 2001 From: rachelmcr Date: Fri, 9 Oct 2020 11:20:28 +0100 Subject: [PATCH 5/5] Don't clear screenshots to retain them across dark and light mode runs --- Scripts/fastlane/ScreenshotFastfile | 6 ------ Scripts/fastlane/Snapfile | 1 - 2 files changed, 7 deletions(-) diff --git a/Scripts/fastlane/ScreenshotFastfile b/Scripts/fastlane/ScreenshotFastfile index db6db441e043..687afc497efa 100644 --- a/Scripts/fastlane/ScreenshotFastfile +++ b/Scripts/fastlane/ScreenshotFastfile @@ -38,8 +38,6 @@ platform :ios do derived_data_path: derived_data_path, ) - # By default, clear previous screenshots - should_clear_previous_screenshots = true languages = "da de-DE en-AU en-CA en-GB es-ES fr-FR id it ja ko no nl-NL pt-BR pt-PT ru sv th tr zh-Hans zh-Hant en-US".split(" ") # Allow creating screenshots for just one languages @@ -47,9 +45,6 @@ platform :ios do languages.keep_if { |language| language.casecmp(options[:language]) == 0 } - - # Don't clear, because we might just be fixing one locale - should_clear_previous_screenshots = false end puts languages @@ -59,7 +54,6 @@ platform :ios do test_without_building: true, derived_data_path: derived_data_path, languages: languages, - clear_previous_screenshots: should_clear_previous_screenshots, dark_mode: dark_mode_enabled ) } diff --git a/Scripts/fastlane/Snapfile b/Scripts/fastlane/Snapfile index 84d79a4ebcd3..2e9a64ef8b7c 100644 --- a/Scripts/fastlane/Snapfile +++ b/Scripts/fastlane/Snapfile @@ -34,7 +34,6 @@ reinstall_app true erase_simulator true localize_simulator true concurrent_simulators false -clear_previous_screenshots true # By default, the latest version should be used automatically. If you want to change it, do it here # ios_version '8.1'