From 4fd191830532466658319c8f2cc4e2d5a5c16754 Mon Sep 17 00:00:00 2001 From: Sasha Heinen Date: Tue, 26 Jun 2018 13:38:40 -0600 Subject: [PATCH] initial screenshots infrastructure (#490) * Added fastlane support, screenshot setup * added documentation for taking screenshots * use restmail email * add a note about 3rd-party * updated snapshothelper --- .gitignore | 1 + Lockbox.xcodeproj/project.pbxproj | 4 + .../xcshareddata/xcschemes/uispecs.xcscheme | 90 +++++++ LockboxXCUITests/BaseTestCase.swift | 4 + LockboxXCUITests/LockboxXCUITests.swift | 13 +- LockboxXCUITests/SnapshotHelper.swift | 244 ++++++++++++++++++ README.md | 2 + docs/releases.md | 12 +- fastlane/Appfile | 7 + fastlane/Fastfile | 73 ++++++ fastlane/README.md | 55 ++++ fastlane/Snapfile | 14 + fastlane/report.xml | 18 ++ .../OnboardingConfirmation.storyboard | 1 + 14 files changed, 533 insertions(+), 5 deletions(-) create mode 100644 Lockbox.xcodeproj/xcshareddata/xcschemes/uispecs.xcscheme create mode 100644 LockboxXCUITests/SnapshotHelper.swift create mode 100644 fastlane/Appfile create mode 100644 fastlane/Fastfile create mode 100644 fastlane/README.md create mode 100644 fastlane/Snapfile create mode 100644 fastlane/report.xml diff --git a/.gitignore b/.gitignore index c27eb285f..837eb7645 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ xcuserdata .DS_Store lockbox-ios/binaries/ site/ +fastlane/screenshots/ diff --git a/Lockbox.xcodeproj/project.pbxproj b/Lockbox.xcodeproj/project.pbxproj index e4952c9fe..4eb4c363d 100644 --- a/Lockbox.xcodeproj/project.pbxproj +++ b/Lockbox.xcodeproj/project.pbxproj @@ -74,6 +74,7 @@ 7AA548BBE384D40397D26E9B /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA546C351E7D7C7DE3BEF0C /* RootView.swift */; }; 7AA5493A70D5B26D3FF1808B /* StaticURLPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA545D5E7A7EB65733B8A8A /* StaticURLPresenter.swift */; }; 7AA54A03CA11221087E4F9F8 /* Date+Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA545A802C0592A820AD230 /* Date+Spec.swift */; }; + 7AA54A18A8CB61CD8D086821 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA54CF29E6B76D6894A841E /* SnapshotHelper.swift */; }; 7AA54A58F7F289BAD2CE21C3 /* ErrorActionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA545A7472772984D517CB0 /* ErrorActionSpec.swift */; }; 7AA54A743320656025DC5B3E /* ItemDetailCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA547106248ADBADAF76239 /* ItemDetailCell.swift */; }; 7AA54AA7964A1C95FDB7AA6E /* UserDefaults+Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA546618500E909B1E98632 /* UserDefaults+Spec.swift */; }; @@ -329,6 +330,7 @@ 7AA54C72B63B2B4C8200F3F8 /* RootPresenterSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootPresenterSpec.swift; sourceTree = ""; }; 7AA54C7A277D5D7C03F4F453 /* MainNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainNavigationController.swift; sourceTree = ""; }; 7AA54CDEA56C47263160AD0E /* Dispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dispatcher.swift; sourceTree = ""; }; + 7AA54CF29E6B76D6894A841E /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = ""; }; 7AA54D3C8D454F79BD580AAF /* UserDefaults+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+.swift"; sourceTree = ""; }; 7AA54D4E439D76B910034E2A /* ErrorAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorAction.swift; sourceTree = ""; }; 7AA54DC6857783DACB5DA3EC /* ExternalWebsiteNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExternalWebsiteNavigationController.swift; sourceTree = ""; }; @@ -489,6 +491,7 @@ 2C2EC55620ADA85A00AF8C44 /* Info.plist */, 2C2EC56120ADAB1D00AF8C44 /* LockBoxScreenGraph.swift */, 2C2EC56320ADAB5400AF8C44 /* BaseTestCase.swift */, + 7AA54CF29E6B76D6894A841E /* SnapshotHelper.swift */, ); path = LockboxXCUITests; sourceTree = ""; @@ -1090,6 +1093,7 @@ 2C2EC55520ADA85A00AF8C44 /* LockboxXCUITests.swift in Sources */, 2C2EC56420ADAB5400AF8C44 /* BaseTestCase.swift in Sources */, 2C2EC56220ADAB1D00AF8C44 /* LockBoxScreenGraph.swift in Sources */, + 7AA54A18A8CB61CD8D086821 /* SnapshotHelper.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Lockbox.xcodeproj/xcshareddata/xcschemes/uispecs.xcscheme b/Lockbox.xcodeproj/xcshareddata/xcschemes/uispecs.xcscheme new file mode 100644 index 000000000..1226aea16 --- /dev/null +++ b/Lockbox.xcodeproj/xcshareddata/xcschemes/uispecs.xcscheme @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LockboxXCUITests/BaseTestCase.swift b/LockboxXCUITests/BaseTestCase.swift index 6d15b264e..9a1ced474 100644 --- a/LockboxXCUITests/BaseTestCase.swift +++ b/LockboxXCUITests/BaseTestCase.swift @@ -5,6 +5,8 @@ import MappaMundi import XCTest +let CONTENT_SIZE = "UICTContentSizeCategoryL" + class BaseTestCase: XCTestCase { var navigator: MMNavigator! let app = XCUIApplication() @@ -18,6 +20,8 @@ class BaseTestCase: XCTestCase { override func setUp() { super.setUp() continueAfterFailure = false + setupSnapshot(app) + app.launchArguments += [ "-UIPreferredContentSizeCategoryName", CONTENT_SIZE ] app.launch() setUpScreenGraph() } diff --git a/LockboxXCUITests/LockboxXCUITests.swift b/LockboxXCUITests/LockboxXCUITests.swift index 036d8b1dd..cc20e5603 100644 --- a/LockboxXCUITests/LockboxXCUITests.swift +++ b/LockboxXCUITests/LockboxXCUITests.swift @@ -10,6 +10,7 @@ let passwordTestAccountLogins = "FRCuQaPm" class LockboxXCUITests: BaseTestCase { func test1LoginWithSavedLogins() { + snapshot("01Welcome" + CONTENT_SIZE) navigator.goto(Screen.FxASigninScreen) waitforExistence(app.webViews.textFields["Email"]) @@ -45,8 +46,9 @@ class LockboxXCUITests: BaseTestCase { // App should start showing the main page // Instead of waiting we could pull down to refresh to force the logins appear and so the buttons are available - waitforExistence(app.buttons["Finish"]) - app.buttons["Finish"].tap() + waitforExistence(app.buttons["finish.button"]) + app.buttons["finish.button"].tap() + sleep(8) waitforExistence(app.navigationBars["Firefox Lockbox"]) XCTAssertTrue(app.buttons["sorting.button"].exists) @@ -55,6 +57,7 @@ class LockboxXCUITests: BaseTestCase { XCTAssertTrue(app.navigationBars["Firefox Lockbox"].exists) XCTAssertTrue(app.navigationBars.buttons["Settings"].exists) + snapshot("02EntryList" + CONTENT_SIZE) // Go to Settings to disable the AutoLock // This is a temporary workaround needed to run other tests after this one until defining a tearDown @@ -81,6 +84,8 @@ class LockboxXCUITests: BaseTestCase { func test2SettingsAccountUI() { waitforExistence(app.navigationBars["Firefox Lockbox"]) + snapshot("03Settings" + CONTENT_SIZE) + navigator.goto(Screen.AccountSettingsMenu) waitforExistence(app.navigationBars["Account"]) // Some checks can be done here to be sure the Account UI is fine @@ -131,8 +136,8 @@ class LockboxXCUITests: BaseTestCase { // In small screens it is necessary to dismiss the keyboard app.buttons["Done"].tap() navigator.performAction(Action.FxATapOnSignInButton) - waitforExistence(app.buttons["Finish"]) - app.buttons["Finish"].tap() + waitforExistence(app.buttons["finish.button"]) + app.buttons["finish.button"].tap() waitforExistence(app.navigationBars["Firefox Lockbox"]) } diff --git a/LockboxXCUITests/SnapshotHelper.swift b/LockboxXCUITests/SnapshotHelper.swift new file mode 100644 index 000000000..5b7ead01a --- /dev/null +++ b/LockboxXCUITests/SnapshotHelper.swift @@ -0,0 +1,244 @@ +// +// 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) { + Snapshot.setupSnapshot(app) +} + +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) + + 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?" + } + } +} + +open class Snapshot: NSObject { + static var app: XCUIApplication! + static var cacheDirectory: URL! + static var screenshotsDirectory: URL? { + return cacheDirectory.appendingPathComponent("screenshots", isDirectory: true) + } + + open class func setupSnapshot(_ app: XCUIApplication) { + do { + let cacheDir = try pathPrefix() + Snapshot.cacheDirectory = cacheDir + Snapshot.app = app + setLanguage(app) + setLocale(app) + setLaunchArguments(app) + } catch let error { + print(error) + } + } + + class func setLanguage(_ app: XCUIApplication) { + 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 { + print("Couldn't detect/set language...") + } + } + + class func setLocale(_ app: XCUIApplication) { + let path = cacheDirectory.appendingPathComponent("locale.txt") + + do { + let trimCharacterSet = CharacterSet.whitespacesAndNewlines + locale = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet) + } catch { + print("Couldn't detect/set locale...") + } + if locale.isEmpty { + locale = Locale(identifier: deviceLanguage).identifier + } + app.launchArguments += ["-AppleLocale", "\"\(locale)\""] + } + + class func setLaunchArguments(_ app: XCUIApplication) { + 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 { + print("Couldn't detect/set launch_arguments...") + } + } + + open class func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) { + if timeout > 0 { + waitForLoadingIndicatorToDisappear(within: timeout) + } + + print("snapshot: \(name)") // more information about this, check out https://github.com/fastlane/fastlane/tree/master/snapshot#how-does-it-work + + sleep(1) // Waiting for the animation to be finished (kind of) + + #if os(OSX) + XCUIApplication().typeKey(XCUIKeyboardKeySecondaryFn, modifierFlags: []) + #else + let screenshot = app.windows.firstMatch.screenshot() + guard let simulator = ProcessInfo().environment["SIMULATOR_DEVICE_NAME"], let screenshotsDir = screenshotsDirectory else { return } + let path = screenshotsDir.appendingPathComponent("\(simulator)-\(name).png") + do { + try screenshot.pngRepresentation.write(to: path) + } catch let error { + print("Problem writing screenshot: \(name) to \(path)") + print(error) + } + #endif + } + + class func waitForLoadingIndicatorToDisappear(within timeout: TimeInterval) { + #if os(tvOS) + return + #endif + + let networkLoadingIndicator = XCUIApplication().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 + 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) + #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 { + let deviceWidth = XCUIApplication().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.7] diff --git a/README.md b/README.md index b5a117613..9d3002493 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,8 @@ This project is governed by a [Code Of Conduct][coc-link]. This module is licensed under the [Mozilla Public License, version 2.0][license-link]. +Note that some test fixtures use source code from third-party services, and are not subject to this license. + [buddybuild-image]: https://dashboard.buddybuild.com/api/statusImage?appID=5a0ddb736e19370001034f85&branch=master&build=latest [buddybuild-link]: https://dashboard.buddybuild.com/apps/5a0ddb736e19370001034f85/build/latest?branch=master [codecov-image]: https://img.shields.io/codecov/c/github/mozilla-lockbox/lockbox-ios.svg diff --git a/docs/releases.md b/docs/releases.md index da4e3b2ed..b9ff44b04 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -17,7 +17,7 @@ _all commits on all branches and pull requests are automatically built_ 2. Open [buddybuild][1] from a mobile device and browse to the build 3. Alternatively, add an email address to the "Deployments" email list(s) - this is expected to be a small group of contributors and Mozillians - + ## Preparing a Release (for TestFlight or App Store) - Update the release notes under `docs/release-notes.md` @@ -60,9 +60,19 @@ _all `master` branch builds are automatically uploaded from buddybuild_ 5. Save and "Submit for Review" 6. ??? +## Taking screenshots for new releases + +Screenshots are automated via Fastlane. To get Fastlane, run `brew cask install fastlane`. From there, you will be able to run `fastlane snapshot` in the root directory of the project to run the screenshot task. + +Configuration: +- [languages] Update / add desired locales to `fastlane/Snapfile` +- [devices] Update / add desired device sizes to `fastlane/Snapfile` +- [text size][5] Update the `CONTENT_SIZE` variable in `LockboxXCUITests/BaseTestCase.swift` + --- [1]: https://dashboard.buddybuild.com/apps/5a0ddb736e19370001034f85 [2]: https://developer.apple.com/testflight/testers/ [3]: https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/app/1314000270/testflight?section=iosbuilds [4]: https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/ng/app/1314000270 +[5]: https://stackoverflow.com/questions/38316591/how-to-test-dynamic-type-larger-font-sizes-in-ios-simulator diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 000000000..70e3983f2 --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,7 @@ +app_identifier "org.mozilla.ios.Lockbox" # The bundle identifier of your app +apple_id "sheinen@mozilla.com" # Your Apple email address + +team_id "[[DEV_PORTAL_TEAM_ID]]" # Developer Portal Team ID + +# you can even provide different app identifiers, Apple IDs and team names per lane: +# More information: https://docs.fastlane.tools/advanced/#appfile diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 000000000..4bec6ce2b --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,73 @@ +# Customize this file, documentation can be found here: +# https://docs.fastlane.tools/actions/ +# All available actions: https://docs.fastlane.tools/actions +# can also be listed using the `fastlane actions` command + +# Change the syntax highlighting to Ruby +# All lines starting with a # are ignored when running `fastlane` + +# If you want to automatically update fastlane if a new version is available: +# update_fastlane + +# This is the minimum version number required. +# Update this, if you use features of a newer version +fastlane_version "2.64.1" + +default_platform :ios + +platform :ios do + before_all do + # ENV["SLACK_URL"] = "https://hooks.slack.com/services/..." + # cocoapods + carthage + end + + desc "Runs all the tests" + lane :test do + scan + end + + desc "Submit a new Beta Build to Apple TestFlight" + desc "This will also make sure the profile is up to date" + lane :beta do + # match(type: "appstore") # more information: https://codesigning.guide + gym(scheme: "Firefox Lockbox") # Build your app - more options available + pilot + + # sh "your_script.sh" + # You can also use other beta testing services here (run `fastlane actions`) + end + + desc "Deploy a new version to the App Store" + lane :release do + # match(type: "appstore") + # snapshot + gym(scheme: "Firefox Lockbox") # Build your app - more options available + deliver(force: true) + # frameit + end + + # You can define as many lanes as you want + + after_all do |lane| + # This block is called, only if the executed lane was successful + + # slack( + # message: "Successfully deployed new App Update." + # ) + end + + error do |lane, exception| + # slack( + # message: exception.message, + # success: false + # ) + end +end + + +# More information about multiple platforms in fastlane: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform +# All available actions: https://docs.fastlane.tools/actions + +# fastlane reports which actions are used. No personal data is recorded. +# Learn more at https://docs.fastlane.tools/#metrics diff --git a/fastlane/README.md b/fastlane/README.md new file mode 100644 index 000000000..93a7c3dce --- /dev/null +++ b/fastlane/README.md @@ -0,0 +1,55 @@ +fastlane documentation +================ +# Installation + +Make sure you have the latest version of the Xcode command line tools installed: + +``` +xcode-select --install +``` + +## Choose your installation method: + + + + + + + + + + + + + + + + + +
HomebrewInstaller ScriptRubyGems
macOSmacOSmacOS or Linux with Ruby 2.0.0 or above
brew cask install fastlaneDownload the zip file. Then double click on the install script (or run it in a terminal window).sudo gem install fastlane -NV
+ +# Available Actions +## iOS +### ios test +``` +fastlane ios test +``` +Runs all the tests +### ios beta +``` +fastlane ios beta +``` +Submit a new Beta Build to Apple TestFlight + +This will also make sure the profile is up to date +### ios release +``` +fastlane ios release +``` +Deploy a new version to the App Store + +---- + +This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. +More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). +The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/fastlane/Snapfile b/fastlane/Snapfile new file mode 100644 index 000000000..01c6cc50e --- /dev/null +++ b/fastlane/Snapfile @@ -0,0 +1,14 @@ +# Uncomment the lines below you want to change by removing the # in the beginning + +# A list of devices you want to take the screenshots from +devices([ + "iPhone 8" +]) + +languages([ + "en-US", + "de-DE" +]) + +# The name of the scheme which contains the UI Tests +scheme "uispecs" diff --git a/fastlane/report.xml b/fastlane/report.xml new file mode 100644 index 000000000..5dd84db10 --- /dev/null +++ b/fastlane/report.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/lockbox-ios/Storyboard/OnboardingConfirmation.storyboard b/lockbox-ios/Storyboard/OnboardingConfirmation.storyboard index 9ac59a518..126e95319 100644 --- a/lockbox-ios/Storyboard/OnboardingConfirmation.storyboard +++ b/lockbox-ios/Storyboard/OnboardingConfirmation.storyboard @@ -30,6 +30,7 @@