Autogenerate framed screenshots from HTML/CSS (#1000)
Add TV screenshots and organize PassepartoutUITests with two test plans for generating iOS/macOS (Main) and tvOS (TV) screenshots. Revert to the .attachment destination and use `xcparse` to export the screenshots. Change iPad screenshots to portrait. Then autogenerate framed screenshots in two steps: - Export the UITests screenshots per device (`export.sh`) - Embed the results in a HTML/CSS template and take snapshots with Chrome headless (`compose.sh`) - Repeat for all devices (iPhone, iPad, Mac and Apple TV) - Save framed screenshots to the `fastlane` screenshots directory
This commit is contained in:
parent
ff88b3562d
commit
76a570b7b3
|
@ -10,9 +10,10 @@ fastlane/**/review_information
|
|||
fastlane/**/trade_representative_contact_information
|
||||
build/
|
||||
dist/
|
||||
/iap
|
||||
templates/
|
||||
vendor/
|
||||
/iap/
|
||||
screenshots/html/*/0*.png
|
||||
screenshots/results/
|
||||
/templates/
|
||||
Preview.html
|
||||
default.profraw
|
||||
.api
|
||||
|
|
|
@ -58,7 +58,7 @@ GEM
|
|||
artifactory (3.0.17)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.3.0)
|
||||
aws-partitions (1.1019.0)
|
||||
aws-partitions (1.1020.0)
|
||||
aws-sdk-core (3.214.0)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.992.0)
|
||||
|
@ -185,10 +185,10 @@ GEM
|
|||
nanaimo (0.4.0)
|
||||
naturally (2.2.1)
|
||||
nkf (0.2.0)
|
||||
nokogiri (1.17.0)
|
||||
nokogiri (1.17.1)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.17.0-arm64-darwin)
|
||||
nokogiri (1.17.1-arm64-darwin)
|
||||
racc (~> 1.4)
|
||||
optparse (0.6.0)
|
||||
os (1.1.4)
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
/* Begin PBXBuildFile section */
|
||||
0E08447C2CF86F2A00ECED7C /* XCTestCase+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E08447B2CF86F2A00ECED7C /* XCTestCase+Extensions.swift */; };
|
||||
0E2267862D0A059B0000B557 /* MainScreenshots.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = 0E2267852D0A059B0000B557 /* MainScreenshots.xctestplan */; platformFilters = (ios, macos, ); };
|
||||
0E2267882D0A05D20000B557 /* TVScreenshots.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = 0E2267872D0A05D20000B557 /* TVScreenshots.xctestplan */; platformFilters = (tvos, ); };
|
||||
0E3E22962CE53510005135DF /* AppUIMain in Frameworks */ = {isa = PBXBuildFile; platformFilters = (ios, macos, ); productRef = 0E3E22952CE53510005135DF /* AppUIMain */; };
|
||||
0E3E22982CE53510005135DF /* AppUITV in Frameworks */ = {isa = PBXBuildFile; platformFilters = (tvos, ); productRef = 0E3E22972CE53510005135DF /* AppUITV */; };
|
||||
0E3FF4BA2CE3AFBC00BFF640 /* Profiles.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = 0E3FF4B72CE3AFBC00BFF640 /* Profiles.sqlite */; };
|
||||
|
@ -23,7 +25,7 @@
|
|||
0E7C3CCD2C9AF44600B72E69 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7C3CCC2C9AF44600B72E69 /* AppDelegate.swift */; };
|
||||
0E7E3D692B9345FD002BBDB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E7E3D5C2B9345FD002BBDB4 /* Assets.xcassets */; };
|
||||
0E7E3D6B2B9345FD002BBDB4 /* PassepartoutApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E3D5F2B9345FD002BBDB4 /* PassepartoutApp.swift */; };
|
||||
0E7F460E2CF7F01600B1C53A /* FlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F460B2CF7F01600B1C53A /* FlowTests.swift */; platformFilters = (ios, macos, ); };
|
||||
0E7F460E2CF7F01600B1C53A /* MainFlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F460B2CF7F01600B1C53A /* MainFlowTests.swift */; platformFilters = (ios, macos, ); };
|
||||
0E7F460F2CF7F01600B1C53A /* XCUIApplication+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7F460C2CF7F01600B1C53A /* XCUIApplication+Extensions.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 */; };
|
||||
|
@ -40,10 +42,10 @@
|
|||
0E916B7C2CF811EB0072921A /* XCUIElement+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E916B7B2CF811EB0072921A /* XCUIElement+Extensions.swift */; };
|
||||
0E94EE582B93554B00588243 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7E3D672B9345FD002BBDB4 /* PacketTunnelProvider.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, ); };
|
||||
0EA6340E2D08995800180D7C /* TVScreenshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA6340D2D08995800180D7C /* TVScreenshotTests.swift */; platformFilters = (tvos, ); };
|
||||
0EA634122D08997300180D7C /* TVFlowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA634112D08997300180D7C /* TVFlowTests.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, ); };
|
||||
0EAD6A1B2CF7F79A00CC1F02 /* MainScreenshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAD6A1A2CF7F79A00CC1F02 /* MainScreenshotTests.swift */; platformFilters = (ios, macos, ); };
|
||||
0EAEC8A92D05DB8D001AA50C /* DefaultAppProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAEC8A62D05DB8D001AA50C /* DefaultAppProcessor.swift */; };
|
||||
0EAEC8AA2D05DB8D001AA50C /* DefaultTunnelProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EAEC8A72D05DB8D001AA50C /* DefaultTunnelProcessor.swift */; };
|
||||
0EB08B982CA46F4900A02591 /* AppPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0EB08B962CA46F4900A02591 /* AppPlist.strings */; };
|
||||
|
@ -143,6 +145,8 @@
|
|||
/* Begin PBXFileReference section */
|
||||
0E06D18F2B87629100176E1D /* Passepartout.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Passepartout.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0E08447B2CF86F2A00ECED7C /* XCTestCase+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCTestCase+Extensions.swift"; sourceTree = "<group>"; };
|
||||
0E2267852D0A059B0000B557 /* MainScreenshots.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = MainScreenshots.xctestplan; sourceTree = "<group>"; };
|
||||
0E2267872D0A05D20000B557 /* TVScreenshots.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = TVScreenshots.xctestplan; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
0E3FF4B92CE3AFBC00BFF640 /* MigrationManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationManagerTests.swift; sourceTree = "<group>"; };
|
||||
|
@ -163,7 +167,7 @@
|
|||
0E7E3D5F2B9345FD002BBDB4 /* PassepartoutApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassepartoutApp.swift; sourceTree = "<group>"; };
|
||||
0E7E3D662B9345FD002BBDB4 /* Tunnel.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Tunnel.entitlements; sourceTree = "<group>"; };
|
||||
0E7E3D672B9345FD002BBDB4 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = "<group>"; };
|
||||
0E7F460B2CF7F01600B1C53A /* FlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowTests.swift; sourceTree = "<group>"; };
|
||||
0E7F460B2CF7F01600B1C53A /* MainFlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlowTests.swift; sourceTree = "<group>"; };
|
||||
0E7F460C2CF7F01600B1C53A /* XCUIApplication+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIApplication+Extensions.swift"; sourceTree = "<group>"; };
|
||||
0E7F46102CF7F44C00B1C53A /* AppScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScreen.swift; sourceTree = "<group>"; };
|
||||
0E8195592CFDA75200CC8FFD /* Dependencies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dependencies.swift; sourceTree = "<group>"; };
|
||||
|
@ -176,10 +180,10 @@
|
|||
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>"; };
|
||||
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>"; };
|
||||
0EA6340D2D08995800180D7C /* TVScreenshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVScreenshotTests.swift; sourceTree = "<group>"; };
|
||||
0EA634112D08997300180D7C /* TVFlowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVFlowTests.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 /* MainScreenshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainScreenshotTests.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>"; };
|
||||
0EB08B972CA46F4900A02591 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AppPlist.strings; sourceTree = "<group>"; };
|
||||
|
@ -309,8 +313,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
0EA634102D08996500180D7C /* Screens */,
|
||||
0EA634112D08997300180D7C /* FlowTests.swift */,
|
||||
0EA6340D2D08995800180D7C /* ScreenshotTests.swift */,
|
||||
0EA634112D08997300180D7C /* TVFlowTests.swift */,
|
||||
0EA6340D2D08995800180D7C /* TVScreenshotTests.swift */,
|
||||
);
|
||||
path = TV;
|
||||
sourceTree = "<group>";
|
||||
|
@ -386,6 +390,8 @@
|
|||
0E916B7A2CF811DE0072921A /* Extensions */,
|
||||
0EC418C92CF81C6A00AC6F2F /* Main */,
|
||||
0E418AB72D0752D100D33D47 /* TV */,
|
||||
0E2267852D0A059B0000B557 /* MainScreenshots.xctestplan */,
|
||||
0E2267872D0A05D20000B557 /* TVScreenshots.xctestplan */,
|
||||
);
|
||||
path = UITests;
|
||||
sourceTree = "<group>";
|
||||
|
@ -445,8 +451,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
0E916B7E2CF81A110072921A /* Screens */,
|
||||
0E7F460B2CF7F01600B1C53A /* FlowTests.swift */,
|
||||
0EAD6A1A2CF7F79A00CC1F02 /* ScreenshotTests.swift */,
|
||||
0E7F460B2CF7F01600B1C53A /* MainFlowTests.swift */,
|
||||
0EAD6A1A2CF7F79A00CC1F02 /* MainScreenshotTests.swift */,
|
||||
);
|
||||
path = Main;
|
||||
sourceTree = "<group>";
|
||||
|
@ -685,6 +691,8 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E2267862D0A059B0000B557 /* MainScreenshots.xctestplan in Resources */,
|
||||
0E2267882D0A05D20000B557 /* TVScreenshots.xctestplan in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -770,13 +778,13 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0E08447C2CF86F2A00ECED7C /* XCTestCase+Extensions.swift in Sources */,
|
||||
0EA6340E2D08995800180D7C /* ScreenshotTests.swift in Sources */,
|
||||
0EA634122D08997300180D7C /* FlowTests.swift in Sources */,
|
||||
0E7F460E2CF7F01600B1C53A /* FlowTests.swift in Sources */,
|
||||
0EA6340E2D08995800180D7C /* TVScreenshotTests.swift in Sources */,
|
||||
0EA634122D08997300180D7C /* TVFlowTests.swift in Sources */,
|
||||
0E7F460E2CF7F01600B1C53A /* MainFlowTests.swift in Sources */,
|
||||
0E916B782CF80FD60072921A /* ProfileEditorScreen.swift in Sources */,
|
||||
0EA634142D08998700180D7C /* AppScreen.swift in Sources */,
|
||||
0E916B7C2CF811EB0072921A /* XCUIElement+Extensions.swift in Sources */,
|
||||
0EAD6A1B2CF7F79A00CC1F02 /* ScreenshotTests.swift in Sources */,
|
||||
0EAD6A1B2CF7F79A00CC1F02 /* MainScreenshotTests.swift in Sources */,
|
||||
0E7F460F2CF7F01600B1C53A /* XCUIApplication+Extensions.swift in Sources */,
|
||||
0E418AB52D074F0E00D33D47 /* VPNServersScreen.swift in Sources */,
|
||||
0EC418D22CF86B7400AC6F2F /* ProfileMenuScreen.swift in Sources */,
|
||||
|
|
|
@ -22,8 +22,15 @@
|
|||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<TestPlans>
|
||||
<TestPlanReference
|
||||
reference = "container:Passepartout/UITests/TVScreenshots.xctestplan">
|
||||
</TestPlanReference>
|
||||
<TestPlanReference
|
||||
reference = "container:Passepartout/UITests/MainScreenshots.xctestplan">
|
||||
</TestPlanReference>
|
||||
</TestPlans>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
|
@ -55,15 +62,6 @@
|
|||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0E78FE4B2CF799F400B0C5BF"
|
||||
BuildableName = "PassepartoutUITests.xctest"
|
||||
BlueprintName = "PassepartoutUITests"
|
||||
ReferencedContainer = "container:Passepartout.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
|
|
|
@ -59,8 +59,9 @@ extension XCUIApplicationProviding where Self: XCTestCase {
|
|||
}
|
||||
|
||||
func snapshot(
|
||||
_ name: String,
|
||||
destination: ScreenshotDestination = .temporary,
|
||||
_ index: String,
|
||||
_ title: String,
|
||||
destination: ScreenshotDestination = .attachment,
|
||||
target: ScreenshotTarget = .window
|
||||
) throws {
|
||||
let container = container(for: target)
|
||||
|
@ -69,26 +70,26 @@ extension XCUIApplicationProviding where Self: XCTestCase {
|
|||
switch destination {
|
||||
case .attachment:
|
||||
let attachment = XCTAttachment(screenshot: screenshot)
|
||||
attachment.name = name
|
||||
attachment.name = index
|
||||
attachment.lifetime = .keepAlways
|
||||
add(attachment)
|
||||
|
||||
case .temporary:
|
||||
let filename = deviceFilename(for: name)
|
||||
let filename = deviceFilename(for: index)
|
||||
let url = URL(fileURLWithPath: filename, relativeTo: destination.url)
|
||||
try screenshot.pngRepresentation.write(to: url)
|
||||
}
|
||||
}
|
||||
|
||||
private func deviceFilename(for name: String) -> String {
|
||||
private func deviceFilename(for index: String) -> String {
|
||||
#if os(iOS)
|
||||
let device = UIDevice.current.userInterfaceIdiom == .pad ? "iPad" : "iPhone"
|
||||
let device = UIDevice.current.userInterfaceIdiom == .pad ? "ipad" : "iphone"
|
||||
#elseif os(macOS)
|
||||
let device = "Mac"
|
||||
let device = "mac"
|
||||
#elseif os(tvOS)
|
||||
let device = "AppleTV"
|
||||
let device = "appletv"
|
||||
#endif
|
||||
return "\(device)_\(name).png"
|
||||
return "\(device)_\(index).png"
|
||||
}
|
||||
|
||||
private func container(for target: ScreenshotTarget) -> XCUIElement {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// FlowTests.swift
|
||||
// MainFlowTests.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 11/27/24.
|
||||
|
@ -28,7 +28,7 @@ import UIAccessibility
|
|||
import XCTest
|
||||
|
||||
@MainActor
|
||||
final class FlowTests: XCTestCase {
|
||||
final class MainFlowTests: XCTestCase {
|
||||
private var app: XCUIApplication!
|
||||
|
||||
override func setUp() async throws {
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// ScreenshotTests.swift
|
||||
// MainScreenshotTests.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 11/28/24.
|
||||
|
@ -28,7 +28,7 @@ import UIAccessibility
|
|||
import XCTest
|
||||
|
||||
@MainActor
|
||||
final class ScreenshotTests: XCTestCase, XCUIApplicationProviding {
|
||||
final class MainScreenshotTests: XCTestCase, XCUIApplicationProviding {
|
||||
let app: XCUIApplication = {
|
||||
let app = XCUIApplication()
|
||||
app.appArguments = [.uiTesting]
|
||||
|
@ -40,7 +40,7 @@ final class ScreenshotTests: XCTestCase, XCUIApplicationProviding {
|
|||
app.launch()
|
||||
#if os(iOS)
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
XCUIDevice.shared.orientation = .landscapeLeft
|
||||
XCUIDevice.shared.orientation = .portrait
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -55,27 +55,27 @@ final class ScreenshotTests: XCTestCase, XCUIApplicationProviding {
|
|||
.editProfile()
|
||||
|
||||
await pause()
|
||||
try snapshot("02_ProfileEditor", target: .sheet)
|
||||
try snapshot("03", "ProfileEditor", target: .sheet)
|
||||
|
||||
profile
|
||||
.enterModule(at: 1)
|
||||
|
||||
await pause()
|
||||
try snapshot("03_OnDemand", target: .sheet)
|
||||
try snapshot("02", "OnDemand", target: .sheet)
|
||||
|
||||
profile
|
||||
.leaveModule()
|
||||
.enterModule(at: 2)
|
||||
|
||||
await pause()
|
||||
try snapshot("04_DNS", target: .sheet)
|
||||
try snapshot("04", "DNS", target: .sheet)
|
||||
|
||||
let app = profile
|
||||
.leaveModule()
|
||||
.closeProfile()
|
||||
|
||||
await pause()
|
||||
try snapshot("01_Connected")
|
||||
try snapshot("01", "Connected")
|
||||
|
||||
app
|
||||
.openProfileMenu(at: 2)
|
||||
|
@ -85,7 +85,7 @@ final class ScreenshotTests: XCTestCase, XCUIApplicationProviding {
|
|||
#endif
|
||||
|
||||
await pause()
|
||||
try snapshot("05_ProviderServers", target: .sheet)
|
||||
try snapshot("05", "ProviderServers", target: .sheet)
|
||||
|
||||
print("Saved to: \(ScreenshotDestination.temporary.url)")
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"configurations" : [
|
||||
{
|
||||
"id" : "30682571-78CE-444A-9D31-4E1310B4D166",
|
||||
"name" : "Configuration 1",
|
||||
"options" : {
|
||||
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultOptions" : {
|
||||
"testTimeoutsEnabled" : true
|
||||
},
|
||||
"testTargets" : [
|
||||
{
|
||||
"skippedTests" : [
|
||||
"FlowTests",
|
||||
"FlowTests\/testConnect()",
|
||||
"FlowTests\/testConnectToProviderServer()",
|
||||
"FlowTests\/testDiscloseProviderCountry()",
|
||||
"FlowTests\/testEditProfile()",
|
||||
"FlowTests\/testEditProfileModule()",
|
||||
"FlowTests\/testPresentProfiles()",
|
||||
"FlowTests\/testReconnectToOtherProfile()",
|
||||
"FlowTests\/testShow()",
|
||||
"MainFlowTests",
|
||||
"MainFlowTests\/testConnect()",
|
||||
"MainFlowTests\/testConnectToProviderServer()",
|
||||
"MainFlowTests\/testDiscloseProviderCountry()",
|
||||
"MainFlowTests\/testEditProfile()",
|
||||
"MainFlowTests\/testEditProfileModule()",
|
||||
"TVFlowTests",
|
||||
"TVFlowTests\/testConnect()",
|
||||
"TVFlowTests\/testPresentProfiles()",
|
||||
"TVFlowTests\/testReconnectToOtherProfile()",
|
||||
"TVFlowTests\/testShow()",
|
||||
"TVScreenshotTests",
|
||||
"TVScreenshotTests\/testTakeScreenshots()"
|
||||
],
|
||||
"target" : {
|
||||
"containerPath" : "container:Passepartout.xcodeproj",
|
||||
"identifier" : "0E78FE4B2CF799F400B0C5BF",
|
||||
"name" : "PassepartoutUITests"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 1
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// FlowTests.swift
|
||||
// TVFlowTests.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/10/24.
|
||||
|
@ -28,7 +28,7 @@ import UIAccessibility
|
|||
import XCTest
|
||||
|
||||
@MainActor
|
||||
final class FlowTests: XCTestCase {
|
||||
final class TVFlowTests: XCTestCase {
|
||||
private var app: XCUIApplication!
|
||||
|
||||
override func setUp() async throws {
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// ScreenshotTests.swift
|
||||
// TVScreenshotTests.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 12/10/24.
|
||||
|
@ -28,7 +28,7 @@ import UIAccessibility
|
|||
import XCTest
|
||||
|
||||
@MainActor
|
||||
final class ScreenshotTests: XCTestCase, XCUIApplicationProviding {
|
||||
final class TVScreenshotTests: XCTestCase, XCUIApplicationProviding {
|
||||
let app: XCUIApplication = {
|
||||
let app = XCUIApplication()
|
||||
app.appArguments = [.uiTesting]
|
||||
|
@ -47,19 +47,19 @@ final class ScreenshotTests: XCTestCase, XCUIApplicationProviding {
|
|||
.enableProfile(up: 1)
|
||||
|
||||
await pause()
|
||||
try snapshot("01_Connected")
|
||||
try snapshot("01", "Connected")
|
||||
|
||||
root
|
||||
.presentProfilesWhileConnected()
|
||||
|
||||
await pause()
|
||||
try snapshot("02_ConnectedWithProfileList")
|
||||
try snapshot("02", "ConnectedWithProfileList")
|
||||
|
||||
root
|
||||
.enableProfile(up: 0)
|
||||
|
||||
await pause()
|
||||
try snapshot("03_OnDemand")
|
||||
try snapshot("03", "OnDemand")
|
||||
|
||||
print("Saved to: \(ScreenshotDestination.temporary.url)")
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"configurations" : [
|
||||
{
|
||||
"id" : "00256A48-9D59-44ED-822A-B35521760457",
|
||||
"name" : "Configuration 1",
|
||||
"options" : {
|
||||
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultOptions" : {
|
||||
"testTimeoutsEnabled" : true
|
||||
},
|
||||
"testTargets" : [
|
||||
{
|
||||
"skippedTests" : [
|
||||
"FlowTests",
|
||||
"FlowTests\/testConnect()",
|
||||
"FlowTests\/testConnectToProviderServer()",
|
||||
"FlowTests\/testEditProfile()",
|
||||
"FlowTests\/testEditProfileModule()",
|
||||
"FlowTests\/testPresentProfiles()",
|
||||
"FlowTests\/testReconnectToOtherProfile()",
|
||||
"FlowTests\/testShow()",
|
||||
"MainFlowTests",
|
||||
"MainFlowTests\/testConnect()",
|
||||
"MainFlowTests\/testConnectToProviderServer()",
|
||||
"MainFlowTests\/testDiscloseProviderCountry()",
|
||||
"MainFlowTests\/testEditProfile()",
|
||||
"MainFlowTests\/testEditProfileModule()",
|
||||
"MainScreenshotTests",
|
||||
"MainScreenshotTests\/testTakeScreenshots()",
|
||||
"ScreenshotTests",
|
||||
"TVFlowTests",
|
||||
"TVFlowTests\/testConnect()",
|
||||
"TVFlowTests\/testPresentProfiles()",
|
||||
"TVFlowTests\/testReconnectToOtherProfile()",
|
||||
"TVFlowTests\/testShow()"
|
||||
],
|
||||
"target" : {
|
||||
"containerPath" : "container:Passepartout.xcodeproj",
|
||||
"identifier" : "0E78FE4B2CF799F400B0C5BF",
|
||||
"name" : "PassepartoutUITests"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 1
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
cwd=`dirname $0`
|
||||
devices=("iphone ipad mac appletv")
|
||||
for device in $devices; do
|
||||
$cwd/../screenshots/export.sh $device
|
||||
$cwd/../screenshots/compose-device.sh $device
|
||||
done
|
|
@ -0,0 +1,49 @@
|
|||
#!/bin/bash
|
||||
cwd=`dirname $0`
|
||||
device=$1
|
||||
compose_cmd="$cwd/compose.sh"
|
||||
fastlane_screenshots_root="$cwd/../fastlane/screenshots"
|
||||
|
||||
case $device in
|
||||
|
||||
"iphone")
|
||||
nums=("01 02 03 04 05")
|
||||
template="main"
|
||||
width=1242
|
||||
height=2688
|
||||
fastlane="iOS"
|
||||
;;
|
||||
|
||||
"ipad")
|
||||
nums=("01 02 03 04 05")
|
||||
template="main"
|
||||
width=2048
|
||||
height=2732
|
||||
fastlane="iOS"
|
||||
;;
|
||||
|
||||
"mac")
|
||||
nums=("01 02 03 04 05")
|
||||
template="main"
|
||||
width=2880
|
||||
height=1800
|
||||
fastlane="macOS"
|
||||
;;
|
||||
|
||||
"appletv")
|
||||
nums=("01 02 03")
|
||||
template="tv"
|
||||
width=3840
|
||||
height=2160
|
||||
fastlane="tvOS"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown device: $device"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
for num in $nums; do
|
||||
$compose_cmd $template $device $num $width $height "$fastlane_screenshots_root/$fastlane/en-US"
|
||||
done
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/bash
|
||||
cwd=`dirname $0`
|
||||
chrome_app="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
|
||||
# e.g.: <self> main iphone 01 1242 2688 "fastlane/screenshots/iOS"
|
||||
|
||||
template=$1
|
||||
device=$2
|
||||
num=$3
|
||||
width=$4
|
||||
height=$5
|
||||
screenshots_root="$6"
|
||||
|
||||
# work around Chrome bug
|
||||
height_bottom_padding="100"
|
||||
padded_height=$(($height + $height_bottom_padding))
|
||||
|
||||
tmp_screenshot_path="tmp.png"
|
||||
|
||||
echo "Take screenshot $num for $device..."
|
||||
page_url="file://`pwd`/$cwd/html/${template}.html?classes=${device},screen-${num}"
|
||||
"$chrome_app" --headless --disable-gpu --window-size="$width,$padded_height" --screenshot="$tmp_screenshot_path" --virtual-time-budget=10000 "$page_url"
|
||||
|
||||
if [[ $device = "ipad" ]]; then
|
||||
device="ipadPro129"
|
||||
fi
|
||||
screenshot_path="$screenshots_root/$device-$num.png"
|
||||
|
||||
magick $tmp_screenshot_path -geometry 50% -crop ${width}x${height}+0+0 +repage "$screenshot_path"
|
||||
rm $tmp_screenshot_path
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/bash
|
||||
cwd=`dirname $0`
|
||||
device="$1"
|
||||
xcscheme="PassepartoutUITests"
|
||||
results_root="$cwd/results"
|
||||
results_path="$results_root/$device"
|
||||
screenshots_path="$cwd/html/$device"
|
||||
|
||||
mkdir -p "$results_root"
|
||||
mkdir -p "$screenshots_path"
|
||||
|
||||
case $device in
|
||||
|
||||
"iphone")
|
||||
xcplan="MainScreenshots"
|
||||
xcdestination="name=iPhone 16 Pro Max"
|
||||
;;
|
||||
|
||||
"ipad")
|
||||
xcplan="MainScreenshots"
|
||||
xcdestination="name=iPad (10th generation)"
|
||||
;;
|
||||
|
||||
"mac")
|
||||
xcplan="MainScreenshots"
|
||||
xcdestination="platform=macOS,arch=arm64"
|
||||
;;
|
||||
|
||||
"appletv")
|
||||
xcplan="TVScreenshots"
|
||||
xcdestination="name=Apple TV 4K (3rd generation)"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown device: $device"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# 1. run the tests
|
||||
rm -rf "$results_path"
|
||||
xcodebuild -scheme "$xcscheme" -testPlan "$xcplan" -destination "$xcdestination" -resultBundlePath "$results_path" test
|
||||
|
||||
# 2. parse the screenshots
|
||||
xcparse screenshots "$results_path" "$screenshots_path"
|
||||
|
||||
# 3. drop the filename suffix
|
||||
cd "$screenshots_path"
|
||||
for file in 0[1-9]_*.png; do
|
||||
if [[ -e "$file" ]]; then
|
||||
new_name="${file%%_*}.png"
|
||||
mv "$file" "$new_name"
|
||||
fi
|
||||
done
|
Binary file not shown.
After Width: | Height: | Size: 296 KiB |
|
@ -0,0 +1,55 @@
|
|||
.appletv #background {
|
||||
width: 3840px;
|
||||
height: 2160px;
|
||||
}
|
||||
|
||||
.appletv #background header {
|
||||
height: 5em;
|
||||
}
|
||||
|
||||
.appletv #background .heading {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.appletv #box {
|
||||
left: 50%;
|
||||
top: 240%;
|
||||
}
|
||||
|
||||
.appletv #appletv {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#appletv .screenshot {
|
||||
width: 2376px;
|
||||
top: 100px;
|
||||
}
|
||||
|
||||
#appletv .frame {
|
||||
width: 2520px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.screen-01.appletv .heading {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.screen-02.appletv .heading {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.screen-03.appletv .heading {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.screen-01 #appletv :nth-child(1) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-02 #appletv :nth-child(2) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-03 #appletv :nth-child(3) {
|
||||
display: block;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 103 KiB |
|
@ -0,0 +1,71 @@
|
|||
.ipad #background {
|
||||
width: 2048px;
|
||||
height: 2732px;
|
||||
}
|
||||
|
||||
.ipad #background header {
|
||||
height: 10em;
|
||||
}
|
||||
|
||||
.ipad #background .heading {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.ipad #box {
|
||||
left: 60%;
|
||||
bottom: -95%;
|
||||
}
|
||||
|
||||
.ipad #ipad {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#ipad .screenshot {
|
||||
width: 1600px;
|
||||
top: 100px;
|
||||
}
|
||||
|
||||
#ipad .frame {
|
||||
width: 1780px;
|
||||
top: -68px;
|
||||
}
|
||||
|
||||
.screen-01.ipad .heading {
|
||||
padding: 3.5em;
|
||||
}
|
||||
|
||||
.screen-02.ipad .heading {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.screen-03.ipad .heading {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.screen-04.ipad .heading {
|
||||
padding: 3em;
|
||||
}
|
||||
|
||||
.screen-05.ipad .heading {
|
||||
padding: 4em;
|
||||
}
|
||||
|
||||
.screen-01 #ipad :nth-child(1) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-02 #ipad :nth-child(2) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-03 #ipad :nth-child(3) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-04 #ipad :nth-child(4) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-05 #ipad :nth-child(5) {
|
||||
display: block;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 374 KiB |
|
@ -0,0 +1,72 @@
|
|||
.iphone #background {
|
||||
width: 1242px;
|
||||
height: 2688px;
|
||||
}
|
||||
|
||||
.iphone #background header {
|
||||
height: 10em;
|
||||
}
|
||||
|
||||
.iphone #background .heading {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.iphone #box {
|
||||
left: 15%;
|
||||
bottom: -90%;
|
||||
}
|
||||
|
||||
.iphone #iphone {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#iphone .screenshot {
|
||||
width: 900px;
|
||||
top: 100px;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
#iphone .frame {
|
||||
width: 1000px;
|
||||
top: 70px;
|
||||
}
|
||||
|
||||
.screen-01.iphone .heading {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.screen-02.iphone .heading {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.screen-03.iphone .heading {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.screen-04.iphone .heading {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.screen-05.iphone .heading {
|
||||
padding: 1.5em;
|
||||
}
|
||||
|
||||
.screen-01 #iphone :nth-child(1) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-02 #iphone :nth-child(2) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-03 #iphone :nth-child(3) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-04 #iphone :nth-child(4) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-05 #iphone :nth-child(5) {
|
||||
display: block;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
.mac #background {
|
||||
width: 2880px;
|
||||
height: 1800px;
|
||||
}
|
||||
|
||||
.mac #background header {
|
||||
height: 6em;
|
||||
}
|
||||
|
||||
.mac #background .heading {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.mac #box {
|
||||
left: 45%;
|
||||
top: 140%;
|
||||
}
|
||||
|
||||
.mac #mac {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#mac .screenshot {
|
||||
width: 1600px;
|
||||
top: 20px;
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.screen-01.mac .heading {
|
||||
padding: 5em;
|
||||
}
|
||||
|
||||
.screen-02.mac .heading {
|
||||
padding: 3em;
|
||||
}
|
||||
|
||||
.screen-03.mac .heading {
|
||||
padding: 5em;
|
||||
}
|
||||
|
||||
.screen-04.mac .heading {
|
||||
padding: 6em;
|
||||
}
|
||||
|
||||
.screen-01 #mac :nth-child(1) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-02 #mac :nth-child(2) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-03 #mac :nth-child(3) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-04 #mac :nth-child(4) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-05 #mac :nth-child(5) {
|
||||
display: block;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="http://schema.org/Blog">
|
||||
<head>
|
||||
<link rel="stylesheet" href="style.css?1" />
|
||||
<link rel="stylesheet" href="iphone/style.css?1" />
|
||||
<link rel="stylesheet" href="ipad/style.css?1" />
|
||||
<link rel="stylesheet" href="mac/style.css?1" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Ubuntu&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="background">
|
||||
<div id="box"></div>
|
||||
<header>
|
||||
<p class="heading">
|
||||
All your <em>Profiles</em> in a single <em>App</em>
|
||||
</p>
|
||||
<p class="heading">
|
||||
Turn <em>VPN off</em> automatically when <em>at Home</em>
|
||||
</p>
|
||||
<p class="heading">
|
||||
Use with <em>Shortcuts</em>, <em>iCloud</em> and <em>Apple TV</em>
|
||||
</p>
|
||||
<p class="heading">
|
||||
<em>Override</em> your network settings
|
||||
</p>
|
||||
<p class="heading">
|
||||
<em>Presets</em> for<br />well-known <em>VPN Providers</em>
|
||||
</p>
|
||||
</header>
|
||||
<main>
|
||||
<div id="iphone" class="container">
|
||||
<img class="screenshot" src="iphone/01.png" />
|
||||
<img class="screenshot" src="iphone/02.png" />
|
||||
<img class="screenshot" src="iphone/03.png" />
|
||||
<img class="screenshot" src="iphone/04.png" />
|
||||
<img class="screenshot" src="iphone/05.png" />
|
||||
<img class="frame" src="iphone/frame.png" />
|
||||
</div>
|
||||
<div id="ipad" class="container">
|
||||
<img class="screenshot" src="ipad/01.png" />
|
||||
<img class="screenshot" src="ipad/02.png" />
|
||||
<img class="screenshot" src="ipad/03.png" />
|
||||
<img class="screenshot" src="ipad/04.png" />
|
||||
<img class="screenshot" src="ipad/05.png" />
|
||||
<img class="frame" src="ipad/frame.png" />
|
||||
</div>
|
||||
<div id="mac" class="container">
|
||||
<img class="screenshot" src="mac/01.png" />
|
||||
<img class="screenshot" src="mac/02.png" />
|
||||
<img class="screenshot" src="mac/03.png" />
|
||||
<img class="screenshot" src="mac/04.png" />
|
||||
<img class="screenshot" src="mac/05.png" />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
<script src="script.js"></script>
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
let params = new URLSearchParams(document.location.search);
|
||||
let classes = params.get("classes").split(",");
|
||||
document.body.className = classes.join(" ");
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Ubuntu", sans-serif;
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
#background {
|
||||
position: absolute;
|
||||
background-color: #515d71;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
header {
|
||||
height: 10em;
|
||||
}
|
||||
|
||||
header p {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
line-height: 1.7em;
|
||||
}
|
||||
|
||||
em {
|
||||
color: #d69c68;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
main {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#box {
|
||||
position: absolute;
|
||||
background-color: #d69c68;
|
||||
width: 250%;
|
||||
height: 150%;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.screenshot {
|
||||
position: relative;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.frame {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.heading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.screen-01 .heading:nth-of-type(1) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-02 .heading:nth-of-type(2) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-03 .heading:nth-of-type(3) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-04 .heading:nth-of-type(4) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen-05 .heading:nth-of-type(5) {
|
||||
display: block;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="http://schema.org/Blog">
|
||||
<head>
|
||||
<link rel="stylesheet" href="style.css?1" />
|
||||
<link rel="stylesheet" href="appletv/style.css?1" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Ubuntu&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="background">
|
||||
<header>
|
||||
<p class="heading">
|
||||
<em>OpenVPN</em> and <em>WireGuard</em> on your <em>Apple TV</em>
|
||||
</p>
|
||||
<p class="heading">
|
||||
Synchronize your <em>Profiles</em> within <em>seconds</em>
|
||||
</p>
|
||||
<p class="heading">
|
||||
Customize <em>On-demand</em>, <em>DNS</em>, <em>Proxy</em> and <em>Routing</em>
|
||||
</p>
|
||||
</header>
|
||||
<main>
|
||||
<div id="box"></div>
|
||||
<div id="appletv" class="container">
|
||||
<img class="screenshot" src="appletv/01.png" />
|
||||
<img class="screenshot" src="appletv/02.png" />
|
||||
<img class="screenshot" src="appletv/03.png" />
|
||||
<img class="frame" src="appletv/frame.png" />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
<script src="script.js"></script>
|
||||
</html>
|
Loading…
Reference in New Issue