Skip to content
This repository has been archived by the owner on Sep 5, 2023. It is now read-only.

Commit

Permalink
Screenshot automation (#885)
Browse files Browse the repository at this point in the history
* Screenshot automation

* Update screenshots.yml

* Update screenshots.yml

* Update screenshots.yml

* Update screenshots.yml

* Update screenshots.yml

* Rename disabletravis.txt to travis.yml

* Delete travis.yml

* Update screenshots.yml

* Update project.yml

* Update Snapfile

* Update screenshots.yml

* Update SnapshotHelper.swift

* Update screenshots.yml

* Update Snapfile

* Update Snapfile

* Update setup.sh

* Update screenshots.yml

* Update setup.sh

* Update setup.sh

* mock conform to protocols

* use a lane for snapshots

* setup.sh should not build carthage in CI mode, as it is done by fastlane

* Update screenshots.yml

Co-authored-by: Philippe Auriach <[email protected]>
  • Loading branch information
teolemon and philippeauriach authored Feb 9, 2021
1 parent 4ba48fd commit 8e8e8eb
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 40 deletions.
57 changes: 57 additions & 0 deletions .github/workflows/screenshots.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Screenshot automation
on:
push:
branches:
- 'screenshots/*'

jobs:
screenshot-automation:
name: Screenshot automation
runs-on: macos-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Decrypt AuthKey file
run: cd ./fastlane/envfiles && ./decrypt_secrets.sh
env:
AUTH_KEY_FILE_DECRYPTKEY: ${{ secrets.AUTH_KEY_FILE_DECRYPTKEY }}

- uses: actions/cache@v2
id: cache-carthage
with:
path: ~/Carthage
key: ${{ runner.os }}-carthage-${{ hashFiles('Cartfile.resolved') }}
env:
GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install sentry-cli
run: curl -sL https://sentry.io/get-cli/ | bash

- name: Install Dependencies
run: gem install bundler:1.17.3 && bundle install

- name: Prepare xcodeproj
run: sh scripts/setup.sh
env:
GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Run Fastlane snapshot
run: bundle exec fastlane perform_snapshots
env:
GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ secrets.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
MATCH_KEYCHAIN_PASSWORD: ${{ secrets.MATCH_KEYCHAIN_PASSWORD }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
PILOT_APPLE_ID: ${{ secrets.PILOT_APPLE_ID }}
SPACESHIP_CONNECT_API_ISSUER_ID: ${{ secrets.SPACESHIP_CONNECT_API_ISSUER_ID }}
SPACESHIP_CONNECT_API_KEY_ID: ${{ secrets.SPACESHIP_CONNECT_API_KEY_ID }}
SPACESHIP_CONNECT_API_KEY_FILEPATH: envfiles/AuthKey_KDAUTTM76R.p8
CI_RELEASE: true
- uses: actions/upload-artifact@v2
with:
name: screenshots
path: fastlane/screenshots/ # or path/to/artifact
78 changes: 40 additions & 38 deletions Snapshots/SnapshotHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
Expand All @@ -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)
Expand Down Expand Up @@ -174,6 +165,12 @@ open class Snapshot: NSObject {
}

let screenshot = XCUIScreen.main.screenshot()
#if os(iOS)
let image = XCUIDevice.shared.orientation.isLandscape ? fixLandscapeOrientation(image: screenshot.image) : screenshot.image
#else
let image = screenshot.image
#endif

guard var simulator = ProcessInfo().environment["SIMULATOR_DEVICE_NAME"], let screenshotsDir = screenshotsDirectory else { return }

do {
Expand All @@ -183,14 +180,31 @@ 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)
#if swift(<5.0)
UIImagePNGRepresentation(image)?.write(to: path, options: .atomic)
#else
try image.pngData()?.write(to: path, options: .atomic)
#endif
} catch let error {
NSLog("Problem writing screenshot: \(name) to \(screenshotsDir)/\(simulator)-\(name).png")
NSLog(error.localizedDescription)
}
#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
Expand All @@ -206,51 +220,39 @@ 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/<username>/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) || arch(arm64)
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)

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 {
Expand Down Expand Up @@ -300,4 +302,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]
5 changes: 5 additions & 0 deletions Tests/Common/DataManagerMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import RealmSwift
import UIKit

class DataManagerMock: DataManagerProtocol {

// Search
var query: String?
var page: Int?
Expand Down Expand Up @@ -123,6 +124,10 @@ class DataManagerMock: DataManagerProtocol {
func allergen(forTag: Tag) -> Allergen? {
return nil
}

func label(forTag: String) -> Label? {
return nil
}

func additive(forTag: Tag) -> Additive? {
return nil
Expand Down
2 changes: 2 additions & 0 deletions Tests/Models/PersistenceManagerMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import RealmSwift

class PersistenceManagerMock: PersistenceManagerProtocol {
var labelsIsEmpty: Bool = false

// =======------------------======= //
// TODO: implement code below if needed (for now only add the function/propety signatures, so it builds)
// =======------------------======= //
Expand Down
6 changes: 6 additions & 0 deletions fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,9 @@ desc "Upload metadata"
lane :metadata do
deliver(skip_binary_upload: true)
end

lane :perform_snapshots do
carthage(command: "bootstrap", platform: "iOS", cache_builds: true)

snapshot
end
9 changes: 7 additions & 2 deletions fastlane/Snapfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ devices([

# "iPhone Xs Max",
# "iPad Pro (12.9-inch) (2nd generation)",
"iPad Pro (12.9-inch) (3rd generation)"
"iPad Pro (12.9-inch) (4th generation)"

#"iPad Pro (12.9-inch)",
# "iPad Pro (9.7-inch)",
# "Apple TV 1080p"
])



languages([
"en-US",
"fr-FR"
Expand Down Expand Up @@ -171,7 +173,10 @@ scheme("OpenFoodFactsSnapshots")
output_directory("./fastlane/screenshots")

# remove the '#' to clear all previously generated screenshots before creating new ones
# clear_previous_screenshots(true)
clear_previous_screenshots(true)

override_status_bar(true)
localize_simulator(true)

# Arguments to pass to the app on launch. See https://docs.fastlane.tools/actions/snapshot/#launch-arguments
launch_arguments(["debug"])
Expand Down

0 comments on commit 8e8e8eb

Please sign in to comment.