DaysUntilBirthdayUITests_iOS.swift 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. * Copyright 2022 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import XCTest
  17. class DaysUntilBirthdayUITests_iOS: XCTestCase {
  18. private let signInStaticText =
  19. "“DaysUntilBirthday (iOS)” Wants to Use “google.com” to Sign In"
  20. private let timeout: TimeInterval = 5
  21. private let sampleApp = XCUIApplication()
  22. private let springboardApp = XCUIApplication(
  23. bundleIdentifier: "com.apple.springboard"
  24. )
  25. func testSignInNavigateToDaysUntilBirthdayAndDisconnect() {
  26. sampleApp.launch()
  27. XCTAssertTrue(signIn())
  28. XCTAssertTrue(navigateToDaysUntilBirthday())
  29. XCTAssertTrue(navigateBackToUserProfileView())
  30. sampleApp.navigationBars.buttons["Disconnect"].tap()
  31. guard sampleApp
  32. .buttons["GoogleSignInButton"]
  33. .waitForExistence(timeout: timeout) else {
  34. return XCTFail("Disconnecting should return user to sign in view")
  35. }
  36. }
  37. func testSignInAndSignOut() {
  38. sampleApp.launch()
  39. XCTAssertTrue(signIn())
  40. guard sampleApp
  41. .navigationBars
  42. .buttons["Sign Out"]
  43. .waitForExistence(timeout: timeout) else {
  44. return XCTFail("Failed to find the 'Disconnect' button")
  45. }
  46. sampleApp.buttons["Sign Out"].tap()
  47. guard sampleApp
  48. .buttons["GoogleSignInButton"]
  49. .waitForExistence(timeout: timeout) else {
  50. return XCTFail("Signing out should return user to sign in view")
  51. }
  52. }
  53. }
  54. extension DaysUntilBirthdayUITests_iOS {
  55. /// Performs a sign in.
  56. /// - returns: `true` if the sign in was succesfull.
  57. func signIn() -> Bool {
  58. let signInButton = sampleApp.buttons["GoogleSignInButton"]
  59. guard signInButton.exists else {
  60. XCTFail("Sign in button does not exist")
  61. return false
  62. }
  63. signInButton.tap()
  64. guard springboardApp
  65. .staticTexts[signInStaticText]
  66. .waitForExistence(timeout: timeout) else {
  67. XCTFail("Failed to display permission prompt")
  68. return false
  69. }
  70. guard springboardApp
  71. .buttons["Continue"]
  72. .waitForExistence(timeout: timeout) else {
  73. XCTFail("Failed to find 'Continue' button")
  74. return false
  75. }
  76. springboardApp.buttons["Continue"].tap()
  77. if sampleApp
  78. .staticTexts[Credential.email.rawValue]
  79. .waitForExistence(timeout: timeout) {
  80. // This email was previously used to sign in
  81. XCTAssertTrue(useExistingSignIn())
  82. } else {
  83. // This is a first time sign in
  84. XCTAssertTrue(signInForTheFirstTime())
  85. }
  86. guard sampleApp.wait(for: .runningForeground, timeout: timeout) else {
  87. XCTFail("Failed to return sample app to foreground")
  88. return false
  89. }
  90. guard sampleApp.staticTexts["User Profile"]
  91. .waitForExistence(timeout: timeout) else {
  92. XCTFail("Failed to sign in and return to app's User Profile view.")
  93. return false
  94. }
  95. return true
  96. }
  97. /// Signs in expecting the first time flow.
  98. /// @discussion
  99. /// This will assumme the full flow where a user must type in an email and
  100. /// password to sign in with.
  101. func signInForTheFirstTime() -> Bool {
  102. guard sampleApp.textFields["Email or phone"]
  103. .waitForExistence(timeout: timeout) else {
  104. XCTFail("Failed to find email textfield")
  105. return false
  106. }
  107. guard sampleApp
  108. .keyboards
  109. .element
  110. .buttons["return"]
  111. .waitForExistence(timeout: timeout) else {
  112. XCTFail("Failed to find 'return' button")
  113. return false
  114. }
  115. sampleApp.textFields["Email or phone"].typeText(Credential.email.rawValue)
  116. sampleApp.keyboards.element.buttons["return"].tap()
  117. guard sampleApp.secureTextFields["Enter your password"]
  118. .waitForExistence(timeout: timeout) else {
  119. XCTFail("Failed to find password textfield")
  120. return false
  121. }
  122. guard sampleApp
  123. .keyboards
  124. .element
  125. .buttons["go"]
  126. .waitForExistence(timeout: timeout) else {
  127. XCTFail("Failed to find 'go' button")
  128. return false
  129. }
  130. sampleApp
  131. .secureTextFields["Enter your password"]
  132. .typeText(Credential.password.rawValue)
  133. sampleApp.keyboards.element.buttons["go"].tap()
  134. return true
  135. }
  136. /// Signs in expecting a prior sign in.
  137. /// @discussion
  138. /// This will check that there is a `Credential.email` in a list to select and
  139. /// sign in with.
  140. func useExistingSignIn() -> Bool {
  141. guard sampleApp.staticTexts[Credential.email.rawValue].exists else {
  142. XCTFail("Email used for previous sign-in not in list")
  143. return false
  144. }
  145. guard sampleApp.staticTexts[Credential.email.rawValue].isHittable else {
  146. XCTFail("Email used for previous sign-in not tappable")
  147. return false
  148. }
  149. sampleApp.staticTexts[Credential.email.rawValue].tap()
  150. return true
  151. }
  152. /// Navigates to the days until birthday view from the user profile view.
  153. /// - returns: `true` if the navigation was performed successfully.
  154. /// - note: This method will attempt to find a pop up asking for permission to
  155. /// sign in with Google.
  156. func navigateToDaysUntilBirthday() -> Bool {
  157. guard sampleApp.buttons["View Days Until Birthday"]
  158. .waitForExistence(timeout: timeout) else {
  159. XCTFail("Failed to find button navigating to days until birthday view")
  160. return false
  161. }
  162. sampleApp.buttons["View Days Until Birthday"].tap()
  163. if springboardApp
  164. .staticTexts[signInStaticText]
  165. .waitForExistence(timeout: timeout) {
  166. guard springboardApp
  167. .buttons["Continue"]
  168. .waitForExistence(timeout: timeout) else {
  169. XCTFail("Failed to find 'Continue' button")
  170. return false
  171. }
  172. springboardApp.buttons["Continue"].tap()
  173. guard sampleApp
  174. .staticTexts["Days Until Birthday wants to access your Google Account"]
  175. .waitForExistence(timeout: timeout) else {
  176. XCTFail("Failed to find permission screen")
  177. return false
  178. }
  179. guard sampleApp.buttons["Allow"].waitForExistence(timeout: timeout) else {
  180. XCTFail("Failed to find 'Allow' button")
  181. return false
  182. }
  183. sampleApp.buttons["Allow"].tap()
  184. }
  185. guard sampleApp.staticTexts["Days Until Birthday"]
  186. .waitForExistence(timeout: timeout) else {
  187. XCTFail("Failed to show days until birthday view")
  188. return false
  189. }
  190. return true
  191. }
  192. /// Navigates back to the User Profile view from the Days Until Birthday View.
  193. /// - returns: `true` if the navigation was successfully performed.
  194. func navigateBackToUserProfileView() -> Bool {
  195. guard sampleApp
  196. .navigationBars
  197. .buttons["User Profile"]
  198. .waitForExistence(timeout: timeout) else {
  199. XCTFail("Failed to show navigation button back to user profile view")
  200. return false
  201. }
  202. sampleApp.navigationBars.buttons["User Profile"].tap()
  203. guard sampleApp
  204. .navigationBars
  205. .buttons["Disconnect"]
  206. .waitForExistence(timeout: timeout) else {
  207. XCTFail("Failed to find the 'Disconnect' button")
  208. return false
  209. }
  210. return true
  211. }
  212. }