FBSDKLoginKit/FBSDKLoginKitTests/LoginButtonTests.swift (976 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under the license found in the * LICENSE file in the root directory of this source tree. */ @testable import FBSDKLoginKit import TestTools import XCTest final class LoginButtonTests: XCTestCase { let validNonce: String = "abc123" // swiftlint:disable implicitly_unwrapped_optional var loginProvider: TestLoginProvider! var stringProvider: TestUserInterfaceStringProvider! var elementProvider: TestUserInterfaceElementProvider! var graphRequestFactory: TestGraphRequestFactory! var loginButton: FBLoginButton! var sampleToken: AuthenticationToken! var delegate: TestLoginButtonDelegate! // swiftlint:enable implicitly_unwrapped_optional override func setUp() { super.setUp() loginProvider = TestLoginProvider() stringProvider = TestUserInterfaceStringProvider() elementProvider = TestUserInterfaceElementProvider() graphRequestFactory = TestGraphRequestFactory() sampleToken = AuthenticationToken(tokenString: "abc", nonce: "123") delegate = TestLoginButtonDelegate() loginButton = FBLoginButton() loginButton.configure( elementProvider: elementProvider, stringProvider: stringProvider, loginProvider: loginProvider, graphRequestFactory: graphRequestFactory ) loginButton.delegate = delegate AccessToken.setCurrent(nil, shouldDispatchNotif: false) AuthenticationToken.current = nil Profile.current = nil } override func tearDown() { loginProvider = nil stringProvider = nil elementProvider = nil graphRequestFactory = nil sampleToken = nil loginButton = nil delegate = nil super.tearDown() } // MARK: - Dependencies func testDefaultDependencies() { let loginButton = FBLoginButton() XCTAssertIdentical( loginButton.elementProvider, InternalUtility.shared, .hasDefaultElementProvider ) XCTAssertIdentical( loginButton.stringProvider, InternalUtility.shared, .hasDefaultStringProvider ) XCTAssertTrue( loginButton.loginProvider is LoginManager, .hasDefaultLoginProvider ) XCTAssertTrue( loginButton.graphRequestFactory is GraphRequestFactory, .hasDefaultGraphRequestFactory ) } func testCustomDependencies() { XCTAssertIdentical( loginButton.elementProvider, elementProvider, .hasCustomElementProvider ) XCTAssertIdentical( loginButton.stringProvider, stringProvider, .hasCustomStringProvider ) XCTAssertIdentical( loginButton.loginProvider, loginProvider, .hasCustomLoginProvider ) XCTAssertIdentical( loginButton.graphRequestFactory, graphRequestFactory, .hasCustomLoginProvider ) } // MARK: - Nonce func testDefaultNonce() { XCTAssertNil(FBLoginButton().nonce, "Should not have a default nonce") } func testSettingInvalidNonce() { loginButton.nonce = " " XCTAssertNil( loginButton.nonce, "Should not set an invalid nonce" ) } func testSettingValidNonce() { loginButton.nonce = validNonce XCTAssertEqual( loginButton.nonce, validNonce, "Should set a valid nonce" ) } func testLoginConfigurationWithoutNonce() { XCTAssertNotNil( loginButton.loginConfiguration(), "Should be able to create a login configuration without a provided nonce" ) } func testLoginConfigurationWithInvalidNonce() { loginButton.nonce = " " XCTAssertNotNil( loginButton.loginConfiguration(), "Should not create a login configuration with an invalid nonce" ) } func testLoginConfigurationWithValidNonce() { loginButton.nonce = validNonce XCTAssertEqual( loginButton.loginConfiguration().nonce, validNonce, "Should create a login configuration with valid nonce" ) } // MARK: - Initial Content Update func testInitialContentUpdateWithInactiveAccessTokenWithProfile() { AccessToken.setCurrent(nil, shouldDispatchNotif: false) let mockedProfile = SampleUserProfiles.createValid() Profile.setCurrent(mockedProfile, shouldPostNotification: false) loginButton._initializeContent() XCTAssertTrue( loginButton.isSelected, .selectsButtonWithProfile ) XCTAssertEqual( loginButton.userName(), mockedProfile.name, .setsUserNameWithProfile ) XCTAssertEqual( loginButton.userID(), mockedProfile.userID, .setsUserIDWithProfile ) } func testInitialContentUpdateWithActiveAccessTokenWithProfile() throws { AccessToken.setCurrent(SampleAccessTokens.validToken, shouldDispatchNotif: false) let mockedProfile = SampleUserProfiles.createValid() Profile.setCurrent(mockedProfile, shouldPostNotification: false) loginButton._initializeContent() let request = try XCTUnwrap( graphRequestFactory.capturedRequests.first, .createsRequest ) let result = [ "id": SampleAccessTokens.validToken.userID, "name": SampleUserProfiles.defaultName, ] let completion = try XCTUnwrap( graphRequestFactory.capturedRequests.first?.capturedCompletionHandler, .createsRequest ) completion( nil, result, nil ) XCTAssertTrue( loginButton.isSelected, .selectsButtonWithAccessToken ) XCTAssertEqual(request.graphPath, "me", .createsRequest) XCTAssertEqual( request.parameters["fields"] as? String, "id,name", .createsRequestWithParameters ) XCTAssertEqual( loginButton.userName(), result["name"], .setsUserNameWithAccessToken ) XCTAssertEqual( loginButton.userID(), result["id"], .setsUserIDWithAccessToken ) } func testInitialContentUpdateWithoutAccessTokenWithoutProfile() { AccessToken.setCurrent(nil, shouldDispatchNotif: false) Profile.setCurrent(nil, shouldPostNotification: false) loginButton._initializeContent() XCTAssertFalse( loginButton.isSelected, .doesNotSelectsButtonWithoutAccessToken ) } // MARK: - Determining Authentication Status func testDeterminingAuthenticationWithAccessTokenWithoutAuthToken() { AccessToken.setCurrent(SampleAccessTokens.validToken, shouldDispatchNotif: false) XCTAssertTrue( loginButton._isAuthenticated(), "Should consider a user authenticated if they have a current access token" ) } func testDeterminingAuthenticationWithoutAccessTokenWithAuthToken() { AuthenticationToken.current = sampleToken XCTAssertTrue( loginButton._isAuthenticated(), "Should consider a user authenticated if they have a current authentication token" ) } // MARK: - Handling Notifications func testReceivingAccessTokenNotificationWithDidChangeUserIdKey() throws { let notification = Notification( name: .AccessTokenDidChange, object: nil, userInfo: [AccessTokenDidChangeUserIDKey: "foo"] ) AccessToken.setCurrent(SampleAccessTokens.validToken, shouldDispatchNotif: false) loginButton._accessTokenDidChange(notification) let request = try XCTUnwrap( graphRequestFactory.capturedRequests.first, .createsRequest ) let result = [ "id": SampleAccessTokens.validToken.userID, "name": SampleUserProfiles.defaultName, ] let completion = try XCTUnwrap( graphRequestFactory.capturedRequests.first?.capturedCompletionHandler, .createsRequest ) completion( nil, result, nil ) XCTAssertTrue( loginButton.isSelected, .selectsButtonNotificationUserIdKey ) XCTAssertEqual(request.graphPath, "me", .createsRequest) XCTAssertEqual( request.parameters["fields"] as? String, "id,name", .createsRequestWithParameters ) XCTAssertEqual( loginButton.userName(), result["name"], .setsUserNameWithNotificationUserIdKey ) XCTAssertEqual( loginButton.userID(), result["id"], .setsUserIDWithNotificationUserIdKey ) } func testReceivingAccessTokenNotificationWithTokenDidExpireKey() { let notification = Notification( name: .AccessTokenDidChange, object: nil, userInfo: [AccessTokenDidExpireKey: "foo"] ) loginButton._accessTokenDidChange(notification) XCTAssertFalse( loginButton.isSelected, .doesNotSelectsButtonWithExpiredKey ) } func testReceivingAccessTokenNotificationWithoutRelevantUserInfo() { let notification = Notification( name: .AccessTokenDidChange, object: nil, userInfo: nil ) loginButton.isSelected = true let profile = SampleUserProfiles.createValid(userID: SampleAccessTokens.validToken.userID) loginButton._updateContent(forUserProfile: profile) loginButton._accessTokenDidChange(notification) XCTAssertTrue( loginButton.isSelected, .doesNotChangeButtonStateWithoutUserInfo ) XCTAssertEqual( loginButton.userName(), profile.name, .doesNotChangeUserNameWithoutUserInfo ) XCTAssertEqual( loginButton.userID(), profile.userID, .doesNotChangeUserIDWithoutUserInfo ) loginButton.isSelected = false loginButton._accessTokenDidChange(notification) XCTAssertFalse( loginButton.isSelected, .doesNotChangeButtonStateWithoutUserInfo ) } func testReceivingProfileNotificationWithNoProfile() { let notification = Notification( name: .ProfileDidChange, object: nil, userInfo: nil ) let oldUserName = loginButton.userName() let oldUserId = loginButton.userID() loginButton._profileDidChange(notification) XCTAssertFalse( loginButton.isSelected, .doesNotSelectsButtonWithNoProfileNotification ) XCTAssertEqual( loginButton.userName(), oldUserName, .doesNotChangeUserNameWithNoProfileNotification ) XCTAssertEqual( loginButton.userID(), oldUserId, .doesNotChangeUserIDWithNoProfileNotification ) } func testReceivingProfileNotificationWithProfile() { let notification = Notification( name: .ProfileDidChange, object: nil, userInfo: nil ) let mockedProfile = SampleUserProfiles.createValid() Profile.setCurrent(mockedProfile, shouldPostNotification: false) loginButton._profileDidChange(notification) XCTAssertTrue( loginButton.isSelected, .selectsButtonWithProfileNotification ) XCTAssertEqual( loginButton.userName(), mockedProfile.name, .setsUserNameWithProfileNotification ) XCTAssertEqual( loginButton.userID(), mockedProfile.userID, .setsUserIDWithProfileNotification ) } func testReceivingProfileNotificationWithSameProfile() { let notification = Notification( name: .ProfileDidChange, object: nil, userInfo: nil ) let mockedProfile = SampleUserProfiles.createValid() Profile.setCurrent(mockedProfile, shouldPostNotification: false) loginButton._updateContent(forUserProfile: mockedProfile) loginButton._profileDidChange(notification) XCTAssertTrue( loginButton.isSelected, .selectsButtonWithProfileNotification ) XCTAssertEqual( loginButton.userName(), mockedProfile.name, .doesNotChangeUserNameWithSameProfile ) XCTAssertEqual( loginButton.userID(), mockedProfile.userID, .doesNotChangeUserIdWithSameProfile ) } // MARK: - Updating Content func testUpdatingContentWithMissingProfile() { loginButton._updateContent(forUserProfile: nil) XCTAssertFalse( loginButton.isSelected, "Should not be selected if there is not a profile" ) XCTAssertNil(loginButton.userName()) XCTAssertNil(loginButton.userID()) } func testUpdatingContentWithProfile() { let profile = SampleUserProfiles.createValid() loginButton._updateContent(forUserProfile: profile) XCTAssertTrue( loginButton.isSelected, "Should be selected if there is a valid profile" ) XCTAssertEqual(loginButton.userName(), profile.name) XCTAssertEqual(loginButton.userID(), profile.userID) } func testUpdatingContentForProfileWithNewId() { let profile = SampleUserProfiles.createValid(userID: "345") loginButton._updateContent(forUserProfile: SampleUserProfiles.createValid()) loginButton._updateContent(forUserProfile: profile) XCTAssertTrue( loginButton.isSelected, .selectsButtonForProfileWithNewId ) XCTAssertEqual( loginButton.userName(), profile.name, .updatesUserNameForProfileWithNewId ) XCTAssertEqual( loginButton.userID(), profile.userID, .updatesUserIdForProfileWithNewId ) } func testUpdatingContentForProfileWithNewName() { let profile = SampleUserProfiles.createValid(userID: "345") loginButton._updateContent( forUserProfile: SampleUserProfiles.createValid(userID: "345", name: "Paul Smith") ) loginButton._updateContent(forUserProfile: profile) XCTAssertTrue( loginButton.isSelected, .selectsButtonForProfileWithNewName ) XCTAssertEqual( loginButton.userName(), profile.name, .updatesUserNameForProfileWithNewName ) XCTAssertEqual( loginButton.userID(), profile.userID, .updatesUserIdForProfileWithNewName ) } func testUpdatingContentWithValidAccessToken() throws { AccessToken.setCurrent(SampleAccessTokens.validToken, shouldDispatchNotif: false) loginButton._updateContentForAccessToken() let request = try XCTUnwrap( graphRequestFactory.capturedRequests.first, .createsRequest ) let result = [ "id": SampleAccessTokens.validToken.userID, "name": SampleUserProfiles.defaultName, ] let completion = try XCTUnwrap( graphRequestFactory.capturedRequests.first?.capturedCompletionHandler, .createsRequest ) completion( nil, result, nil ) XCTAssertTrue( loginButton.isSelected, .selectsButtonWithAccessTokenUpdate ) XCTAssertEqual(request.graphPath, "me", .createsRequest) XCTAssertEqual( request.parameters["fields"] as? String, "id,name", .createsRequestWithParameters ) XCTAssertEqual( loginButton.userName(), result["name"], .updatesUserNameWithAccessToken ) XCTAssertEqual( loginButton.userID(), result["id"], .updatesUserIDWithAccessToken ) } func testUpdatingContentWithInvalidAccessToken() { AccessToken.setCurrent(SampleAccessTokens.expiredToken, shouldDispatchNotif: false) loginButton._updateContentForAccessToken() XCTAssertFalse( loginButton.isSelected, .doesNotSelectButtonWithInvalidAccessToken ) } func testUpdatingContentWithIdenticalAccessToken() { // Make sure the username and id properties on button are set to the same values // as the access token. This is an easy way to do with without having to stub // a network call let profile = SampleUserProfiles.createValid(userID: SampleAccessTokens.validToken.userID) loginButton._updateContent(forUserProfile: profile) AccessToken.setCurrent(SampleAccessTokens.validToken, shouldDispatchNotif: false) loginButton._updateContentForAccessToken() XCTAssertTrue( loginButton.isSelected, .selectsButtonWithIdenticalAccessToken ) XCTAssertEqual( loginButton.userName(), profile.name, .doesNotChangeUserNameWithIdenticalAccessToken ) XCTAssertEqual( loginButton.userID(), profile.userID, .doesNotChangeUserIdWithIdenticalAccessToken ) } // MARK: - Fetching Content func testFetchContentGraphRequestCreation() throws { loginButton._fetchAndSetContent() let request = try XCTUnwrap(graphRequestFactory.capturedRequests.first) XCTAssertEqual(request.graphPath, "me") XCTAssertEqual(request.parameters["fields"] as? String, "id,name") } func testFetchContentCompleteWithError() throws { AccessToken.current = SampleAccessTokens.validToken loginButton._fetchAndSetContent() let completion = try XCTUnwrap(graphRequestFactory.capturedRequests.first?.capturedCompletionHandler) completion( nil, [ "id": SampleAccessTokens.validToken.userID, "name": SampleUserProfiles.defaultName, ], NSError(domain: "foo", code: 0, userInfo: nil) ) XCTAssertNil(loginButton.userID()) XCTAssertNil(loginButton.userName()) } func testFetchContentCompleteWithNilResponse() throws { loginButton._fetchAndSetContent() let completion = try XCTUnwrap(graphRequestFactory.capturedRequests.first?.capturedCompletionHandler) completion(nil, nil, nil) XCTAssertNil(loginButton.userID()) XCTAssertNil(loginButton.userName()) } func testFetchContentCompleteWithEmptyResponse() throws { loginButton._fetchAndSetContent() let completion = try XCTUnwrap(graphRequestFactory.capturedRequests.first?.capturedCompletionHandler) completion(nil, [], nil) XCTAssertNil(loginButton.userID()) XCTAssertNil(loginButton.userName()) } func testFetchContentCompleteWithMatchingUID() throws { AccessToken.current = SampleAccessTokens.validToken loginButton._fetchAndSetContent() let completion = try XCTUnwrap(graphRequestFactory.capturedRequests.first?.capturedCompletionHandler) completion( nil, [ "id": SampleAccessTokens.validToken.userID, "name": SampleUserProfiles.defaultName, ], nil ) XCTAssertEqual(loginButton.userID(), SampleAccessTokens.validToken.userID) XCTAssertEqual(loginButton.userName(), SampleUserProfiles.defaultName) } func testFetchContentCompleteWithNonmatchingUID() throws { AccessToken.current = SampleAccessTokens.validToken loginButton._fetchAndSetContent() let completion = try XCTUnwrap(graphRequestFactory.capturedRequests.first?.capturedCompletionHandler) completion( nil, [ "id": name, "name": SampleUserProfiles.defaultName, ], nil ) XCTAssertNil(loginButton.userID()) XCTAssertNil(loginButton.userName()) } // MARK: - Setting Messenger Page ID func testDefaultMessengerPageId() { XCTAssertNil(FBLoginButton().messengerPageId, "Should not have a default Messenger Page ID") } func testSettingMessengerPageId() { loginButton.messengerPageId = "1234" XCTAssertEqual( loginButton.messengerPageId, "1234", "Should set a valid Messenger Page ID" ) } func testLoginConfigurationWithMessengerPageId() { loginButton.messengerPageId = "1234" XCTAssertNotNil( loginButton.loginConfiguration(), "Should be able to create a configuration with Messenger Page Id" ) } // MARK: - Setting Auth Type func testDefaultAuthType() { XCTAssertEqual( FBLoginButton().authType, LoginAuthType.rerequest, "Default auth_type should be rerequest" ) } func testSettingAuthType() { loginButton.authType = .reauthorize XCTAssertEqual( loginButton.authType, .reauthorize, "Should set a valid auth type" ) } func testLoginConfigurationWithAuthType() { loginButton.authType = .reauthorize XCTAssertNotNil( loginButton.loginConfiguration(), "Should be able to create a configuration with auth type" ) XCTAssertEqual(loginButton.loginConfiguration().authType, .reauthorize) } func testLoginConfigurationWithNilAuthType() { loginButton.authType = nil XCTAssertNotNil( loginButton.loginConfiguration(), "Should be able to create a configuration with nil auth type" ) XCTAssertNil(loginButton.loginConfiguration().authType) } func testLoginConfigurationWithNoAuthType() { XCTAssertNotNil( loginButton.loginConfiguration(), "Should be able to create a configuration with default auth type" ) XCTAssertEqual(loginButton.loginConfiguration().authType, .rerequest) } // MARK: default audience func testDefaultAudience() { XCTAssertEqual( loginButton.defaultAudience, .friends, "Should have a default audience of friends" ) } func testSettingDefaultAudience() { loginButton.defaultAudience = .onlyMe XCTAssertEqual( loginButton.defaultAudience, .onlyMe, "Should set the default audience to only me" ) XCTAssertEqual( loginProvider.defaultAudience, .onlyMe, "Should set the default audience of the underlying login provider to only me" ) } // MARK: login tracking func testDefaultLoginTracking() { XCTAssertEqual( loginButton.loginTracking, .enabled, "Should set the default login tracking to be enabled" ) } func testSettingLoginTracking() { loginButton.loginTracking = .limited XCTAssertEqual( loginButton.loginTracking, .limited, "Should set the login tracking to limited" ) XCTAssertEqual( loginButton.loginConfiguration().tracking, .limited, "Should created a login configuration with the expected tracking" ) } // MARK: Code Verifier func testSettingCodeVerifier() { let codeVerifier = CodeVerifier() loginButton.codeVerifier = codeVerifier XCTAssertEqual( loginButton.codeVerifier.value, codeVerifier.value, "Should set the code verifier to the expected value" ) XCTAssertEqual( loginButton.loginConfiguration().codeVerifier.value, codeVerifier.value, "Should create a login configuration with the expected code verifier" ) } func testDefaultCodeVerifier() { XCTAssertNotNil( loginButton.codeVerifier, "Default code verifier should not be nil" ) XCTAssertNotNil( loginButton.loginConfiguration().codeVerifier, "Should create a login configuration with the default code verifier" ) } // MARK: Button Press func testButtonPressNotAuthenticatedLoginNotAllowed() throws { delegate.shouldLogin = false loginButton._buttonPressed(self) XCTAssert(delegate.willLogin) XCTAssertNil(loginProvider.capturedCompletion) XCTAssertNil(loginProvider.capturedConfiguration) } func testButtonPressNotAuthenticatedLoginAllowed() throws { loginButton._buttonPressed(self) XCTAssert(delegate.willLogin) XCTAssertNotNil(loginProvider.capturedConfiguration) let completion = try XCTUnwrap(loginProvider.capturedCompletion) let granted = Set(SampleAccessTokens.validToken.permissions.map { $0.name }) let declined = Set(SampleAccessTokens.validToken.declinedPermissions.map { $0.name }) let result = LoginManagerLoginResult( token: SampleAccessTokens.validToken, authenticationToken: sampleToken, isCancelled: false, grantedPermissions: granted, declinedPermissions: declined ) completion(result, nil) XCTAssertEqual(delegate.capturedResult, result) } func testButtonPressAuthenticated() throws { AuthenticationToken.current = sampleToken let rootVC = UIViewController() elementProvider.stubbedTopMostViewController = rootVC let window = UIWindow() window.rootViewController = rootVC window.makeKeyAndVisible() rootVC.view.addSubview(loginButton) loginButton._buttonPressed(self) let presentedVC = try XCTUnwrap(window.rootViewController?.presentedViewController, .showsAlertViewController) XCTAssertTrue( presentedVC is UIAlertController, .showsAlertViewController ) } func testLogout() { loginButton._logout() XCTAssert(loginProvider.didLogout) XCTAssert(delegate.didLoggedOut) } // MARK: - Tooltip func testShowTooltipIfNeeded() { let button = FBLoginButton() button.tooltipBehavior = .forceDisplay let window = UIWindow() let rootVC = UIViewController() window.rootViewController = rootVC window.makeKeyAndVisible() rootVC.view.addSubview(button) if !rootVC.view.subviews.contains(where: { $0 is FBTooltipView }) { XCTFail(.showsTooltip) } } func testShowTooltipIfNeededWithAuthenticated() { AuthenticationToken.current = sampleToken let button = FBLoginButton() button.tooltipBehavior = .forceDisplay let window = UIWindow() let rootVC = UIViewController() window.rootViewController = rootVC window.makeKeyAndVisible() rootVC.view.addSubview(button) if rootVC.view.subviews.contains(where: { $0 is FBTooltipView }) { XCTFail(.doesNotShowTooltipForAuthenticated) } } func testShowTooltipIfNeededWithDisabledBehavior() { let button = FBLoginButton() button.tooltipBehavior = .disable let window = UIWindow() let rootVC = UIViewController() window.rootViewController = rootVC window.makeKeyAndVisible() rootVC.view.addSubview(button) if rootVC.view.subviews.contains(where: { $0 is FBTooltipView }) { XCTFail(.doesNotShowTooltipIfDisabled) } } // MARK: - Layout func testImageRectForContentRect() { let contentRect = CGRect(x: 0, y: 0, width: 100, height: 100) let imageRect = loginButton.imageRect(forContentRect: contentRect) let expectedImageRect = CGRect(x: 6.0, y: 42.0, width: 16.0, height: 16.0) XCTAssertEqual( imageRect, expectedImageRect, .hasCustomImageFrame ) } func testTitleRectForContentRect() { let contentRect = CGRect(x: 0, y: 0, width: 100, height: 100) loginButton.frame = contentRect let titleRect = loginButton.titleRect(forContentRect: contentRect) let expectedTitleRect = CGRect(x: 30.0, y: 0.0, width: 62.0, height: 100.0) XCTAssertEqual( titleRect, expectedTitleRect, .hasCustomTitleFrame ) } } // MARK: - Assumptions fileprivate extension String { static let hasCustomImageFrame = """ The image frame for a button should be vertically centered and \ given left padding of 6 points and sized like the logo """ static let hasCustomTitleFrame = """ The title frame for a button should have a 8 points horizontal padding and no vertical padding. \ Size should be as big as content frame minus paddings and image frame """ static let showsTooltip = "Shows a tooltip if user is not authenticated or tooltip is not disabled" static let doesNotShowTooltipForAuthenticated = "Does not show a tooltip if user is authenticated" static let doesNotShowTooltipIfDisabled = "Does not show a tooltip if tooltip has been disabled" static let showsAlertViewController = """ Shows an alert view controller when the login button is pressed, if the user is already authenticated """ static let hasDefaultElementProvider = """ Should have a default element provider dependency """ static let hasDefaultStringProvider = """ Should have a default string provider dependency """ static let hasDefaultLoginProvider = """ Should have a default login provider dependency """ static let hasDefaultGraphRequestFactory = """ Should have a default graph request factory dependency """ static let hasCustomElementProvider = """ Should have a custom element provider dependency """ static let hasCustomStringProvider = """ Should have a custom string provider dependency """ static let hasCustomLoginProvider = """ Should have a custom login provider dependency """ static let hasCustomGraphRequestFactory = """ Should have a custom graph request factory dependency """ static let selectsButtonWithProfile = """ Login button should be selected when initializing content \ and using a profile """ static let setsUserNameWithProfile = """ User name should be set when initializing content \ and using a profile """ static let setsUserIDWithProfile = """ User id should be set when initializing content \ and using a profile """ static let selectsButtonWithAccessToken = """ Login button should be selected when initializing content \ and using an access token """ static let setsUserNameWithAccessToken = """ User name should be set when initializing content \ and using an access token """ static let setsUserIDWithAccessToken = """ User id should be set when initializing content \ and using an access token """ static let createsRequest = """ Should create a request with the expected path """ static let createsRequestWithParameters = """ Should create a request with the expected parameters """ static let doesNotSelectsButtonWithoutAccessToken = """ Login button should not be selected when there is no access token \ or current profile """ static let selectsButtonNotificationUserIdKey = """ Login button should be selected when we receive an access token \ change notification with a user id key """ static let setsUserNameWithNotificationUserIdKey = """ User name should be equal to user name we received in the response \ after we receive an access token change notification with \ a user id key """ static let setsUserIDWithNotificationUserIdKey = """ User id should be equal to user id we received in the response \ after we receive an access token change notification with \ a user id key """ static let doesNotSelectsButtonWithExpiredKey = """ Login button should not be selected after we receive an \ access token change notification with an expired key """ static let doesNotChangeButtonStateWithoutUserInfo = """ Login button should not change current state after we \ receive an access token change notification with no user info """ static let doesNotChangeUserNameWithoutUserInfo = """ User name should not change after we receive an \ access token change notification with no user info """ static let doesNotChangeUserIDWithoutUserInfo = """ User id should not change after we receive an \ access token change notification with no user info """ static let selectsButtonWithProfileNotification = """ Login button should be selected after we receive a \ profile change notification with a valid profile """ static let setsUserNameWithProfileNotification = """ User name should be changed after we receive a \ profile change notification with a different profile """ static let setsUserIDWithProfileNotification = """ User id should be changed after we receive a \ profile change notification with a different profile """ static let doesNotSelectsButtonWithNoProfileNotification = """ Login button should not be selected after we receive a \ profile change notification and there is no profile """ static let doesNotChangeUserNameWithNoProfileNotification = """ User name should not change after we receive a \ profile change notification and there is no profile """ static let doesNotChangeUserIDWithNoProfileNotification = """ User id should not change after we receive a \ profile change notification and there is no profile """ static let doesNotChangeUserNameWithSameProfile = """ User name should change after we receive a \ profile change notification with same profile """ static let doesNotChangeUserIdWithSameProfile = """ User id should change after we receive a \ profile change notification with same profile """ static let selectsButtonWithAccessTokenUpdate = """ Login button should be selected when updating content \ and using an access token """ static let updatesUserNameWithAccessToken = """ User name should be set when updating content \ and using an access token """ static let updatesUserIDWithAccessToken = """ User id should be set when updating content \ and using an access token """ static let doesNotSelectButtonWithInvalidAccessToken = """ Login button should not be selected when updating content \ and using an invalid access token """ static let selectsButtonWithIdenticalAccessToken = """ Login button should be selected when attempting to update content \ with an identical access token """ static let doesNotChangeUserNameWithIdenticalAccessToken = """ User name should not change when attempting to update content \ with an identical access token """ static let doesNotChangeUserIdWithIdenticalAccessToken = """ User id should not change when attempting to update content \ with an identical access token """ static let selectsButtonForProfileWithNewId = """ Login button should be selected when attempting to update content \ with a profile with a new user id """ static let updatesUserNameForProfileWithNewId = """ User name should change when attempting to update content \ with a profile with a new user id """ static let updatesUserIdForProfileWithNewId = """ User id should change when attempting to update content \ with a profile with a new user id """ static let selectsButtonForProfileWithNewName = """ Login button should be selected when attempting to update content \ with a profile with an identical user id but has a new name """ static let updatesUserNameForProfileWithNewName = """ User name should change when attempting to update content \ with a profile with an identical user id has a new name """ static let updatesUserIdForProfileWithNewName = """ User id should change when attempting to update content \ with a profile with an identical user id has a new name """ }