#40: Add UserSessionStoreProtocol.

Only log out of the specific account.
Add tests for the keychain controller.
Expand test coverage.
PR comments
This commit is contained in:
Doug 2022-06-15 10:10:22 +01:00 committed by Doug
parent abd6a9c7c0
commit 8653f1dd6b
17 changed files with 261 additions and 78 deletions

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
@ -21,7 +21,6 @@
066A1E9B94723EE9F3038044 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47EBB5D698CE9A25BB553A2D /* Strings.swift */; };
072BA9DBA932374CCA300125 /* MessageComposerTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE6C10032A77AE7DC5AA4C50 /* MessageComposerTextField.swift */; };
0B1F80C2BF7D223159FBA82C /* ImageAnonymizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6045E825AE900A92D61FEFF0 /* ImageAnonymizerTests.swift */; };
0C601923A872A87C775B889A /* KeychainControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3689E6F87850DD65DAA45428 /* KeychainControllerProtocol.swift */; };
0E8C480700870BB34A2A360F /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 78A5A8DE1E2B09C978C7F3B0 /* KeychainAccess */; };
0EA6537A07E2DC882AEA5962 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 187853A7E643995EE49FAD43 /* Localizable.stringsdict */; };
0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */; };
@ -67,6 +66,7 @@
352C439BE0F75E101EF11FB1 /* RoomScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2886615BEBAE33A0AA4D5F8 /* RoomScreenModels.swift */; };
35E975CFDA60E05362A7CF79 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 1222DB76B917EB8A55365BA5 /* target.yml */; };
368C8758FCD079E6AAA18C2C /* NoticeRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */; };
36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */; };
3772354754450F2B54107E17 /* TemplateSimpleScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4EDB32B97910AAAFE632B2 /* TemplateSimpleScreenViewModelProtocol.swift */; };
38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EA1D2CBAEA5D0BD00B90D1B /* BindableState.swift */; };
39AE84C8E5F2FE9D2DC7775C /* EventBasedTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56008790A9C4479A6B31FDF4 /* EventBasedTimelineView.swift */; };
@ -125,6 +125,7 @@
77E192BA943B90F9F310CA23 /* WeakDictionaryKeyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FFCC48E7F701B6C24484593 /* WeakDictionaryKeyReference.swift */; };
78B71D53C1FC55FB7A9B75F0 /* RoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24B0C97D2F560BCB72BE73B1 /* RoomTimelineController.swift */; };
7963F98CDFDEAC75E072BD81 /* TextRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6A8C632CEF4600107792899 /* TextRoomTimelineItem.swift */; };
79A6E08ADE6E7C460A8A17A5 /* UserSessionStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C37FB986891D90BEAA93EAE /* UserSessionStore.swift */; };
7A54700193DC1F264368746A /* UserIndicatorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E077F76026C85ED96FEBB810 /* UserIndicatorPresenter.swift */; };
7B3D3AFD511D496DED18910B /* TemplateSimpleScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C485C186CEC78443DA96BDC8 /* TemplateSimpleScreenViewModelTests.swift */; };
7BB31E67648CF32D2AB5E502 /* RoomScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CE3C90E487B255B735D73C8 /* RoomScreenViewModel.swift */; };
@ -133,7 +134,6 @@
7D1DAAA364A9A29D554BD24E /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0950733DD4BA83EEE752E259 /* PlaceholderAvatarImage.swift */; };
7DE5EB4CB2401C672257283C /* WeakKeyDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12969CEC0051BC750DA5068 /* WeakKeyDictionary.swift */; };
7E1EDBA3934E6C29E5BD045B /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77DD2DA5DC8654F2A80FF1D /* Bundle.swift */; };
7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8BDC092D817B68CD9040C5 /* UserSessionStore.swift */; };
7F19E97E7985F518C9018B83 /* RootRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF47564C584F614B7287F3EB /* RootRouter.swift */; };
7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */; };
7FA4227B2BAAA71560252866 /* UserIndicatorDismissal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D1532B5D9FB0C8461A1453 /* UserIndicatorDismissal.swift */; };
@ -174,7 +174,6 @@
A851635B3255C6DC07034A12 /* RoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */; };
A941EAD7F407F2ED6DA54A31 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA97D630B74B0616C1468CBD /* LoginScreen.swift */; };
AB34401E4E1CAD5D2EC3072B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9760103CF316DF68698BCFE6 /* LaunchScreen.storyboard */; };
AE5360A13B915F1CAECDE4CC /* AuthenticationMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09DD4E0CB6EE4AD939E46A62 /* AuthenticationMetrics.swift */; };
B0EDAF55877DE19B67837C22 /* TemplateSimpleScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1C29670CEC77346F31EE94C /* TemplateSimpleScreenModels.swift */; };
B245583C63F8F90357B87FAE /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 3853B78FB8531B83936C5DA6 /* SwiftState */; };
B3FDB1D9CF40777695DBBD1D /* AppCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9AB74614131D6706894E0C /* AppCoordinatorStateMachine.swift */; };
@ -209,18 +208,21 @@
DCB781BD227CA958809AFADF /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CC95CD75B688E946438165 /* Coordinator.swift */; };
DDB80FD2753FEAAE43CC2AAE /* ImageRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A63815AD6A5C306453342F2 /* ImageRoomTimelineItem.swift */; };
DE4F8C4E0F1DB4832F09DE97 /* HomeScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D6764D6976D235926FE5FC /* HomeScreenViewModel.swift */; };
DF790EF2E4D41D1091AEB263 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317F02B15921BF5CC8486990 /* KeychainController.swift */; };
DFF7D6A6C26DDD40D00AE579 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = F012CB5EE3F2B67359F6CC52 /* target.yml */; };
E81EEC1675F2371D12A880A3 /* MockRoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61ADFB893DEF81E58DF3FAB9 /* MockRoomTimelineController.swift */; };
E9CEAF2C38E4E00459B811D9 /* LoginScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2082B5226B2A3A4D0798B6 /* LoginScreenModels.swift */; };
EA1E7949533E19C6D862680A /* MediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885D8C42DD17625B5261BEFF /* MediaProvider.swift */; };
EA31DD9043B91ECB8E45A9A6 /* ScreenshotDetectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03C9D319676F3C0DC6B0203 /* ScreenshotDetectorTests.swift */; };
EA65360A0EC026DD83AC0CF5 /* AuthenticationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA5F386C7701C129398945 /* AuthenticationCoordinator.swift */; };
EBD6C79705B3DDB2F7E5F554 /* UserSessionStoreProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1B52D0ABBA7091A991CAFE /* UserSessionStoreProtocol.swift */; };
ED4F663C783E9A8C0E80B983 /* TemplateSimpleScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47543EB19F3DCF308751F53C /* TemplateSimpleScreenViewModel.swift */; };
EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */; };
EEC40663922856C65D1E0DF5 /* KeychainControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */; };
EF99A92701E401C4CD5ADC50 /* SplashScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE978A6118C131D7F2A04B3 /* SplashScreenModels.swift */; };
F01DB7DD607015557CD48B33 /* ViewFrameReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 242A3BC7FAE2256930FB8527 /* ViewFrameReader.swift */; };
F03E16ED043C62FED5A07AE0 /* MatrixEntitityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B81C8227BBEA95CCE86037 /* MatrixEntitityRegex.swift */; };
F2DD8661B5C0BA2BB526FA6C /* KeychainControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD51F9FDC91C231906D76C8 /* KeychainControllerProtocol.swift */; };
F4C3FEDB1B3A05376A1723A3 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4427F9E0571B4E6E048A2B /* KeychainController.swift */; };
F56261126E368C831B3DE976 /* NavigationRouterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 752DEC02D93AFF46BC13313A /* NavigationRouterType.swift */; };
F656F92A63D3DC1978D79427 /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 531CE4334AC5CA8DFF6AEB84 /* DTCoreText */; };
F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */; };
@ -261,14 +263,13 @@
095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProviderProtocol.swift; sourceTree = "<group>"; };
0960A7F5C1B0B6679BDF26F9 /* ElementToggleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementToggleStyle.swift; sourceTree = "<group>"; };
09747989908EC5E4AA29F844 /* MemberDetailsProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberDetailsProviderProtocol.swift; sourceTree = "<group>"; };
09DD4E0CB6EE4AD939E46A62 /* AuthenticationMetrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationMetrics.swift; sourceTree = "<group>"; };
0A191D3FDB995309C7E2DE7D /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
0AB7A0C06CB527A1095DEB33 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = da; path = da.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
0C13A92C1E9C79F055B8133D /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ar; path = ar.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
0CB569EAA5017B5B23970655 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = "<group>"; };
0CD51F9FDC91C231906D76C8 /* KeychainControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerProtocol.swift; sourceTree = "<group>"; };
0DD16CE9A66C9040B066AD60 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
0E7062F88E9D5F79C8A80524 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = th; path = th.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
0E8BDC092D817B68CD9040C5 /* UserSessionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStore.swift; sourceTree = "<group>"; };
0EE9EAF0309A2A1D67D8FAF5 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sv; path = sv.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
0F7A812F160E75B69A9181A2 /* SplashScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenCoordinator.swift; sourceTree = "<group>"; };
105B2A8426404EF66F00CFDB /* RoomTimelineItemFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFactory.swift; sourceTree = "<group>"; };
@ -308,18 +309,17 @@
2AE83A3DD63BCFBB956FE5CB /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nl; path = nl.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
2BEB3259B2208E5AE5BB3F65 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
2CF9FE7E0CF9F40D1509E63A /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bg; path = bg.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
317F02B15921BF5CC8486990 /* KeychainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainController.swift; sourceTree = "<group>"; };
31D6764D6976D235926FE5FC /* HomeScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModel.swift; sourceTree = "<group>"; };
325A2B3278875554DDEB8A9B /* SplashScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenUITests.swift; sourceTree = "<group>"; };
32CE6D4FF64C9A3C18619224 /* SplashScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreen.swift; sourceTree = "<group>"; };
33E49C5C6F802B4D94CA78D1 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
35AFCF4C05DEED04E3DB1A16 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
36322DD0D4E29D31B0945ADC /* EventBriefFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBriefFactory.swift; sourceTree = "<group>"; };
3689E6F87850DD65DAA45428 /* KeychainControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerProtocol.swift; sourceTree = "<group>"; };
3747C96188856006F784BF49 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ko; path = ko.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
3782C506F4FF1AADF61B6212 /* tlh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tlh; path = tlh.lproj/Localizable.strings; sourceTree = "<group>"; };
399427358A80BA2848E698A2 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Localizable.strings"; sourceTree = "<group>"; };
39EBB6903EFD4236B8D11A42 /* fr-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "fr-CA"; path = "fr-CA.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
3A4427F9E0571B4E6E048A2B /* KeychainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainController.swift; sourceTree = "<group>"; };
3ACBDC1D28EFB7789EB467E0 /* MockRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomProxy.swift; sourceTree = "<group>"; };
3B5B535DA49C54523FF7A412 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Localizable.strings; sourceTree = "<group>"; };
3CDF9E55650D6035D6536538 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "nb-NO"; path = "nb-NO.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
@ -446,6 +446,7 @@
8BF686BA36D0C2FA3C63DFDF /* ImageRoomMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRoomMessage.swift; sourceTree = "<group>"; };
8C0AA893D6F8A2F563E01BB9 /* in */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = in; path = in.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
8C2ABC1A9B62BDB3D216E7FD /* MemberDetailProviderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberDetailProviderManager.swift; sourceTree = "<group>"; };
8C37FB986891D90BEAA93EAE /* UserSessionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStore.swift; sourceTree = "<group>"; };
8D8169443E5AC5FF71BFB3DB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
90733775209F4D4D366A268F /* RootRouterType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootRouterType.swift; sourceTree = "<group>"; };
92B61C243325DC76D3086494 /* EventBriefFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBriefFactoryProtocol.swift; sourceTree = "<group>"; };
@ -463,6 +464,7 @@
9A68BCE6438873D2661D93D0 /* BugReportServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceProtocol.swift; sourceTree = "<group>"; };
9C5E81214D27A6B898FC397D /* ElementX.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ElementX.entitlements; sourceTree = "<group>"; };
9CE3C90E487B255B735D73C8 /* RoomScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModel.swift; sourceTree = "<group>"; };
9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIConstants.swift; sourceTree = "<group>"; };
A00C7A331B72C0F05C00392F /* RoomScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModelProtocol.swift; sourceTree = "<group>"; };
A1C29670CEC77346F31EE94C /* TemplateSimpleScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateSimpleScreenModels.swift; sourceTree = "<group>"; };
A1ED7E89865201EE7D53E6DA /* SeparatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineItem.swift; sourceTree = "<group>"; };
@ -510,6 +512,7 @@
BE03C54FC7AAE0FC03EC8976 /* SplashScreenPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenPage.swift; sourceTree = "<group>"; };
BE6C10032A77AE7DC5AA4C50 /* MessageComposerTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposerTextField.swift; sourceTree = "<group>"; };
BEE6BF9BA63FF42F8AF6EEEA /* sr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sr; path = sr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
BF1B52D0ABBA7091A991CAFE /* UserSessionStoreProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStoreProtocol.swift; sourceTree = "<group>"; };
C024C151639C4E1B91FCC68B /* ElementXAttributeScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementXAttributeScope.swift; sourceTree = "<group>"; };
C070FD43DC6BF4E50217965A /* LocalizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationTests.swift; sourceTree = "<group>"; };
C21ECC295F4DE8DAA86D62AC /* RoomSummaryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProtocol.swift; sourceTree = "<group>"; };
@ -582,6 +585,7 @@
F9BA045DC4CA12D030ACF558 /* TemplateSimpleScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateSimpleScreen.swift; sourceTree = "<group>"; };
F9E785D5137510481733A3E8 /* TextRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineView.swift; sourceTree = "<group>"; };
FA154570F693D93513E584C1 /* RoomMessageFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMessageFactory.swift; sourceTree = "<group>"; };
FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerTests.swift; sourceTree = "<group>"; };
FE2DF459F1737A594667CC46 /* EmoteRoomMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomMessage.swift; sourceTree = "<group>"; };
FF4EDB32B97910AAAFE632B2 /* TemplateSimpleScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateSimpleScreenViewModelProtocol.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -660,7 +664,7 @@
40E6246F03D1FE377BC5D963 /* Room */,
82D5AD3EAE3A5C1068A44A88 /* Session */,
FCDF06BDB123505F0334B4F9 /* Timeline */,
CBBF6127C313A5412E438BC6 /* UserSession */,
90C85A862720155C0CF63B02 /* UserSessionStore */,
);
path = Services;
sourceTree = "<group>";
@ -716,13 +720,6 @@
path = Resources;
sourceTree = "<group>";
};
298F75357B344DE964106404 /* Login */ = {
isa = PBXGroup;
children = (
);
path = Login;
sourceTree = "<group>";
};
304D3532D4FFC1F0ABC0626E /* ViewFrameReader */ = {
isa = PBXGroup;
children = (
@ -956,6 +953,7 @@
DBFEAC3AC691CBB84983E275 /* ElementXTests.swift */,
505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */,
6045E825AE900A92D61FEFF0 /* ImageAnonymizerTests.swift */,
FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */,
C070FD43DC6BF4E50217965A /* LocalizationTests.swift */,
3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */,
5A43964330459965AF048A8C /* LoginScreenViewModelTests.swift */,
@ -1066,6 +1064,33 @@
path = HTMLParsing;
sourceTree = "<group>";
};
90C85A862720155C0CF63B02 /* UserSessionStore */ = {
isa = PBXGroup;
children = (
3A4427F9E0571B4E6E048A2B /* KeychainController.swift */,
0CD51F9FDC91C231906D76C8 /* KeychainControllerProtocol.swift */,
8C37FB986891D90BEAA93EAE /* UserSessionStore.swift */,
BF1B52D0ABBA7091A991CAFE /* UserSessionStoreProtocol.swift */,
);
path = UserSessionStore;
sourceTree = "<group>";
};
90F48FEF84016ED42A94BA24 /* LoginScreen */ = {
isa = PBXGroup;
children = (
);
path = LoginScreen;
sourceTree = "<group>";
};
91AC284E285B25BB00B7ADB9 /* Extensions */ = {
isa = PBXGroup;
children = (
D77DD2DA5DC8654F2A80FF1D /* Bundle.swift */,
4519B90DE2F54443E5F43DA8 /* String.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
9413F680ECDFB2B0DDB0DEF2 /* Packages */ = {
isa = PBXGroup;
children = (
@ -1227,11 +1252,10 @@
children = (
49EAD710A2C16EFF7C3EA16F /* Benchmark.swift */,
E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */,
D77DD2DA5DC8654F2A80FF1D /* Bundle.swift */,
95CC95CD75B688E946438165 /* Coordinator.swift */,
12A626D74BBE9F4A60763B45 /* ImageAnonymizer.swift */,
F7B81C8227BBEA95CCE86037 /* MatrixEntitityRegex.swift */,
4519B90DE2F54443E5F43DA8 /* String.swift */,
91AC284E285B25BB00B7ADB9 /* Extensions */,
8F9A844EB44B6AD7CA18FD96 /* HTMLParsing */,
06501F0E978B2D5C92771DC7 /* Logging */,
FE50232944F9E67ADD7A2D21 /* Routers */,
@ -1251,16 +1275,6 @@
path = UITests;
sourceTree = "<group>";
};
CBBF6127C313A5412E438BC6 /* UserSession */ = {
isa = PBXGroup;
children = (
317F02B15921BF5CC8486990 /* KeychainController.swift */,
3689E6F87850DD65DAA45428 /* KeychainControllerProtocol.swift */,
0E8BDC092D817B68CD9040C5 /* UserSessionStore.swift */,
);
path = UserSession;
sourceTree = "<group>";
};
E59565F441830B19DBAE567C /* Screens */ = {
isa = PBXGroup;
children = (
@ -1305,8 +1319,8 @@
isa = PBXGroup;
children = (
D6CA5F386C7701C129398945 /* AuthenticationCoordinator.swift */,
09DD4E0CB6EE4AD939E46A62 /* AuthenticationMetrics.swift */,
298F75357B344DE964106404 /* Login */,
9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */,
90F48FEF84016ED42A94BA24 /* LoginScreen */,
);
path = Authentication;
sourceTree = "<group>";
@ -1650,6 +1664,7 @@
9C45CE85325CD591DADBC4CA /* ElementXTests.swift in Sources */,
F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */,
0B1F80C2BF7D223159FBA82C /* ImageAnonymizerTests.swift in Sources */,
EEC40663922856C65D1E0DF5 /* KeychainControllerTests.swift in Sources */,
0033481EE363E4914295F188 /* LocalizationTests.swift in Sources */,
149D1942DC005D0485FB8D93 /* LoggingTests.swift in Sources */,
7434A7F02D587A920B376A9A /* LoginScreenViewModelTests.swift in Sources */,
@ -1678,7 +1693,6 @@
3ED2725734568F6B8CC87544 /* AttributedStringBuilder.swift in Sources */,
A6DEC1ADEC8FEEC206A0FA37 /* AttributedStringBuilderProtocol.swift in Sources */,
EA65360A0EC026DD83AC0CF5 /* AuthenticationCoordinator.swift in Sources */,
AE5360A13B915F1CAECDE4CC /* AuthenticationMetrics.swift in Sources */,
CB326BAB54E9B68658909E36 /* Benchmark.swift in Sources */,
38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */,
B6DF6B6FA8734B70F9BF261E /* BlurHashDecode.swift in Sources */,
@ -1719,8 +1733,8 @@
DDB80FD2753FEAAE43CC2AAE /* ImageRoomTimelineItem.swift in Sources */,
D5EA4C6C80579279770D5804 /* ImageRoomTimelineView.swift in Sources */,
A5C8F013ED9FB8AA6FEE18A7 /* InfoPlist.swift in Sources */,
DF790EF2E4D41D1091AEB263 /* KeychainController.swift in Sources */,
0C601923A872A87C775B889A /* KeychainControllerProtocol.swift in Sources */,
F4C3FEDB1B3A05376A1723A3 /* KeychainController.swift in Sources */,
F2DD8661B5C0BA2BB526FA6C /* KeychainControllerProtocol.swift in Sources */,
9C9E48A627C7C166084E3F5B /* LabelledActivityIndicatorView.swift in Sources */,
D826154612415D2A3BB6EBF3 /* ListTableViewAdapter.swift in Sources */,
A941EAD7F407F2ED6DA54A31 /* LoginScreen.swift in Sources */,
@ -1813,6 +1827,7 @@
500CB65ED116B81DA52FDAEE /* TimelineView.swift in Sources */,
4669804D0369FBED4E8625D1 /* ToastViewPresenter.swift in Sources */,
9CB5129C83F75921E5E28028 /* ToastViewState.swift in Sources */,
36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */,
0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */,
03CB204C52F18E24A5C3D219 /* UITestsAppCoordinator.swift in Sources */,
17CC4FB64F3A670F43ECBE5F /* UITestsRootView.swift in Sources */,
@ -1826,7 +1841,8 @@
80E04BE80A89A78FBB4863BB /* UserIndicatorViewPresentable.swift in Sources */,
8AB8ED1051216546CB35FA0E /* UserSession.swift in Sources */,
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */,
7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */,
79A6E08ADE6E7C460A8A17A5 /* UserSessionStore.swift in Sources */,
EBD6C79705B3DDB2F7E5F554 /* UserSessionStoreProtocol.swift in Sources */,
F01DB7DD607015557CD48B33 /* ViewFrameReader.swift in Sources */,
01F4A40C1EDCEC8DC4EC9CFA /* WeakDictionary.swift in Sources */,
77E192BA943B90F9F310CA23 /* WeakDictionaryKeyReference.swift in Sources */,

View File

@ -18,9 +18,9 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
private let navigationRouter: NavigationRouter
private let userSessionStore: UserSessionStore
private let userSessionStore: UserSessionStoreProtocol
private var userSession: UserSession!
private var userSession: UserSessionProtocol!
private let memberDetailProviderManager: MemberDetailProviderManager
@ -88,7 +88,7 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
stateMachine.processEvent(.attemptedSignIn)
}
func authenticationCoordinator(_ authenticationCoordinator: AuthenticationCoordinator, didLoginWithSession userSession: UserSession) {
func authenticationCoordinator(_ authenticationCoordinator: AuthenticationCoordinator, didLoginWithSession userSession: UserSessionProtocol) {
self.userSession = userSession
remove(childCoordinator: authenticationCoordinator)
stateMachine.processEvent(.succeededSigningIn)
@ -100,14 +100,14 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
// MARK: - Private
// swiftlint:disable cyclomatic_complexity
// swiftlint:disable cyclomatic_complexity function_body_length
private func setupStateMachine() {
stateMachine.addTransitionHandler { [weak self] context in
guard let self = self else { return }
switch (context.fromState, context.event, context.toState) {
case (.initial, .startWithAuthentication, .signedOut):
self.showAuthentication()
self.startAuthentication()
case (.signedOut, .attemptedSignIn, .signingIn):
self.showLoadingIndicator()
case (.signingIn, .failedSigningIn, .signedOut):
@ -151,7 +151,7 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
fatalError("Failed transition with context: \(context)")
}
}
// swiftlint:enable cyclomatic_complexity
// swiftlint:enable cyclomatic_complexity function_body_length
private func restoreUserSession() {
Task {
@ -166,7 +166,7 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
}
}
private func showAuthentication() {
private func startAuthentication() {
let coordinator = AuthenticationCoordinator(userSessionStore: userSessionStore,
navigationRouter: navigationRouter)
coordinator.delegate = self
@ -184,7 +184,7 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
mainNavigationController.setViewControllers([splashViewController], animated: false)
showAuthentication()
startAuthentication()
}
private func presentHomeScreen() {

View File

@ -19,7 +19,7 @@ protocol AuthenticationCoordinatorDelegate: AnyObject {
func authenticationCoordinatorDidStartLoading(_ authenticationCoordinator: AuthenticationCoordinator)
func authenticationCoordinator(_ authenticationCoordinator: AuthenticationCoordinator,
didLoginWithSession userSession: UserSession)
didLoginWithSession userSession: UserSessionProtocol)
func authenticationCoordinator(_ authenticationCoordinator: AuthenticationCoordinator,
didFailWithError error: AuthenticationCoordinatorError)
@ -27,7 +27,7 @@ protocol AuthenticationCoordinatorDelegate: AnyObject {
class AuthenticationCoordinator: Coordinator {
private let userSessionStore: UserSessionStore
private let userSessionStore: UserSessionStoreProtocol
private let navigationRouter: NavigationRouter
private(set) var clientProxy: ClientProxyProtocol?
@ -35,7 +35,7 @@ class AuthenticationCoordinator: Coordinator {
weak var delegate: AuthenticationCoordinatorDelegate?
init(userSessionStore: UserSessionStore,
init(userSessionStore: UserSessionStoreProtocol,
navigationRouter: NavigationRouter) {
self.userSessionStore = userSessionStore
self.navigationRouter = navigationRouter

View File

@ -16,13 +16,11 @@
import SwiftUI
/// Metrics used across the entire onboarding flow.
struct AuthenticationMetrics {
/// Standard constants used across the app's UI.
struct UIConstants {
static let maxContentWidth: CGFloat = 600
static let maxContentHeight: CGFloat = 750
/// The padding used between the top of the main content and the navigation bar.
static let topPaddingToNavigationBar: CGFloat = 16
/// The width/height used for the main icon shown in most of the screens.
static let iconSize: CGFloat = 90
}

View File

@ -16,11 +16,7 @@
import SwiftUI
protocol SplashScreenCoordinatorProtocol: Coordinator, Presentable {
var callback: ((SplashScreenCoordinatorAction) -> Void)? { get set }
}
final class SplashScreenCoordinator: SplashScreenCoordinatorProtocol {
final class SplashScreenCoordinator: Coordinator, Presentable {
// MARK: - Properties

View File

@ -40,7 +40,7 @@ enum SplashScreenViewModelAction {
// MARK: View
struct SplashScreenViewState: BindableState, CustomDebugStringConvertible {
struct SplashScreenViewState: BindableState {
private enum Constants {
static let gradientColors = [
Color(red: 0.95, green: 0.98, blue: 0.96),
@ -55,11 +55,6 @@ struct SplashScreenViewState: BindableState, CustomDebugStringConvertible {
let content: [SplashScreenPageContent]
var bindings: SplashScreenBindings
/// Custom debug description to reduce noise in the logs.
var debugDescription: String {
"SplashScreenViewState at page \(bindings.pageIndex)."
}
init() {
// The pun doesn't translate, so we only use it for English.
let locale = Locale.current

View File

@ -51,6 +51,7 @@ struct SplashScreen: View {
overlayHeight: overlayFrame.height + geometry.safeAreaInsets.bottom)
.frame(width: geometry.size.width)
.tag(-1)
.accessibilityIdentifier("hiddenPage")
ForEach(0..<pageCount, id: \.self) { index in
SplashScreenPage(content: viewModel.viewState.content[index],
@ -90,7 +91,7 @@ struct SplashScreen: View {
buttons
.padding(.horizontal, 16)
.frame(maxWidth: AuthenticationMetrics.maxContentWidth)
.frame(maxWidth: UIConstants.maxContentWidth)
Spacer()
}
.background(ViewFrameReader(frame: $overlayFrame))

View File

@ -68,8 +68,8 @@ struct SplashScreenPage: View {
Spacer().frame(maxHeight: overlayHeight)
}
.padding(.horizontal, 16)
.frame(maxWidth: AuthenticationMetrics.maxContentWidth,
maxHeight: AuthenticationMetrics.maxContentHeight)
.frame(maxWidth: UIConstants.maxContentWidth,
maxHeight: UIConstants.maxContentHeight)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(backgroundGradient.ignoresSafeArea())

View File

@ -20,7 +20,7 @@ class KeychainController: KeychainControllerProtocol {
do {
try keychain.set(accessToken, key: username)
} catch {
MXLog.error("Failed storing user restore token with error: \(error)")
MXLog.error("Failed storing user access token with error: \(error)")
}
}
@ -28,7 +28,7 @@ class KeychainController: KeychainControllerProtocol {
do {
return try keychain.get(username)
} catch {
MXLog.error("Failed retrieving user restore token")
MXLog.error("Failed retrieving user access token")
return nil
}
}
@ -43,6 +43,14 @@ class KeychainController: KeychainControllerProtocol {
}
}
func removeAccessTokenForUsername(_ username: String) {
do {
try keychain.remove(username)
} catch {
MXLog.error("Failed removing access token with error: \(error)")
}
}
func removeAllAccessTokens() {
do {
try keychain.removeAll()

View File

@ -12,5 +12,6 @@ protocol KeychainControllerProtocol {
func setAccessToken(_ accessToken: String, forUsername username: String)
func accessTokenForUsername(_ username: String) -> String?
func accessTokens() -> [(username: String, accessToken: String)]
func removeAccessTokenForUsername(_ username: String)
func removeAllAccessTokens()
}

View File

@ -18,14 +18,7 @@ import Foundation
import MatrixRustSDK
import Kingfisher
enum UserSessionStoreError: Error {
case missingCredentials
case failedRestoringLogin
case failedSettingUpSession
}
@MainActor
class UserSessionStore {
class UserSessionStore: UserSessionStoreProtocol {
private let keychainController: KeychainControllerProtocol
@ -70,8 +63,9 @@ class UserSessionStore {
}
func logout(userSession: UserSessionProtocol) {
keychainController.removeAllAccessTokens()
deleteBaseDirectory(for: userSession.clientProxy.userIdentifier)
let username = userSession.clientProxy.userIdentifier
keychainController.removeAccessTokenForUsername(username)
deleteBaseDirectory(for: username)
}
private func restorePreviousLogin(_ usernameTokenTuple: (username: String, accessToken: String)) async -> Result<ClientProxyProtocol, UserSessionStoreError> {

View File

@ -0,0 +1,42 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import MatrixRustSDK
enum UserSessionStoreError: Error {
case missingCredentials
case failedRestoringLogin
case failedSettingUpSession
}
@MainActor
protocol UserSessionStoreProtocol {
/// Whether or not there are sessions in the store.
var hasSessions: Bool { get }
/// Restores an existing user session.
func restoreUserSession() async -> Result<UserSession, UserSessionStoreError>
/// Creates a user session for a new client from the SDK.
func userSession(for client: Client) async -> Result<UserSession, UserSessionStoreError>
/// Logs out of the specified session.
func logout(userSession: UserSessionProtocol)
/// Returns the location to store user data for a particular username.
func baseDirectoryPath(for username: String) -> String
}

View File

@ -22,6 +22,8 @@ class UITestsAppCoordinator: Coordinator {
window.tintColor = .element.accent
let screens = mockScreens()
screens.forEach { $0.coordinator.start() }
let rootView = UITestsRootView(mockScreens: screens) { id in
guard let screen = screens.first(where: { $0.id == id }) else {
fatalError()
@ -52,5 +54,5 @@ class UITestsAppCoordinator: Coordinator {
struct MockScreen: Identifiable {
let id: String
let coordinator: Presentable
let coordinator: Coordinator & Presentable
}

View File

@ -16,11 +16,52 @@
import XCTest
@MainActor
class SplashScreenUITests: XCTestCase {
func testInitialStateComponents() {
let app = Application.launch()
app.goToScreenWithIdentifier("Splash Screen")
XCTAssert(app.buttons["Get started"].exists)
let getStartedButton = app.buttons["Get started"]
XCTAssertTrue(getStartedButton.exists, "The primary action button should be shown.")
}
func testSwipingBetweenPages() async throws {
let app = Application.launch()
app.goToScreenWithIdentifier("Splash Screen")
// Given the splash screen in its initial state.
let page1TitleText = app.staticTexts["Own your conversations."]
let page2TitleText = app.staticTexts["You're in control."]
let hiddenPageTitleText = app.staticTexts["hiddenPage"].firstMatch
XCTAssertTrue(page1TitleText.isHittable, "The title from the first page of the carousel should be onscreen.")
XCTAssertFalse(page2TitleText.isHittable, "The title from the second page of the carousel should be offscreen.")
XCTAssertFalse(hiddenPageTitleText.isHittable, "The hidden page of the carousel should be offscreen.")
// When swiping to the next screen.
page1TitleText.swipeLeft()
try await Task.sleep(nanoseconds: 200_000_000) // Wait for the animation.
// Then the second screen should be shown.
XCTAssertFalse(page1TitleText.isHittable, "The title from the first page of the carousel should be offscreen.")
XCTAssertTrue(page2TitleText.isHittable, "The title from the second page of the carousel should be onscreen.")
// When swiping back to the previous screen.
page2TitleText.swipeRight()
try await Task.sleep(nanoseconds: 200_000_000) // Wait for the animation.
// Then the first screen should be shown again.
XCTAssertTrue(page1TitleText.isHittable, "The title from the first page of the carousel should be onscreen.")
XCTAssertFalse(page2TitleText.isHittable, "The title from the second page of the carousel should be offscreen.")
// When swiping back to the previous screen.
page1TitleText.swipeRight()
try await Task.sleep(nanoseconds: 200_000_000) // Wait for the animation.
// Then the screen shouldn't change and the hidden screen should be ignored.
XCTAssertTrue(page1TitleText.isHittable, "The title from the first page of the carousel should be still be onscreen.")
XCTAssertFalse(page2TitleText.isHittable, "The title from the second page of the carousel should be offscreen.")
XCTAssertFalse(hiddenPageTitleText.isHittable, "It shouldn't be possible to swipe to the hidden page of the carousel.")
}
}

View File

@ -0,0 +1,89 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import XCTest
@testable import ElementX
class KeychainControllerTests: XCTestCase {
var keychain: KeychainController!
override func setUp() {
keychain = KeychainController(identifier: "\(ElementInfoPlist.cfBundleIdentifier).tests")
keychain.removeAllAccessTokens()
}
func testAddAccessToken() {
// Given an empty keychain.
XCTAssertTrue(keychain.accessTokens().isEmpty, "The keychain should be empty to begin with.")
// When adding an access token.
let username = "@test:example.com"
let accessToken = UUID().uuidString
keychain.setAccessToken(accessToken, forUsername: username)
// Then the access token should be stored in the keychain.
XCTAssertEqual(keychain.accessTokenForUsername(username), accessToken, "The retrieved access token should match the value that was stored.")
}
func testRemovingAccessToken() {
// Given a keychain with a stored access token.
let username = "@test:example.com"
let accessToken = UUID().uuidString
keychain.setAccessToken(accessToken, forUsername: username)
XCTAssertEqual(keychain.accessTokens().count, 1, "The keychain should have 1 access token.")
XCTAssertEqual(keychain.accessTokenForUsername(username), accessToken, "The initial access token should match the value that was stored.")
// When deleting the access token.
keychain.removeAccessTokenForUsername(username)
// Then the keychain should be empty.
XCTAssertTrue(keychain.accessTokens().isEmpty, "The keychain should be empty after deleting the token.")
XCTAssertNil(keychain.accessTokenForUsername(username), "There access token should not be returned after removal.")
}
func testRemovingAllAccessTokens() {
// Given a keychain with 5 stored access tokens.
for index in 0..<5 {
keychain.setAccessToken(UUID().uuidString, forUsername: "@test\(index):example.com")
}
XCTAssertEqual(keychain.accessTokens().count, 5, "The keychain should have 5 access tokens.")
// When deleting all of the access tokens.
keychain.removeAllAccessTokens()
// Then the keychain should be empty.
XCTAssertTrue(keychain.accessTokens().isEmpty, "The keychain should be empty after deleting the token.")
}
func testRemovingSingleAccessTokens() {
// Given a keychain with 5 stored access tokens.
for index in 0..<5 {
keychain.setAccessToken(UUID().uuidString, forUsername: "@test\(index):example.com")
}
XCTAssertEqual(keychain.accessTokens().count, 5, "The keychain should have 5 access tokens.")
// When deleting one of the access tokens.
keychain.removeAccessTokenForUsername("@test2:example.com")
// Then the other 4 items should remain untouched.
XCTAssertEqual(keychain.accessTokens().count, 4, "The keychain have 4 remaining access tokens.")
XCTAssertNotNil(keychain.accessTokenForUsername("@test0:example.com"), "The access token should not have been deleted.")
XCTAssertNotNil(keychain.accessTokenForUsername("@test1:example.com"), "The access token should not have been deleted.")
XCTAssertNil(keychain.accessTokenForUsername("@test2:example.com"), "The access token should have been deleted.")
XCTAssertNotNil(keychain.accessTokenForUsername("@test3:example.com"), "The access token should not have been deleted.")
XCTAssertNotNil(keychain.accessTokenForUsername("@test4:example.com"), "The access token should not have been deleted.")
}
}