Screenshot tests (#130)

* #9 Add snapshot testing library

* #9 Create script to boot test simulators

* #9 Create the UI test plan

* #9 Create shared schemes for test targets

* #9 Disable split view for UI tests

* #9 Fix fastlane dependencies

* #9 Add snapshot testing to the application

* #9 assert screenshots

* #9 fix swipe gestures on iPad

* #9 Fix accessing items in session verification screen

* #9 Workaround for flaky unit test

* #9 Specify scheme for alpha build

* #9 Add reference screenshots

* Update python script path and check assets for png check

* Update script path

* Use static timezone for simulator time

* Fix build after SwiftFormat

* Add changelog

* Upload failed screenshots artifact

* Always upload artifacts

* Update boot simulator script

* Update simulator overridden time

* Install pytz before tests

* Get time from Ruby script

* Disable SwiftUI animation when running UI tests

* Update screenshots after animation setting

* Include reference images in the artifact

* Update matching precision

* Update image matching precision & revert artifact content

* Include Xcode result in the artifact

* Update test output directory

* Disable gradient on splash screen for tests

* Tap next button explicitly

* Wait a bit before checking alert

* Wait 1 second

* Run SwiftFormat on project

* Ignore temporary screenshots

* Fix most of the PR remarks

* Fix conflicts

* Bump Python version to 3

* Update reference screenshots for authentication screens

* Update SwiftFormat

* Fix flakey session verification test.

* Update scheme.

Co-authored-by: Doug <douglase@element.io>
This commit is contained in:
ismailgulek 2022-08-11 15:02:47 +03:00 committed by GitHub
parent 207cbdebfd
commit 2cb6dc1cd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
127 changed files with 944 additions and 83 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
UITests/Sources/__Snapshots__/** filter=lfs diff=lfs merge=lfs -text

View File

@ -17,7 +17,14 @@ jobs:
cancel-in-progress: true
steps:
- name: Setup Xcode version
uses: maxim-lobanov/setup-xcode@v1.4.1
with:
xcode-version: '13.4'
- uses: actions/checkout@v3
with:
lfs: true
- uses: actions/cache@v3
with:
@ -34,5 +41,14 @@ jobs:
- name: Run tests
run: bundle exec fastlane tests
- name: Archive artifacts
uses: actions/upload-artifact@v3
if: always()
with:
name: test-output
path: fastlane/test_output
retention-days: 7
if-no-files-found: ignore
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3

11
.gitignore vendored
View File

@ -38,3 +38,14 @@ Tools/Scripts/element-android
## macOS Files
.DS_Store
._*
## Temporary Screenshots
# ignore all
/UITests/Sources/__Snapshots__/Application/*
# but keep the references
!/UITests/Sources/__Snapshots__/Application/15-5-de-DE-iPad-9th-generation.*.png
!/UITests/Sources/__Snapshots__/Application/15-5-fr-FR-iPad-9th-generation.*.png
!/UITests/Sources/__Snapshots__/Application/15-5-en-GB-iPad-9th-generation.*.png
!/UITests/Sources/__Snapshots__/Application/15-5-de-DE-iPhone-13-Pro-Max.*.png
!/UITests/Sources/__Snapshots__/Application/15-5-fr-FR-iPhone-13-Pro-Max.*.png
!/UITests/Sources/__Snapshots__/Application/15-5-en-GB-iPhone-13-Pro-Max.*.png

View File

@ -77,7 +77,7 @@ if hasChangedViews {
}
// Check for pngs on resources
let hasPngs = !editedFiles.filter { $0.lowercased().hasSuffix(".png") }.isEmpty
let hasPngs = !editedFiles.filter { $0.lowercased().contains(".xcassets") && $0.lowercased().hasSuffix(".png") }.isEmpty
if hasPngs {
warn("You seem to have made changes to some images. Please consider using an SVG or PDF.")
warn("You seem to have made changes to some resource images. Please consider using an SVG or PDF.")
}

View File

@ -100,6 +100,7 @@
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */; };
4669804D0369FBED4E8625D1 /* ToastViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4470B8CB654B097D807AA713 /* ToastViewPresenter.swift */; };
490E606044B18985055FF690 /* SettingsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E29F98CF0E960689A410E3 /* SettingsUITests.swift */; };
492274DA6691EE985C2FCCAA /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 21C83087604B154AA30E9A8F /* SnapshotTesting */; };
499A26EB06C97E48C27A2DB9 /* BuildSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F87116470221880017CF522 /* BuildSettings.swift */; };
49E9B99CB6A275C7744351F0 /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D58333B377888012740101 /* LoginViewModel.swift */; };
49F2E7DD8CAACE09CEECE3E6 /* SeparatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6390A6DC140CA3D6865A66FF /* SeparatorRoomTimelineView.swift */; };
@ -265,6 +266,7 @@
D6417E5A799C3C7F14F9EC0A /* SessionVerificationViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3069ADED46D063202FE7698 /* SessionVerificationViewModelProtocol.swift */; };
D826154612415D2A3BB6EBF3 /* ListTableViewAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E854E7CF531DAC5CBEBDC75 /* ListTableViewAdapter.swift */; };
D8359F67AF3A83516E9083C1 /* MockUserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4756C5A8C8649AD6C10C615 /* MockUserSession.swift */; };
D85D4FA590305180B4A41795 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3073CCD77D906B330BC1D6 /* Tests.swift */; };
D8CFF02C2730EE5BC4F17ABF /* ElementToggleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960A7F5C1B0B6679BDF26F9 /* ElementToggleStyle.swift */; };
D94F664677C380A3CAB8D7F6 /* ActivityIndicatorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68706A66BBA04268F7747A2F /* ActivityIndicatorPresenter.swift */; };
DCB781BD227CA958809AFADF /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95CC95CD75B688E946438165 /* Coordinator.swift */; };
@ -295,6 +297,7 @@
FA9C427FFB11B1AA2DCC5602 /* RoomProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */; };
FC6B7436C3A5B3D0565227D5 /* ActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF05352F28D4E7336228E9F4 /* ActivityIndicatorView.swift */; };
FCB640C576292BEAF7FA3B2E /* SplashViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F395A2E917115C7AAF7F34 /* SplashViewController.swift */; };
FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF1593DD87F974F8509BB619 /* ElementAnimations.swift */; };
FE79E2BCCF69E8BF4D21E15A /* RoomMessageFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA154570F693D93513E584C1 /* RoomMessageFactory.swift */; };
FFD3E4FF948E06C7585317FC /* TimelineStyler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 892E29C98C4E8182C9037F84 /* TimelineStyler.swift */; };
/* End PBXBuildFile section */
@ -536,6 +539,7 @@
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>"; };
8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyle.swift; sourceTree = "<group>"; };
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = "<group>"; };
9010EE0CC913D095887EF36E /* OIDCService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCService.swift; 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>"; };
@ -613,6 +617,7 @@
B83CB897B183BF3C33715F55 /* bn-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "bn-IN"; path = "bn-IN.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
B8A56EA2A5AE726F445CB2E3 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = eo; path = eo.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
B902EA6CD3296B0E10EE432B /* HomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = "<group>"; };
BB3073CCD77D906B330BC1D6 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = "<group>"; };
BC9B05D6B293A039EB963CA7 /* az */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = az; path = az.lproj/Localizable.strings; 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>"; };
@ -684,6 +689,7 @@
EDB6E40BAD4504D899FAAC9A /* TemplateViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateViewModel.swift; sourceTree = "<group>"; };
EE8BCD14EFED23459A43FDFF /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
EEE384418EB1FEDFA62C9CD0 /* RoomTimelineViewFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineViewFactoryProtocol.swift; sourceTree = "<group>"; };
EF1593DD87F974F8509BB619 /* ElementAnimations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementAnimations.swift; sourceTree = "<group>"; };
EF188681D6B6068CFAEAFC3F /* MXLogger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXLogger.m; sourceTree = "<group>"; };
EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewFrameReader.swift; sourceTree = "<group>"; };
EFFA5FD06AAAC4AF544B594E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@ -720,6 +726,7 @@
A4E885358D7DD5A072A06824 /* SwiftState in Frameworks */,
29EE1791E0AFA1ABB7F23D2F /* GZIP in Frameworks */,
33CAC1226DFB8B5D8447D286 /* Sentry in Frameworks */,
492274DA6691EE985C2FCCAA /* SnapshotTesting in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -756,6 +763,7 @@
052CC920F473C10B509F9FC1 /* SwiftUI */ = {
isa = PBXGroup;
children = (
E2DA161C142B7AB8CC40F752 /* Animation */,
595B8797ED6A7489ABDCE384 /* ErrorHandling */,
CE2FBFD64A89F5DBE4EB30DB /* Layout */,
10578D9852BA78D309A1CBDF /* ViewModel */,
@ -850,13 +858,6 @@
path = Resources;
sourceTree = "<group>";
};
3180C73BA7B8F5F7447C99B0 /* React */ = {
isa = PBXGroup;
children = (
);
path = React;
sourceTree = "<group>";
};
328DD5DA1281F758B72006C7 /* Views */ = {
isa = PBXGroup;
children = (
@ -1143,6 +1144,7 @@
children = (
49D2C8E66E83EA578A7F318A /* Info.plist */,
D4DA544B2520BFA65D6DB4BB /* target.yml */,
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */,
);
path = SupportingFiles;
sourceTree = "<group>";
@ -1454,6 +1456,7 @@
1027BB9A852F445B7623897F /* ElementSettings.swift */,
12A626D74BBE9F4A60763B45 /* ImageAnonymizer.swift */,
6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */,
BB3073CCD77D906B330BC1D6 /* Tests.swift */,
44BBB96FAA2F0D53C507396B /* Extensions */,
8F9A844EB44B6AD7CA18FD96 /* HTMLParsing */,
06501F0E978B2D5C92771DC7 /* Logging */,
@ -1497,13 +1500,20 @@
path = SessionVerification;
sourceTree = "<group>";
};
E2DA161C142B7AB8CC40F752 /* Animation */ = {
isa = PBXGroup;
children = (
EF1593DD87F974F8509BB619 /* ElementAnimations.swift */,
);
path = Animation;
sourceTree = "<group>";
};
E59565F441830B19DBAE567C /* Screens */ = {
isa = PBXGroup;
children = (
E74CD7681375AD2EAA34D66B /* Authentication */,
4009BE2E791C16AC6EE39A7E /* BugReport */,
B53CA9BECD3F97805E1432D0 /* HomeScreen */,
3180C73BA7B8F5F7447C99B0 /* React */,
679E9837ECA8D6776079D16E /* RoomScreen */,
D958761758AA1110476DE6A3 /* SessionVerification */,
70B74A432C241E56A7ACE610 /* Settings */,
@ -1623,6 +1633,7 @@
isa = PBXNativeTarget;
buildConfigurationList = F1B67CF63C1231AEB14D70E6 /* Build configuration list for PBXNativeTarget "UITests" */;
buildPhases = (
17364E8B37FC780E07DDEAF7 /* Override Simulator Status Bars */,
BAD5CD7BE53A7C832569B67A /* Sources */,
86982BD498105258F3778110 /* Resources */,
CD30252A70288BD4BF476ED7 /* Frameworks */,
@ -1644,6 +1655,7 @@
3853B78FB8531B83936C5DA6 /* SwiftState */,
1BCD21310B997A6837B854D6 /* GZIP */,
67E7A6F388D3BF85767609D9 /* Sentry */,
21C83087604B154AA30E9A8F /* SnapshotTesting */,
);
productName = UITests;
productReference = F506C6ADB1E1DA6638078E11 /* UITests.xctest */;
@ -1805,6 +1817,7 @@
D283517192CAC3E2E6920765 /* XCRemoteSwiftPackageReference "Kingfisher" */,
80B898A3AD2AC63F3ABFC218 /* XCRemoteSwiftPackageReference "matrix-rust-components-swift" */,
A08925A9D5E3770DEB9D8509 /* XCRemoteSwiftPackageReference "sentry-cocoa" */,
F34594223DB1E7DCE48F92B8 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */,
6582B5AF3F104B0F7E031E7D /* XCRemoteSwiftPackageReference "SwiftState" */,
25B4484A6A20B9F1705DEEDA /* XCRemoteSwiftPackageReference "SwiftyBeaver" */,
);
@ -1864,6 +1877,24 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
17364E8B37FC780E07DDEAF7 /* Override Simulator Status Bars */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Override Simulator Status Bars";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "python3 $PROJECT_DIR/Tools/Scripts/bootTestSimulator.py --name 'iPhone 13 Pro Max' --version 'iOS.15.5'\npython3 $PROJECT_DIR/Tools/Scripts/bootTestSimulator.py --name 'iPad (9th generation)' --version 'iOS.15.5'\n";
};
98CA896D84BFD53B2554E891 /* ⚠️ SwiftLint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -1991,6 +2022,7 @@
DCB781BD227CA958809AFADF /* Coordinator.swift in Sources */,
C4F69156C31A447FEFF2A47C /* DTHTMLElement+AttributedStringBuilder.swift in Sources */,
EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */,
FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */,
06E93B2E3B32740B40F47CC5 /* ElementNavigationController.swift in Sources */,
9738F894DB1BD383BE05767A /* ElementSettings.swift in Sources */,
D8CFF02C2730EE5BC4F17ABF /* ElementToggleStyle.swift in Sources */,
@ -2126,6 +2158,7 @@
1555A7643D85187D4851040C /* TemplateScreen.swift in Sources */,
75EA4ABBFAA810AFF289D6F4 /* TemplateViewModel.swift in Sources */,
5F1FDE49DFD0C680386E48F9 /* TemplateViewModelProtocol.swift in Sources */,
D85D4FA590305180B4A41795 /* Tests.swift in Sources */,
D013E70C8E28E43497820444 /* TextRoomMessage.swift in Sources */,
7963F98CDFDEAC75E072BD81 /* TextRoomTimelineItem.swift in Sources */,
5E0F2E612718BB4397A6D40A /* TextRoomTimelineView.swift in Sources */,
@ -2742,6 +2775,14 @@
minimumVersion = 7.2.0;
};
};
F34594223DB1E7DCE48F92B8 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/pointfreeco/swift-snapshot-testing.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.9.0;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
@ -2765,6 +2806,11 @@
package = 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */;
productName = GZIP;
};
21C83087604B154AA30E9A8F /* SnapshotTesting */ = {
isa = XCSwiftPackageProductDependency;
package = F34594223DB1E7DCE48F92B8 /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */;
productName = SnapshotTesting;
};
36B7FC232711031AA2B0D188 /* DTCoreText */ = {
isa = XCSwiftPackageProductDependency;
package = C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */;

View File

@ -66,7 +66,7 @@
{
"identity" : "matrix-rust-components-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
"location" : "https://github.com/matrix-org/matrix-rust-components-swift.git",
"state" : {
"revision" : "1358c9c2a85cfb5fc1bfadce13565e02618725d4",
"version" : "1.0.13-alpha"
@ -81,6 +81,15 @@
"version" : "7.18.1"
}
},
{
"identity" : "swift-snapshot-testing",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-snapshot-testing.git",
"state" : {
"revision" : "f8a9c997c3c1dab4e216a8ec9014e23144cbab37",
"version" : "1.9.0"
}
},
{
"identity" : "swiftstate",
"kind" : "remoteSourceControl",

View File

@ -63,13 +63,6 @@
</Testables>
<CommandLineArguments>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "IS_RUNNING_UNIT_TESTS"
value = "1"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E28CD62691FDBC63147D5E3"
BuildableName = "UITests.xctest"
BlueprintName = "UITests"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES">
<TestPlans>
<TestPlanReference
default = "YES"
reference = "container:UITests/SupportingFiles/UITests.xctestplan">
</TestPlanReference>
</TestPlans>
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E28CD62691FDBC63147D5E3"
BuildableName = "UITests.xctest"
BlueprintName = "UITests"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E28CD62691FDBC63147D5E3"
BuildableName = "UITests.xctest"
BlueprintName = "UITests"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0E28CD62691FDBC63147D5E3"
BuildableName = "UITests.xctest"
BlueprintName = "UITests"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "32C23C8D224D46EFE62AFAD0"
BuildableName = "UnitTests.xctest"
BlueprintName = "UnitTests"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO"
shouldUseLaunchSchemeArgsEnv = "YES"
systemAttachmentLifetime = "keepNever">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "32C23C8D224D46EFE62AFAD0"
BuildableName = "UnitTests.xctest"
BlueprintName = "UnitTests"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "32C23C8D224D46EFE62AFAD0"
BuildableName = "UnitTests.xctest"
BlueprintName = "UnitTests"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "32C23C8D224D46EFE62AFAD0"
BuildableName = "UnitTests.xctest"
BlueprintName = "UnitTests"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "IS_RUNNING_UNIT_TESTS"
value = "1"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "32C23C8D224D46EFE62AFAD0"
BuildableName = "UnitTests.xctest"
BlueprintName = "UnitTests"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -18,7 +18,7 @@ import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
private lazy var appCoordinator: Coordinator = isRunningUITests ? UITestsAppCoordinator() : AppCoordinator()
private lazy var appCoordinator: Coordinator = Tests.isRunningUITests ? UITestsAppCoordinator() : AppCoordinator()
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
// use `en` as fallback language
@ -28,7 +28,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
if isRunningUnitTests {
if Tests.isRunningUnitTests {
return true
}
@ -36,20 +36,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}
private var isRunningUnitTests: Bool {
#if DEBUG
ProcessInfo.processInfo.environment["IS_RUNNING_UNIT_TESTS"] == "1"
#else
false
#endif
}
private var isRunningUITests: Bool {
#if DEBUG
ProcessInfo.processInfo.environment["IS_RUNNING_UI_TESTS"] == "1"
#else
false
#endif
}
}

View File

@ -0,0 +1,34 @@
//
// ElementAnimations.swift
// ElementX
//
// Created by Ismail on 8.07.2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
import SwiftUI
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public extension Animation {
/// Animation to be used to disable animations.
static let noAnimation: Animation = .linear(duration: 0)
/// `noAnimation` if running UI tests, otherwise `default` animation.
static var elementDefault: Animation {
Tests.isRunningUITests ? .noAnimation : .default
}
}
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
/// Returns the result of recomputing the view's body with the provided
/// animation.
/// - Parameters:
/// - animation: Animation
/// - body: operations to be animated
public func withElementAnimation<Result>(_ animation: Animation? = .default, _ body: () throws -> Result) rethrows -> Result {
if Tests.isRunningUITests {
return try withAnimation(.noAnimation, body)
}
return try withAnimation(animation, body)
}

View File

@ -0,0 +1,29 @@
//
// Tests.swift
// ElementX
//
// Created by Ismail on 29.07.2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
public enum Tests {
/// Flag indicating whether the app is running the unit tests.
static var isRunningUnitTests: Bool {
#if DEBUG
ProcessInfo.processInfo.environment["IS_RUNNING_UNIT_TESTS"] == "1"
#else
false
#endif
}
/// Flag indicating whether the app is running the UI tests.
static var isRunningUITests: Bool {
#if DEBUG
ProcessInfo.processInfo.environment["IS_RUNNING_UI_TESTS"] == "1"
#else
false
#endif
}
}

View File

@ -51,7 +51,7 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie
func displayError(_ type: ServerSelectionErrorType) {
switch type {
case .footerMessage(let message):
withAnimation {
withElementAnimation {
state.footerErrorMessage = message
}
}
@ -62,6 +62,6 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie
/// Clear any errors shown in the text field footer.
private func clearFooterError() {
guard state.footerErrorMessage != nil else { return }
withAnimation { state.footerErrorMessage = nil }
withElementAnimation { state.footerErrorMessage = nil }
}
}

View File

@ -64,7 +64,7 @@ struct HomeScreen: View {
Spacer()
}
.transition(.slide)
.animation(.default, value: context.viewState.showSessionVerificationBanner)
.animation(.elementDefault, value: context.viewState.showSessionVerificationBanner)
.ignoresSafeArea(.all, edges: .bottom)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
@ -72,11 +72,11 @@ struct HomeScreen: View {
Button { context.send(viewAction: .tapUserAvatar) } label: {
HStack {
userAvatarImage
.animation(.default, value: context.viewState.userAvatar)
.animation(.elementDefault, value: context.viewState.userAvatar)
.transition(.opacity)
userDisplayNameView
.animation(.default, value: context.viewState.userDisplayName)
.animation(.elementDefault, value: context.viewState.userDisplayName)
.transition(.opacity)
}
}
@ -147,7 +147,7 @@ struct RoomCell: View {
}
}
}
.animation(.default, value: room)
.animation(.elementDefault, value: room)
.frame(minHeight: 60.0)
.task {
context.send(viewAction: .loadRoomData(roomIdentifier: room.id))

View File

@ -36,7 +36,7 @@ struct MessageComposer: View {
.padding(.bottom, 6.0)
.disabled(disabled)
.opacity(disabled ? 0.5 : 1.0)
.animation(.default, value: disabled)
.animation(.elementDefault, value: disabled)
.keyboardShortcut(.return, modifiers: [.command])
}
}

View File

@ -57,7 +57,7 @@ struct MessageComposerTextField: View {
.background(placeholderView, alignment: .topLeading)
.clipShape(rect)
.overlay(rect.stroke(borderColor, lineWidth: borderWidth))
.animation(.default, value: isEditing)
.animation(.elementDefault, value: isEditing)
}
@ViewBuilder

View File

@ -42,7 +42,7 @@ struct ImageRoomTimelineView: View {
}
}
.id(timelineItem.id)
.animation(.default, value: timelineItem.image)
.animation(.elementDefault, value: timelineItem.image)
.frame(maxHeight: 1000.0)
} else {
TimelineStyler(timelineItem: timelineItem) {

View File

@ -37,7 +37,7 @@ struct TimelineItemList: View {
Spacer()
ProgressView()
.opacity(context.viewState.isBackPaginating ? 1.0 : 0.0)
.animation(.default, value: context.viewState.isBackPaginating)
.animation(.elementDefault, value: context.viewState.isBackPaginating)
Spacer()
}
.listRowBackground(Color.clear)

View File

@ -40,6 +40,6 @@ struct TimelineSenderAvatarView: View {
.stroke(Color.element.background, lineWidth: 2)
)
.animation(.default, value: timelineItem.senderAvatar)
.animation(.elementDefault, value: timelineItem.senderAvatar)
}
}

