Autogenerate screenshots for all platforms (#998)

Unframed for now.

- Split Main/TV targets
- Extend ProfileManager for screenshot scenarios
- Rename UITesting to UIAccessibility

Active profile looks very big on TV simulator, temporarily commented
`.padding(.top, 50)` in ActiveProfileView.

Closes #974
This commit is contained in:
Davide 2024-12-10 18:06:23 +01:00 committed by GitHub
parent a626722224
commit f441a2eed0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 588 additions and 81 deletions

View File

@ -55,12 +55,12 @@ let package = Package(
] ]
), ),
.library( .library(
name: "UILibrary", name: "UIAccessibility",
targets: ["UILibrary"] targets: ["UIAccessibility"]
), ),
.library( .library(
name: "UITesting", name: "UILibrary",
targets: ["UITesting"] targets: ["UILibrary"]
) )
], ],
dependencies: [ dependencies: [
@ -186,20 +186,20 @@ let package = Package(
.product(name: "PassepartoutWireGuardGo", package: "passepartoutkit-source-wireguard-go") .product(name: "PassepartoutWireGuardGo", package: "passepartoutkit-source-wireguard-go")
] ]
), ),
.target(
name: "UIAccessibility"
),
.target( .target(
name: "UILibrary", name: "UILibrary",
dependencies: [ dependencies: [
"CommonAPI", "CommonAPI",
"CommonLibrary", "CommonLibrary",
"UITesting" "UIAccessibility"
], ],
resources: [ resources: [
.process("Resources") .process("Resources")
] ]
), ),
.target(
name: "UITesting"
),
.testTarget( .testTarget(
name: "AppUIMainTests", name: "AppUIMainTests",
dependencies: ["AppUIMain"] dependencies: ["AppUIMain"]

View File

@ -27,7 +27,7 @@ import CommonLibrary
import CommonUtils import CommonUtils
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
import UITesting import UIAccessibility
struct InstalledProfileView: View, Routable { struct InstalledProfileView: View, Routable {

View File

@ -27,7 +27,7 @@ import CommonLibrary
import CommonUtils import CommonUtils
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
import UITesting import UIAccessibility
struct ProfileContextMenu: View, Routable { struct ProfileContextMenu: View, Routable {
enum Style { enum Style {

View File

@ -25,9 +25,13 @@
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
import UIAccessibility
struct ModuleViewModifier<T>: ViewModifier where T: ModuleBuilder & Equatable { struct ModuleViewModifier<T>: ViewModifier where T: ModuleBuilder & Equatable {
@Environment(\.isUITesting)
private var isUITesting
@ObservedObject @ObservedObject
var editor: ProfileEditor var editor: ProfileEditor
@ -37,7 +41,9 @@ struct ModuleViewModifier<T>: ViewModifier where T: ModuleBuilder & Equatable {
Form { Form {
content content
#if DEBUG #if DEBUG
if !isUITesting {
UUIDSection(uuid: draft.id) UUIDSection(uuid: draft.id)
}
#endif #endif
} }
.themeForm() .themeForm()

View File

@ -37,5 +37,6 @@ struct ProfileNameSection: View {
placeholder: Strings.Placeholders.Profile.name, placeholder: Strings.Placeholders.Profile.name,
footer: Strings.Views.Profile.Sections.Name.footer footer: Strings.Views.Profile.Sections.Name.footer
) )
.uiAccessibility(.Profile.name)
} }
} }

View File

@ -131,6 +131,7 @@ private extension ProfileEditView {
.contentShape(.rect) .contentShape(.rect)
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.uiAccessibility(.Profile.moduleLink)
} }
} }

View File

@ -29,10 +29,14 @@ import CommonLibrary
import CommonUtils import CommonUtils
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
import UIAccessibility
struct ModuleListView: View, Routable { struct ModuleListView: View, Routable {
static let generalModuleId = UUID() static let generalModuleId = UUID()
@Environment(\.isUITesting)
private var isUITesting
@ObservedObject @ObservedObject
var profileEditor: ProfileEditor var profileEditor: ProfileEditor
@ -58,6 +62,7 @@ struct ModuleListView: View, Routable {
NavigationLink(value: ProfileSplitView.Detail.module(id: module.id)) { NavigationLink(value: ProfileSplitView.Detail.module(id: module.id)) {
moduleRow(for: module) moduleRow(for: module)
} }
.uiAccessibility(.Profile.moduleLink)
} }
.onMove(perform: moveModules) .onMove(perform: moveModules)
} }
@ -82,11 +87,13 @@ private extension ModuleListView {
) )
} }
Spacer() Spacer()
if !isUITesting {
EditorModuleToggle(profileEditor: profileEditor, module: module) { EditorModuleToggle(profileEditor: profileEditor, module: module) {
EmptyView() EmptyView()
} }
} }
} }
}
@ViewBuilder @ViewBuilder
func toolbarContent() -> some View { func toolbarContent() -> some View {

View File

@ -29,6 +29,7 @@ import CommonAPI
import CommonLibrary import CommonLibrary
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
import UIAccessibility
extension VPNProviderServerView { extension VPNProviderServerView {
struct ContentView: View { struct ContentView: View {
@ -129,6 +130,7 @@ private extension VPNProviderServerView.ContentView {
} label: { } label: {
ThemeCountryText(code) ThemeCountryText(code)
} }
.uiAccessibility(.VPNServers.countryGroup)
} }
} }

View File

@ -27,6 +27,7 @@ import CommonLibrary
import CommonUtils import CommonUtils
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
import UIAccessibility
struct ActiveProfileView: View { struct ActiveProfileView: View {
@ -73,7 +74,7 @@ struct ActiveProfileView: View {
.clipShape(RoundedRectangle(cornerRadius: 50)) .clipShape(RoundedRectangle(cornerRadius: 50))
} }
.padding(.horizontal, 100) .padding(.horizontal, 100)
.padding(.top, 50) // .padding(.top, 50)
Spacer() Spacer()
} }
@ -86,6 +87,7 @@ private extension ActiveProfileView {
.font(.title) .font(.title)
.fontWeight(theme.relevantWeight) .fontWeight(theme.relevantWeight)
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
.uiAccessibility(.App.installedProfile)
} }
var statusView: some View { var statusView: some View {

View File

@ -27,6 +27,7 @@ import CommonLibrary
import CommonUtils import CommonUtils
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
import UIAccessibility
struct ProfileListView: View { struct ProfileListView: View {
@ -84,6 +85,7 @@ private extension ProfileListView {
} }
) )
.focused($focusedField, equals: .profile(preview.id)) .focused($focusedField, equals: .profile(preview.id))
.uiAccessibility(.App.ProfileList.profile)
} }
func toggleView(for preview: ProfilePreview) -> some View { func toggleView(for preview: ProfilePreview) -> some View {

View File

@ -29,6 +29,8 @@ public struct AccessibilityInfo: Equatable, Sendable {
public enum ElementType: Sendable { public enum ElementType: Sendable {
case button case button
case link
case menu case menu
case menuItem case menuItem

View File

@ -33,14 +33,14 @@ extension AccessibilityInfo {
public static let connectTo = AccessibilityInfo("app.profileMenu.connectTo", .menuItem) public static let connectTo = AccessibilityInfo("app.profileMenu.connectTo", .menuItem)
} }
public enum ProfileList {
public static let profile = AccessibilityInfo("app.profileList.profile", .button)
}
public static let installedProfile = AccessibilityInfo("app.installedProfile", .text) public static let installedProfile = AccessibilityInfo("app.installedProfile", .text)
public static let profileToggle = AccessibilityInfo("app.profileToggle", .button) public static let profileToggle = AccessibilityInfo("app.profileToggle", .button)
public static let profileMenu = AccessibilityInfo("app.profileMenu", .menu) public static let profileMenu = AccessibilityInfo("app.profileMenu", .menu)
} }
public enum Profile {
public static let cancel = AccessibilityInfo("profile.cancel", .button)
}
} }

