From 10a23d075f25e5050e8674eb43d697a2807ff9cc Mon Sep 17 00:00:00 2001 From: Franco Leto Date: Tue, 15 Feb 2022 11:00:37 -0300 Subject: [PATCH 01/13] Deprecating Fastlane --- 6.0.1 | 0 Gemfile | 7 -- README.md | 19 +--- fastlane/Appfile | 38 ------- fastlane/Fastfile | 247 -------------------------------------------- fastlane/Pluginfile | 5 - fastlane/README.md | 72 ------------- 7 files changed, 1 insertion(+), 387 deletions(-) delete mode 100644 6.0.1 delete mode 100644 Gemfile delete mode 100644 fastlane/Appfile delete mode 100644 fastlane/Fastfile delete mode 100644 fastlane/Pluginfile delete mode 100644 fastlane/README.md diff --git a/6.0.1 b/6.0.1 deleted file mode 100644 index e69de29b..00000000 diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 0007d641..00000000 --- a/Gemfile +++ /dev/null @@ -1,7 +0,0 @@ -source 'https://rubygems.org' - -gem 'slather' -gem "fastlane" -gem "cocoapods" -plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') -eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/README.md b/README.md index f8f056a3..bee04245 100644 --- a/README.md +++ b/README.md @@ -107,29 +107,12 @@ We recommend using [AWS S3](https://docs.aws.amazon.com/AmazonS3/latest/userguid Another alternative for managing sensitive files whithin the repo using Git-Secret can be found in the [**feature/git-secret**](https://github.com/rootstrap/ios-base/tree/feature/jenkins) branch -## CI/CD configuration with Bitrise (updated on Dec 12th 2021) +## CI/CD configuration with Bitrise We are going to start using a tool called Bitrise to configure de CI/CD pipelines for mobiles apps. --> For iOS apps you can find how to do it in this link: https://www.notion.so/rootstrap/iOS-CI-CD-01e00409a0144f5b85212bf889c627dd - -## Automated Build and Deployment using Fastlane (DEPRECATED) - -We use [Fastlane](https://docs.fastlane.tools) to automate code signing, building and release to TestFlight. - -See details in [Fastlane folder](fastlane/README.md). - -## Continuous Integration / Delivery (DEPRECATED) - -We recommend [GitHub Actions](https://docs.github.com/en/actions) for integrating Fastlane into a CI/CD pipeline. You can find two workflows in the GitHub workflows folder: -* [ci.yml](.github/workflows/release.myl) : triggered on any push and PR, runs unit tests, coverage report and static analysis with [CodeClimate](https://github.com/codeclimate/codeclimate) -* [release.yml](.github/workflows/release.yml) : triggered on push to specific branches, builds, signs and submits to TestFlight - -Alternatively you can merge branch [**feature/jenkins**](https://github.com/rootstrap/ios-base/tree/feature/jenkins) for some equivalent CICD boilerplate with **Jenkins**. - -On both alternatives we assume usage of Fastlane match for managing signing Certificates and Profiles, and AWS S3 for storing other files containing third-party keys - ## License iOS-Base is available under the MIT license. See the LICENSE file for more info. diff --git a/fastlane/Appfile b/fastlane/Appfile deleted file mode 100644 index e53ea1ee..00000000 --- a/fastlane/Appfile +++ /dev/null @@ -1,38 +0,0 @@ -# app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app -# apple_id("[[APPLE_ID]]") # Your Apple email address - - -# For more information about the Appfile, see: -# https://docs.fastlane.tools/advanced/#appfile - -for_platform :ios do - - apple_id(ENV['FASTLANE_USER']) - - team_id(ENV['APPLE_TEAM_ID']) - - for_lane :build_develop do - app_identifier('com.rootstrap.ios-base-develop') - end - - for_lane :release_develop do - app_identifier('com.rootstrap.ios-base-develop') - end - - for_lane :build_staging do - app_identifier('com.rootstrap.ios-base-staging') - end - - for_lane :release_staging do - app_identifier('com.rootstrap.ios-base-staging') - end - - for_lane :build_production do - app_identifier('com.rootstrap.ios-base-production') - end - - for_lane :release_production do - app_identifier('com.rootstrap.ios-base-production') - end - -end diff --git a/fastlane/Fastfile b/fastlane/Fastfile deleted file mode 100644 index 39f39020..00000000 --- a/fastlane/Fastfile +++ /dev/null @@ -1,247 +0,0 @@ -# This file contains the fastlane.tools configuration -# You can find the documentation at https://docs.fastlane.tools -# -# For a list of all available actions, check out -# -# https://docs.fastlane.tools/actions -# -# For a list of all available plugins, check out -# -# https://docs.fastlane.tools/plugins/available-plugins -# - -# Uncomment the line if you want fastlane to automatically update itself -# update_fastlane - -skip_docs - -default_platform(:ios) - -# CONFIG VARIABLES -app_name = 'ios-base' -team_id = ENV["APPLE_TEAM_ID"] # The organization's team id in the Apple Developer portal -cert = ENV["APPLE_CERT"] # Local path to distribution certificate file to be used for signing the build -key = ENV["APPLE_KEY"] # Private key (.p12 file) used for encrypting certificate -key_pwd = ENV["APPLE_KEY_PASSWORD"] # Password to private key file -appstore_key_id = ENV["APP_STORE_CONNECT_API_KEY_KEY_ID"] # AppStore Connect API id and issuer -appstore_issuer_id = ENV["APP_STORE_CONNECT_API_KEY_ISSUER_ID"] # -appstore_key_filepath = ENV["APP_STORE_CONNECT_API_KEY_FILE"] # location of .p8 API key file - -# S3 -s3_key = ENV["AWS_ACCESS_KEY_ID"] # credentials for uploading files to S3 -s3_secret_key = ENV["AWS_SECRET_ACCESS_KEY"] # -s3_region = ENV["AWS_REGION"] # -s3_bucket = ENV["BUILDS_BUCKET"] # S3 bucket and parent folder to upload to -folder = ENV["FOLDER"] # - -# Slack -slack_channel = ENV["SLACK_CHANNEL"] # Slack webhook url and channel name for sending notifications upon completion -slack_url = ENV["SLACK_URL"] # - - - -platform :ios do - - lane :set_signing do - # Create keychain - (Travis setup works for GitHhub Actions too) - setup_ci( - force: true, - provider: "travis" - ) - # Unlock keychain and set as default - unlock_keychain( - path: "fastlane_tmp_keychain", - password: "", - set_default: true - ) - # Import .cer and .p12 - this is a workaround for fastlane match when we retrieve certs from a custom location - import_certificate( - certificate_path: key, - certificate_password: key_pwd, - keychain_name: "fastlane_tmp_keychain", - keychain_password: "", - log_output: true - ) - import_certificate( - certificate_path: cert, - keychain_name: "fastlane_tmp_keychain", - keychain_password: "", - log_output: true - ) - end - - lane :build_and_sign do |options| - set_signing - # pod install - cocoapods - gym( - scheme: options[:scheme], - workspace: app_name+'.xcworkspace', - export_method: options[:method], - export_options: {iCloudContainerEnvironment: 'Production'}, - clean: true, - include_bitcode: true, - output_name: options[:scheme]+".ipa" - ) - end - - lane :publish_appstore do |options| - # get timestamp - datetime = sh("date +%Y%m%d%H%M").chomp - # get branch name - branch = git_branch() - # get app version - version = get_version_number(target: options[:scheme]) - changelog_from_git_commits( - pretty: "- (%ae) %s",# Optional, lets you provide a custom format to apply to each commit when generating the changelog text - date_format: "short",# Optional, lets you provide an additional date format to dates within the pretty-formatted string - match_lightweight_tag: false, # Optional, lets you ignore lightweight (non-annotated) tags when searching for the last tag - merge_commit_filtering: "exclude_merges" # Optional, lets you filter out merge commits - ) - # Load an App Store Connect API token - api_key = app_store_connect_api_key( - key_id: appstore_key_id, - issuer_id: appstore_issuer_id, - key_filepath: appstore_key_filepath, - in_house: false # determines this is an AppStore team - ) - # Submit to TestFlight with the previous token - upload_to_testflight( - api_key: api_key, - skip_waiting_for_build_processing: true - ) - # send Slack notification - optional - slack( - message: "Hi! A new iOS "+options[:scheme]+" build has been submitted to TestFlight", - payload: { - "Build Date" => Time.new.to_s, - "Release Version" => version - }, - channel: slack_channel, - slack_url: slack_url, - use_webhook_configured_username_and_icon: true, - fail_on_error: false, - success: true - ) - end - - lane :publish_s3 do |options| - # get timestamp - datetime = sh("date +%Y%m%d%H%M").chomp - # get branch name - branch = git_branch() - # get app version - version = get_version_number(target: options[:scheme]) - # push to S3 - s3_path = folder+"/ios/"+branch+"/"+version+"-"+datetime+"/" - aws_s3( - access_key: s3_key, - secret_access_key: s3_secret_key, - bucket: s3_bucket, - region: s3_region, - ipa: lane_context[SharedValues::IPA_OUTPUT_PATH], - path: s3_path, - acl: "public-read", - upload_metadata: true - ) - # send notification - slack( - message: "Hi! A new iOS build has been uploaded for "+options[:scheme], - payload: { - "Build Date" => Time.new.to_s, - "Release Version" => version, - "Location" => lane_context[SharedValues::S3_FOLDER_OUTPUT_PATH], - "Download link" => "https://"+s3_bucket+".s3.amazonaws.com/"+s3_path+options[:scheme]+".ipa" - }, - channel: slack_channel, - slack_url: slack_url, - use_webhook_configured_username_and_icon: true, - fail_on_error: false, - success: true - ) - end - -#DEVELOP - lane :build_develop do - build_and_sign( - scheme: app_name+'-develop', - method: 'ad-hoc' - ) - end - - lane :share_develop do - build_and_sign( - scheme: app_name+'-develop', - method: 'ad-hoc' - ) - publish_s3( - scheme: app_name+'-develop' - ) - end - - lane :release_develop do - build_and_sign( - scheme: app_name+'-develop', - method: 'app-store' - ) - publish_appstore( - scheme: app_name+'-develop' - ) - end - - #STAGING - lane :build_staging do - build_and_sign( - scheme: app_name+'-staging', - method: 'ad-hoc' - ) - end - - lane :share_staging do - build_and_sign( - scheme: app_name+'-staging', - method: 'ad-hoc' - ) - publish_s3( - scheme: app_name+'-staging' - ) - end - - lane :release_staging do - build_and_sign( - scheme: app_name+'-staging', - method: 'app-store' - ) - publish_appstore( - scheme: app_name+'-staging' - ) - end - - #PRODUCTION - lane :build_production do - build_and_sign( - scheme: app_name+'-production', - method: 'ad-hoc' - ) - end - - lane :share_production do - build_and_sign( - scheme: app_name+'-production', - method: 'ad-hoc' - ) - publish_s3( - scheme: app_name+'-production' - ) - end - - lane :release_production do - build_and_sign( - scheme: app_name+'-production', - method: 'app-store' - ) - publish_appstore( - scheme: app_name+'-production' - ) - end -end diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile deleted file mode 100644 index 79ba9e7d..00000000 --- a/fastlane/Pluginfile +++ /dev/null @@ -1,5 +0,0 @@ -# Autogenerated by fastlane -# -# Ensure this file is checked in to source control! - -gem 'fastlane-plugin-aws_s3' \ No newline at end of file diff --git a/fastlane/README.md b/fastlane/README.md deleted file mode 100644 index 7d2ad0d1..00000000 --- a/fastlane/README.md +++ /dev/null @@ -1,72 +0,0 @@ -Fastlane documentation -================ -We use [Fastlane](https://docs.fastlane.tools/) for automating the iOS application build and submission - -# Installation - -Make sure you have the latest version of the Xcode command line tools installed: - -``` -xcode-select --install -``` - -Install _fastlane_ using -``` -[sudo] gem install fastlane -NV -``` -or alternatively using `brew cask install fastlane` - -# Project setup - -1. Generate certificate and profiles for each target -2. [Disable automatic signing](https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html#//apple_ref/doc/uid/TP40005929-CH4-SW7) for each target in XCode and associate to the right provisioning profile -3. For uploading the builds to TestFlight, [AppStore Connect API](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) keys are required -4. For uploading the builds to S3, [AWS keys](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys) with valid permissions are required - -# Required environment variables - -* `FASTLANE_USER` : Your App Store Connect / Apple Developer Portal id used for managing certificates and submitting to the App Store -* `FASTLANE_PASSWORD` : Your App Store Connect / Apple Developer Portal password, usually only needed if you also set the -* `FASTLANE_TEAM_ID` : Developer Portal team id -* `LANG` and `LC_ALL` : These set up the locale your shell and all the commands you execute run at. These need to be set to UTF-8 to work correctly,for example en_US.UTF-8 -* `APPLE_CERT` : Local path to distribution certificate file to be used for signing the build -* `APPLE_KEY` : Private key (.p12 file) used for encrypting certificate -* `APPLE_KEY_PASSWORD` : Password to private key file -* `APP_STORE_CONNECT_API_KEY_KEY_ID` : AppStore Connect API ID -* `APP_STORE_CONNECT_API_KEY_ISSUER_ID` : AppStore Connect issuer ID -* `APP_STORE_CONNECT_API_KEY_FILE` : location of .p8 API key file -* `AWS_ACCESS_KEY_ID` : credentials for uploading files to S3 -* `AWS_SECRET_ACCESS_KEY` -* `AWS_REGION` -* `BUILDS_BUCKET` : S3 bucket to upload the build to -* `SLACK_CHANNEL` : Slack webhook url for sending notifications upon completion -* `SLACK_URL` : Slack channel name - -# Available Actions - -## build_* -* Runs `pod install` -* If needed downloads and installs the corresponding distribution certificate and profile -* Builds and archive corresponding target (.ipa file is kept locally) -``` -fastlane build_develop -``` - -## share_* -* Runs the build steps for the corresponding target -* Gathers build version -* Uploads the resulting .ipa to S3 -* Sends a Slack notification -``` -fastlane share_develop -``` - -## release_* -* Checks for the Git status -* Runs the build steps for the corresponding target -* Generates changelog -* Pushes the resulting .ipa to TestFlight -* Sends a Slack notification -``` -fastlane release_develop -``` From 31e9d844eccb0ea9e6989e4ebde76da874d3a4cf Mon Sep 17 00:00:00 2001 From: German Lopez Date: Thu, 1 Sep 2022 14:25:33 -0300 Subject: [PATCH 02/13] Run test suit on Github Actions --- .github/workflows/ci.yml | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b57d2b6f..26693a48 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,13 +3,17 @@ name: CI Build # Run for any commits to any branch on: [push, pull_request] - env: LANG: en_US.UTF-8 # CodeClimate CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} # Notifications SLACK_WEBHOOK_URL: ${{ secrets.SLACK_URL }} + FOLDER: GoogleFirebase + AWS_REGION: us-east-1 + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + BUILDS_BUCKET: ${{ secrets.AWS_S3_BUILDS_BUCKET }} jobs: @@ -29,11 +33,31 @@ jobs: chmod +x ./cc-test-reporter ./cc-test-reporter before-build - # Executes pod install and runs test against Debug target - - name: Run tests - uses: maierj/fastlane-action@v1.4.0 + # Executes pod install + - name: Installing Dependencies + run: pod install --repo-update + + # Downloads Firebase files + - name: Downloading Google Firebase plist files + run: aws s3 cp s3://$BUILDS_BUCKET/$FOLDER/ ios-base/Resources/$FOLDER/ --recursive + # uses: keithweaver/aws-s3-github-action@v1.0.0 + # with: + # command: cp + # source: s3://${{ secrets.BUILDS_BUCKET }}/ios-base/GoogleService-Info.plist + # destination: "ios-base/Resources/GoogleService-Info.plist" + # aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} + # aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # aws_region: us-east-1 + + # Runs test on the develop scheme + - name: Running Test Suite + uses: mxcl/xcodebuild@v1 with: - lane: debug_develop + platform: iOS + use_modern_build_system: true + code-coverage: true + scheme: ios-base-develop + configuration: Debug # no default, ie. `xcodebuild` decides itself - name: Send test coverage report run: ./cc-test-reporter after-build @@ -44,4 +68,4 @@ jobs: status: ${{ job.status }} text: 'ios-base build status is ${{ job.status }}' fields: repo,message,commit,author,action,eventName,ref,workflow,job,took - if: always() + if: always() From 4dcb6caa2834aa3ad073004361a272bcd8f89807 Mon Sep 17 00:00:00 2001 From: German Lopez Date: Thu, 1 Sep 2022 16:36:46 -0300 Subject: [PATCH 03/13] Set workspace in CI yml --- .github/workflows/ci.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26693a48..9b49836c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,22 +40,14 @@ jobs: # Downloads Firebase files - name: Downloading Google Firebase plist files run: aws s3 cp s3://$BUILDS_BUCKET/$FOLDER/ ios-base/Resources/$FOLDER/ --recursive - # uses: keithweaver/aws-s3-github-action@v1.0.0 - # with: - # command: cp - # source: s3://${{ secrets.BUILDS_BUCKET }}/ios-base/GoogleService-Info.plist - # destination: "ios-base/Resources/GoogleService-Info.plist" - # aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} - # aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - # aws_region: us-east-1 # Runs test on the develop scheme - name: Running Test Suite uses: mxcl/xcodebuild@v1 with: platform: iOS - use_modern_build_system: true code-coverage: true + workspace: ios-base.xcworkspace scheme: ios-base-develop configuration: Debug # no default, ie. `xcodebuild` decides itself From e60107da489c00c7756a1aa64b800ac71b63ebf6 Mon Sep 17 00:00:00 2001 From: German Lopez Date: Thu, 1 Sep 2022 17:07:58 -0300 Subject: [PATCH 04/13] Add dummy Google plist file --- ios-base.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ios-base.xcodeproj/project.pbxproj b/ios-base.xcodeproj/project.pbxproj index fed39d2c..0f584766 100644 --- a/ios-base.xcodeproj/project.pbxproj +++ b/ios-base.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 07741D72218CDED600DB3B97 /* FirstViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07741D71218CDED600DB3B97 /* FirstViewModel.swift */; }; 9B0C72711C738D3400BAF3B1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0C72691C738D3400BAF3B1 /* AppDelegate.swift */; }; 9B0C72721C738D3400BAF3B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9B0C726A1C738D3400BAF3B1 /* Assets.xcassets */; }; + 9B0E254728C140E3006FBCD4 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9B0E254628C140E3006FBCD4 /* GoogleService-Info.plist */; }; 9B2D00F3278C8ACE000657BE /* HeaderProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B2D00F2278C8ACE000657BE /* HeaderProvider.swift */; }; 9B2D00F5278C8C87000657BE /* SessionHeadersProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B2D00F4278C8C87000657BE /* SessionHeadersProvider.swift */; }; 9B2D00F7278C8DEF000657BE /* RailsAPIHeadersProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B2D00F6278C8DEF000657BE /* RailsAPIHeadersProvider.swift */; }; @@ -137,6 +138,7 @@ 9B0C72691C738D3400BAF3B1 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; tabWidth = 2; }; 9B0C726A1C738D3400BAF3B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 9B0C726D1C738D3400BAF3B1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9B0E254628C140E3006FBCD4 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 9B12AF6B1F269B03005FD465 /* ThirdPartyKeys.example.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ThirdPartyKeys.example.plist; sourceTree = ""; }; 9B2D00F2278C8ACE000657BE /* HeaderProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderProvider.swift; sourceTree = ""; }; 9B2D00F4278C8C87000657BE /* SessionHeadersProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionHeadersProvider.swift; sourceTree = ""; }; @@ -471,6 +473,7 @@ 9B8D306720AB45160050697F /* Resources */ = { isa = PBXGroup; children = ( + 9B0E254628C140E3006FBCD4 /* GoogleService-Info.plist */, 9B8D306820AB451E0050697F /* Localization */, ); path = Resources; @@ -788,6 +791,7 @@ buildActionMask = 2147483647; files = ( FDFAAB0B269CD56D0007DB8B /* Localizable.strings in Resources */, + 9B0E254728C140E3006FBCD4 /* GoogleService-Info.plist in Resources */, 9BFA84F31C776827009F64E4 /* LaunchScreen.storyboard in Resources */, 9B0C72721C738D3400BAF3B1 /* Assets.xcassets in Resources */, ); From 702d69464138da4451e513b43b932fbf0aa92ee6 Mon Sep 17 00:00:00 2001 From: German Lopez Date: Fri, 25 Feb 2022 15:12:11 -0300 Subject: [PATCH 05/13] Fix UI test suite --- ios-baseUITests/ios_baseUITests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ios-baseUITests/ios_baseUITests.swift b/ios-baseUITests/ios_baseUITests.swift index 8c395de4..9c22692d 100644 --- a/ios-baseUITests/ios_baseUITests.swift +++ b/ios-baseUITests/ios_baseUITests.swift @@ -21,6 +21,7 @@ class ios_baseUITests: XCTestCase { app.launchArguments = ["Automation Test"] try? networkMocker.setUp() + app.logOutIfNeeded(in: self) } override func tearDown() { From d9d2dd850f1a7cf17747e278cdbd8e2152b80784 Mon Sep 17 00:00:00 2001 From: German Lopez Date: Fri, 25 Feb 2022 16:08:07 -0300 Subject: [PATCH 06/13] Fix UI tests logout setup --- ios-baseUITests/ios_baseUITests.swift | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/ios-baseUITests/ios_baseUITests.swift b/ios-baseUITests/ios_baseUITests.swift index 9c22692d..0d247ccf 100644 --- a/ios-baseUITests/ios_baseUITests.swift +++ b/ios-baseUITests/ios_baseUITests.swift @@ -7,7 +7,6 @@ // import XCTest -@testable import ios_base_Debug class ios_baseUITests: XCTestCase { @@ -21,6 +20,7 @@ class ios_baseUITests: XCTestCase { app.launchArguments = ["Automation Test"] try? networkMocker.setUp() + networkMocker.stubLogOut() app.logOutIfNeeded(in: self) } @@ -87,13 +87,6 @@ class ios_baseUITests: XCTestCase { alert.buttons.allElementsBoundByIndex.first?.tap() } - - let logOutButton = app.buttons["LogoutButton"] - waitFor(element: logOutButton, timeOut: 5) - - networkMocker.stubLogOut() - - logOutButton.tap() } func testSignInSuccess() { @@ -107,12 +100,6 @@ class ios_baseUITests: XCTestCase { let logOutButton = app.buttons["LogoutButton"] waitFor(element: logOutButton, timeOut: 10) - - networkMocker.stubLogOut() - logOutButton.forceTap() - - let goToSignInButton = app.buttons["GoToSignInButton"] - waitFor(element: goToSignInButton, timeOut: 10) } func testSignInFailure() { From c85198c7dadc93a1ceb7035037f28a8cd422d85f Mon Sep 17 00:00:00 2001 From: German Lopez Date: Thu, 1 Sep 2022 17:30:29 -0300 Subject: [PATCH 07/13] Test S3 download phase --- .github/workflows/ci.yml | 5 ++++- ios-base.xcodeproj/project.pbxproj | 10 +++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b49836c..f368d201 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,10 @@ jobs: # Downloads Firebase files - name: Downloading Google Firebase plist files - run: aws s3 cp s3://$BUILDS_BUCKET/$FOLDER/ ios-base/Resources/$FOLDER/ --recursive + run: | + aws s3 cp s3://$BUILDS_BUCKET/$FOLDER/ ios-base/Resources/$FOLDER/ --recursive + ls + ls ios-base/Resources # Runs test on the develop scheme - name: Running Test Suite diff --git a/ios-base.xcodeproj/project.pbxproj b/ios-base.xcodeproj/project.pbxproj index 9b69b489..ce718b1d 100644 --- a/ios-base.xcodeproj/project.pbxproj +++ b/ios-base.xcodeproj/project.pbxproj @@ -368,6 +368,14 @@ path = "ios-base"; sourceTree = ""; }; + 9B0E254B28C15003006FBCD4 /* GoogleFirebase */ = { + isa = PBXGroup; + children = ( + 9B0E254628C140E3006FBCD4 /* GoogleService-Info.plist */, + ); + path = GoogleFirebase; + sourceTree = ""; + }; 9B2D00FC278CAFFC000657BE /* Endpoints */ = { isa = PBXGroup; children = ( @@ -448,7 +456,7 @@ 9B8D306720AB45160050697F /* Resources */ = { isa = PBXGroup; children = ( - 9B0E254628C140E3006FBCD4 /* GoogleService-Info.plist */, + 9B0E254B28C15003006FBCD4 /* GoogleFirebase */, 9B8D306820AB451E0050697F /* Localization */, ); path = Resources; From 35dcd2e282c6cf3c7dfa85d3348f3a05da7b7fab Mon Sep 17 00:00:00 2001 From: German Lopez Date: Fri, 2 Sep 2022 13:25:22 -0300 Subject: [PATCH 08/13] Add unit tests --- Podfile | 12 +- ios-base.xcodeproj/project.pbxproj | 328 +++++++++++++----- ios-baseUITests/NetworkMocker+Stubs.swift | 56 +++ ios-baseUITests/NetworkMocker.swift | 54 +-- ios-baseUITests/NetworkMockerExtension.swift | 56 --- .../AuthenticationError.json | 0 .../StubResponses/GetProfileFailure.json | 3 + .../GetProfileSuccessfully.json | 0 .../LogOutSuccessfully.json | 0 .../LoginSuccessfully.json | 0 .../SignUpSuccessfully.json | 0 .../Extensions/StringExtensionUnitTests.swift | 2 +- .../Cases/Managers/SessionManagerTests.swift | 67 ++++ .../Cases/Managers/UserDataManagerTests.swift | 36 ++ .../AuthenticationServicesTests.swift | 16 + .../Cases/Services/UserServiceUnitTests.swift | 71 ++++ ios-baseUnitTests/Extensions/User+Tests.swift | 14 + .../Services/UserServiceUnitTests.swift | 96 ----- 18 files changed, 547 insertions(+), 264 deletions(-) create mode 100644 ios-baseUITests/NetworkMocker+Stubs.swift delete mode 100644 ios-baseUITests/NetworkMockerExtension.swift rename ios-baseUITests/Resources/{ => StubResponses}/AuthenticationError.json (100%) create mode 100644 ios-baseUITests/Resources/StubResponses/GetProfileFailure.json rename ios-baseUITests/Resources/{ => StubResponses}/GetProfileSuccessfully.json (100%) rename ios-baseUITests/Resources/{ => StubResponses}/LogOutSuccessfully.json (100%) rename ios-baseUITests/Resources/{ => StubResponses}/LoginSuccessfully.json (100%) rename ios-baseUITests/Resources/{ => StubResponses}/SignUpSuccessfully.json (100%) rename ios-baseUnitTests/{ => Cases}/Extensions/StringExtensionUnitTests.swift (95%) create mode 100644 ios-baseUnitTests/Cases/Managers/SessionManagerTests.swift create mode 100644 ios-baseUnitTests/Cases/Managers/UserDataManagerTests.swift create mode 100644 ios-baseUnitTests/Cases/Services/AuthenticationServicesTests.swift create mode 100644 ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift create mode 100644 ios-baseUnitTests/Extensions/User+Tests.swift delete mode 100644 ios-baseUnitTests/Services/UserServiceUnitTests.swift diff --git a/Podfile b/Podfile index 15d413ca..d6db13bc 100644 --- a/Podfile +++ b/Podfile @@ -2,6 +2,11 @@ platform :ios, '14.0' use_frameworks! inhibit_all_warnings! +def shared_test_dependencies + inherit! :complete + pod 'Swifter', '~> 1.5.0' +end + target 'ios-base' do pod 'RSSwiftNetworking/AlamofireProvider', '~> 1.1.0' pod 'IQKeyboardManagerSwift', '~> 6.1.1' @@ -24,7 +29,10 @@ target 'ios-base' do # ------ target 'ios-baseUITests' do - inherit! :complete - pod 'Swifter', '~> 1.5.0' + shared_test_dependencies + end + + target 'ios-baseUnitTests' do + shared_test_dependencies end end diff --git a/ios-base.xcodeproj/project.pbxproj b/ios-base.xcodeproj/project.pbxproj index ce718b1d..016b9c1c 100644 --- a/ios-base.xcodeproj/project.pbxproj +++ b/ios-base.xcodeproj/project.pbxproj @@ -17,16 +17,29 @@ 0737296024AD389F008C54D9 /* AuthenticationError.json in Resources */ = {isa = PBXBuildFile; fileRef = 0737295B24AD389F008C54D9 /* AuthenticationError.json */; }; 0737296124AD389F008C54D9 /* LogOutSuccessfully.json in Resources */ = {isa = PBXBuildFile; fileRef = 0737295C24AD389F008C54D9 /* LogOutSuccessfully.json */; }; 0737296224AD389F008C54D9 /* SignUpSuccessfully.json in Resources */ = {isa = PBXBuildFile; fileRef = 0737295D24AD389F008C54D9 /* SignUpSuccessfully.json */; }; - 0737296524AD38CE008C54D9 /* NetworkMockerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0737296324AD38CE008C54D9 /* NetworkMockerExtension.swift */; }; + 0737296524AD38CE008C54D9 /* NetworkMocker+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0737296324AD38CE008C54D9 /* NetworkMocker+Stubs.swift */; }; 0737296624AD38CE008C54D9 /* NetworkMocker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0737296424AD38CE008C54D9 /* NetworkMocker.swift */; }; 074889A72477251500A0029E /* ActivityIndicatorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074889A62477251500A0029E /* ActivityIndicatorPresenter.swift */; }; - 0748CCCC6E80D2040F1C75D3 /* Pods_ios_base.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A449B00976D690BCC4003AA1 /* Pods_ios_base.framework */; }; 074D20D9248E993B002A39B4 /* AuthenticationServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074D20D8248E993B002A39B4 /* AuthenticationServices.swift */; }; 074D20DB248EA832002A39B4 /* UserServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074D20DA248EA832002A39B4 /* UserServices.swift */; }; 07741D72218CDED600DB3B97 /* FirstViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07741D71218CDED600DB3B97 /* FirstViewModel.swift */; }; + 21EFE678CD3F8778FC4DD4A1 /* Pods_ios_base_ios_baseUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EB114986F25A947DAF55BD /* Pods_ios_base_ios_baseUITests.framework */; }; 9B0C72711C738D3400BAF3B1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0C72691C738D3400BAF3B1 /* AppDelegate.swift */; }; 9B0C72721C738D3400BAF3B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9B0C726A1C738D3400BAF3B1 /* Assets.xcassets */; }; 9B0E254728C140E3006FBCD4 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9B0E254628C140E3006FBCD4 /* GoogleService-Info.plist */; }; + 9B0E254D28C151D0006FBCD4 /* AuthenticationServicesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0E254C28C151D0006FBCD4 /* AuthenticationServicesTests.swift */; }; + 9B0E255028C152A8006FBCD4 /* UserDataManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0E254F28C152A8006FBCD4 /* UserDataManagerTests.swift */; }; + 9B0E255228C24B81006FBCD4 /* User+Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0E255128C24B81006FBCD4 /* User+Tests.swift */; }; + 9B0E255628C24F96006FBCD4 /* SessionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0E255528C24F96006FBCD4 /* SessionManagerTests.swift */; }; + 9B0E255728C2544E006FBCD4 /* NetworkMocker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0737296424AD38CE008C54D9 /* NetworkMocker.swift */; }; + 9B0E255828C25460006FBCD4 /* NetworkMocker+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0737296324AD38CE008C54D9 /* NetworkMocker+Stubs.swift */; }; + 9B0E255A28C254AB006FBCD4 /* LoginSuccessfully.json in Resources */ = {isa = PBXBuildFile; fileRef = 0737295A24AD389F008C54D9 /* LoginSuccessfully.json */; }; + 9B0E255B28C254AB006FBCD4 /* GetProfileSuccessfully.json in Resources */ = {isa = PBXBuildFile; fileRef = 0737295924AD389F008C54D9 /* GetProfileSuccessfully.json */; }; + 9B0E255C28C254AB006FBCD4 /* LogOutSuccessfully.json in Resources */ = {isa = PBXBuildFile; fileRef = 0737295C24AD389F008C54D9 /* LogOutSuccessfully.json */; }; + 9B0E255D28C254AB006FBCD4 /* SignUpSuccessfully.json in Resources */ = {isa = PBXBuildFile; fileRef = 0737295D24AD389F008C54D9 /* SignUpSuccessfully.json */; }; + 9B0E255E28C254AB006FBCD4 /* AuthenticationError.json in Resources */ = {isa = PBXBuildFile; fileRef = 0737295B24AD389F008C54D9 /* AuthenticationError.json */; }; + 9B0E256028C25987006FBCD4 /* GetProfileFailure.json in Resources */ = {isa = PBXBuildFile; fileRef = 9B0E255F28C25987006FBCD4 /* GetProfileFailure.json */; }; + 9B0E256128C26C63006FBCD4 /* GetProfileFailure.json in Resources */ = {isa = PBXBuildFile; fileRef = 9B0E255F28C25987006FBCD4 /* GetProfileFailure.json */; }; 9B2D0100278CB1C2000657BE /* AuthEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B2D00FF278CB1C2000657BE /* AuthEndpoint.swift */; }; 9B2D0102278DE207000657BE /* UserEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B2D0101278DE207000657BE /* UserEndpoint.swift */; }; 9B2F322C28999F2100D9C710 /* APIClient+Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B2F322B28999F2100D9C710 /* APIClient+Application.swift */; }; @@ -50,8 +63,9 @@ 9BAFB7C32114E3CD0099DC61 /* SignInViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BAFB7C22114E3CD0099DC61 /* SignInViewModel.swift */; }; 9BD01E361F01641E007255E3 /* DictionaryExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BD01E351F01641E007255E3 /* DictionaryExtension.swift */; }; 9BFA84F31C776827009F64E4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9BFA84F21C776827009F64E4 /* LaunchScreen.storyboard */; }; + B851CCEB05F5B120379F25DF /* Pods_ios_base.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CA40D9689960D750465B012 /* Pods_ios_base.framework */; }; BB83CFA64940E660BA4A4420 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; - C0EC01A885BD14BD1552DE80 /* Pods_ios_base_ios_baseUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A1760A5E754FEA0591B3C8A /* Pods_ios_base_ios_baseUITests.framework */; }; + BD289BF04FC44C8FB571FA6D /* Pods_ios_base_ios_baseUnitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 485F72F34746F5517F7E7B0E /* Pods_ios_base_ios_baseUnitTests.framework */; }; E59EB5E2CE360C64F9DB38FF /* (null) in Frameworks */ = {isa = PBXBuildFile; }; E81171921DE5EFB7003D3DF5 /* ViewControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81171911DE5EFB7003D3DF5 /* ViewControllerExtension.swift */; }; E8290BFE1D832D9200599960 /* ViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8290BFD1D832D9200599960 /* ViewExtension.swift */; }; @@ -94,6 +108,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 05EB114986F25A947DAF55BD /* Pods_ios_base_ios_baseUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ios_base_ios_baseUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 071CD2612228544700E6D385 /* FontExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontExtension.swift; sourceTree = ""; }; 07276EB023F5CC460089C0AD /* ios-baseUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ios-baseUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 07276EB223F5CC460089C0AD /* ios_baseUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ios_baseUITests.swift; sourceTree = ""; }; @@ -106,18 +121,27 @@ 0737295B24AD389F008C54D9 /* AuthenticationError.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = AuthenticationError.json; sourceTree = ""; }; 0737295C24AD389F008C54D9 /* LogOutSuccessfully.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = LogOutSuccessfully.json; sourceTree = ""; }; 0737295D24AD389F008C54D9 /* SignUpSuccessfully.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = SignUpSuccessfully.json; sourceTree = ""; }; - 0737296324AD38CE008C54D9 /* NetworkMockerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkMockerExtension.swift; sourceTree = ""; }; + 0737296324AD38CE008C54D9 /* NetworkMocker+Stubs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NetworkMocker+Stubs.swift"; sourceTree = ""; }; 0737296424AD38CE008C54D9 /* NetworkMocker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkMocker.swift; sourceTree = ""; }; 074889A62477251500A0029E /* ActivityIndicatorPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorPresenter.swift; sourceTree = ""; }; 074D20D8248E993B002A39B4 /* AuthenticationServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServices.swift; sourceTree = ""; }; 074D20DA248EA832002A39B4 /* UserServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserServices.swift; sourceTree = ""; }; 07741D71218CDED600DB3B97 /* FirstViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstViewModel.swift; sourceTree = ""; }; - 2C8C72195C24B451D2FEA654 /* Pods-ios-base.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base.debug.xcconfig"; path = "Target Support Files/Pods-ios-base/Pods-ios-base.debug.xcconfig"; sourceTree = ""; }; - 6A1760A5E754FEA0591B3C8A /* Pods_ios_base_ios_baseUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ios_base_ios_baseUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 18B6971A49CF5630F7B81E0C /* Pods-ios-base.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base.release.xcconfig"; path = "Target Support Files/Pods-ios-base/Pods-ios-base.release.xcconfig"; sourceTree = ""; }; + 1CA40D9689960D750465B012 /* Pods_ios_base.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ios_base.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 357CCDB2088853F34FB33593 /* Pods-ios-base-ios-baseUITests.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base-ios-baseUITests.staging.xcconfig"; path = "Target Support Files/Pods-ios-base-ios-baseUITests/Pods-ios-base-ios-baseUITests.staging.xcconfig"; sourceTree = ""; }; + 485F72F34746F5517F7E7B0E /* Pods_ios_base_ios_baseUnitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ios_base_ios_baseUnitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4D27CF6609E56949C1BE4CFE /* Pods-ios-base-ios-baseUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base-ios-baseUITests.debug.xcconfig"; path = "Target Support Files/Pods-ios-base-ios-baseUITests/Pods-ios-base-ios-baseUITests.debug.xcconfig"; sourceTree = ""; }; + 5B0BC77FA89B89E84632FAA3 /* Pods-ios-base-ios-baseUnitTests.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base-ios-baseUnitTests.staging.xcconfig"; path = "Target Support Files/Pods-ios-base-ios-baseUnitTests/Pods-ios-base-ios-baseUnitTests.staging.xcconfig"; sourceTree = ""; }; 9B0C72691C738D3400BAF3B1 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; tabWidth = 2; }; 9B0C726A1C738D3400BAF3B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 9B0C726D1C738D3400BAF3B1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9B0E254628C140E3006FBCD4 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 9B0E254C28C151D0006FBCD4 /* AuthenticationServicesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServicesTests.swift; sourceTree = ""; }; + 9B0E254F28C152A8006FBCD4 /* UserDataManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDataManagerTests.swift; sourceTree = ""; }; + 9B0E255128C24B81006FBCD4 /* User+Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "User+Tests.swift"; sourceTree = ""; }; + 9B0E255528C24F96006FBCD4 /* SessionManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManagerTests.swift; sourceTree = ""; }; + 9B0E255F28C25987006FBCD4 /* GetProfileFailure.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = GetProfileFailure.json; sourceTree = ""; }; 9B12AF6B1F269B03005FD465 /* ThirdPartyKeys.example.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ThirdPartyKeys.example.plist; sourceTree = ""; }; 9B2D00FF278CB1C2000657BE /* AuthEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthEndpoint.swift; sourceTree = ""; }; 9B2D0101278DE207000657BE /* UserEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserEndpoint.swift; sourceTree = ""; }; @@ -146,19 +170,19 @@ 9BAFB7C22114E3CD0099DC61 /* SignInViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignInViewModel.swift; sourceTree = ""; }; 9BD01E351F01641E007255E3 /* DictionaryExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DictionaryExtension.swift; sourceTree = ""; }; 9BFA84F21C776827009F64E4 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; - A143F3EC1A6E7D9DE1CA4A68 /* Pods-ios-base.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base.release.xcconfig"; path = "Target Support Files/Pods-ios-base/Pods-ios-base.release.xcconfig"; sourceTree = ""; }; - A449B00976D690BCC4003AA1 /* Pods_ios_base.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ios_base.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - B08689AA3A7C35FB70274F30 /* Pods-ios-base-ios-baseUITests.uitests.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base-ios-baseUITests.uitests.xcconfig"; path = "Target Support Files/Pods-ios-base-ios-baseUITests/Pods-ios-base-ios-baseUITests.uitests.xcconfig"; sourceTree = ""; }; - C37D0E0BA2DB34C9FBF07583 /* Pods-ios-base-ios-baseUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base-ios-baseUITests.release.xcconfig"; path = "Target Support Files/Pods-ios-base-ios-baseUITests/Pods-ios-base-ios-baseUITests.release.xcconfig"; sourceTree = ""; }; - C5763904A89F593C577044A0 /* Pods-ios-base-ios-baseUITests.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base-ios-baseUITests.staging.xcconfig"; path = "Target Support Files/Pods-ios-base-ios-baseUITests/Pods-ios-base-ios-baseUITests.staging.xcconfig"; sourceTree = ""; }; - C88232494AFE27E3FBFC69FE /* Pods-ios-base.uitests.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base.uitests.xcconfig"; path = "Target Support Files/Pods-ios-base/Pods-ios-base.uitests.xcconfig"; sourceTree = ""; }; - DA3A451D7100561A04D3D350 /* Pods-ios-base-ios-baseUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base-ios-baseUITests.debug.xcconfig"; path = "Target Support Files/Pods-ios-base-ios-baseUITests/Pods-ios-base-ios-baseUITests.debug.xcconfig"; sourceTree = ""; }; + A3634E941E71B5A93BFF9683 /* Pods-ios-base-ios-baseUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base-ios-baseUnitTests.release.xcconfig"; path = "Target Support Files/Pods-ios-base-ios-baseUnitTests/Pods-ios-base-ios-baseUnitTests.release.xcconfig"; sourceTree = ""; }; + BF22B306B6EF3149AD601B7D /* Pods-ios-base-ios-baseUITests.uitests.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base-ios-baseUITests.uitests.xcconfig"; path = "Target Support Files/Pods-ios-base-ios-baseUITests/Pods-ios-base-ios-baseUITests.uitests.xcconfig"; sourceTree = ""; }; + C21ACBC83B98EC2694374178 /* Pods-ios-base-ios-baseUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base-ios-baseUnitTests.debug.xcconfig"; path = "Target Support Files/Pods-ios-base-ios-baseUnitTests/Pods-ios-base-ios-baseUnitTests.debug.xcconfig"; sourceTree = ""; }; + C38496367A3FA02BB7FD7332 /* Pods-ios-base.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base.debug.xcconfig"; path = "Target Support Files/Pods-ios-base/Pods-ios-base.debug.xcconfig"; sourceTree = ""; }; + C76229E5314F62B6F01F4ACC /* Pods-ios-base.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base.staging.xcconfig"; path = "Target Support Files/Pods-ios-base/Pods-ios-base.staging.xcconfig"; sourceTree = ""; }; + D3A940731572555EE856ED09 /* Pods-ios-base.uitests.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base.uitests.xcconfig"; path = "Target Support Files/Pods-ios-base/Pods-ios-base.uitests.xcconfig"; sourceTree = ""; }; E81171911DE5EFB7003D3DF5 /* ViewControllerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = ViewControllerExtension.swift; sourceTree = ""; tabWidth = 2; }; E8290BFD1D832D9200599960 /* ViewExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = ViewExtension.swift; sourceTree = ""; tabWidth = 2; }; E8290C011D8330A800599960 /* StringExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = ""; tabWidth = 2; }; E8FBB1BE1DD21A32000D6740 /* Session.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = ""; }; E8FBB1C01DD21D18000D6740 /* SessionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = ""; }; - E9C31B61648676473C98614C /* Pods-ios-base.staging.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base.staging.xcconfig"; path = "Target Support Files/Pods-ios-base/Pods-ios-base.staging.xcconfig"; sourceTree = ""; }; + F193585DECDF701D9A03A8E1 /* Pods-ios-base-ios-baseUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base-ios-baseUITests.release.xcconfig"; path = "Target Support Files/Pods-ios-base-ios-baseUITests/Pods-ios-base-ios-baseUITests.release.xcconfig"; sourceTree = ""; }; + F9D7DF4589E7E4A9991E1C55 /* Pods-ios-base-ios-baseUnitTests.uitests.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ios-base-ios-baseUnitTests.uitests.xcconfig"; path = "Target Support Files/Pods-ios-base-ios-baseUnitTests/Pods-ios-base-ios-baseUnitTests.uitests.xcconfig"; sourceTree = ""; }; FA54D5871E11C59600F0DBEA /* FirstViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = FirstViewController.swift; sourceTree = ""; tabWidth = 2; }; FABDC9211EE1EB2B000DDAC3 /* ConfigurationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurationManager.swift; sourceTree = ""; }; FD8C0271269E2F2E003F86CE /* UIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIConstants.swift; sourceTree = ""; }; @@ -184,7 +208,7 @@ files = ( BB83CFA64940E660BA4A4420 /* (null) in Frameworks */, E59EB5E2CE360C64F9DB38FF /* (null) in Frameworks */, - C0EC01A885BD14BD1552DE80 /* Pods_ios_base_ios_baseUITests.framework in Frameworks */, + 21EFE678CD3F8778FC4DD4A1 /* Pods_ios_base_ios_baseUITests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -192,7 +216,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0748CCCC6E80D2040F1C75D3 /* Pods_ios_base.framework in Frameworks */, + B851CCEB05F5B120379F25DF /* Pods_ios_base.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -200,6 +224,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + BD289BF04FC44C8FB571FA6D /* Pods_ios_base_ios_baseUnitTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -210,7 +235,7 @@ isa = PBXGroup; children = ( 0737296424AD38CE008C54D9 /* NetworkMocker.swift */, - 0737296324AD38CE008C54D9 /* NetworkMockerExtension.swift */, + 0737296324AD38CE008C54D9 /* NetworkMocker+Stubs.swift */, 0737295824AD389F008C54D9 /* Resources */, 07276EB223F5CC460089C0AD /* ios_baseUITests.swift */, 07276EB423F5CC460089C0AD /* Info.plist */, @@ -224,11 +249,7 @@ 0737295824AD389F008C54D9 /* Resources */ = { isa = PBXGroup; children = ( - 0737295924AD389F008C54D9 /* GetProfileSuccessfully.json */, - 0737295A24AD389F008C54D9 /* LoginSuccessfully.json */, - 0737295B24AD389F008C54D9 /* AuthenticationError.json */, - 0737295C24AD389F008C54D9 /* LogOutSuccessfully.json */, - 0737295D24AD389F008C54D9 /* SignUpSuccessfully.json */, + 9B0E255928C2548F006FBCD4 /* StubResponses */, ); path = Resources; sourceTree = ""; @@ -331,17 +352,31 @@ path = Views; sourceTree = ""; }; + 12CFFD1CC7E90042DAA6625E /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1CA40D9689960D750465B012 /* Pods_ios_base.framework */, + 05EB114986F25A947DAF55BD /* Pods_ios_base_ios_baseUITests.framework */, + 485F72F34746F5517F7E7B0E /* Pods_ios_base_ios_baseUnitTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 506A29AE0C7D2BF634BD7D7D /* Pods */ = { isa = PBXGroup; children = ( - C37D0E0BA2DB34C9FBF07583 /* Pods-ios-base-ios-baseUITests.release.xcconfig */, - E9C31B61648676473C98614C /* Pods-ios-base.staging.xcconfig */, - A143F3EC1A6E7D9DE1CA4A68 /* Pods-ios-base.release.xcconfig */, - DA3A451D7100561A04D3D350 /* Pods-ios-base-ios-baseUITests.debug.xcconfig */, - 2C8C72195C24B451D2FEA654 /* Pods-ios-base.debug.xcconfig */, - C88232494AFE27E3FBFC69FE /* Pods-ios-base.uitests.xcconfig */, - B08689AA3A7C35FB70274F30 /* Pods-ios-base-ios-baseUITests.uitests.xcconfig */, - C5763904A89F593C577044A0 /* Pods-ios-base-ios-baseUITests.staging.xcconfig */, + C38496367A3FA02BB7FD7332 /* Pods-ios-base.debug.xcconfig */, + D3A940731572555EE856ED09 /* Pods-ios-base.uitests.xcconfig */, + C76229E5314F62B6F01F4ACC /* Pods-ios-base.staging.xcconfig */, + 18B6971A49CF5630F7B81E0C /* Pods-ios-base.release.xcconfig */, + 4D27CF6609E56949C1BE4CFE /* Pods-ios-base-ios-baseUITests.debug.xcconfig */, + BF22B306B6EF3149AD601B7D /* Pods-ios-base-ios-baseUITests.uitests.xcconfig */, + 357CCDB2088853F34FB33593 /* Pods-ios-base-ios-baseUITests.staging.xcconfig */, + F193585DECDF701D9A03A8E1 /* Pods-ios-base-ios-baseUITests.release.xcconfig */, + C21ACBC83B98EC2694374178 /* Pods-ios-base-ios-baseUnitTests.debug.xcconfig */, + F9D7DF4589E7E4A9991E1C55 /* Pods-ios-base-ios-baseUnitTests.uitests.xcconfig */, + 5B0BC77FA89B89E84632FAA3 /* Pods-ios-base-ios-baseUnitTests.staging.xcconfig */, + A3634E941E71B5A93BFF9683 /* Pods-ios-base-ios-baseUnitTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -376,6 +411,46 @@ path = GoogleFirebase; sourceTree = ""; }; + 9B0E254E28C1529D006FBCD4 /* Cases */ = { + isa = PBXGroup; + children = ( + 9B0E255428C24F87006FBCD4 /* Managers */, + 9B0E255328C24BE3006FBCD4 /* Extensions */, + 9B5E1441245B504C0059DF62 /* Services */, + ); + path = Cases; + sourceTree = ""; + }; + 9B0E255328C24BE3006FBCD4 /* Extensions */ = { + isa = PBXGroup; + children = ( + 9B5E1442245B50630059DF62 /* StringExtensionUnitTests.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 9B0E255428C24F87006FBCD4 /* Managers */ = { + isa = PBXGroup; + children = ( + 9B0E254F28C152A8006FBCD4 /* UserDataManagerTests.swift */, + 9B0E255528C24F96006FBCD4 /* SessionManagerTests.swift */, + ); + path = Managers; + sourceTree = ""; + }; + 9B0E255928C2548F006FBCD4 /* StubResponses */ = { + isa = PBXGroup; + children = ( + 0737295924AD389F008C54D9 /* GetProfileSuccessfully.json */, + 0737295A24AD389F008C54D9 /* LoginSuccessfully.json */, + 0737295B24AD389F008C54D9 /* AuthenticationError.json */, + 0737295C24AD389F008C54D9 /* LogOutSuccessfully.json */, + 0737295D24AD389F008C54D9 /* SignUpSuccessfully.json */, + 9B0E255F28C25987006FBCD4 /* GetProfileFailure.json */, + ); + path = StubResponses; + sourceTree = ""; + }; 9B2D00FC278CAFFC000657BE /* Endpoints */ = { isa = PBXGroup; children = ( @@ -411,7 +486,7 @@ 9B5AFADB1C7205EC002347D6 /* Products */, 9B0C72511C738C3100BAF3B1 /* ios-base */, 506A29AE0C7D2BF634BD7D7D /* Pods */, - A2B617806945C0F51EB3BD85 /* Frameworks */, + 12CFFD1CC7E90042DAA6625E /* Frameworks */, ); indentWidth = 2; sourceTree = ""; @@ -430,7 +505,7 @@ 9B5E1436245B2AA00059DF62 /* ios-baseUnitTests */ = { isa = PBXGroup; children = ( - 9B5E1441245B504C0059DF62 /* Services */, + 9B0E254E28C1529D006FBCD4 /* Cases */, 9B5E1440245B503F0059DF62 /* Extensions */, 9B5E1439245B2AA00059DF62 /* Info.plist */, ); @@ -440,7 +515,7 @@ 9B5E1440245B503F0059DF62 /* Extensions */ = { isa = PBXGroup; children = ( - 9B5E1442245B50630059DF62 /* StringExtensionUnitTests.swift */, + 9B0E255128C24B81006FBCD4 /* User+Tests.swift */, ); path = Extensions; sourceTree = ""; @@ -449,6 +524,7 @@ isa = PBXGroup; children = ( 9B5E1437245B2AA00059DF62 /* UserServiceUnitTests.swift */, + 9B0E254C28C151D0006FBCD4 /* AuthenticationServicesTests.swift */, ); path = Services; sourceTree = ""; @@ -528,15 +604,6 @@ path = Services; sourceTree = ""; }; - A2B617806945C0F51EB3BD85 /* Frameworks */ = { - isa = PBXGroup; - children = ( - A449B00976D690BCC4003AA1 /* Pods_ios_base.framework */, - 6A1760A5E754FEA0591B3C8A /* Pods_ios_base_ios_baseUITests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; E8290BFC1D832D6400599960 /* Extensions */ = { isa = PBXGroup; children = ( @@ -582,11 +649,11 @@ isa = PBXNativeTarget; buildConfigurationList = 07276EB723F5CC460089C0AD /* Build configuration list for PBXNativeTarget "ios-baseUITests" */; buildPhases = ( - 971447569E59788E7C493CB8 /* [CP] Check Pods Manifest.lock */, + 73526C47A8E1EFAA1A75DE35 /* [CP] Check Pods Manifest.lock */, 07276EAC23F5CC460089C0AD /* Sources */, 07276EAD23F5CC460089C0AD /* Frameworks */, 07276EAE23F5CC460089C0AD /* Resources */, - 615A91082393B511F0042D1B /* [CP] Embed Pods Frameworks */, + DCED9B4F16B93E74FFC8C620 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -602,14 +669,14 @@ isa = PBXNativeTarget; buildConfigurationList = 9B5AFAEC1C7205EC002347D6 /* Build configuration list for PBXNativeTarget "ios-base" */; buildPhases = ( - 56AD5304A1278E1FC403D34E /* [CP] Check Pods Manifest.lock */, + 136E268935841BFFD4E0F6FE /* [CP] Check Pods Manifest.lock */, 9B22F5581C720E7D003DDCD8 /* ShellScript */, 9B5AFAD71C7205EB002347D6 /* Frameworks */, FE06840022CE7D8300C6294F /* R.swift */, 9B5AFAD61C7205EB002347D6 /* Sources */, 9B5AFAD81C7205EB002347D6 /* Resources */, FE583F0222AEDC6F00EF3CDA /* Crashlytics */, - B28E807AADD115C4CEC66D80 /* [CP] Embed Pods Frameworks */, + CD618B66BE714A27751DC6AF /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -624,9 +691,11 @@ isa = PBXNativeTarget; buildConfigurationList = 9B5E143F245B2AA00059DF62 /* Build configuration list for PBXNativeTarget "ios-baseUnitTests" */; buildPhases = ( + 770AEC8141B6C3D598B87A68 /* [CP] Check Pods Manifest.lock */, 9B5E1431245B2AA00059DF62 /* Sources */, 9B5E1432245B2AA00059DF62 /* Frameworks */, 9B5E1433245B2AA00059DF62 /* Resources */, + A114EE88D4476F5AB93EE449 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -695,6 +764,7 @@ buildActionMask = 2147483647; files = ( 0737295F24AD389F008C54D9 /* LoginSuccessfully.json in Resources */, + 9B0E256028C25987006FBCD4 /* GetProfileFailure.json in Resources */, 0737296224AD389F008C54D9 /* SignUpSuccessfully.json in Resources */, 0737296024AD389F008C54D9 /* AuthenticationError.json in Resources */, 0737296124AD389F008C54D9 /* LogOutSuccessfully.json in Resources */, @@ -717,13 +787,19 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9B0E255C28C254AB006FBCD4 /* LogOutSuccessfully.json in Resources */, + 9B0E256128C26C63006FBCD4 /* GetProfileFailure.json in Resources */, + 9B0E255E28C254AB006FBCD4 /* AuthenticationError.json in Resources */, + 9B0E255A28C254AB006FBCD4 /* LoginSuccessfully.json in Resources */, + 9B0E255D28C254AB006FBCD4 /* SignUpSuccessfully.json in Resources */, + 9B0E255B28C254AB006FBCD4 /* GetProfileSuccessfully.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 56AD5304A1278E1FC403D34E /* [CP] Check Pods Manifest.lock */ = { + 136E268935841BFFD4E0F6FE /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -745,13 +821,70 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 615A91082393B511F0042D1B /* [CP] Embed Pods Frameworks */ = { + 73526C47A8E1EFAA1A75DE35 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ios-base-ios-baseUITests/Pods-ios-base-ios-baseUITests-frameworks.sh", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ios-base-ios-baseUITests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 770AEC8141B6C3D598B87A68 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ios-base-ios-baseUnitTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9B22F5581C720E7D003DDCD8 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\"\n"; + }; + A114EE88D4476F5AB93EE449 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ios-base-ios-baseUnitTests/Pods-ios-base-ios-baseUnitTests-frameworks.sh", "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", "${BUILT_PRODUCTS_DIR}/Device/Device.framework", "${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework", @@ -792,51 +925,64 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ios-base-ios-baseUITests/Pods-ios-base-ios-baseUITests-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ios-base-ios-baseUnitTests/Pods-ios-base-ios-baseUnitTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 971447569E59788E7C493CB8 /* [CP] Check Pods Manifest.lock */ = { + CD618B66BE714A27751DC6AF /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ios-base/Pods-ios-base-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", + "${BUILT_PRODUCTS_DIR}/Device/Device.framework", + "${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework", + "${BUILT_PRODUCTS_DIR}/FBSDKLoginKit/FBSDKLoginKit.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCoreDiagnostics/FirebaseCoreDiagnostics.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCrashlytics/FirebaseCrashlytics.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", + "${BUILT_PRODUCTS_DIR}/GoogleDataTransport/GoogleDataTransport.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework", + "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", + "${BUILT_PRODUCTS_DIR}/R.swift.Library/Rswift.framework", + "${BUILT_PRODUCTS_DIR}/RSFontSizes/RSFontSizes.framework", + "${BUILT_PRODUCTS_DIR}/RSSwiftNetworking/RSSwiftNetworking.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", ); + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ios-base-ios-baseUITests-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Device.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKLoginKit.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreDiagnostics.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCrashlytics.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleDataTransport.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/IQKeyboardManagerSwift.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Rswift.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RSFontSizes.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RSSwiftNetworking.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ios-base/Pods-ios-base-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 9B22F5581C720E7D003DDCD8 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\"\n"; - }; - B28E807AADD115C4CEC66D80 /* [CP] Embed Pods Frameworks */ = { + DCED9B4F16B93E74FFC8C620 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ios-base/Pods-ios-base-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-ios-base-ios-baseUITests/Pods-ios-base-ios-baseUITests-frameworks.sh", "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", "${BUILT_PRODUCTS_DIR}/Device/Device.framework", "${BUILT_PRODUCTS_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework", @@ -853,6 +999,7 @@ "${BUILT_PRODUCTS_DIR}/RSFontSizes/RSFontSizes.framework", "${BUILT_PRODUCTS_DIR}/RSSwiftNetworking/RSSwiftNetworking.framework", "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", + "${BUILT_PRODUCTS_DIR}/Swifter/Swifter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -872,10 +1019,11 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RSFontSizes.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RSSwiftNetworking.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Swifter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ios-base/Pods-ios-base-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ios-base-ios-baseUITests/Pods-ios-base-ios-baseUITests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; FE06840022CE7D8300C6294F /* R.swift */ = { @@ -931,7 +1079,7 @@ 07276EBC23F5CCA70089C0AD /* XCUIApplicationExtension.swift in Sources */, 0737296624AD38CE008C54D9 /* NetworkMocker.swift in Sources */, 07276EBE23F5CCCA0089C0AD /* XCUIElementExtension.swift in Sources */, - 0737296524AD38CE008C54D9 /* NetworkMockerExtension.swift in Sources */, + 0737296524AD38CE008C54D9 /* NetworkMocker+Stubs.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -992,8 +1140,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9B0E255228C24B81006FBCD4 /* User+Tests.swift in Sources */, + 9B0E255028C152A8006FBCD4 /* UserDataManagerTests.swift in Sources */, + 9B0E254D28C151D0006FBCD4 /* AuthenticationServicesTests.swift in Sources */, + 9B0E255728C2544E006FBCD4 /* NetworkMocker.swift in Sources */, 9B5E1443245B50630059DF62 /* StringExtensionUnitTests.swift in Sources */, + 9B0E255628C24F96006FBCD4 /* SessionManagerTests.swift in Sources */, 9B5E1438245B2AA00059DF62 /* UserServiceUnitTests.swift in Sources */, + 9B0E255828C25460006FBCD4 /* NetworkMocker+Stubs.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1015,7 +1169,7 @@ /* Begin XCBuildConfiguration section */ 07276EB823F5CC460089C0AD /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = DA3A451D7100561A04D3D350 /* Pods-ios-base-ios-baseUITests.debug.xcconfig */; + baseConfigurationReference = 4D27CF6609E56949C1BE4CFE /* Pods-ios-base-ios-baseUITests.debug.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -1044,7 +1198,7 @@ }; 07276EB923F5CC460089C0AD /* Staging */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C5763904A89F593C577044A0 /* Pods-ios-base-ios-baseUITests.staging.xcconfig */; + baseConfigurationReference = 357CCDB2088853F34FB33593 /* Pods-ios-base-ios-baseUITests.staging.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -1071,7 +1225,7 @@ }; 07276EBA23F5CC460089C0AD /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C37D0E0BA2DB34C9FBF07583 /* Pods-ios-base-ios-baseUITests.release.xcconfig */; + baseConfigurationReference = F193585DECDF701D9A03A8E1 /* Pods-ios-base-ios-baseUITests.release.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -1156,7 +1310,7 @@ }; 0737295524AD37BE008C54D9 /* UITests */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C88232494AFE27E3FBFC69FE /* Pods-ios-base.uitests.xcconfig */; + baseConfigurationReference = D3A940731572555EE856ED09 /* Pods-ios-base.uitests.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -1188,7 +1342,7 @@ }; 0737295624AD37BE008C54D9 /* UITests */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B08689AA3A7C35FB70274F30 /* Pods-ios-base-ios-baseUITests.uitests.xcconfig */; + baseConfigurationReference = BF22B306B6EF3149AD601B7D /* Pods-ios-base-ios-baseUITests.uitests.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -1217,6 +1371,7 @@ }; 0737295724AD37BE008C54D9 /* UITests */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F9D7DF4589E7E4A9991E1C55 /* Pods-ios-base-ios-baseUnitTests.uitests.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1358,7 +1513,7 @@ }; 9B5AFAED1C7205EC002347D6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2C8C72195C24B451D2FEA654 /* Pods-ios-base.debug.xcconfig */; + baseConfigurationReference = C38496367A3FA02BB7FD7332 /* Pods-ios-base.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -1390,7 +1545,7 @@ }; 9B5AFAEE1C7205EC002347D6 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A143F3EC1A6E7D9DE1CA4A68 /* Pods-ios-base.release.xcconfig */; + baseConfigurationReference = 18B6971A49CF5630F7B81E0C /* Pods-ios-base.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -1420,6 +1575,7 @@ }; 9B5E143C245B2AA00059DF62 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = C21ACBC83B98EC2694374178 /* Pods-ios-base-ios-baseUnitTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1451,6 +1607,7 @@ }; 9B5E143D245B2AA00059DF62 /* Staging */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 5B0BC77FA89B89E84632FAA3 /* Pods-ios-base-ios-baseUnitTests.staging.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1480,6 +1637,7 @@ }; 9B5E143E245B2AA00059DF62 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A3634E941E71B5A93BFF9683 /* Pods-ios-base-ios-baseUnitTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NONNULL = YES; @@ -1567,7 +1725,7 @@ }; 9BEE614C1C736054002E43B2 /* Staging */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E9C31B61648676473C98614C /* Pods-ios-base.staging.xcconfig */; + baseConfigurationReference = C76229E5314F62B6F01F4ACC /* Pods-ios-base.staging.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; diff --git a/ios-baseUITests/NetworkMocker+Stubs.swift b/ios-baseUITests/NetworkMocker+Stubs.swift new file mode 100644 index 00000000..0646dd33 --- /dev/null +++ b/ios-baseUITests/NetworkMocker+Stubs.swift @@ -0,0 +1,56 @@ +// +// NetworkMockerExtension.swift +// ios-baseUITests +// +// Created by Germán Stábile on 6/30/20. +// Copyright © 2020 Rootstrap. All rights reserved. +// + +import Foundation + +internal enum NetworkStub { + case signUp(success: Bool) + case signIn(success: Bool) + case logOut + case profile(success: Bool) + case deleteAccount + + var urlString: String { + switch self { + case .signUp: + return "/users/" + case .signIn: + return "/users/sign_in" + case .logOut: + return "/users/sign_out" + case .profile: + return "/user/profile" + case .deleteAccount: + return "/user/delete_account" + } + } + + var responseFileName: String { + switch self { + case .signUp(let success): + return success ? "SignUpSuccessfully" : "AuthenticationError" + case .signIn(let success): + return success ? "LoginSuccessfully" : "AuthenticationError" + case .logOut, .deleteAccount: + return "LogOutSuccessfully" + case .profile(let success): + return success ? "GetProfileSuccessfully" : "GetProfileFailure" + } + } +} + +extension NetworkMocker { + + func stub(with networkStub: NetworkStub, method: HTTPMethod) { + stub( + url: networkStub.urlString, + responseFilename: networkStub.responseFileName, + method: method + ) + } +} diff --git a/ios-baseUITests/NetworkMocker.swift b/ios-baseUITests/NetworkMocker.swift index 2e04a900..2aeeb49f 100644 --- a/ios-baseUITests/NetworkMocker.swift +++ b/ios-baseUITests/NetworkMocker.swift @@ -8,6 +8,7 @@ import Foundation import Swifter +import XCTest enum HTTPMethod { case POST @@ -16,7 +17,7 @@ enum HTTPMethod { case DELETE } -class NetworkMocker { +internal class NetworkMocker { var server = HttpServer() @@ -28,37 +29,42 @@ class NetworkMocker { server.stop() } - public func setupStub( + public func stub( url: String, responseFilename: String, method: HTTPMethod = .GET ) { let testBundle = Bundle(for: type(of: self)) let filePath = testBundle.path(forResource: responseFilename, ofType: "json") ?? "" - let fileUrl = URL(fileURLWithPath: filePath) - guard let data = try? Data(contentsOf: fileUrl, options: .uncached) else { - fatalError("Could not parse mocked data") - } - let json = dataToJSON(data: data) - - let response: ((HttpRequest) -> HttpResponse) = { _ in - HttpResponse.ok(.json(json as AnyObject)) - } - - switch method { - case .GET: server.GET[url] = response - case .POST: server.POST[url] = response - case .DELETE: server.DELETE[url] = response - case .PUT: server.PUT[url] = response - } - } - - func dataToJSON(data: Data) -> Any? { + let fileURL = URL(fileURLWithPath: filePath) + do { - return try JSONSerialization.jsonObject(with: data, options: .mutableContainers) + let jsonObject = try Data(contentsOf: fileURL, options: .uncached).jsonObject() + guard let jsonObject = jsonObject else { + XCTFail("A valid JSON couldn't be parsed") + return + } + + let response: ((HttpRequest) -> HttpResponse) = { _ in + HttpResponse.ok(.json(jsonObject)) + } + + switch method { + case .GET: server.GET[url] = response + case .POST: server.POST[url] = response + case .DELETE: server.DELETE[url] = response + case .PUT: server.PUT[url] = response + } } catch let error { - print(error) + XCTFail("Failed to serialize JSON. Error \(error)") } - return nil + } +} + +internal extension Data { + func jsonObject( + options: JSONSerialization.ReadingOptions = .mutableContainers + ) throws -> Any? { + try JSONSerialization.jsonObject(with: self, options: options) } } diff --git a/ios-baseUITests/NetworkMockerExtension.swift b/ios-baseUITests/NetworkMockerExtension.swift deleted file mode 100644 index 47be6b54..00000000 --- a/ios-baseUITests/NetworkMockerExtension.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// NetworkMockerExtension.swift -// ios-baseUITests -// -// Created by Germán Stábile on 6/30/20. -// Copyright © 2020 Rootstrap. All rights reserved. -// - -import Foundation - -extension NetworkMocker { - - func stubSignUp(shouldSucceed: Bool = true) { - let responseFileName = shouldSucceed ? "SignUpSuccessfully" : "AuthenticationError" - - setupStub( - url: "/users/", - responseFilename: responseFileName, - method: .POST - ) - } - - func stubLogOut() { - setupStub( - url: "/users/sign_out", - responseFilename: "LogOutSuccessfully", - method: .DELETE - ) - } - - func stubDeleteAccount() { - setupStub( - url: "/user/delete_account", - responseFilename: "LogOutSuccessfully", - method: .DELETE - ) - } - - func stubLogIn(shouldSucceed: Bool = true) { - let responseFilename = shouldSucceed ? "LoginSuccessfully" : "AuthenticationError" - - setupStub( - url: "/users/sign_in", - responseFilename: responseFilename, - method: .POST - ) - } - - func stubGetProfile() { - setupStub( - url: "/user/profile", - responseFilename: "GetProfileSuccessfully", - method: .GET - ) - } -} diff --git a/ios-baseUITests/Resources/AuthenticationError.json b/ios-baseUITests/Resources/StubResponses/AuthenticationError.json similarity index 100% rename from ios-baseUITests/Resources/AuthenticationError.json rename to ios-baseUITests/Resources/StubResponses/AuthenticationError.json diff --git a/ios-baseUITests/Resources/StubResponses/GetProfileFailure.json b/ios-baseUITests/Resources/StubResponses/GetProfileFailure.json new file mode 100644 index 00000000..ac809810 --- /dev/null +++ b/ios-baseUITests/Resources/StubResponses/GetProfileFailure.json @@ -0,0 +1,3 @@ +{ + "user": {} +} diff --git a/ios-baseUITests/Resources/GetProfileSuccessfully.json b/ios-baseUITests/Resources/StubResponses/GetProfileSuccessfully.json similarity index 100% rename from ios-baseUITests/Resources/GetProfileSuccessfully.json rename to ios-baseUITests/Resources/StubResponses/GetProfileSuccessfully.json diff --git a/ios-baseUITests/Resources/LogOutSuccessfully.json b/ios-baseUITests/Resources/StubResponses/LogOutSuccessfully.json similarity index 100% rename from ios-baseUITests/Resources/LogOutSuccessfully.json rename to ios-baseUITests/Resources/StubResponses/LogOutSuccessfully.json diff --git a/ios-baseUITests/Resources/LoginSuccessfully.json b/ios-baseUITests/Resources/StubResponses/LoginSuccessfully.json similarity index 100% rename from ios-baseUITests/Resources/LoginSuccessfully.json rename to ios-baseUITests/Resources/StubResponses/LoginSuccessfully.json diff --git a/ios-baseUITests/Resources/SignUpSuccessfully.json b/ios-baseUITests/Resources/StubResponses/SignUpSuccessfully.json similarity index 100% rename from ios-baseUITests/Resources/SignUpSuccessfully.json rename to ios-baseUITests/Resources/StubResponses/SignUpSuccessfully.json diff --git a/ios-baseUnitTests/Extensions/StringExtensionUnitTests.swift b/ios-baseUnitTests/Cases/Extensions/StringExtensionUnitTests.swift similarity index 95% rename from ios-baseUnitTests/Extensions/StringExtensionUnitTests.swift rename to ios-baseUnitTests/Cases/Extensions/StringExtensionUnitTests.swift index fcaf970d..56bcd94e 100644 --- a/ios-baseUnitTests/Extensions/StringExtensionUnitTests.swift +++ b/ios-baseUnitTests/Cases/Extensions/StringExtensionUnitTests.swift @@ -9,7 +9,7 @@ import XCTest @testable import ios_base_Debug -class StringExtensionUnitTests: XCTestCase { +internal class StringExtensionUnitTests: XCTestCase { func testEmailValidation() { XCTAssertFalse("username".isEmailFormatted()) XCTAssertFalse("username@test".isEmailFormatted()) diff --git a/ios-baseUnitTests/Cases/Managers/SessionManagerTests.swift b/ios-baseUnitTests/Cases/Managers/SessionManagerTests.swift new file mode 100644 index 00000000..b22fd0e5 --- /dev/null +++ b/ios-baseUnitTests/Cases/Managers/SessionManagerTests.swift @@ -0,0 +1,67 @@ +// +// SessionManagerTests.swift +// ios-baseUnitTests +// +// Created by German on 2/9/22. +// Copyright © 2022 Rootstrap Inc. All rights reserved. +// + +import Foundation +import XCTest +@testable import ios_base_Debug + +internal final class SessionManagerTests: XCTestCase { + + private let testSession = Session( + uid: "uuid", + client: "client", + token: "token", + expires: Date() + ) + + private var userDefaults: UserDefaults! + + override func setUp() { + super.setUp() + + // swiftlint:disable:next force_unwrapping + userDefaults = UserDefaults(suiteName: "Test Suite")! + } + + func testSessionStoredCorrectly() { + let sessionManager = SessionManager(userDefaults: userDefaults) + sessionManager.currentSession = testSession + + XCTAssertEqual(testSession.client, sessionManager.currentSession?.client) + XCTAssertEqual(testSession.uid, sessionManager.currentSession?.uid) + XCTAssertEqual(testSession.expiry, sessionManager.currentSession?.expiry) + XCTAssertEqual(testSession.accessToken, sessionManager.currentSession?.accessToken) + } + + func testCurrentUserDeletion() { + let sessionManager = SessionManager(userDefaults: userDefaults) + sessionManager.currentSession = testSession + + XCTAssertNotNil(sessionManager.currentSession) + + sessionManager.deleteSession() + XCTAssertNil(sessionManager.currentSession) + } + + func testSessionValidation() { + let sessionManager = SessionManager(userDefaults: userDefaults) + sessionManager.currentSession = testSession + XCTAssertTrue(sessionManager.validSession) + + let invalidSessions: [Session] = [ + .init(uid: "", client: "client", token: "token", expires: Date()), + .init(uid: "uid", client: "", token: "token", expires: nil), + .init(uid: "uid", client: "client", token: "", expires: Date.distantFuture), + .init(uid: nil, client: nil, token: "accessToken", expires: Date.distantFuture) + ] + for session in invalidSessions { + sessionManager.currentSession = session + XCTAssertFalse(sessionManager.validSession) + } + } +} diff --git a/ios-baseUnitTests/Cases/Managers/UserDataManagerTests.swift b/ios-baseUnitTests/Cases/Managers/UserDataManagerTests.swift new file mode 100644 index 00000000..74600f0c --- /dev/null +++ b/ios-baseUnitTests/Cases/Managers/UserDataManagerTests.swift @@ -0,0 +1,36 @@ +// +// UserDataManagerTests.swift +// ios-baseUnitTests +// +// Created by German on 1/9/22. +// Copyright © 2022 Rootstrap Inc. All rights reserved. +// + +import Foundation +import XCTest +@testable import ios_base_Debug + +internal final class UserDataManagerTests: XCTestCase { + + func testUserPersistedCorrectly() { + let testUser: User = .mock + UserDataManager.currentUser = testUser + + let persistedUser = UserDataManager.currentUser + XCTAssertEqual(persistedUser?.id, testUser.id) + XCTAssertEqual(persistedUser?.username, testUser.username) + XCTAssertEqual(persistedUser?.email, testUser.email) + XCTAssertTrue(UserDataManager.isUserLogged) + } + + func testCurrentUserDeletion() { + UserDataManager.currentUser = .mock + + XCTAssertNotNil(UserDataManager.currentUser) + XCTAssertTrue(UserDataManager.isUserLogged) + + UserDataManager.deleteUser() + XCTAssertNil(UserDataManager.currentUser) + XCTAssertFalse(UserDataManager.isUserLogged) + } +} diff --git a/ios-baseUnitTests/Cases/Services/AuthenticationServicesTests.swift b/ios-baseUnitTests/Cases/Services/AuthenticationServicesTests.swift new file mode 100644 index 00000000..b03630ae --- /dev/null +++ b/ios-baseUnitTests/Cases/Services/AuthenticationServicesTests.swift @@ -0,0 +1,16 @@ +// +// AuthenticationServicesTests.swift +// ios-baseUnitTests +// +// Created by German on 1/9/22. +// Copyright © 2022 Rootstrap Inc. All rights reserved. +// + +import Foundation +import XCTest + +internal final class AuthenticationServicesTests: XCTestCase { + func testAuthenticationService() { + + } +} diff --git a/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift b/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift new file mode 100644 index 00000000..b954bdce --- /dev/null +++ b/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift @@ -0,0 +1,71 @@ +// +// UserServiceUnitTests.swift +// ios-baseUnitTests +// +// Created by German on 4/30/20. +// Copyright © 2020 Rootstrap Inc. All rights reserved. +// + +import XCTest +import RSSwiftNetworking +@testable import ios_base_Debug + +class UserServiceUnitTests: XCTestCase { + + private var userService: UserServices! + private let networkMocker = NetworkMocker() + + override func setUpWithError() throws { + try super.setUpWithError() + + try networkMocker.setUp() + userService = UserServices() + UserDataManager.deleteUser() + } + + override func tearDown() { + super.tearDown() + networkMocker.tearDown() + } + + func testServiceSavesUser() { + let expectation = expectation(description: "User object should be persisted") + testUserStorageAfterProfileFetch(success: true) { result in + switch result { + case .success(let user): + expectation.fulfill() + XCTAssertNotNil(UserDataManager.currentUser) + XCTAssertEqual(UserDataManager.currentUser?.id, user.id) + XCTAssertEqual(UserDataManager.currentUser?.email, user.email) + case .failure(let error): + XCTFail("Test expected to succeed. \(error)") + } + } + + wait(for: [expectation], timeout: 3.0) + } + + func testServicesReturnsError() { + let expectation = expectation(description: "Request should fail") + testUserStorageAfterProfileFetch(success: false) { result in + switch result { + case .success: + XCTFail("Test expected to fail") + case .failure(let error): + expectation.fulfill() + XCTAssertNil(UserDataManager.currentUser) + XCTAssertNotNil(error) + } + + } + wait(for: [expectation], timeout: 3.0) + } + + private func testUserStorageAfterProfileFetch( + success: Bool, + completion: @escaping (Result) -> Void + ) { + networkMocker.stub(with: .profile(success: success), method: .GET) + userService.getMyProfile(completion: completion) + } +} diff --git a/ios-baseUnitTests/Extensions/User+Tests.swift b/ios-baseUnitTests/Extensions/User+Tests.swift new file mode 100644 index 00000000..1a0429f5 --- /dev/null +++ b/ios-baseUnitTests/Extensions/User+Tests.swift @@ -0,0 +1,14 @@ +// +// User+Tests.swift +// ios-baseUnitTests +// +// Created by German on 2/9/22. +// Copyright © 2022 Rootstrap Inc. All rights reserved. +// + +import Foundation +@testable import ios_base_Debug + +internal extension User { + static let mock = User(id: 1, username: "tester", email: "tester@rootstrap.com") +} diff --git a/ios-baseUnitTests/Services/UserServiceUnitTests.swift b/ios-baseUnitTests/Services/UserServiceUnitTests.swift deleted file mode 100644 index 0991c288..00000000 --- a/ios-baseUnitTests/Services/UserServiceUnitTests.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// UserServiceUnitTests.swift -// ios-baseUnitTests -// -// Created by German on 4/30/20. -// Copyright © 2020 Rootstrap Inc. All rights reserved. -// - -import XCTest -@testable import ios_base_Debug - -class UserServiceUnitTests: XCTestCase { - - let userResponse: [String: Any] = [ - "user": [ - "id": 0, - "username": "test", - "email": "test-user@rootstrap.com" - ] - ] - - var testUser: User! - - override func setUp() { - super.setUp() - if let userDictionary = userResponse["user"] as? [String: Any] { - testUser = User(dictionary: userDictionary) - } - SessionManager.deleteSession() - UserDataManager.deleteUser() - } - - func testUserPersistence() { - AuthenticationServices.saveUserSession(fromResponse: userResponse, headers: [:]) - guard let persistedUser = UserDataManager.currentUser else { - XCTFail("User should NOT be nil") - return - } - XCTAssert(UserDataManager.isUserLogged) - XCTAssert(persistedUser.id == testUser.id) - XCTAssert(persistedUser.username == testUser.username) - XCTAssert(persistedUser.email == testUser.email) - } - - func testGoodSessionPersistence() { - let client = "dummySessionClient" - let token = "dummySessionToken" - let uid = testUser.email - let expiry = "\(Date.timeIntervalSinceReferenceDate)" - let sessionHeaders: [String: Any] = [ - APIClient.HTTPHeader.uid.rawValue: uid, - APIClient.HTTPHeader.client.rawValue: client, - APIClient.HTTPHeader.token.rawValue: token, - APIClient.HTTPHeader.expiry.rawValue: expiry - ] - - AuthenticationServices.saveUserSession( - fromResponse: userResponse, - headers: sessionHeaders - ) - guard let persistedSession = SessionManager.currentSession else { - XCTFail("Session should NOT be nil") - return - } - - XCTAssert(persistedSession.client == client) - XCTAssert(persistedSession.accessToken == token) - XCTAssert(persistedSession.uid == uid) - XCTAssert(persistedSession.accessToken == token) - } - - func testBadSessionPersistence() { - // Testing case where shouldn't be session at all - let unusableHeaders = [APIClient.HTTPHeader.client: "badHeaderKey"] - AuthenticationServices.saveUserSession( - fromResponse: userResponse, - headers: unusableHeaders - ) - XCTAssert(SessionManager.currentSession == nil) - XCTAssertFalse(SessionManager.validSession) - - // Testing case where should be session but not valid - let wrongSessionHeaders = [ - "testKey": "testValue", - APIClient.HTTPHeader.uid.rawValue: "", - APIClient.HTTPHeader.client.rawValue: "", - APIClient.HTTPHeader.token.rawValue: "" - ] - AuthenticationServices.saveUserSession( - fromResponse: userResponse, - headers: wrongSessionHeaders - ) - XCTAssert(SessionManager.currentSession != nil) - XCTAssertFalse(SessionManager.validSession) - } -} From 2caa3761a264a5067265d0d1c3ecf1e7e7264bbf Mon Sep 17 00:00:00 2001 From: German Lopez Date: Fri, 2 Sep 2022 16:22:19 -0300 Subject: [PATCH 09/13] Fix UI tests --- ios-base/Home/Views/HomeViewController.swift | 6 ++++ .../Views/SignInViewController.swift | 7 +++++ .../Views/SignUpViewController.swift | 8 ++++++ ios-baseUITests/NetworkMocker+Stubs.swift | 28 +++++++++++++++++-- ios-baseUITests/NetworkMocker.swift | 23 +++++++++------ .../StubResponses/GetProfileFailure.json | 2 +- .../StubResponses/GetProfileSuccessfully.json | 12 ++++---- .../StubResponses/LoginSuccessfully.json | 18 +++++------- .../StubResponses/SignUpSuccessfully.json | 18 +++++------- ios-baseUITests/ios_baseUITests.swift | 16 +++++------ 10 files changed, 90 insertions(+), 48 deletions(-) diff --git a/ios-base/Home/Views/HomeViewController.swift b/ios-base/Home/Views/HomeViewController.swift index 40202347..c97e4022 100644 --- a/ios-base/Home/Views/HomeViewController.swift +++ b/ios-base/Home/Views/HomeViewController.swift @@ -92,6 +92,12 @@ private extension HomeViewController { subviews: [welcomeLabel, logOutButton, deleteAccountButton, getProfileButton] ) activateConstraints() + setupAccessibility() + } + + func setupAccessibility() { + getProfileButton.accessibilityIdentifier = "GetMyProfileButton" + logOutButton.accessibilityIdentifier = "LogoutButton" } private func activateConstraints() { diff --git a/ios-base/Onboarding/Views/SignInViewController.swift b/ios-base/Onboarding/Views/SignInViewController.swift index f4dad3bd..3c5de5d8 100644 --- a/ios-base/Onboarding/Views/SignInViewController.swift +++ b/ios-base/Onboarding/Views/SignInViewController.swift @@ -96,6 +96,13 @@ private extension SignInViewController { ]) activateConstrains() + setupAccessibility() + } + + func setupAccessibility() { + logInButton.accessibilityIdentifier = "SignInButton" + emailField.accessibilityIdentifier = "EmailTextField" + passwordField.accessibilityIdentifier = "PasswordTextField" } func activateConstrains() { diff --git a/ios-base/Onboarding/Views/SignUpViewController.swift b/ios-base/Onboarding/Views/SignUpViewController.swift index d2e6b7c1..aaf013e6 100644 --- a/ios-base/Onboarding/Views/SignUpViewController.swift +++ b/ios-base/Onboarding/Views/SignUpViewController.swift @@ -108,6 +108,14 @@ private extension SignUpViewController { ]) activateConstrains() + setupAccessibility() + } + + func setupAccessibility() { + signUpButton.accessibilityIdentifier = "SignUpButton" + emailField.accessibilityIdentifier = "EmailTextField" + passwordField.accessibilityIdentifier = "PasswordTextField" + passwordConfirmationField.accessibilityIdentifier = "ConfirmPasswordTextField" } func activateConstrains() { diff --git a/ios-baseUITests/NetworkMocker+Stubs.swift b/ios-baseUITests/NetworkMocker+Stubs.swift index 0646dd33..2e3cbc50 100644 --- a/ios-baseUITests/NetworkMocker+Stubs.swift +++ b/ios-baseUITests/NetworkMocker+Stubs.swift @@ -7,6 +7,7 @@ // import Foundation +import RSSwiftNetworking internal enum NetworkStub { case signUp(success: Bool) @@ -42,15 +43,38 @@ internal enum NetworkStub { return success ? "GetProfileSuccessfully" : "GetProfileFailure" } } + + var headers: [String: String]? { + switch self { + case .signUp(let success), .signIn(let success): + if success { + return [ + HTTPHeader.uid.rawValue: "uid", + HTTPHeader.client.rawValue: "client", + HTTPHeader.token.rawValue: "accessToken", + HTTPHeader.expiry.rawValue: "\(Date.distantFuture.timeIntervalSinceNow)", + HTTPHeader.contentType.rawValue: "application/json" + ] + } + return nil + default: + return nil + } + } } extension NetworkMocker { - func stub(with networkStub: NetworkStub, method: HTTPMethod) { + func stub( + with networkStub: NetworkStub, + method: HTTPMethod, + customHeaders: [String: String]? = nil + ) { stub( url: networkStub.urlString, responseFilename: networkStub.responseFileName, - method: method + method: method, + customHeaders: customHeaders ?? networkStub.headers ) } } diff --git a/ios-baseUITests/NetworkMocker.swift b/ios-baseUITests/NetworkMocker.swift index 2aeeb49f..fc8ad07b 100644 --- a/ios-baseUITests/NetworkMocker.swift +++ b/ios-baseUITests/NetworkMocker.swift @@ -32,23 +32,30 @@ internal class NetworkMocker { public func stub( url: String, responseFilename: String, - method: HTTPMethod = .GET + method: HTTPMethod = .GET, + customHeaders: [String: String]? = nil ) { let testBundle = Bundle(for: type(of: self)) let filePath = testBundle.path(forResource: responseFilename, ofType: "json") ?? "" let fileURL = URL(fileURLWithPath: filePath) do { - let jsonObject = try Data(contentsOf: fileURL, options: .uncached).jsonObject() - guard let jsonObject = jsonObject else { - XCTFail("A valid JSON couldn't be parsed") - return - } + let data = try Data(contentsOf: fileURL, options: .uncached) + let jsonObject = try data.jsonObject() let response: ((HttpRequest) -> HttpResponse) = { _ in - HttpResponse.ok(.json(jsonObject)) + if let customHeaders = customHeaders { + return HttpResponse.raw(200, "OK", customHeaders) { bodyWriter in + try bodyWriter.write(data) + } + } + guard let jsonObject = jsonObject else { + XCTFail("A valid JSON couldn't be parsed") + return .badRequest(.none) + } + return HttpResponse.ok(.json(jsonObject)) } - + switch method { case .GET: server.GET[url] = response case .POST: server.POST[url] = response diff --git a/ios-baseUITests/Resources/StubResponses/GetProfileFailure.json b/ios-baseUITests/Resources/StubResponses/GetProfileFailure.json index ac809810..2bb5158a 100644 --- a/ios-baseUITests/Resources/StubResponses/GetProfileFailure.json +++ b/ios-baseUITests/Resources/StubResponses/GetProfileFailure.json @@ -1,3 +1,3 @@ { - "user": {} + "id": null } diff --git a/ios-baseUITests/Resources/StubResponses/GetProfileSuccessfully.json b/ios-baseUITests/Resources/StubResponses/GetProfileSuccessfully.json index 681908f8..8a53291c 100644 --- a/ios-baseUITests/Resources/StubResponses/GetProfileSuccessfully.json +++ b/ios-baseUITests/Resources/StubResponses/GetProfileSuccessfully.json @@ -1,9 +1,7 @@ { - "user": { - "id": 1, - "email": "automation@test.com", - "first_name": "FirstName", - "last_name": "LastName", - "username": "test" - } + "id": 1, + "email": "automation@test.com", + "first_name": "FirstName", + "last_name": "LastName", + "username": "test" } diff --git a/ios-baseUITests/Resources/StubResponses/LoginSuccessfully.json b/ios-baseUITests/Resources/StubResponses/LoginSuccessfully.json index 9185609a..7e004c02 100644 --- a/ios-baseUITests/Resources/StubResponses/LoginSuccessfully.json +++ b/ios-baseUITests/Resources/StubResponses/LoginSuccessfully.json @@ -1,13 +1,9 @@ { - "user": { - "id": 1, - "email": "automation@test.com", - "provider": "email", - "uid": "automation@test.com", - "first_name": "FirstName", - "last_name": "LastName", - "username": "test", - "created_at": "2017-02-23T13:54:33.283Z", - "updated_at": "2017-02-23T13:54:33.425Z" - } + "id": 1, + "email": "automation@test.com", + "provider": "email", + "uid": "automation@test.com", + "first_name": "FirstName", + "last_name": "LastName", + "username": "test" } diff --git a/ios-baseUITests/Resources/StubResponses/SignUpSuccessfully.json b/ios-baseUITests/Resources/StubResponses/SignUpSuccessfully.json index 9185609a..7e004c02 100644 --- a/ios-baseUITests/Resources/StubResponses/SignUpSuccessfully.json +++ b/ios-baseUITests/Resources/StubResponses/SignUpSuccessfully.json @@ -1,13 +1,9 @@ { - "user": { - "id": 1, - "email": "automation@test.com", - "provider": "email", - "uid": "automation@test.com", - "first_name": "FirstName", - "last_name": "LastName", - "username": "test", - "created_at": "2017-02-23T13:54:33.283Z", - "updated_at": "2017-02-23T13:54:33.425Z" - } + "id": 1, + "email": "automation@test.com", + "provider": "email", + "uid": "automation@test.com", + "first_name": "FirstName", + "last_name": "LastName", + "username": "test" } diff --git a/ios-baseUITests/ios_baseUITests.swift b/ios-baseUITests/ios_baseUITests.swift index 0d247ccf..93d2dc7c 100644 --- a/ios-baseUITests/ios_baseUITests.swift +++ b/ios-baseUITests/ios_baseUITests.swift @@ -14,13 +14,13 @@ class ios_baseUITests: XCTestCase { let networkMocker = NetworkMocker() - override func setUp() { - super.setUp() + override func setUpWithError() throws { + try super.setUpWithError() app = XCUIApplication() app.launchArguments = ["Automation Test"] - try? networkMocker.setUp() - networkMocker.stubLogOut() + try networkMocker.setUp() + networkMocker.stub(with: .logOut, method: .DELETE) app.logOutIfNeeded(in: self) } @@ -68,7 +68,7 @@ class ios_baseUITests: XCTestCase { func testAccountCreation() { app.launch() - networkMocker.stubSignUp() + networkMocker.stub(with: .signUp(success: true), method: .POST) app.attemptSignUp( in: self, @@ -76,7 +76,7 @@ class ios_baseUITests: XCTestCase { password: "holahola" ) - networkMocker.stubGetProfile() + networkMocker.stub(with: .profile(success: true), method: .GET) let getMyProfile = app.buttons["GetMyProfileButton"] waitFor(element: getMyProfile, timeOut: 10) getMyProfile.tap() @@ -92,7 +92,7 @@ class ios_baseUITests: XCTestCase { func testSignInSuccess() { app.launch() - networkMocker.stubLogIn() + networkMocker.stub(with: .signIn(success: true), method: .POST) app.attemptSignIn(in: self, with: "automation@test.com", @@ -105,7 +105,7 @@ class ios_baseUITests: XCTestCase { func testSignInFailure() { app.launch() - networkMocker.stubLogIn(shouldSucceed: false) + networkMocker.stub(with: .signIn(success: false), method: .POST) app.attemptSignIn(in: self, with: "automation@test.com", From ed376238eecfb45a6bfb4e538b9f337cc11bbb16 Mon Sep 17 00:00:00 2001 From: German Lopez Date: Mon, 5 Sep 2022 12:48:32 -0300 Subject: [PATCH 10/13] Update CI flow --- .github/workflows/ci.yml | 2 -- README.md | 2 +- ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f368d201..2e0ce1ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,8 +41,6 @@ jobs: - name: Downloading Google Firebase plist files run: | aws s3 cp s3://$BUILDS_BUCKET/$FOLDER/ ios-base/Resources/$FOLDER/ --recursive - ls - ls ios-base/Resources # Runs test on the develop scheme - name: Running Test Suite diff --git a/README.md b/README.md index bee04245..94d4e395 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://img.shields.io/travis/rootstrap/ios-base/master.svg)](https://travis-ci.org/rootstrap/ios-base) +[![Build Status](https://img.shields.io/github/workflow/status/rootstrap/ios-base/CI%20Build)](https://github.com/rootstrap/ios-base/actions/workflows/ci.yml) [![Maintainability](https://api.codeclimate.com/v1/badges/21b076c80057210cda75/maintainability)](https://codeclimate.com/github/rootstrap/ios-base/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/21b076c80057210cda75/test_coverage)](https://codeclimate.com/github/rootstrap/ios-base/test_coverage) [![License](https://img.shields.io/github/license/rootstrap/ios-base.svg)](https://github.com/rootstrap/ios-base/blob/master/LICENSE.md) diff --git a/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift b/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift index b954bdce..22078600 100644 --- a/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift +++ b/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift @@ -42,7 +42,7 @@ class UserServiceUnitTests: XCTestCase { } } - wait(for: [expectation], timeout: 3.0) + wait(for: [expectation], timeout: 5.0) } func testServicesReturnsError() { @@ -58,7 +58,7 @@ class UserServiceUnitTests: XCTestCase { } } - wait(for: [expectation], timeout: 3.0) + wait(for: [expectation], timeout: 5.0) } private func testUserStorageAfterProfileFetch( From f7dca29aab685fc825ebb63872975a014c3598f7 Mon Sep 17 00:00:00 2001 From: German Lopez Date: Mon, 5 Sep 2022 14:13:07 -0300 Subject: [PATCH 11/13] Try UI tests app launch --- ios-baseUITests/XCUIApplicationExtension.swift | 8 ++++---- ios-baseUITests/ios_baseUITests.swift | 18 +++++------------- .../Cases/Services/UserServiceUnitTests.swift | 9 ++++++--- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/ios-baseUITests/XCUIApplicationExtension.swift b/ios-baseUITests/XCUIApplicationExtension.swift index 28d16534..baa60d2b 100644 --- a/ios-baseUITests/XCUIApplicationExtension.swift +++ b/ios-baseUITests/XCUIApplicationExtension.swift @@ -25,7 +25,7 @@ extension XCUIApplication { func logOutIfNeeded(in testCase: XCTestCase) { let logOutButton = buttons["LogoutButton"] let goToSignInButton = buttons["GoToSignInButton"] - + if logOutButton.exists { logOutButton.forceTap() testCase.waitFor(element: goToSignInButton, timeOut: 5) @@ -40,12 +40,12 @@ extension XCUIApplication { let goToSignInButton = buttons["GoToSignInButton"] let toolbarDoneButton = buttons["Toolbar Done Button"] - testCase.waitFor(element: goToSignInButton, timeOut: 2) + testCase.waitFor(element: goToSignInButton, timeOut: 5) goToSignInButton.forceTap() let signInButton = buttons["SignInButton"] - testCase.waitFor(element: signInButton, timeOut: 2) + testCase.waitFor(element: signInButton, timeOut: 5) type(text: email, on: "EmailTextField") @@ -67,7 +67,7 @@ extension XCUIApplication { let toolbarDoneButton = buttons["Toolbar Done Button"] let signUpButton = buttons["SignUpButton"] - testCase.waitFor(element: signUpButton, timeOut: 2) + testCase.waitFor(element: signUpButton, timeOut: 5) type(text: email, on: "EmailTextField") diff --git a/ios-baseUITests/ios_baseUITests.swift b/ios-baseUITests/ios_baseUITests.swift index 93d2dc7c..efb84262 100644 --- a/ios-baseUITests/ios_baseUITests.swift +++ b/ios-baseUITests/ios_baseUITests.swift @@ -9,16 +9,18 @@ import XCTest class ios_baseUITests: XCTestCase { - + var app: XCUIApplication! - let networkMocker = NetworkMocker() + private var networkMocker: NetworkMocker! override func setUpWithError() throws { try super.setUpWithError() app = XCUIApplication() app.launchArguments = ["Automation Test"] - + app.launch() + + networkMocker = NetworkMocker() try networkMocker.setUp() networkMocker.stub(with: .logOut, method: .DELETE) app.logOutIfNeeded(in: self) @@ -30,8 +32,6 @@ class ios_baseUITests: XCTestCase { } func testCreateAccountValidations() { - app.launch() - app.buttons["GoToSignUpButton"].forceTap() let toolbarDoneButton = app.buttons["Toolbar Done Button"] @@ -66,8 +66,6 @@ class ios_baseUITests: XCTestCase { } func testAccountCreation() { - app.launch() - networkMocker.stub(with: .signUp(success: true), method: .POST) app.attemptSignUp( @@ -90,8 +88,6 @@ class ios_baseUITests: XCTestCase { } func testSignInSuccess() { - app.launch() - networkMocker.stub(with: .signIn(success: true), method: .POST) app.attemptSignIn(in: self, @@ -103,8 +99,6 @@ class ios_baseUITests: XCTestCase { } func testSignInFailure() { - app.launch() - networkMocker.stub(with: .signIn(success: false), method: .POST) app.attemptSignIn(in: self, @@ -122,8 +116,6 @@ class ios_baseUITests: XCTestCase { } func testSignInValidations() { - app.launch() - app.buttons["GoToSignInButton"].forceTap() let toolbarDoneButton = app.buttons["Toolbar Done Button"] diff --git a/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift b/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift index 22078600..f2e13d3e 100644 --- a/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift +++ b/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift @@ -13,11 +13,12 @@ import RSSwiftNetworking class UserServiceUnitTests: XCTestCase { private var userService: UserServices! - private let networkMocker = NetworkMocker() + private var networkMocker: NetworkMocker! override func setUpWithError() throws { try super.setUpWithError() + networkMocker = NetworkMocker() try networkMocker.setUp() userService = UserServices() UserDataManager.deleteUser() @@ -49,8 +50,10 @@ class UserServiceUnitTests: XCTestCase { let expectation = expectation(description: "Request should fail") testUserStorageAfterProfileFetch(success: false) { result in switch result { - case .success: - XCTFail("Test expected to fail") + case .success(let user): + print("DB* \(user)") + print("DB* \(String(describing: UserDataManager.currentUser))") + XCTFail("GET Profile Request expected to fail") case .failure(let error): expectation.fulfill() XCTAssertNil(UserDataManager.currentUser) From 687583c8629d72ef2b8564dff7ae910c2d0851cf Mon Sep 17 00:00:00 2001 From: German Lopez Date: Tue, 6 Sep 2022 11:25:05 -0300 Subject: [PATCH 12/13] Remove unused code --- ios-base.xcodeproj/project.pbxproj | 16 ---- .../Common/Views/PlaceholderTextView.swift | 76 ------------------- .../Extensions/JSONEncodingExtension.swift | 19 ----- .../Extensions/ViewControllerExtension.swift | 26 ------- 4 files changed, 137 deletions(-) delete mode 100644 ios-base/Common/Views/PlaceholderTextView.swift delete mode 100644 ios-base/Extensions/JSONEncodingExtension.swift diff --git a/ios-base.xcodeproj/project.pbxproj b/ios-base.xcodeproj/project.pbxproj index 016b9c1c..ebd40a44 100644 --- a/ios-base.xcodeproj/project.pbxproj +++ b/ios-base.xcodeproj/project.pbxproj @@ -50,13 +50,11 @@ 9B5E1438245B2AA00059DF62 /* UserServiceUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B5E1437245B2AA00059DF62 /* UserServiceUnitTests.swift */; }; 9B5E1443245B50630059DF62 /* StringExtensionUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B5E1442245B50630059DF62 /* StringExtensionUnitTests.swift */; }; 9B5EAB4A232C146D00B9CE3C /* TextFieldExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B5EAB49232C146D00B9CE3C /* TextFieldExtension.swift */; }; - 9B715A461E28083600C0C039 /* PlaceholderTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B715A451E28083600C0C039 /* PlaceholderTextView.swift */; }; 9B77E0731E2FB6350020E450 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B77E0721E2FB6350020E450 /* User.swift */; }; 9B77E0751E2FB66F0020E450 /* UserDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B77E0741E2FB66F0020E450 /* UserDataManager.swift */; }; 9B8574D7212C81950063A3E2 /* SignUpViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B8574D6212C81950063A3E2 /* SignUpViewModel.swift */; }; 9B8574D9212C84130063A3E2 /* ImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B8574D8212C84130063A3E2 /* ImageExtension.swift */; }; 9B88CC671CAB305900AE60C5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B88CC661CAB305900AE60C5 /* Constants.swift */; }; - 9B8D307020AB47E90050697F /* JSONEncodingExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B8D306F20AB47E90050697F /* JSONEncodingExtension.swift */; }; 9B8EB5BD2475890A0082370F /* NetworkState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B8EB5BC2475890A0082370F /* NetworkState.swift */; }; 9B8EB5BF247590710082370F /* AuthViewModelStateDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B8EB5BE247590710082370F /* AuthViewModelStateDelegate.swift */; }; 9BAFB7BF2114D9910099DC61 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BAFB7BE2114D9910099DC61 /* HomeViewModel.swift */; }; @@ -156,13 +154,11 @@ 9B5E1439245B2AA00059DF62 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9B5E1442245B50630059DF62 /* StringExtensionUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensionUnitTests.swift; sourceTree = ""; }; 9B5EAB49232C146D00B9CE3C /* TextFieldExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldExtension.swift; sourceTree = ""; }; - 9B715A451E28083600C0C039 /* PlaceholderTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = PlaceholderTextView.swift; sourceTree = ""; tabWidth = 2; }; 9B77E0721E2FB6350020E450 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 9B77E0741E2FB66F0020E450 /* UserDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDataManager.swift; sourceTree = ""; }; 9B8574D6212C81950063A3E2 /* SignUpViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpViewModel.swift; sourceTree = ""; }; 9B8574D8212C84130063A3E2 /* ImageExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageExtension.swift; sourceTree = ""; }; 9B88CC661CAB305900AE60C5 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; - 9B8D306F20AB47E90050697F /* JSONEncodingExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONEncodingExtension.swift; sourceTree = ""; }; 9B8EB5BC2475890A0082370F /* NetworkState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkState.swift; sourceTree = ""; }; 9B8EB5BE247590710082370F /* AuthViewModelStateDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthViewModelStateDelegate.swift; sourceTree = ""; }; 9B9C6BFD20C7160700EB0523 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; @@ -338,20 +334,11 @@ isa = PBXGroup; children = ( 074889A5247724EF00A0029E /* Protocols */, - 078642F322E669270027BF11 /* Views */, 9BFA84EE1C7767D9009F64E4 /* Models */, ); path = Common; sourceTree = ""; }; - 078642F322E669270027BF11 /* Views */ = { - isa = PBXGroup; - children = ( - 9B715A451E28083600C0C039 /* PlaceholderTextView.swift */, - ); - path = Views; - sourceTree = ""; - }; 12CFFD1CC7E90042DAA6625E /* Frameworks */ = { isa = PBXGroup; children = ( @@ -611,7 +598,6 @@ E8290C011D8330A800599960 /* StringExtension.swift */, E81171911DE5EFB7003D3DF5 /* ViewControllerExtension.swift */, E8290BFD1D832D9200599960 /* ViewExtension.swift */, - 9B8D306F20AB47E90050697F /* JSONEncodingExtension.swift */, 9B8574D8212C84130063A3E2 /* ImageExtension.swift */, 071CD2612228544700E6D385 /* FontExtension.swift */, 9B5EAB49232C146D00B9CE3C /* TextFieldExtension.swift */, @@ -1099,7 +1085,6 @@ E8290C021D8330A800599960 /* StringExtension.swift in Sources */, FE4D636222B2CBB000685161 /* OnboardingRoutes.swift in Sources */, 071CD2622228544700E6D385 /* FontExtension.swift in Sources */, - 9B8D307020AB47E90050697F /* JSONEncodingExtension.swift in Sources */, 074D20D9248E993B002A39B4 /* AuthenticationServices.swift in Sources */, E81171921DE5EFB7003D3DF5 /* ViewControllerExtension.swift in Sources */, 9B3AA3991ED35007005A4D26 /* SignInViewController.swift in Sources */, @@ -1131,7 +1116,6 @@ 9B88CC671CAB305900AE60C5 /* Constants.swift in Sources */, FDFAAB07269CCCD30007DB8B /* LabelExtension.swift in Sources */, 9BAFB7BF2114D9910099DC61 /* HomeViewModel.swift in Sources */, - 9B715A461E28083600C0C039 /* PlaceholderTextView.swift in Sources */, FD8C0272269E2F2E003F86CE /* UIConstants.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios-base/Common/Views/PlaceholderTextView.swift b/ios-base/Common/Views/PlaceholderTextView.swift deleted file mode 100644 index da69d7ec..00000000 --- a/ios-base/Common/Views/PlaceholderTextView.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// PlaceholderTextView.swift -// talkative-iOS -// -// Created by German Lopez on 6/6/16. -// Copyright © 2016 Rootstrap Inc. All rights reserved. -// - -import UIKit - -class PlaceholderTextView: UITextView { - - override var text: String! { - didSet { - // First text set, when placeholder is empty - if text.isEmpty && text != placeholder && !self.isFirstResponder { - text = placeholder - return - } - textColor = text == placeholder ? placeholderColor : fontColor - } - } - @IBInspectable var placeholder: String = "" { - didSet { - if text.isEmpty { - text = placeholder - textColor = placeholderColor - } - } - } - @IBInspectable var placeholderColor: UIColor? = .lightGray - var fontColor: UIColor = .black - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - if let txtC = textColor { - self.fontColor = txtC - } - } - - override func awakeFromNib() { - super.awakeFromNib() - - textColor = text == placeholder ? placeholderColor : fontColor - } - - init( - frame: CGRect, - placeholder: String, - placeholderColor: UIColor = .lightGray - ) { - super.init(frame: frame, textContainer: nil) - self.placeholderColor = placeholderColor - self.placeholder = placeholder - if let txtC = textColor { - self.fontColor = txtC - } - } - - override func becomeFirstResponder() -> Bool { - let isFirstResponder = super.becomeFirstResponder() - if text == placeholder && textColor == placeholderColor { - text = "" - } - textColor = fontColor - return isFirstResponder - } - - override func resignFirstResponder() -> Bool { - if text.isEmpty { - text = placeholder - textColor = placeholderColor - } - return super.resignFirstResponder() - } -} diff --git a/ios-base/Extensions/JSONEncodingExtension.swift b/ios-base/Extensions/JSONEncodingExtension.swift deleted file mode 100644 index d322fe4b..00000000 --- a/ios-base/Extensions/JSONEncodingExtension.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// JSONEncodingExtension.swift -// ios-base -// -// Created by German on 5/15/18. -// Copyright © 2018 Rootstrap Inc. All rights reserved. -// - -import Foundation - -extension JSONDecoder { - - func decode( - _ type: T.Type, from dictionary: [String: Any] - ) throws -> T where T: Decodable { - let data = try? JSONSerialization.data(withJSONObject: dictionary, options: []) - return try decode(T.self, from: data ?? Data()) - } -} diff --git a/ios-base/Extensions/ViewControllerExtension.swift b/ios-base/Extensions/ViewControllerExtension.swift index 7b89edb1..40b7b887 100644 --- a/ios-base/Extensions/ViewControllerExtension.swift +++ b/ios-base/Extensions/ViewControllerExtension.swift @@ -24,32 +24,6 @@ extension UIViewController { present(alert, animated: true, completion: nil) } - func goToScreen(withIdentifier identifier: String, - storyboardId: String? = nil, - modally: Bool = false, - viewControllerConfigurationBlock: ((UIViewController) -> Void)? = nil) { - var storyboard = self.storyboard - - if let storyboardId = storyboardId { - storyboard = UIStoryboard(name: storyboardId, bundle: nil) - } - - guard let viewController = - storyboard?.instantiateViewController(withIdentifier: identifier) else { - assert(false, "No view controller found with that identifier") - return - } - - viewControllerConfigurationBlock?(viewController) - - if modally { - present(viewController, animated: true) - } else { - assert(navigationController != nil, "navigation controller is nil") - navigationController?.pushViewController(viewController, animated: true) - } - } - func applyDefaultUIConfigs() { view.backgroundColor = .screenBackground } From 6813b49f64108fb05b7e4bcf10707df37507338f Mon Sep 17 00:00:00 2001 From: German Lopez Date: Tue, 6 Sep 2022 12:21:48 -0300 Subject: [PATCH 13/13] test suite fixes --- ios-baseUITests/NetworkMocker.swift | 2 +- ios-baseUITests/ios_baseUITests.swift | 10 +++------- .../Cases/Services/UserServiceUnitTests.swift | 4 +--- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/ios-baseUITests/NetworkMocker.swift b/ios-baseUITests/NetworkMocker.swift index fc8ad07b..9d38cea4 100644 --- a/ios-baseUITests/NetworkMocker.swift +++ b/ios-baseUITests/NetworkMocker.swift @@ -19,7 +19,7 @@ enum HTTPMethod { internal class NetworkMocker { - var server = HttpServer() + private(set) lazy var server = HttpServer() func setUp() throws { try server.start() diff --git a/ios-baseUITests/ios_baseUITests.swift b/ios-baseUITests/ios_baseUITests.swift index efb84262..6762543c 100644 --- a/ios-baseUITests/ios_baseUITests.swift +++ b/ios-baseUITests/ios_baseUITests.swift @@ -105,14 +105,10 @@ class ios_baseUITests: XCTestCase { with: "automation@test.com", password: "incorrect password") - if let alert = app.alerts.allElementsBoundByIndex.first { - waitFor(element: alert, timeOut: 2) - - alert.buttons.allElementsBoundByIndex.first?.forceTap() + guard let alert = app.alerts.allElementsBoundByIndex.first else { + return XCTFail("An error alert is expected to appear on Sign In failure") } - - let signInButton = app.buttons["SignInButton"] - waitFor(element: signInButton, timeOut: 2) + alert.buttons.allElementsBoundByIndex.first?.forceTap() } func testSignInValidations() { diff --git a/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift b/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift index f2e13d3e..ba9d6b4b 100644 --- a/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift +++ b/ios-baseUnitTests/Cases/Services/UserServiceUnitTests.swift @@ -50,9 +50,7 @@ class UserServiceUnitTests: XCTestCase { let expectation = expectation(description: "Request should fail") testUserStorageAfterProfileFetch(success: false) { result in switch result { - case .success(let user): - print("DB* \(user)") - print("DB* \(String(describing: UserDataManager.currentUser))") + case .success: XCTFail("GET Profile Request expected to fail") case .failure(let error): expectation.fulfill()