View File

@ -47,7 +47,7 @@ struct TimelineView: View {
scollToBottomButtonVisible = !visible
})
.opacity(scollToBottomButtonVisible ? 1.0 : 0.0)
.animation(.default, value: scollToBottomButtonVisible)
.animation(.elementDefault, value: scollToBottomButtonVisible)
}
}

View File

@ -50,6 +50,11 @@ class SessionVerificationViewModel: SessionVerificationViewModelType, SessionVer
switch callback {
case .receivedVerificationData(let emojis):
guard self.stateMachine.state == .requestingVerification else {
MXLog.warning("[SessionVerificationViewModel] callbacks: Ignoring receivedVerificationData due to invalid state.")
return
}
self.stateMachine.processEvent(.didReceiveChallenge(emojis: emojis))
case .finished:
self.stateMachine.processEvent(.didAcceptChallenge)

View File

@ -53,6 +53,9 @@ struct SplashScreenViewState: BindableState {
/// The background gradient for all 4 pages and the hidden page at the start of the carousel.
var backgroundGradient: Gradient {
if Tests.isRunningUITests {
return Gradient(colors: [.white])
}
// Include the extra stop for the hidden page at the start of the carousel.
// (The last color is the right-hand stop, but we need the left-hand stop,
// so take the last but one color from the array).

View File

@ -123,11 +123,11 @@ struct SplashScreen: View {
if context.pageIndex == pageCount - 1 {
showHiddenPage()
withAnimation(.easeInOut(duration: 0.7)) {
withElementAnimation(.easeInOut(duration: 0.7)) {
showNextPage()
}
} else {
withAnimation(.easeInOut(duration: 0.7)) {
withElementAnimation(.easeInOut(duration: 0.7)) {
showNextPage()
}
}
@ -185,7 +185,7 @@ struct SplashScreen: View {
stopTimer()
// Animate the change over a few frames to smooth out any stuttering.
withAnimation(.linear(duration: 0.05)) {
withElementAnimation(.linear(duration: 0.05)) {
dragOffset = isLeftToRight ? drag.translation.width : -drag.translation.width
}
}
@ -195,11 +195,11 @@ struct SplashScreen: View {
private func handleDragGestureEnded(_ drag: DragGesture.Value, viewSize: CGSize) {
guard shouldSwipeForTranslation(drag.predictedEndTranslation.width) else {
// Reset the offset just in case.
withAnimation { dragOffset = 0 }
withElementAnimation { dragOffset = 0 }
return
}
withAnimation(.easeInOut(duration: 0.2)) {
withElementAnimation(.easeInOut(duration: 0.2)) {
if drag.predictedEndTranslation.width < -viewSize.width / 2 {
showNextPage()
} else if drag.predictedEndTranslation.width > viewSize.width / 2 {

View File

@ -89,7 +89,7 @@ class ScreenshotDetector {
}
}
private extension PHAsset {
extension PHAsset {
static func fetchLastScreenshot() -> PHAsset? {
let options = PHFetchOptions()

View File

@ -31,5 +31,6 @@ struct UITestsRootView: View {
.listStyle(.plain)
}
.navigationTitle("Screens")
.navigationViewStyle(.stack)
}
}

View File

@ -23,8 +23,6 @@ schemes:
gatherCoverageData: true
coverageTargets:
- ElementX
environmentVariables:
IS_RUNNING_UNIT_TESTS: "1"
targets:
- UnitTests
- UITests

View File

@ -8,6 +8,7 @@ GEM
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
artifactory (3.0.15)
@ -246,10 +247,10 @@ GEM
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
zeitwerk (2.6.0)
PLATFORMS
arm64-darwin-21
x86_64-linux
ruby
DEPENDENCIES
fastlane
@ -259,4 +260,4 @@ DEPENDENCIES
slather
BUNDLED WITH
2.3.7
2.1.4

View File

@ -15,3 +15,11 @@ Usage:
```
./localizer.py
```
## Boot Test Simulator
Boots a desired simulator and makes status bar overrides on that.
Usage:
```
./bootTestSimulator.py 'iPhone 13 Pro Max'
```

View File

@ -43,7 +43,7 @@ struct TemplateScreen: View {
.padding(.horizontal)
.padding(.vertical)
.readableFrame()
.background(.regularMaterial)
.background(Color.element.system)
}
}

View File

@ -26,6 +26,8 @@ class TemplateScreenUITests: XCTestCase {
XCTAssert(title.exists)
XCTAssertEqual(title.label, "Make this chat public?")
app.assertScreenshot(.simpleRegular)
}
func testUpgradeScreen() {
@ -36,5 +38,7 @@ class TemplateScreenUITests: XCTestCase {
XCTAssert(title.exists)
XCTAssertEqual(title.label, "Privacy warning")
app.assertScreenshot(.simpleUpgrade)
}
}

View File

@ -0,0 +1,52 @@
#!/usr/bin/python3
from encodings import utf_8
import os
import subprocess
import json
import argparse
from datetime import datetime
RUNTIME_PREFIX = 'com.apple.CoreSimulator.SimRuntime.'
def device_name(device):
return device['name']
def runtime_name(runtime):
return runtime.replace(RUNTIME_PREFIX, '').replace('-', '.')
parser = argparse.ArgumentParser()
parser.add_argument('--name', type=str, help='Simulator name (like \'iPhone 13 Pro Max\')', required=True)
parser.add_argument('--version', type=str, default='iOS.15.5', help='OS version (defaults to \'iOS.15.5\')', required=False)
args = vars(parser.parse_args())
simulator_name = args['name']
os_version = args['version'].replace('.', '-')
runtimes_map = subprocess.check_output("/usr/bin/xcrun simctl list --json devices available", shell=True)
runtime = RUNTIME_PREFIX + os_version
json_object = json.loads(runtimes_map)
if runtime in json_object['devices']:
devices = json_object['devices'][runtime]
device_found=False
for device in devices:
if device_name(device) == simulator_name:
UDID=device['udid']
print("Found device UDID: " + UDID)
dirname = os.path.dirname(os.path.abspath(__file__))
overridden_time = datetime(2007, 1, 9, 9, 41, 0).astimezone().isoformat()
print("Will override simulator with time: " + overridden_time)
os.system("/usr/bin/xcrun simctl boot '" + UDID + "' > /dev/null 2>&1")
os.system("/usr/bin/xcrun simctl status_bar '" + UDID + "' override --time '" + overridden_time + "' --dataNetwork 'wifi' --wifiMode 'active' --wifiBars 3 --cellularMode 'active' --cellularBars 4 --batteryState 'charged' --batteryLevel 100 > /dev/null 2>&1")
print("Simulator booted and status bar overriden")
device_found=True
break
if device_found == False:
print("Device could not be found. \n\nAvailable devices: " + ', '.join(map(device_name, devices)))
exit(1)
else:
print("Runtime could not be found. \n\nAvailable runtimes: " + ', '.join(map(runtime_name, json_object['devices'].keys())))
exit(1)

View File

@ -14,12 +14,14 @@
// limitations under the License.
//
import SnapshotTesting
import XCTest
struct Application {
static func launch() -> XCUIApplication {
let app = XCUIApplication()
app.launchEnvironment = ["IS_RUNNING_UI_TESTS": "1"]
Bundle.elementFallbackLanguage = "en"
app.launch()
return app
}
@ -36,4 +38,39 @@ extension XCUIApplication {
button.tap()
}
/// Assert screenshot for a screen with the given identifier. Does not fail if a screenshot is newly created.
/// - Parameter identifier: Identifier of the UI test screen
func assertScreenshot(_ identifier: UITestScreenIdentifier) {
let failure = verifySnapshot(matching: screenshot().image,
as: .image(precision: 0.98, scale: nil),
named: identifier.rawValue,
testName: testName)
if let failure = failure,
!failure.contains("No reference was found on disk."),
!failure.contains("to test against the newly-recorded snapshot") {
XCTFail(failure)
}
}
private var testName: String {
osVersion + "-" + languageCode + "-" + regionCode + "-" + deviceName
}
private var deviceName: String {
UIDevice.current.name
}
private var languageCode: String {
Locale.current.languageCode ?? ""
}
private var regionCode: String {
Locale.current.regionCode ?? ""
}
private var osVersion: String {
UIDevice.current.systemVersion.replacingOccurrences(of: ".", with: "-")
}
}

View File

@ -34,6 +34,8 @@ class AuthenticationCoordinatorUITests: XCTestCase {
app.secureTextFields["passwordTextField"].tap()
app.typeText("12345678")
app.assertScreenshot(.authenticationFlow)
// Login Screen: Tap next
app.buttons["nextButton"].tap()
@ -41,7 +43,7 @@ class AuthenticationCoordinatorUITests: XCTestCase {
XCTAssertFalse(app.alerts.element.exists, "No alert should be shown when logging in with valid credentials.")
}
func testLoginWithIncorrectPassword() {
func testLoginWithIncorrectPassword() async throws {
// Given the authentication flow.
let app = Application.launch()
app.goToScreenWithIdentifier(.authenticationFlow)
@ -51,11 +53,15 @@ class AuthenticationCoordinatorUITests: XCTestCase {
// Login Screen: Enter invalid credentials
app.textFields["usernameTextField"].tap()
app.typeText("alice\n")
app.typeText("87654321\n")
app.typeText("alice")
app.secureTextFields["passwordTextField"].tap()
app.typeText("87654321")
// Login Screen: Tap next
app.buttons["nextButton"].tap()
// Then login should fail.
XCTAssertTrue(app.alerts.element.exists, "An error alert should be shown when attempting login with invalid credentials.")
XCTAssertTrue(app.alerts.element.waitForExistence(timeout: 1), "An error alert should be shown when attempting login with invalid credentials.")
}
func testSelectingOIDCServer() {

View File

@ -26,6 +26,8 @@ class BugReportUITests: XCTestCase {
XCTAssertFalse(app.images["screenshotImage"].exists)
XCTAssertFalse(app.buttons["removeScreenshotButton"].exists)
app.assertScreenshot(.bugReport)
}
func testToggleSendingLogs() {
@ -62,6 +64,8 @@ class BugReportUITests: XCTestCase {
XCTAssert(app.images["screenshotImage"].exists)
XCTAssert(app.buttons["removeScreenshotButton"].exists)
app.assertScreenshot(.bugReportWithScreenshot)
}
func verifyInitialStateComponents(in app: XCUIApplication) {

View File

@ -37,6 +37,8 @@ class LoginScreenUITests: XCTestCase {
validateNextButtonIsDisabled(for: state)
validateUnsupportedServerTextIsHidden(for: state)
app.assertScreenshot(.login)
// When typing in a username and password.
app.textFields.element.tap()
app.typeText("@test:matrix.org")

View File

@ -26,6 +26,8 @@ class RoomScreenUITests: XCTestCase {
XCTAssert(app.staticTexts["roomNameLabel"].exists)
XCTAssert(app.staticTexts["roomAvatarPlaceholderImage"].exists)
XCTAssertFalse(app.images["encryptionBadgeIcon"].exists)
app.assertScreenshot(.roomPlainNoAvatar)
}
func testEncryptedWithAvatar() {
@ -35,5 +37,7 @@ class RoomScreenUITests: XCTestCase {
XCTAssert(app.staticTexts["roomNameLabel"].exists)
XCTAssert(app.images["roomAvatarImage"].exists)
XCTAssert(app.images["encryptionBadgeIcon"].exists)
app.assertScreenshot(.roomEncryptedWithAvatar)
}
}

View File

@ -41,6 +41,8 @@ class ServerSelectionUITests: XCTestCase {
let dismissButton = app.buttons["dismissButton"]
XCTAssertTrue(dismissButton.exists, "The dismiss button should be shown during modal presentation.")
app.assertScreenshot(.serverSelection)
}
func testEmptyAddress() async {
@ -95,5 +97,7 @@ class ServerSelectionUITests: XCTestCase {
let confirmButton = app.buttons["confirmButton"]
XCTAssertEqual(confirmButton.label, ElementL10n.actionNext, "The confirm button should say Next when not in modal presentation.")
app.assertScreenshot(.serverSelectionNonModal)
}
}

View File

@ -22,12 +22,14 @@ class SessionVerificationUITests: XCTestCase {
let app = Application.launch()
app.goToScreenWithIdentifier(.sessionVerification)
XCTAssert(app.navigationBars["Verify this session"].exists)
XCTAssert(app.navigationBars[ElementL10n.verificationVerifyDevice].exists)
XCTAssert(app.buttons["startButton"].exists)
XCTAssert(app.buttons["dismissButton"].exists)
XCTAssert(app.staticTexts["titleLabel"].exists)
app.assertScreenshot(.sessionVerification)
app.buttons["startButton"].tap()
XCTAssert(app.activityIndicators["requestingVerificationProgressView"].exists)
@ -52,7 +54,7 @@ class SessionVerificationUITests: XCTestCase {
let app = Application.launch()
app.goToScreenWithIdentifier(.sessionVerification)
XCTAssert(app.navigationBars["Verify this session"].exists)
XCTAssert(app.navigationBars[ElementL10n.verificationVerifyDevice].exists)
XCTAssert(app.buttons["startButton"].exists)
XCTAssert(app.buttons["dismissButton"].exists)
@ -80,7 +82,7 @@ class SessionVerificationUITests: XCTestCase {
let app = Application.launch()
app.goToScreenWithIdentifier(.sessionVerification)
XCTAssert(app.navigationBars["Verify this session"].exists)
XCTAssert(app.navigationBars[ElementL10n.verificationVerifyDevice].exists)
XCTAssert(app.buttons["startButton"].exists)
XCTAssert(app.buttons["dismissButton"].exists)
@ -88,12 +90,12 @@ class SessionVerificationUITests: XCTestCase {
app.buttons["startButton"].tap()
XCTAssert(app.activityIndicators["requestingVerificationProgressView"].exists)
XCTAssert(app.activityIndicators["requestingVerificationProgressView"].waitForExistence(timeout: 1))
XCTAssert(app.buttons["cancelButton"].exists)
app.buttons["cancelButton"].tap()
XCTAssert(app.images["sessionVerificationFailedIcon"].exists)
XCTAssert(app.images["sessionVerificationFailedIcon"].waitForExistence(timeout: 1))
XCTAssert(app.buttons["restartButton"].exists)
XCTAssert(app.buttons["dismissButton"].exists)

View File

@ -35,5 +35,7 @@ class SettingsUITests: XCTestCase {
let logoutButton = app.buttons["logoutButton"]
XCTAssert(logoutButton.exists)
XCTAssertEqual(logoutButton.label, ElementL10n.logout)
app.assertScreenshot(.settings)
}
}

View File

@ -25,6 +25,8 @@ class SplashScreenUITests: XCTestCase {
let getStartedButton = app.buttons["getStartedButton"]
XCTAssertTrue(getStartedButton.exists, "The primary action button should be shown.")
XCTAssertEqual(getStartedButton.label, ElementL10n.loginSplashSubmit)
app.assertScreenshot(.splash)
}
func testSwipingBetweenPages() {
@ -32,8 +34,8 @@ class SplashScreenUITests: XCTestCase {
app.goToScreenWithIdentifier(.splash)
// Given the splash screen in its initial state.
let page1TitleText = app.staticTexts["Own your conversations."]
let page2TitleText = app.staticTexts["You're in control."]
let page1TitleText = app.staticTexts[ElementL10n.ftueAuthCarouselSecureTitle]
let page2TitleText = app.staticTexts[ElementL10n.ftueAuthCarouselControlTitle]
let hiddenPageTitleText = app.staticTexts["hiddenPage"].firstMatch
XCTAssertTrue(page1TitleText.isHittable, "The title from the first page of the carousel should be onscreen.")
@ -41,21 +43,21 @@ class SplashScreenUITests: XCTestCase {
XCTAssertFalse(hiddenPageTitleText.isHittable, "The hidden page of the carousel should be offscreen.")
// When swiping to the next screen.
page1TitleText.swipeLeft()
page1TitleText.swipeLeft(velocity: .fast)
// 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()
page2TitleText.swipeRight(velocity: .fast)
// 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()
page1TitleText.swipeRight(velocity: .fast)
// 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.")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More