View File

@ -0,0 +1,36 @@
//
// Profile.swift
// Passepartout
//
// Created by Davide De Rosa on 12/2/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
extension AccessibilityInfo {
public enum Profile {
public static let name = AccessibilityInfo("profile.name", .text)
public static let moduleLink = AccessibilityInfo("profile.moduleLink", .link)
public static let cancel = AccessibilityInfo("profile.cancel", .button)
}
}

View File

@ -0,0 +1,32 @@
//
// VPNServers.swift
// Passepartout
//
// Created by Davide De Rosa on 12/9/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
extension AccessibilityInfo {
public enum VPNServers {
public static let countryGroup = AccessibilityInfo("vpnServers.countryGroup", .button)
}
}

View File

@ -28,7 +28,7 @@ import CommonLibrary
import CommonUtils import CommonUtils
import Foundation import Foundation
import PassepartoutKit import PassepartoutKit
import UITesting import UIAccessibility
@MainActor @MainActor
public final class AppContext: ObservableObject, Sendable { public final class AppContext: ObservableObject, Sendable {

View File

@ -8,11 +8,11 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0E08447C2CF86F2A00ECED7C /* XCTestCase+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E08447B2CF86F2A00ECED7C /* XCTestCase+Extensions.swift */; }; 0E08447C2CF86F2A00ECED7C /* XCTestCase+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E08447B2CF86F2A00ECED7C /* XCTestCase+Extensions.swift */; };
0E2D68DC2CF7CF7500DC95BC /* UITesting in Frameworks */ = {isa = PBXBuildFile; productRef = 0E2D68DB2CF7CF7500DC95BC /* UITesting */; };
0E3E22962CE53510005135DF /* AppUIMain in Frameworks */ = {isa = PBXBuildFile; platformFilters = (ios, macos, ); productRef = 0E3E22952CE53510005135DF /* AppUIMain */; }; 0E3E22962CE53510005135DF /* AppUIMain in Frameworks */ = {isa = PBXBuildFile; platformFilters = (ios, macos, ); productRef = 0E3E22952CE53510005135DF /* AppUIMain */; };
0E3E22982CE53510005135DF /* AppUITV in Frameworks */ = {isa = PBXBuildFile; platformFilters = (tvos, ); productRef = 0E3E22972CE53510005135DF /* AppUITV */; }; 0E3E22982CE53510005135DF /* AppUITV in Frameworks */ = {isa = PBXBuildFile; platformFilters = (tvos, ); productRef = 0E3E22972CE53510005135DF /* AppUITV */; };
0E3FF4BA2CE3AFBC00BFF640 /* Profiles.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = 0E3FF4B72CE3AFBC00BFF640 /* Profiles.sqlite */; }; 0E3FF4BA2CE3AFBC00BFF640 /* Profiles.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = 0E3FF4B72CE3AFBC00BFF640 /* Profiles.sqlite */; };
0E3FF4BB2CE3AFBC00BFF640 /* MigrationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3FF4B92CE3AFBC00BFF640 /* MigrationManagerTests.swift */; }; 0E3FF4BB2CE3AFBC00BFF640 /* MigrationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3FF4B92CE3AFBC00BFF640 /* MigrationManagerTests.swift */; };
0E418AB52D074F0E00D33D47 /* VPNServersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E418AB42D074F0E00D33D47 /* VPNServersScreen.swift */; platformFilters = (ios, macos, ); };
0E483E812CE64D6B00584B32 /* TunnelContext+Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E483E7F2CE64D6B00584B32 /* TunnelContext+Shared.swift */; }; 0E483E812CE64D6B00584B32 /* TunnelContext+Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E483E7F2CE64D6B00584B32 /* TunnelContext+Shared.swift */; };
0E60512C2CE5393C00F763D4 /* PassepartoutImplementations in Frameworks */ = {isa = PBXBuildFile; productRef = 0E60512B2CE5393C00F763D4 /* PassepartoutImplementations */; }; 0E60512C2CE5393C00F763D4 /* PassepartoutImplementations in Frameworks */ = {isa = PBXBuildFile; productRef = 0E60512B2CE5393C00F763D4 /* PassepartoutImplementations */; };
0E6EEEE32CF8CABA0076E2B0 /* AppContext+Testing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6EEEE12CF8CABA0076E2B0 /* AppContext+Testing.swift */; }; 0E6EEEE32CF8CABA0076E2B0 /* AppContext+Testing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6EEEE12CF8CABA0076E2B0 /* AppContext+Testing.swift */; };
@ -23,9 +23,9 @@
0E7C3CCD2C9AF44600B72E69 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7C3CCC2C9AF44600B72E69 /* AppDelegate.swift */; }; 0E7C3CCD2C9AF44600B72E69 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7C3CCC2C9AF44600B72E69 /* AppDelegate.swift */; };
0E7E3D692B9345FD002BBDB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E7E3D5C2B9345FD002BBDB4 /* Assets.xcassets */; }; 0E7E3D692B9345FD002BBDB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E7E3D5C2B9345FD002BBDB4 /* Assets.xcassets */; };
0E7E3D6B2B9345FD002BBDB4 /* PassepartoutApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E3D5F2B9345FD002BBDB4 /* PassepartoutApp.swift */; }; 0E7E3D6B2B9345FD002BBDB4 /* PassepartoutApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E3D5F2B9345FD002BBDB4 /* PassepartoutApp.swift */; };
0E7F460E2CF7F01600B1C53A /* FlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F460B2CF7F01600B1C53A /* FlowTests.swift */; }; 0E7F460E2CF7F01600B1C53A /* FlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F460B2CF7F01600B1C53A /* FlowTests.swift */; platformFilters = (ios, macos, ); };
0E7F460F2CF7F01600B1C53A /* XCUIApplication+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F460C2CF7F01600B1C53A /* XCUIApplication+Extensions.swift */; }; 0E7F460F2CF7F01600B1C53A /* XCUIApplication+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F460C2CF7F01600B1C53A /* XCUIApplication+Extensions.swift */; };
0E7F46122CF7F44C00B1C53A /* AppScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F46102CF7F44C00B1C53A /* AppScreen.swift */; }; 0E7F46122CF7F44C00B1C53A /* AppScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F46102CF7F44C00B1C53A /* AppScreen.swift */; platformFilters = (ios, macos, ); };
0E81955A2CFDA75200CC8FFD /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8195592CFDA75200CC8FFD /* Dependencies.swift */; }; 0E81955A2CFDA75200CC8FFD /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8195592CFDA75200CC8FFD /* Dependencies.swift */; };
0E81955B2CFDA7BF00CC8FFD /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8195592CFDA75200CC8FFD /* Dependencies.swift */; }; 0E81955B2CFDA7BF00CC8FFD /* Dependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8195592CFDA75200CC8FFD /* Dependencies.swift */; };
0E8DFD482D05FA7000531CDE /* TunnelContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD472D05FA7000531CDE /* TunnelContext.swift */; }; 0E8DFD482D05FA7000531CDE /* TunnelContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD472D05FA7000531CDE /* TunnelContext.swift */; };
@ -36,10 +36,14 @@
0E8DFD532D05FE5A00531CDE /* Dependencies+PassepartoutKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD4C2D05FE5A00531CDE /* Dependencies+PassepartoutKit.swift */; }; 0E8DFD532D05FE5A00531CDE /* Dependencies+PassepartoutKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD4C2D05FE5A00531CDE /* Dependencies+PassepartoutKit.swift */; };
0E8DFD542D05FE5A00531CDE /* Dependencies+IAPManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD4B2D05FE5A00531CDE /* Dependencies+IAPManager.swift */; }; 0E8DFD542D05FE5A00531CDE /* Dependencies+IAPManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD4B2D05FE5A00531CDE /* Dependencies+IAPManager.swift */; };
0E8DFD562D05FE5A00531CDE /* Dependencies+CoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD492D05FE5A00531CDE /* Dependencies+CoreData.swift */; }; 0E8DFD562D05FE5A00531CDE /* Dependencies+CoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8DFD492D05FE5A00531CDE /* Dependencies+CoreData.swift */; };
0E916B782CF80FD60072921A /* ProfileEditorScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E916B772CF80FD60072921A /* ProfileEditorScreen.swift */; }; 0E916B782CF80FD60072921A /* ProfileEditorScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E916B772CF80FD60072921A /* ProfileEditorScreen.swift */; platformFilters = (ios, macos, ); };
0E916B7C2CF811EB0072921A /* XCUIElement+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E916B7B2CF811EB0072921A /* XCUIElement+Extensions.swift */; }; 0E916B7C2CF811EB0072921A /* XCUIElement+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E916B7B2CF811EB0072921A /* XCUIElement+Extensions.swift */; };
0E94EE582B93554B00588243 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E3D672B9345FD002BBDB4 /* PacketTunnelProvider.swift */; }; 0E94EE582B93554B00588243 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E3D672B9345FD002BBDB4 /* PacketTunnelProvider.swift */; };
0EAD6A1B2CF7F79A00CC1F02 /* ScreenshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAD6A1A2CF7F79A00CC1F02 /* ScreenshotTests.swift */; }; 0EA6340C2D088C8200180D7C /* UIAccessibility in Frameworks */ = {isa = PBXBuildFile; productRef = 0EA6340B2D088C8200180D7C /* UIAccessibility */; };
0EA6340E2D08995800180D7C /* ScreenshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA6340D2D08995800180D7C /* ScreenshotTests.swift */; platformFilters = (tvos, ); };
0EA634122D08997300180D7C /* FlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA634112D08997300180D7C /* FlowTests.swift */; platformFilters = (tvos, ); };
0EA634142D08998700180D7C /* AppScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA634132D08998700180D7C /* AppScreen.swift */; platformFilters = (tvos, ); };
0EAD6A1B2CF7F79A00CC1F02 /* ScreenshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAD6A1A2CF7F79A00CC1F02 /* ScreenshotTests.swift */; platformFilters = (ios, macos, ); };
0EAEC8A92D05DB8D001AA50C /* DefaultAppProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAEC8A62D05DB8D001AA50C /* DefaultAppProcessor.swift */; }; 0EAEC8A92D05DB8D001AA50C /* DefaultAppProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAEC8A62D05DB8D001AA50C /* DefaultAppProcessor.swift */; };
0EAEC8AA2D05DB8D001AA50C /* DefaultTunnelProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAEC8A72D05DB8D001AA50C /* DefaultTunnelProcessor.swift */; }; 0EAEC8AA2D05DB8D001AA50C /* DefaultTunnelProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAEC8A72D05DB8D001AA50C /* DefaultTunnelProcessor.swift */; };
0EB08B982CA46F4900A02591 /* AppPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0EB08B962CA46F4900A02591 /* AppPlist.strings */; }; 0EB08B982CA46F4900A02591 /* AppPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0EB08B962CA46F4900A02591 /* AppPlist.strings */; };
@ -47,7 +51,7 @@
0EC066D12C7DC47600D88A94 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0EC066D02C7DC47600D88A94 /* LaunchScreen.storyboard */; platformFilter = ios; }; 0EC066D12C7DC47600D88A94 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0EC066D02C7DC47600D88A94 /* LaunchScreen.storyboard */; platformFilter = ios; };
0EC332CA2B8A1808000B9C2F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EC332C92B8A1808000B9C2F /* NetworkExtension.framework */; }; 0EC332CA2B8A1808000B9C2F /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EC332C92B8A1808000B9C2F /* NetworkExtension.framework */; };
0EC332D22B8A1808000B9C2F /* PassepartoutTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0EC332C82B8A1808000B9C2F /* PassepartoutTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 0EC332D22B8A1808000B9C2F /* PassepartoutTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0EC332C82B8A1808000B9C2F /* PassepartoutTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
0EC418D22CF86B7400AC6F2F /* ProfileMenuScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC418D12CF86B7400AC6F2F /* ProfileMenuScreen.swift */; }; 0EC418D22CF86B7400AC6F2F /* ProfileMenuScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC418D12CF86B7400AC6F2F /* ProfileMenuScreen.swift */; platformFilters = (ios, macos, ); };
0EC797422B9378E000C093B7 /* AppContext+Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC797402B9378E000C093B7 /* AppContext+Shared.swift */; }; 0EC797422B9378E000C093B7 /* AppContext+Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EC797402B9378E000C093B7 /* AppContext+Shared.swift */; };
0ED61CF82CD0418C008FE259 /* App+macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED61CF72CD0418C008FE259 /* App+macOS.swift */; }; 0ED61CF82CD0418C008FE259 /* App+macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED61CF72CD0418C008FE259 /* App+macOS.swift */; };
0ED61CFA2CD04192008FE259 /* App+iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED61CF92CD04192008FE259 /* App+iOS.swift */; }; 0ED61CFA2CD04192008FE259 /* App+iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED61CF92CD04192008FE259 /* App+iOS.swift */; };
@ -142,6 +146,7 @@
0E3FF4AE2CE3AF6F00BFF640 /* PassepartoutTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PassepartoutTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 0E3FF4AE2CE3AF6F00BFF640 /* PassepartoutTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PassepartoutTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
0E3FF4B72CE3AFBC00BFF640 /* Profiles.sqlite */ = {isa = PBXFileReference; lastKnownFileType = file; path = Profiles.sqlite; sourceTree = "<group>"; }; 0E3FF4B72CE3AFBC00BFF640 /* Profiles.sqlite */ = {isa = PBXFileReference; lastKnownFileType = file; path = Profiles.sqlite; sourceTree = "<group>"; };
0E3FF4B92CE3AFBC00BFF640 /* MigrationManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationManagerTests.swift; sourceTree = "<group>"; }; 0E3FF4B92CE3AFBC00BFF640 /* MigrationManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationManagerTests.swift; sourceTree = "<group>"; };
0E418AB42D074F0E00D33D47 /* VPNServersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNServersScreen.swift; sourceTree = "<group>"; };
0E483E7F2CE64D6B00584B32 /* TunnelContext+Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelContext+Shared.swift"; sourceTree = "<group>"; }; 0E483E7F2CE64D6B00584B32 /* TunnelContext+Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelContext+Shared.swift"; sourceTree = "<group>"; };
0E5DFDDC2CDB8F9100F2DE70 /* Passepartout.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Passepartout.storekit; sourceTree = "<group>"; }; 0E5DFDDC2CDB8F9100F2DE70 /* Passepartout.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Passepartout.storekit; sourceTree = "<group>"; };
0E6EEEE12CF8CABA0076E2B0 /* AppContext+Testing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppContext+Testing.swift"; sourceTree = "<group>"; }; 0E6EEEE12CF8CABA0076E2B0 /* AppContext+Testing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppContext+Testing.swift"; sourceTree = "<group>"; };
@ -171,6 +176,9 @@
0E916B772CF80FD60072921A /* ProfileEditorScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileEditorScreen.swift; sourceTree = "<group>"; }; 0E916B772CF80FD60072921A /* ProfileEditorScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileEditorScreen.swift; sourceTree = "<group>"; };
0E916B7B2CF811EB0072921A /* XCUIElement+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIElement+Extensions.swift"; sourceTree = "<group>"; }; 0E916B7B2CF811EB0072921A /* XCUIElement+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIElement+Extensions.swift"; sourceTree = "<group>"; };
0E94EE5C2B93570600588243 /* Tunnel.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Tunnel.plist; sourceTree = "<group>"; }; 0E94EE5C2B93570600588243 /* Tunnel.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Tunnel.plist; sourceTree = "<group>"; };
0EA6340D2D08995800180D7C /* ScreenshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotTests.swift; sourceTree = "<group>"; };
0EA634112D08997300180D7C /* FlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowTests.swift; sourceTree = "<group>"; };
0EA634132D08998700180D7C /* AppScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScreen.swift; sourceTree = "<group>"; };
0EAD6A1A2CF7F79A00CC1F02 /* ScreenshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotTests.swift; sourceTree = "<group>"; }; 0EAD6A1A2CF7F79A00CC1F02 /* ScreenshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotTests.swift; sourceTree = "<group>"; };
0EAEC8A62D05DB8D001AA50C /* DefaultAppProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultAppProcessor.swift; sourceTree = "<group>"; }; 0EAEC8A62D05DB8D001AA50C /* DefaultAppProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultAppProcessor.swift; sourceTree = "<group>"; };
0EAEC8A72D05DB8D001AA50C /* DefaultTunnelProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultTunnelProcessor.swift; sourceTree = "<group>"; }; 0EAEC8A72D05DB8D001AA50C /* DefaultTunnelProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultTunnelProcessor.swift; sourceTree = "<group>"; };
@ -213,7 +221,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
0E2D68DC2CF7CF7500DC95BC /* UITesting in Frameworks */, 0EA6340C2D088C8200180D7C /* UIAccessibility in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -297,6 +305,16 @@
path = Resources; path = Resources;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
0E418AB72D0752D100D33D47 /* TV */ = {
isa = PBXGroup;
children = (
0EA634102D08996500180D7C /* Screens */,
0EA634112D08997300180D7C /* FlowTests.swift */,
0EA6340D2D08995800180D7C /* ScreenshotTests.swift */,
);
path = TV;
sourceTree = "<group>";
};
0E757F112CD0CFFC006E13E1 /* LoginItem */ = { 0E757F112CD0CFFC006E13E1 /* LoginItem */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -366,9 +384,8 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0E916B7A2CF811DE0072921A /* Extensions */, 0E916B7A2CF811DE0072921A /* Extensions */,
0E916B7E2CF81A110072921A /* Screens */, 0EC418C92CF81C6A00AC6F2F /* Main */,
0E7F460B2CF7F01600B1C53A /* FlowTests.swift */, 0E418AB72D0752D100D33D47 /* TV */,
0EAD6A1A2CF7F79A00CC1F02 /* ScreenshotTests.swift */,
); );
path = UITests; path = UITests;
sourceTree = "<group>"; sourceTree = "<group>";
@ -408,7 +425,18 @@
0E916B7E2CF81A110072921A /* Screens */ = { 0E916B7E2CF81A110072921A /* Screens */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0EC418C92CF81C6A00AC6F2F /* Main */, 0E7F46102CF7F44C00B1C53A /* AppScreen.swift */,
0E916B772CF80FD60072921A /* ProfileEditorScreen.swift */,
0EC418D12CF86B7400AC6F2F /* ProfileMenuScreen.swift */,
0E418AB42D074F0E00D33D47 /* VPNServersScreen.swift */,
);
path = Screens;
sourceTree = "<group>";
};
0EA634102D08996500180D7C /* Screens */ = {
isa = PBXGroup;
children = (
0EA634132D08998700180D7C /* AppScreen.swift */,
); );
path = Screens; path = Screens;
sourceTree = "<group>"; sourceTree = "<group>";
@ -416,9 +444,9 @@
0EC418C92CF81C6A00AC6F2F /* Main */ = { 0EC418C92CF81C6A00AC6F2F /* Main */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0E7F46102CF7F44C00B1C53A /* AppScreen.swift */, 0E916B7E2CF81A110072921A /* Screens */,
0E916B772CF80FD60072921A /* ProfileEditorScreen.swift */, 0E7F460B2CF7F01600B1C53A /* FlowTests.swift */,
0EC418D12CF86B7400AC6F2F /* ProfileMenuScreen.swift */, 0EAD6A1A2CF7F79A00CC1F02 /* ScreenshotTests.swift */,
); );
path = Main; path = Main;
sourceTree = "<group>"; sourceTree = "<group>";
@ -527,7 +555,7 @@
); );
name = PassepartoutUITests; name = PassepartoutUITests;
packageProductDependencies = ( packageProductDependencies = (
0E2D68DB2CF7CF7500DC95BC /* UITesting */, 0EA6340B2D088C8200180D7C /* UIAccessibility */,
); );
productName = PassepartoutUITests; productName = PassepartoutUITests;
productReference = 0E78FE4C2CF799F400B0C5BF /* PassepartoutUITests.xctest */; productReference = 0E78FE4C2CF799F400B0C5BF /* PassepartoutUITests.xctest */;
@ -742,11 +770,15 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
0E08447C2CF86F2A00ECED7C /* XCTestCase+Extensions.swift in Sources */, 0E08447C2CF86F2A00ECED7C /* XCTestCase+Extensions.swift in Sources */,
0EA6340E2D08995800180D7C /* ScreenshotTests.swift in Sources */,
0EA634122D08997300180D7C /* FlowTests.swift in Sources */,
0E7F460E2CF7F01600B1C53A /* FlowTests.swift in Sources */, 0E7F460E2CF7F01600B1C53A /* FlowTests.swift in Sources */,
0E916B782CF80FD60072921A /* ProfileEditorScreen.swift in Sources */, 0E916B782CF80FD60072921A /* ProfileEditorScreen.swift in Sources */,
0EA634142D08998700180D7C /* AppScreen.swift in Sources */,
0E916B7C2CF811EB0072921A /* XCUIElement+Extensions.swift in Sources */, 0E916B7C2CF811EB0072921A /* XCUIElement+Extensions.swift in Sources */,
0EAD6A1B2CF7F79A00CC1F02 /* ScreenshotTests.swift in Sources */, 0EAD6A1B2CF7F79A00CC1F02 /* ScreenshotTests.swift in Sources */,
0E7F460F2CF7F01600B1C53A /* XCUIApplication+Extensions.swift in Sources */, 0E7F460F2CF7F01600B1C53A /* XCUIApplication+Extensions.swift in Sources */,
0E418AB52D074F0E00D33D47 /* VPNServersScreen.swift in Sources */,
0EC418D22CF86B7400AC6F2F /* ProfileMenuScreen.swift in Sources */, 0EC418D22CF86B7400AC6F2F /* ProfileMenuScreen.swift in Sources */,
0E7F46122CF7F44C00B1C53A /* AppScreen.swift in Sources */, 0E7F46122CF7F44C00B1C53A /* AppScreen.swift in Sources */,
); );
@ -1349,10 +1381,6 @@
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCSwiftPackageProductDependency section */ /* Begin XCSwiftPackageProductDependency section */
0E2D68DB2CF7CF7500DC95BC /* UITesting */ = {
isa = XCSwiftPackageProductDependency;
productName = UITesting;
};
0E3E22952CE53510005135DF /* AppUIMain */ = { 0E3E22952CE53510005135DF /* AppUIMain */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
productName = AppUIMain; productName = AppUIMain;
@ -1373,6 +1401,10 @@
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
productName = TunnelLibrary; productName = TunnelLibrary;
}; };
0EA6340B2D088C8200180D7C /* UIAccessibility */ = {
isa = XCSwiftPackageProductDependency;
productName = UIAccessibility;
};
0EBE80DB2BF55C0E00E36A20 /* TunnelLibrary */ = { 0EBE80DB2BF55C0E00E36A20 /* TunnelLibrary */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
productName = TunnelLibrary; productName = TunnelLibrary;

View File

@ -85,7 +85,7 @@
</CommandLineArgument> </CommandLineArgument>
<CommandLineArgument <CommandLineArgument
argument = "-pp_ui_testing" argument = "-pp_ui_testing"
isEnabled = "NO"> isEnabled = "YES">
</CommandLineArgument> </CommandLineArgument>
<CommandLineArgument <CommandLineArgument
argument = "-pp_fake_migration" argument = "-pp_fake_migration"

View File

@ -26,8 +26,8 @@
import CommonLibrary import CommonLibrary
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
import UIAccessibility
import UILibrary import UILibrary
import UITesting
@MainActor @MainActor
final class AppDelegate: NSObject { final class AppDelegate: NSObject {

View File

@ -32,8 +32,8 @@ import CommonUtils
import Foundation import Foundation
import LegacyV2 import LegacyV2
import PassepartoutKit import PassepartoutKit
import UIAccessibility
import UILibrary import UILibrary
import UITesting
extension AppContext { extension AppContext {
static let shared: AppContext = { static let shared: AppContext = {

View File

@ -48,7 +48,10 @@ extension AppContext {
) )
let tunnelEnvironment = InMemoryEnvironment() let tunnelEnvironment = InMemoryEnvironment()
let tunnel = ExtendedTunnel( let tunnel = ExtendedTunnel(
tunnel: Tunnel(strategy: FakeTunnelStrategy(environment: tunnelEnvironment)), tunnel: Tunnel(strategy: FakeTunnelStrategy(
environment: tunnelEnvironment,
dataCountDelta: DataCount(2 * 1200000, 1200000)
)),
environment: tunnelEnvironment, environment: tunnelEnvironment,
processor: processor, processor: processor,
interval: Constants.shared.tunnel.refreshInterval interval: Constants.shared.tunnel.refreshInterval

View File

@ -52,10 +52,47 @@ extension ProfileManager {
assert((moduleBuilder as? any ProviderSelecting)?.providerId == parameters.providerId) assert((moduleBuilder as? any ProviderSelecting)?.providerId == parameters.providerId)
} }
if parameters.name == "Hide.me", if parameters.name == "Hide.me" {
var ovpnBuilder = moduleBuilder as? OpenVPNModule.Builder { if var ovpnBuilder = moduleBuilder as? OpenVPNModule.Builder {
#if !os(tvOS)
ovpnBuilder.isInteractive = true ovpnBuilder.isInteractive = true
#endif
ovpnBuilder.providerEntity = mockHideMeEntity
moduleBuilder = ovpnBuilder moduleBuilder = ovpnBuilder
} else if var onDemandBuilder = moduleBuilder as? OnDemandModule.Builder {
#if !os(tvOS)
onDemandBuilder.isEnabled = true
#endif
onDemandBuilder.policy = .excluding
onDemandBuilder.withSSIDs = [
"Friend's House": false,
"My Home Network": true,
"Safe Wi-Fi": true
]
moduleBuilder = onDemandBuilder
} else if var dnsBuilder = moduleBuilder as? DNSModule.Builder {
dnsBuilder.protocolType = .https
dnsBuilder.dohURL = "https://cloudflare-dns.com/dns-query"
dnsBuilder.servers = ["1.1.1.1", "1.0.0.1"]
dnsBuilder.domainName = "my-domain.com"
dnsBuilder.searchDomains = ["search-one.com", "search-two.org"]
moduleBuilder = dnsBuilder
}
}
if parameters.name == "My VPS" {
if var ovpnBuilder = moduleBuilder as? OpenVPNModule.Builder {
var cfgBuilder = OpenVPN.Configuration.Builder()
cfgBuilder.ca = .init(pem: "...")
cfgBuilder.remotes = [
ExtendedEndpoint(rawValue: "1.2.3.4:UDP:1234")!
]
ovpnBuilder.configurationBuilder = cfgBuilder
moduleBuilder = ovpnBuilder
} else if var onDemandBuilder = moduleBuilder as? OnDemandModule.Builder {
onDemandBuilder.isEnabled = true
moduleBuilder = onDemandBuilder
}
} }
let module = try moduleBuilder.tryBuild() let module = try moduleBuilder.tryBuild()
@ -99,9 +136,47 @@ private extension ProfileManager {
static let mockParameters: [Parameters] = [ static let mockParameters: [Parameters] = [
Parameters("CloudFlare DoT", false, false, [.dns]), Parameters("CloudFlare DoT", false, false, [.dns]),
Parameters("Coffee VPN", true, false, [.wireGuard, .onDemand]), Parameters("Coffee VPN", true, false, [.wireGuard, .onDemand]),
Parameters("Hide.me", true, true, [.openVPN, .onDemand, .ip], .hideme), Parameters("Hide.me", true, true, [.openVPN, .onDemand, .dns, .ip], .hideme),
Parameters("My VPS", true, true, [.openVPN]), Parameters("My VPS", true, true, [.openVPN, .onDemand]),
Parameters("Office", true, false, [.onDemand, .httpProxy]), Parameters("Office", true, false, [.onDemand, .httpProxy]),
Parameters("Personal DoH", false, false, [.dns, .onDemand]) Parameters("Personal DoH", false, false, [.dns, .onDemand])
] ]
static var mockHideMeEntity: VPNEntity<OpenVPN.Configuration> {
do {
var cfgBuilder = OpenVPN.Configuration.Builder()
cfgBuilder.ca = .init(pem: "...")
let cfg = try cfgBuilder.tryBuild(isClient: false)
let cfgData = try JSONEncoder().encode(cfg)
let preset = AnyVPNPreset(
providerId: .hideme,
presetId: "default",
description: "Default",
endpoints: [.init(.udp, 1194)],
configurationIdentifier: "OpenVPN",
configuration: cfgData
)
return VPNEntity(
server: .init(
provider: .init(
id: .hideme,
serverId: "be-v4",
supportedConfigurationIdentifiers: ["OpenVPN"],
supportedPresetIds: nil,
categoryName: "",
countryCode: "BE",
otherCountryCodes: nil,
area: nil
),
hostname: "be-v4.hideservers.net",
ipAddresses: nil
),
preset: try preset.ofType(OpenVPN.Configuration.self)
)
} catch {
fatalError("Unable to build Hide.me entity: \(error)")
}
}
} }

View File

@ -27,7 +27,7 @@
import AppUIMain import AppUIMain
import SwiftUI import SwiftUI
import UITesting import UIAccessibility
extension AppDelegate: UIApplicationDelegate { extension AppDelegate: UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {

View File

@ -31,7 +31,7 @@ import CommonLibrary
import CommonUtils import CommonUtils
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
import UITesting import UIAccessibility
extension AppDelegate: NSApplicationDelegate { extension AppDelegate: NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) { func applicationDidFinishLaunching(_ notification: Notification) {

View File

@ -27,7 +27,7 @@
import AppUITV import AppUITV
import SwiftUI import SwiftUI
import UITesting import UIAccessibility
extension AppDelegate: UIApplicationDelegate { extension AppDelegate: UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {

View File

@ -24,7 +24,7 @@
// //
import Foundation import Foundation
import UITesting import UIAccessibility
import XCTest import XCTest
protocol XCUIApplicationProviding { protocol XCUIApplicationProviding {

View File

@ -24,7 +24,7 @@
// //
import Foundation import Foundation
import UITesting import UIAccessibility
import XCTest import XCTest
extension XCUIElement { extension XCUIElement {
@ -44,14 +44,14 @@ private extension XCUIElement {
func query(for elementType: AccessibilityInfo.ElementType) -> XCUIElementQuery { func query(for elementType: AccessibilityInfo.ElementType) -> XCUIElementQuery {
#if os(iOS) #if os(iOS)
switch elementType { switch elementType {
case .button, .menu, .menuItem: case .button, .link, .menu, .menuItem:
return buttons return buttons
case .text: case .text:
return staticTexts return staticTexts
} }
#else #elseif os(macOS)
switch elementType { switch elementType {
case .button: case .button, .link:
return buttons return buttons
case .menu: case .menu:
return menuButtons return menuButtons
@ -60,6 +60,13 @@ private extension XCUIElement {
case .text: case .text:
return staticTexts return staticTexts
} }
#else
switch elementType {
case .button, .link, .menu, .menuItem:
return buttons
case .text:
return staticTexts
}
#endif #endif
} }
} }

View File

@ -23,7 +23,8 @@
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>. // along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
// //
import UITesting import Foundation
import UIAccessibility
import XCTest import XCTest
@MainActor @MainActor
@ -41,23 +42,42 @@ final class FlowTests: XCTestCase {
try await Task.sleep(for: .seconds(2)) try await Task.sleep(for: .seconds(2))
} }
func testMainConnected() async throws { func testConnect() {
AppScreen(app: app) AppScreen(app: app)
.waitForProfiles() .waitForProfiles()
.enableProfile(at: 2) .enableProfile(at: 2)
} }
func testEditProfile() async throws { func testEditProfile() {
AppScreen(app: app) AppScreen(app: app)
.waitForProfiles() .waitForProfiles()
.openProfileMenu(at: 2) .openProfileMenu(at: 2)
.editProfile() .editProfile()
} }
func testConnectToProviderServer() async throws { func testEditProfileModule() {
AppScreen(app: app)
.waitForProfiles()
.openProfileMenu(at: 2)
.editProfile()
.enterModule(at: 1)
.leaveModule()
}
func testConnectToProviderServer() {
AppScreen(app: app) AppScreen(app: app)
.waitForProfiles() .waitForProfiles()
.openProfileMenu(at: 2) .openProfileMenu(at: 2)
.connectToProfile() .connectToProfile()
} }
#if os(iOS)
func testDiscloseProviderCountry() {
AppScreen(app: app)
.waitForProfiles()
.openProfileMenu(at: 2)
.connectToProfile()
.discloseCountry(at: 2)
}
#endif
} }

View File

@ -24,7 +24,7 @@
// //
import Foundation import Foundation
import UITesting import UIAccessibility
import XCTest import XCTest
@MainActor @MainActor

View File

@ -0,0 +1,57 @@
//
// ProfileEditorScreen.swift
// Passepartout
//
// Created by Davide De Rosa on 11/28/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import UIAccessibility
import XCTest
@MainActor
struct ProfileEditorScreen {
let app: XCUIApplication
@discardableResult
func enterModule(at index: Int) -> Self {
let moduleLink = app.get(.Profile.moduleLink, at: index)
moduleLink.tap()
return self
}
@discardableResult
func leaveModule() -> Self {
#if os(iOS)
let backButton = app.navigationBars.element(boundBy: 1).buttons.element(boundBy: 0)
XCTAssertTrue(backButton.waitForExistence(timeout: 1.0))
backButton.tap()
#endif
return self
}
@discardableResult
func closeProfile() -> AppScreen {
let cancelButton = app.get(.Profile.cancel)
cancelButton.tap()
return AppScreen(app: app)
}
}

View File

@ -24,7 +24,7 @@
// //
import Foundation import Foundation
import UITesting import UIAccessibility
import XCTest import XCTest
@MainActor @MainActor
@ -32,16 +32,17 @@ struct ProfileMenuScreen {
let app: XCUIApplication let app: XCUIApplication
@discardableResult @discardableResult
func editProfile() -> ProfileEditorScreen { func connectToProfile() -> VPNServersScreen {
let editButton = app.get(.App.ProfileMenu.edit) let connectToButton = app.get(.App.ProfileMenu.connectTo)
editButton.tap() connectToButton.tap()
return ProfileEditorScreen(app: app) return VPNServersScreen(app: app)
} }
@discardableResult @discardableResult
func connectToProfile() -> AppScreen { func editProfile() -> ProfileEditorScreen {
let connectToButton = app.get(.App.ProfileMenu.connectTo) let editButton = app.get(.App.ProfileMenu.edit)
connectToButton.tap() editButton.tap()
return AppScreen(app: app) _ = app.get(.Profile.name)
return ProfileEditorScreen(app: app)
} }
} }

View File

@ -1,8 +1,8 @@
// //
// ProfileEditorScreen.swift // VPNServersScreen.swift
// Passepartout // Passepartout
// //
// Created by Davide De Rosa on 11/28/24. // Created by Davide De Rosa on 12/9/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved. // Copyright (c) 2024 Davide De Rosa. All rights reserved.
// //
// https://github.com/passepartoutvpn // https://github.com/passepartoutvpn
@ -24,17 +24,17 @@
// //
import Foundation import Foundation
import UITesting import UIAccessibility
import XCTest import XCTest
@MainActor @MainActor
struct ProfileEditorScreen { struct VPNServersScreen {
let app: XCUIApplication let app: XCUIApplication
@discardableResult @discardableResult
func closeProfile() -> AppScreen { func discloseCountry(at index: Int) -> Self {
let cancelButton = app.get(.Profile.cancel) let group = app.get(.VPNServers.countryGroup, at: index)
cancelButton.tap() group.tap()
return AppScreen(app: app) return self
} }
} }

View File

@ -23,7 +23,8 @@
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>. // along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
// //
import UITesting import Foundation
import UIAccessibility
import XCTest import XCTest
@MainActor @MainActor
@ -50,7 +51,7 @@ final class ScreenshotTests: XCTestCase, XCUIApplicationProviding {
.enableProfile(at: 0) .enableProfile(at: 0)
try await Task.sleep(for: .seconds(2)) try await Task.sleep(for: .seconds(2))
try snapshot("1_MainConnected") try snapshot("1_Connected")
let profile = root let profile = root
.openProfileMenu(at: 2) .openProfileMenu(at: 2)
@ -60,12 +61,29 @@ final class ScreenshotTests: XCTestCase, XCUIApplicationProviding {
try snapshot("2_ProfileEditor", target: .sheet) try snapshot("2_ProfileEditor", target: .sheet)
profile profile
.enterModule(at: 1)
try await Task.sleep(for: .seconds(2))
try snapshot("3_OnDemand", target: .sheet)
profile
.leaveModule()
.enterModule(at: 2)
try await Task.sleep(for: .seconds(2))
try snapshot("4_DNS", target: .sheet)
profile
.leaveModule()
.closeProfile() .closeProfile()
.openProfileMenu(at: 2) .openProfileMenu(at: 2)
.connectToProfile() .connectToProfile()
#if os(iOS)
.discloseCountry(at: 2)
#endif
try await Task.sleep(for: .seconds(2)) try await Task.sleep(for: .seconds(2))
try snapshot("3_ProviderServers", target: .sheet) try snapshot("5_ProviderServers", target: .sheet)
print("Saved to: \(ScreenshotDestination.temporary.url)") print("Saved to: \(ScreenshotDestination.temporary.url)")
} }

View File

@ -0,0 +1,71 @@
//
// FlowTests.swift
// Passepartout
//
// Created by Davide De Rosa on 12/10/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import UIAccessibility
import XCTest
@MainActor
final class FlowTests: XCTestCase {
private var app: XCUIApplication!
override func setUp() async throws {
continueAfterFailure = false
app = XCUIApplication()
app.appArguments = [.uiTesting]
app.launch()
}
override func tearDown() async throws {
try await Task.sleep(for: .seconds(2))
}
func testShow() {
AppScreen(app: app)
.waitForProfiles()
}
func testPresentProfiles() {
AppScreen(app: app)
.waitForProfiles()
.presentInitialProfiles()
}
func testConnect() {
AppScreen(app: app)
.waitForProfiles()
.presentInitialProfiles()
.enableProfile(up: 1)
}
func testReconnectToOtherProfile() {
AppScreen(app: app)
.waitForProfiles()
.presentInitialProfiles()
.enableProfile(up: 1)
.presentProfilesWhileConnected()
.enableProfile(up: 0)
}
}

View File

@ -0,0 +1,66 @@
//
// AppScreen.swift
// Passepartout
//
// Created by Davide De Rosa on 12/10/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import UIAccessibility
import XCTest
@MainActor
struct AppScreen {
let app: XCUIApplication
private let remote: XCUIRemote = .shared
@discardableResult
func waitForProfiles() -> Self {
app.get(.App.installedProfile)
return self
}
@discardableResult
func presentInitialProfiles() -> Self {
remote.press(.down)
return self
}
@discardableResult
func enableProfile(up: Int) -> Self {
let profileButton = app.get(.App.ProfileList.profile)
remote.press(.right)
for _ in 0..<up {
remote.press(.up)
}
remote.press(.select)
profileButton.waitForNonExistence(timeout: 1.0)
return self
}
@discardableResult
func presentProfilesWhileConnected() -> Self {
remote.press(.down)
_ = app.get(.App.ProfileList.profile)
return self
}
}

View File

@ -0,0 +1,66 @@
//
// ScreenshotTests.swift
// Passepartout
//
// Created by Davide De Rosa on 12/10/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import UIAccessibility
import XCTest
@MainActor
final class ScreenshotTests: XCTestCase, XCUIApplicationProviding {
let app: XCUIApplication = {
let app = XCUIApplication()
app.appArguments = [.uiTesting]
return app
}()
override func setUp() async throws {
continueAfterFailure = false
app.launch()
}
func testTakeScreenshots() async throws {
let root = AppScreen(app: app)
.waitForProfiles()
.presentInitialProfiles()
.enableProfile(up: 1)
try await Task.sleep(for: .seconds(2))
try snapshot("1_Connected")
root
.presentProfilesWhileConnected()
try await Task.sleep(for: .seconds(2))
try snapshot("2_ConnectedWithProfileList")
root
.enableProfile(up: 0)
try await Task.sleep(for: .seconds(2))
try snapshot("3_OnDemand")
print("Saved to: \(ScreenshotDestination.temporary.url)")
}
}