ios: snapshots of full screens with the status bar, for the user guide (#8005)
* ios: guide screens rendered with the status bar; group invitation and contact preferences previews * ios: record the full-screen snapshots with the status bar * ios: keep the accepted group invitation preview
No files matched your search
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 253 KiB After Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 255 KiB After Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 52 KiB |
@@ -1,6 +1,7 @@
|
||||
import XCTest
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
@testable import SimpleX
|
||||
@testable import SimpleXChat
|
||||
|
||||
@@ -158,11 +159,13 @@ private struct SnapshotScene {
|
||||
var name: String
|
||||
var height: CGFloat? = nil
|
||||
var allSizes: Bool
|
||||
var device: Bool
|
||||
var view: AnyView
|
||||
|
||||
init<P: PreviewProvider>(_ preview: P.Type, allSizes: Bool = false) {
|
||||
init<P: PreviewProvider>(_ preview: P.Type, allSizes: Bool = false, device: Bool = false) {
|
||||
name = String(describing: preview).replacingOccurrences(of: "_Previews", with: "")
|
||||
self.allSizes = allSizes
|
||||
self.device = device
|
||||
view = AnyView(preview.previews)
|
||||
}
|
||||
}
|
||||
@@ -170,23 +173,23 @@ private struct SnapshotScene {
|
||||
private func previewScenes() -> [SnapshotScene] {
|
||||
[
|
||||
SnapshotScene(ActiveCallOverlay_Previews.self),
|
||||
SnapshotScene(AddChannelView_Previews.self),
|
||||
SnapshotScene(AddChannelView_Previews.self, device: true),
|
||||
SnapshotScene(AddContactLearnMore_Previews.self),
|
||||
SnapshotScene(AddGroupMembersView_Previews.self),
|
||||
SnapshotScene(AddGroupView_Previews.self),
|
||||
SnapshotScene(AdvancedNetworkSettings_Previews.self),
|
||||
SnapshotScene(AddGroupView_Previews.self, device: true),
|
||||
SnapshotScene(AdvancedNetworkSettings_Previews.self, device: true),
|
||||
SnapshotScene(BadgesCheckOrderView_Previews.self),
|
||||
SnapshotScene(BadgesHowItWorksView_Previews.self),
|
||||
SnapshotScene(BadgesHowLongView_Previews.self),
|
||||
SnapshotScene(BadgesRedeemCodeView_Previews.self),
|
||||
SnapshotScene(BadgesSupportSimplexView_Previews.self),
|
||||
SnapshotScene(CallSettings_Previews.self),
|
||||
SnapshotScene(BadgesRedeemCodeView_Previews.self, device: true),
|
||||
SnapshotScene(BadgesSupportSimplexView_Previews.self, device: true),
|
||||
SnapshotScene(CallSettings_Previews.self, device: true),
|
||||
SnapshotScene(ChannelMembersView_Previews.self),
|
||||
SnapshotScene(ChannelRelaysView_Previews.self),
|
||||
SnapshotScene(ChatHelp_Previews.self),
|
||||
SnapshotScene(ChatInfoImage_Previews.self),
|
||||
SnapshotScene(ChatInfoToolbar_Previews.self),
|
||||
SnapshotScene(ChatInfoView_Previews.self),
|
||||
SnapshotScene(ChatInfoView_Previews.self, device: true),
|
||||
SnapshotScene(ChatItemForwardingView_Previews.self),
|
||||
SnapshotScene(ChatItemInfoView_Previews.self),
|
||||
SnapshotScene(ChatItemView_NonMsgContentDeleted_Previews.self, allSizes: true),
|
||||
@@ -194,7 +197,7 @@ private func previewScenes() -> [SnapshotScene] {
|
||||
SnapshotScene(ChatListNavLink_Previews.self),
|
||||
SnapshotScene(ChatListView_Previews.self),
|
||||
SnapshotScene(ChatPreviewView_Previews.self),
|
||||
SnapshotScene(ChatView_Previews.self),
|
||||
SnapshotScene(ChatView_Previews.self, device: true),
|
||||
SnapshotScene(CIChatFeatureView_Previews.self, allSizes: true),
|
||||
SnapshotScene(CIEventView_Previews.self, allSizes: true),
|
||||
SnapshotScene(CIFeaturePreferenceView_Previews.self, allSizes: true),
|
||||
@@ -209,22 +212,22 @@ private func previewScenes() -> [SnapshotScene] {
|
||||
SnapshotScene(ClassicOverviewDirect_Previews.self),
|
||||
SnapshotScene(ClassicOverviewGroup_Previews.self),
|
||||
SnapshotScene(ComposeVoiceView_Previews.self),
|
||||
SnapshotScene(ConnectDesktopView_Previews.self),
|
||||
SnapshotScene(ConnectDesktopView_Previews.self, device: true),
|
||||
SnapshotScene(ContactConnectionInfo_Previews.self),
|
||||
SnapshotScene(ContactConnectionView_Previews.self),
|
||||
SnapshotScene(ContactPreferencesView_Previews.self),
|
||||
SnapshotScene(ContactPreferencesView_Previews.self, device: true),
|
||||
SnapshotScene(ContactRequestView_Previews.self),
|
||||
SnapshotScene(ContextContactRequestActionsView_Previews.self),
|
||||
SnapshotScene(ContextItemView_Previews.self),
|
||||
SnapshotScene(ContextMemberContactActionsView_Previews.self),
|
||||
SnapshotScene(ContextPendingMemberActionsView_Previews.self),
|
||||
SnapshotScene(ContextProfilePickerView_Previews.self),
|
||||
SnapshotScene(CreateProfile_Previews.self),
|
||||
SnapshotScene(CreateProfile_Previews.self, device: true),
|
||||
SnapshotScene(CreateSimpleXAddress_Previews.self),
|
||||
SnapshotScene(CustomTimePicker_Previews.self),
|
||||
SnapshotScene(DatabaseEncryptionView_Previews.self),
|
||||
SnapshotScene(DatabaseEncryptionView_Previews.self, device: true),
|
||||
SnapshotScene(DatabaseErrorView_Previews.self),
|
||||
SnapshotScene(DatabaseView_Previews.self),
|
||||
SnapshotScene(DatabaseView_Previews.self, device: true),
|
||||
SnapshotScene(DeletedItemView_Previews.self, allSizes: true),
|
||||
SnapshotScene(DetermineWidth_Previews.self),
|
||||
SnapshotScene(DeveloperView_Previews.self),
|
||||
@@ -234,11 +237,11 @@ private func previewScenes() -> [SnapshotScene] {
|
||||
SnapshotScene(FramedItemView_Edited_Previews.self, allSizes: true),
|
||||
SnapshotScene(FramedItemView_Previews.self, allSizes: true),
|
||||
SnapshotScene(GetStakeBanner_Previews.self),
|
||||
SnapshotScene(GroupChatInfoView_Previews.self),
|
||||
SnapshotScene(GroupChatInfoView_Previews.self, device: true),
|
||||
SnapshotScene(GroupLinkView_Previews.self),
|
||||
SnapshotScene(GroupMemberInfoView_Previews.self),
|
||||
SnapshotScene(GroupPreferencesView_Previews.self),
|
||||
SnapshotScene(GroupProfileView_Previews.self),
|
||||
SnapshotScene(GroupMemberInfoView_Previews.self, device: true),
|
||||
SnapshotScene(GroupPreferencesView_Previews.self, device: true),
|
||||
SnapshotScene(GroupProfileView_Previews.self, device: true),
|
||||
SnapshotScene(GroupWelcomeView_Previews.self),
|
||||
SnapshotScene(IncognitoHelp_Previews.self),
|
||||
SnapshotScene(IncomingCallView_Previews.self),
|
||||
@@ -250,7 +253,7 @@ private func previewScenes() -> [SnapshotScene] {
|
||||
SnapshotScene(MemberAdmissionView_Previews.self),
|
||||
SnapshotScene(MemberSupportChatToolbar_Previews.self),
|
||||
SnapshotScene(MemberSupportView_Previews.self),
|
||||
SnapshotScene(MigrateToDevice_Previews.self),
|
||||
SnapshotScene(MigrateToDevice_Previews.self, device: true),
|
||||
SnapshotScene(MigrateToGroupView_Previews.self),
|
||||
SnapshotScene(MsgContentView_Previews.self, allSizes: true),
|
||||
SnapshotScene(NativeTextEditor_Previews.self),
|
||||
@@ -258,7 +261,7 @@ private func previewScenes() -> [SnapshotScene] {
|
||||
SnapshotScene(NewChatMenuButton_Previews.self),
|
||||
SnapshotScene(NewFeaturesView_Previews.self),
|
||||
SnapshotScene(NewServerView_Previews.self),
|
||||
SnapshotScene(NotificationsModeView_Previews.self),
|
||||
SnapshotScene(NotificationsModeView_Previews.self, device: true),
|
||||
SnapshotScene(NotificationsView_Previews.self),
|
||||
SnapshotScene(OnboardingConditionsView_Previews.self),
|
||||
SnapshotScene(OnboardingStepsView_Previews.self),
|
||||
@@ -269,7 +272,7 @@ private func previewScenes() -> [SnapshotScene] {
|
||||
SnapshotScene(PreferencesView_Previews.self),
|
||||
SnapshotScene(PrivacySettings_Previews.self),
|
||||
SnapshotScene(ProfileImage_Previews.self),
|
||||
SnapshotScene(ProfilePrivacyView_Previews.self),
|
||||
SnapshotScene(ProfilePrivacyView_Previews.self, device: true),
|
||||
SnapshotScene(ProtocolServerView_Previews.self),
|
||||
SnapshotScene(QRCode_Previews.self),
|
||||
SnapshotScene(RTCServers_Previews.self),
|
||||
@@ -279,8 +282,8 @@ private func previewScenes() -> [SnapshotScene] {
|
||||
SnapshotScene(SendMessageView_Previews.self),
|
||||
SnapshotScene(SetAppPasscodeView_Previews.self),
|
||||
SnapshotScene(SetDeliveryReceiptsView_Previews.self),
|
||||
SnapshotScene(SettingsView_Previews.self),
|
||||
SnapshotScene(SimpleXInfo_Previews.self),
|
||||
SnapshotScene(SettingsView_Previews.self, device: true),
|
||||
SnapshotScene(SimpleXInfo_Previews.self, device: true),
|
||||
SnapshotScene(SmallLinkPreview_Previews.self),
|
||||
SnapshotScene(SupportSimpleXBanner_Previews.self),
|
||||
SnapshotScene(TerminalAvatars_Previews.self, allSizes: true),
|
||||
@@ -294,7 +297,7 @@ private func previewScenes() -> [SnapshotScene] {
|
||||
SnapshotScene(UserAddressLearnMore_Previews.self),
|
||||
SnapshotScene(UserAddressView_Previews.self),
|
||||
SnapshotScene(UserPicker_Previews.self),
|
||||
SnapshotScene(VerifyCodeView_Previews.self),
|
||||
SnapshotScene(VerifyCodeView_Previews.self, device: true),
|
||||
SnapshotScene(VersionView_Previews.self),
|
||||
SnapshotScene(WhySimpleX_Previews.self),
|
||||
SnapshotScene(YourServersView_Previews.self)
|
||||
@@ -325,7 +328,7 @@ private func renderScene(_ scene: SnapshotScene, _ theme: AppTheme, dark: Bool,
|
||||
.frame(width: width, height: scene.height)
|
||||
let host = UIHostingController(rootView: AnyView(root))
|
||||
if #available(iOS 16.4, *) {
|
||||
host.safeAreaRegions = []
|
||||
host.safeAreaRegions = scene.device ? .container : []
|
||||
}
|
||||
let window = UIWindow(windowScene: windowScene)
|
||||
window.overrideUserInterfaceStyle = dark ? .dark : .light
|
||||
@@ -333,7 +336,7 @@ private func renderScene(_ scene: SnapshotScene, _ theme: AppTheme, dark: Bool,
|
||||
window.frame = CGRect(x: 0, y: 0, width: width, height: scene.height ?? screen.bounds.height)
|
||||
window.isHidden = false
|
||||
settle()
|
||||
if scene.height == nil {
|
||||
if scene.height == nil && !scene.device {
|
||||
for _ in 0..<2 {
|
||||
let fit = host.sizeThatFits(in: CGSize(width: width, height: .greatestFiniteMagnitude))
|
||||
let fill = host.sizeThatFits(in: screen.bounds.size)
|
||||
@@ -349,12 +352,31 @@ private func renderScene(_ scene: SnapshotScene, _ theme: AppTheme, dark: Bool,
|
||||
format.opaque = true
|
||||
let image = UIGraphicsImageRenderer(bounds: window.bounds, format: format).image { _ in
|
||||
_ = window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
|
||||
if scene.device {
|
||||
drawStatusBar(dark: dark)
|
||||
}
|
||||
}
|
||||
window.isHidden = true
|
||||
window.rootViewController = nil
|
||||
return image
|
||||
}
|
||||
|
||||
private func drawStatusBar(dark: Bool) {
|
||||
let color: UIColor = dark ? .white : .black
|
||||
let time = NSAttributedString(string: "9:41", attributes: [.font: UIFont.systemFont(ofSize: 17, weight: .semibold), .foregroundColor: color])
|
||||
let timeSize = time.size()
|
||||
time.draw(at: CGPoint(x: 74 - timeSize.width / 2, y: 32.5 - timeSize.height / 2))
|
||||
let icons = [
|
||||
("cellularbars", CGRect(x: 288.5, y: 26.5, width: 19, height: 12)),
|
||||
("wifi", CGRect(x: 315, y: 26.5, width: 17, height: 12)),
|
||||
("battery.100", CGRect(x: 339.5, y: 26, width: 27, height: 13))
|
||||
]
|
||||
for (symbol, box) in icons {
|
||||
let icon = UIImage(systemName: symbol)!.withTintColor(color, renderingMode: .alwaysOriginal)
|
||||
icon.draw(in: AVMakeRect(aspectRatio: icon.size, insideRect: box))
|
||||
}
|
||||
}
|
||||
|
||||
private func removeAnimations(_ layer: CALayer) {
|
||||
layer.removeAllAnimations()
|
||||
layer.sublayers?.forEach(removeAnimations)
|
||||
|
||||
@@ -1130,9 +1130,9 @@ public struct ContactUserPreferences: Decodable, Hashable {
|
||||
|
||||
public static let sampleData = ContactUserPreferences(
|
||||
timedMessages: ContactUserPreference<TimedMessagesPreference>(
|
||||
enabled: FeatureEnabled(forUser: false, forContact: false),
|
||||
enabled: FeatureEnabled(forUser: true, forContact: true),
|
||||
userPreference: ContactUserPref<TimedMessagesPreference>.user(preference: TimedMessagesPreference(allow: .yes)),
|
||||
contactPreference: TimedMessagesPreference(allow: .no)
|
||||
contactPreference: TimedMessagesPreference(allow: .yes)
|
||||
),
|
||||
fullDelete: ContactUserPreference<SimplePreference>(
|
||||
enabled: FeatureEnabled(forUser: false, forContact: false),
|
||||
@@ -1702,7 +1702,7 @@ public struct ContactFeaturesAllowed: Equatable, Hashable {
|
||||
}
|
||||
|
||||
public static let sampleData = ContactFeaturesAllowed(
|
||||
timedMessagesAllowed: false,
|
||||
timedMessagesAllowed: true,
|
||||
timedMessagesTTL: nil,
|
||||
fullDelete: ContactFeatureAllowed.userDefault(.no),
|
||||
reactions: ContactFeatureAllowed.userDefault(.yes),
|
||||
|
||||
@@ -40,7 +40,7 @@ Haiku 5.5 drives the app well: the smoke plan takes a few dozen turns and cents;
|
||||
|
||||
At the end of the app job's smoke test, `smoke-test.sh` runs `snapshots.sh <derived data> <smoke test dir>/snapshots` when the app it tested lies in the derived data of a build (`…/Build/Products/Debug-iphonesimulator`); the Claude job, which installs the app from a tarball, skips it.
|
||||
|
||||
`snapshots.sh` builds the unit-test target `SimpleX Snapshots` (`apps/ios/SimpleX Snapshots/SnapshotTests.swift`, hosted by the app) with `xcodebuild build-for-testing`, then runs it in the smoke test's simulator three times, after `xcrun simctl ui <udid> content_size` `large`, `extra-extra-extra-large` and `accessibility-medium`, and sets `large` back. The test hosts each preview listed in `previewScenes()` (the `previews` of a `PreviewProvider`, in a `VStack`) in a `UIHostingController` in a window of the screen's width and of the larger of two heights, the preview's ideal height and its height in a frame of the screen's size, in the light and dark themes without a wallpaper, ends text editing and removes the running animations, so a text cursor or a spinner is drawn the same in every run, and draws it with `drawHierarchy` at scale 1, one pixel per point. The chat item previews (`allSizes`) are rendered at the three sizes, the other previews at `large` only.
|
||||
`snapshots.sh` builds the unit-test target `SimpleX Snapshots` (`apps/ios/SimpleX Snapshots/SnapshotTests.swift`, hosted by the app) with `xcodebuild build-for-testing`, then runs it in the smoke test's simulator three times, after `xcrun simctl ui <udid> content_size` `large`, `extra-extra-extra-large` and `accessibility-medium`, and sets `large` back. The test hosts each preview listed in `previewScenes()` (the `previews` of a `PreviewProvider`, in a `VStack`) in a `UIHostingController` in a window of the screen's width and of the larger of two heights, the preview's ideal height and its height in a frame of the screen's size, in the light and dark themes without a wallpaper, ends text editing and removes the running animations, so a text cursor or a spinner is drawn the same in every run, and draws it with `drawHierarchy` at scale 1, one pixel per point. The previews marked `device`, the screens shown in the user guide, are rendered as on the device instead: in a window of the screen's size with the device's safe areas, and with a status bar drawn over them (9:41, full cellular, Wi-Fi and battery icons), so that `scripts/docs/frame-ios-renders.py` places them in the iPhone bezel unchanged. The chat item previews (`allSizes`) are rendered at the three sizes, the other previews at `large` only.
|
||||
|
||||
`previewScenes()` lists every `PreviewProvider` in `apps/ios` except those in `EXCLUDED` of `apps/ios/SimpleX Snapshots/check-previews.py`, whose renders differ between runs and can be made identical only by a change of the app's code. The iOS job of `build.yml` runs `check-previews.py`, which fails when a `PreviewProvider` is neither listed nor excluded, or when a file has a `#Preview`, since the test renders previews by their type.
|
||||
|
||||
|
||||