New screenshots
This commit is contained in:
1 parent
3abc6e2483
commit
c249d6796b
6 files changed
+227
-130
No files matched your search
+86
-8
@@ -46,7 +46,7 @@ include `workspace`, `ssh`, `sftp`, `metrics`, `assistant`, `vault`, and
|
||||
|
||||
Raw captures have no `-framed` suffix. Framing scripts may replace a framed file,
|
||||
but must never replace its raw input. The Mac capture script backs up previous raw
|
||||
Workspace files under `.build/screenshot-backups/<timestamp>/` before replacement.
|
||||
files by feature under `.build/screenshot-backups/<timestamp>/` before replacement.
|
||||
|
||||
## iPhone and iPad source captures
|
||||
|
||||
@@ -73,6 +73,12 @@ runtime cannot silently change the marketing UI or select another same-name
|
||||
device. Set `TERMIND_MARKETING_RUNTIME` to a full CoreSimulator runtime identifier
|
||||
only when intentionally updating the runtime used for a capture set.
|
||||
|
||||
Each locale launches a separate test process with matching Apple language, region,
|
||||
and locale arguments. `TERMIND_MARKETING_LOCALE` selects that process's fixture
|
||||
language, so Foundation-localized strings (including Vault's last-connection label)
|
||||
match SwiftUI labels. The iPhone fixture requests portrait orientation through its
|
||||
window scene; the runner does not depend on Simulator menu automation.
|
||||
|
||||
For a targeted recapture, set both filters. This example refreshes only the three
|
||||
localized iPad Assistant sources:
|
||||
|
||||
@@ -122,12 +128,14 @@ Whenever SFTP, Metrics, Assistant navigation, or system chrome changes, recaptur
|
||||
the affected device and every locale. A marketing fixture may be updated to expose
|
||||
the current UI, but it must not add production behavior.
|
||||
|
||||
## Mac Workspace source captures
|
||||
## Mac source captures
|
||||
|
||||
The source is `TFMTests/MacMarketingScreenshotTests.swift`. It constructs the real
|
||||
`RootView` and `ScreenshotDemoConfiguration` inside a native `NSWindow`. Before it
|
||||
The source is `TFMTests/MacMarketingScreenshotTests.swift`. It captures the existing
|
||||
production WindowGroup launched with `ScreenshotDemoConfiguration`; it never
|
||||
mounts a second RootView or duplicates a terminal/transcript surface. Before it
|
||||
publishes the ready marker, it requires both `NSApp.isActive` and
|
||||
`window.isKeyWindow`.
|
||||
`window.isKeyWindow`, allows the focused appearance to render, and requires native
|
||||
2× backing scale. Focus is checked again when the capture is acknowledged.
|
||||
|
||||
Generate all three localized Workspace sources:
|
||||
|
||||
@@ -135,6 +143,20 @@ Generate all three localized Workspace sources:
|
||||
Scripts/run-mac-marketing-screenshots.sh
|
||||
```
|
||||
|
||||
The default remains Workspace. Use `TERMIND_MAC_MARKETING_SCENES` to select a
|
||||
comma-separated subset of `hosts`, `workspace`, `agent`, `command`, and `metrics`.
|
||||
Use `TERMIND_MAC_MARKETING_LOCALES` for `en`, `zh-Hans`, and `zh-Hant` (all three by
|
||||
default). The filename mappings are Vault `mac`, Workspace `mac`, Assistant `mac`,
|
||||
Command Generator `mac`, and Metrics `mac-overview`, respectively. Metrics detail
|
||||
remains available as a development demo, but has no marketing source or export.
|
||||
|
||||
```sh
|
||||
TERMIND_MAC_MARKETING_SCENES=hosts,metrics,command \
|
||||
Scripts/run-mac-marketing-screenshots.sh
|
||||
TERMIND_MAC_MARKETING_SCENES=agent TERMIND_MAC_MARKETING_LOCALES=zh-Hant \
|
||||
Scripts/run-mac-marketing-screenshots.sh
|
||||
```
|
||||
|
||||
The runner:
|
||||
|
||||
1. builds the TFM test host from the pinned package resolution;
|
||||
@@ -142,7 +164,8 @@ The runner:
|
||||
3. waits for an active/key window marker;
|
||||
4. captures the system window by window number;
|
||||
5. removes only the two-pixel transparent capture edge when present;
|
||||
6. verifies a final `2360x1480` sRGB source.
|
||||
6. verifies the test's focus acknowledgement and final dimensions before replacing
|
||||
the source: `2360x1480`, or `2044x1280` for Command Generator.
|
||||
|
||||
The current Workspace contains focused English terminal panes and the current SFTP
|
||||
browser. SFTP filenames, types, dates, and app chrome localize; terminal transcripts
|
||||
@@ -330,6 +353,9 @@ Use Figwright `save_screenshots` with `format: "JPG"` and `scale: 1` for final
|
||||
exports. `get_screenshot` is for visual review and caps its output size; it is not
|
||||
the delivery export path. Keep temporary node-ID filenames outside `Marketing/AppStore`
|
||||
and copy the verified full-size results to the established locale/platform names.
|
||||
If a Figma JPG export has no embedded color profile, add the sRGB ICC profile as
|
||||
JPEG metadata without re-encoding its image data. Verify that decoded RGB pixels
|
||||
are identical to the original export before copying it into the delivery tree.
|
||||
|
||||
## Verification checklist
|
||||
|
||||
@@ -356,8 +382,8 @@ Also confirm manually:
|
||||
- iOS status/navigation chrome is real and complete;
|
||||
- SFTP reflects the current production UI on every platform;
|
||||
- iPad Assistant is the latest capture;
|
||||
- Mac traffic lights are colored, one pane has the active blue outline, and the
|
||||
MacBook render contains no Dock or copyrighted wallpaper;
|
||||
- Mac traffic lights are colored, Workspace's active pane has the blue outline,
|
||||
and the MacBook render contains no Dock or copyrighted wallpaper;
|
||||
- Chinese glyphs use the intended PingFang family;
|
||||
- Traditional Chinese uses `伺服器` and other Taiwan-standard terms;
|
||||
- the store export directory contains exactly 54 final JPG files.
|
||||
@@ -381,3 +407,55 @@ copy, fonts, layout, geometry and FIT mode. SHA-256 comparison against the compl
|
||||
iPhone baseline found exactly 27 changed iPad images and 108 unchanged marketing
|
||||
files, including iPhone, Mac, SSH/SFTP and the first boards. All 54 App Store JPGs
|
||||
remain opaque three-channel sRGB.
|
||||
|
||||
The 2026-09-19 Port Forwarding navigation refresh generated 16 raw captures:
|
||||
Mac Vault, Metrics overview and Command Generator in all three languages,
|
||||
Traditional Chinese Mac Assistant, and iPhone/iPad Vault in all three languages.
|
||||
The two English/Simplified Chinese Mac Metrics detail sources were removed;
|
||||
the product detail view and development demo remain available. Existing Workspace
|
||||
sources and English/Simplified Chinese Mac Assistant sources stayed unchanged.
|
||||
All 15 retained Mac source images and boards 02–06 were checked for colored
|
||||
traffic lights. The ten new Mac captures also passed active/key checks before
|
||||
capture and after acknowledgement. Traditional Chinese Assistant now contains a
|
||||
Traditional Chinese interface and conversation, with English terminal output.
|
||||
|
||||
Only the six mobile Vault MockUPhone frames were regenerated. The Figwright
|
||||
development plugin was reconnected to the verified `Termind` file
|
||||
(`udtCX9xVEeoHsTGTwIBgBw`). Mac boards 03, 05 and 06 in all languages, Traditional
|
||||
Chinese Mac board 04, and mobile board 06 in all languages were replaced and
|
||||
exported as JPG at 1×. All 16 Figma image fills match their local PNG source bytes;
|
||||
readback confirmed unchanged text, fonts, geometry, shadows and FIT mode. The
|
||||
final JPGs have embedded sRGB profiles, opaque RGB pixels and the original
|
||||
platform dimensions. App Store delivery remains 54 JPGs, six per platform/locale.
|
||||
|
||||
The pre-capture backup and SHA-256 manifests are under
|
||||
`.build/marketing-forwarding-refresh/`. The exact-path audit found 35 modified,
|
||||
three added, two deleted and 94 unchanged images: the final tree has 45 raw
|
||||
captures, 33 device frames and 54 App Store exports. English, Simplified Chinese
|
||||
and Traditional Chinese homepages were checked with the rebuilt Vault frames;
|
||||
`/zh/` redirects to `/zh-hans/`. The production website build, Chinese localization
|
||||
check, full Swift format lint and screenshot script syntax checks passed.
|
||||
|
||||
Swift verification for this refresh is not fully green. The opt-in capture runs,
|
||||
`ScreenshotDemoTests` and `AppLanguageTests` passed. The full TFI run completed
|
||||
307 app tests with seven issues and passed all 316 shared-framework tests. The
|
||||
full TFM run reported UI/runtime issues and was interrupted after its upload
|
||||
cancellation test remained blocked in the fixture's socket read. Serial reruns
|
||||
(`-parallel-testing-enabled NO`) passed the previously failing cancellation,
|
||||
input, mobile editor, mobile forwarding and saved-name checks; the isolated
|
||||
`MCPUploadTests` (23 tests) and `MobileUploadBatchTests` (nine tests) also passed.
|
||||
The following failures persisted in unchanged tests:
|
||||
|
||||
- `PortForwardingRenderingTests.saveClosesTheEditorOnlyAfterPersistenceSucceeds`:
|
||||
the native Save accessibility element is missing in both parameter cases.
|
||||
- `PortForwardingRenderingTests.closingJourneyRetainsItsExitViewButDisablesAuthentication`:
|
||||
a secure text field remains when Reduce Motion is off.
|
||||
- `MetricsDetailToolbarTests.legacyCompactSearchOpensTheNativeFieldAndKeepsItsQueryWhenWidened`:
|
||||
the native search field is missing.
|
||||
- `SpeechWaveformViewTests.waveformRespondsToLevelsAndReduceMotionMakesItStable`:
|
||||
the Reduce Motion bitmap equality check fails on Mac, including an isolated rerun.
|
||||
- `MobileProviderDefaultsTests.modelNamesAreMaterializedAfterMergingTheProviderDraft`:
|
||||
the expected validation error is not thrown.
|
||||
|
||||
Logs and `.xcresult` bundles remain in `.build/marketing-forwarding-refresh/`.
|
||||
No production behavior or unrelated tests were changed to address these failures.
|
||||
@@ -8,8 +8,33 @@ repository_directory="${script_directory:h}"
|
||||
derived_data="$repository_directory/.build/mac-marketing-tests"
|
||||
products="$derived_data/Build/Products"
|
||||
backup_directory="$repository_directory/.build/screenshot-backups/$(date +%Y%m%d-%H%M%S)"
|
||||
output_directory="$repository_directory/Marketing/Screenshots/Captures/workspace"
|
||||
output_directory="$repository_directory/Marketing/Screenshots/Captures"
|
||||
marketing_scenes="${TERMIND_MAC_MARKETING_SCENES:-workspace}"
|
||||
marketing_locales="${TERMIND_MAC_MARKETING_LOCALES:-en,zh-Hans,zh-Hant}"
|
||||
local_test_pid=""
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "$local_test_pid" ]]; then
|
||||
kill "$local_test_pid" 2>/dev/null || true
|
||||
wait "$local_test_pid" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
for scene in ${(s:,:)marketing_scenes}; do
|
||||
case "$scene" in
|
||||
hosts|workspace|agent|command|metrics) ;;
|
||||
*) print -u2 "Unsupported marketing scene: $scene"; exit 64 ;;
|
||||
esac
|
||||
done
|
||||
for language in ${(s:,:)marketing_locales}; do
|
||||
case "$language" in
|
||||
en|zh-Hans|zh-Hant) ;;
|
||||
*) print -u2 "Unsupported marketing locale: $language"; exit 64 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
mkdir -p "$derived_data"
|
||||
xcodebuild \
|
||||
-project "$repository_directory/Termind.xcodeproj" \
|
||||
-scheme TFM \
|
||||
@@ -26,21 +51,34 @@ if [[ -z "$source_xctestrun" ]]; then
|
||||
fi
|
||||
|
||||
capture() {
|
||||
local language="$1"
|
||||
local region="$2"
|
||||
local asset_locale="$3"
|
||||
local apple_locale="$4"
|
||||
local xctestrun="$products/Marketing-TFM-$asset_locale.xctestrun"
|
||||
local test_log="$derived_data/workspace-$asset_locale-capture.log"
|
||||
local destination="$output_directory/mac-$asset_locale.png"
|
||||
local scene="$1"
|
||||
local language="$2"
|
||||
local region asset_locale apple_locale feature prefix
|
||||
local width=2360 height=1480
|
||||
case "$language" in
|
||||
en) region=US; asset_locale=en; apple_locale=en_US ;;
|
||||
zh-Hans) region=CN; asset_locale=zh-hans; apple_locale=zh_CN ;;
|
||||
zh-Hant) region=TW; asset_locale=zh-hant; apple_locale=zh_TW ;;
|
||||
esac
|
||||
prefix=mac
|
||||
case "$scene" in
|
||||
hosts) feature=vault ;;
|
||||
workspace) feature=workspace ;;
|
||||
agent) feature=assistant ;;
|
||||
command) feature=command-generator; width=2044; height=1280 ;;
|
||||
metrics) feature=metrics; prefix=mac-overview ;;
|
||||
esac
|
||||
local xctestrun="$products/Marketing-TFM-$scene-$asset_locale.xctestrun"
|
||||
local test_log="$derived_data/$scene-$asset_locale-capture.log"
|
||||
local destination="$output_directory/$feature/$prefix-$asset_locale.png"
|
||||
local captured="$derived_data/$scene-$asset_locale.png"
|
||||
|
||||
cp "$source_xctestrun" "$xctestrun"
|
||||
/usr/libexec/PlistBuddy \
|
||||
-c 'Add :TFMTests:EnvironmentVariables:TERMIND_MAC_MARKETING_SCREENSHOTS string 1' \
|
||||
-c "Add :TFMTests:EnvironmentVariables:TERMIND_MAC_MARKETING_LOCALE string $language" \
|
||||
-c "Set :TFMTests:TestLanguage $language" \
|
||||
-c "Set :TFMTests:TestRegion $region" \
|
||||
-c "Add :TFMTests:CommandLineArguments:0 string --termind-demo=workspace:$language" \
|
||||
-c "Add :TFMTests:CommandLineArguments:0 string --termind-demo=$scene:$language" \
|
||||
-c 'Add :TFMTests:CommandLineArguments:1 string -AppleLanguages' \
|
||||
-c "Add :TFMTests:CommandLineArguments:2 string ($language)" \
|
||||
-c 'Add :TFMTests:CommandLineArguments:3 string -AppleLocale' \
|
||||
@@ -53,10 +91,10 @@ capture() {
|
||||
-destination 'platform=macOS,arch=arm64' \
|
||||
-only-testing:TFMTests/MacMarketingScreenshotTests \
|
||||
test-without-building > "$test_log" 2>&1 &
|
||||
local test_pid=$!
|
||||
local_test_pid=$!
|
||||
local ready_path=""
|
||||
|
||||
for _ in {1..300}; do
|
||||
for _ in {1..600}; do
|
||||
ready_path="$(
|
||||
rg -o 'TERMIND_MAC_MARKETING_READY .*' "$test_log" \
|
||||
| tail -n 1 \
|
||||
@@ -64,47 +102,53 @@ capture() {
|
||||
|| true
|
||||
)"
|
||||
[[ -n "$ready_path" && -f "$ready_path" ]] && break
|
||||
if ! kill -0 "$test_pid" 2>/dev/null; then
|
||||
if ! kill -0 "$local_test_pid" 2>/dev/null; then
|
||||
tail -n 100 "$test_log" >&2
|
||||
exit 70
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
if [[ -z "$ready_path" || ! -f "$ready_path" ]]; then
|
||||
kill "$test_pid" 2>/dev/null || true
|
||||
tail -n 100 "$test_log" >&2
|
||||
exit 70
|
||||
fi
|
||||
|
||||
local feature captured_language window_id token active key
|
||||
IFS=$'\t' read -r feature captured_language window_id token active key < "$ready_path"
|
||||
if [[ "$feature" != workspace || "$captured_language" != "$language" || "$active" != active || "$key" != key ]]; then
|
||||
local captured_scene captured_language window_id token active key
|
||||
IFS=$'\t' read -r captured_scene captured_language window_id token active key < "$ready_path"
|
||||
if [[ "$captured_scene" != "$scene" || "$captured_language" != "$language" || "$active" != active || "$key" != key ]]; then
|
||||
print -u2 "Capture window was not ready and focused: $(<"$ready_path")"
|
||||
exit 70
|
||||
fi
|
||||
|
||||
mkdir -p "$output_directory" "$backup_directory"
|
||||
if [[ -f "$destination" ]]; then
|
||||
cp "$destination" "$backup_directory/${destination:t}"
|
||||
fi
|
||||
screencapture -l "$window_id" -o "$destination"
|
||||
screencapture -l "$window_id" -o "$captured"
|
||||
touch "${ready_path:h}/ack-$token"
|
||||
wait "$test_pid"
|
||||
|
||||
local dimensions="$(identify -format '%wx%h' "$destination")"
|
||||
if [[ "$dimensions" == 2364x1484 ]]; then
|
||||
sips -c 1480 2360 "$destination" >/dev/null
|
||||
dimensions="$(identify -format '%wx%h' "$destination")"
|
||||
fi
|
||||
if [[ "$dimensions" != 2360x1480 ]]; then
|
||||
print -u2 "Unexpected Workspace dimensions for $asset_locale: $dimensions"
|
||||
if ! wait "$local_test_pid"; then
|
||||
tail -n 100 "$test_log" >&2
|
||||
exit 70
|
||||
fi
|
||||
print "Captured focused Workspace -> ${destination#$repository_directory/}"
|
||||
local_test_pid=""
|
||||
|
||||
local dimensions="$(identify -format '%wx%h' "$captured")"
|
||||
if [[ "$dimensions" == "$((width + 4))x$((height + 4))" ]]; then
|
||||
sips -c "$height" "$width" "$captured" >/dev/null
|
||||
dimensions="$(identify -format '%wx%h' "$captured")"
|
||||
fi
|
||||
if [[ "$dimensions" != "${width}x${height}" ]]; then
|
||||
print -u2 "Unexpected $scene dimensions for $asset_locale: $dimensions"
|
||||
exit 70
|
||||
fi
|
||||
mkdir -p "${destination:h}" "$backup_directory/$feature"
|
||||
if [[ -f "$destination" ]]; then
|
||||
cp "$destination" "$backup_directory/$feature/${destination:t}"
|
||||
fi
|
||||
cp "$captured" "$destination"
|
||||
print "Captured focused $scene -> ${destination#$repository_directory/}"
|
||||
}
|
||||
|
||||
capture en US en en_US
|
||||
capture zh-Hans CN zh-hans zh_CN
|
||||
capture zh-Hant TW zh-hant zh_TW
|
||||
for scene in ${(s:,:)marketing_scenes}; do
|
||||
for language in ${(s:,:)marketing_locales}; do
|
||||
capture "$scene" "$language"
|
||||
done
|
||||
done
|
||||
|
||||
print "Previous raw Workspace captures backed up in ${backup_directory#$repository_directory/}"
|
||||
print "Previous captures backed up in ${backup_directory#$repository_directory/}"
|
||||
@@ -14,13 +14,13 @@ marketing_runtime="${TERMIND_MARKETING_RUNTIME:-com.apple.CoreSimulator.SimRunti
|
||||
|
||||
expected_capture_count() {
|
||||
if [[ -z "$marketing_scenes" ]]; then
|
||||
print 15
|
||||
print 5
|
||||
return
|
||||
fi
|
||||
local -a scenes
|
||||
scenes=("${(@s:,:)marketing_scenes}")
|
||||
local scene_count="${#scenes}"
|
||||
print "$((scene_count * 3))"
|
||||
print "$scene_count"
|
||||
}
|
||||
|
||||
find_device() {
|
||||
@@ -31,33 +31,9 @@ find_device() {
|
||||
| head -n 1
|
||||
}
|
||||
|
||||
set_simulator_orientation() {
|
||||
local name="$1"
|
||||
local orientation="$2"
|
||||
osascript - "$name" "$orientation" <<'APPLESCRIPT'
|
||||
on run arguments
|
||||
set deviceName to item 1 of arguments
|
||||
set targetOrientation to item 2 of arguments
|
||||
tell application "Simulator" to activate
|
||||
tell application "System Events"
|
||||
tell process "Simulator"
|
||||
repeat with candidate in menu items of menu 1 of menu bar item "Window" of menu bar 1
|
||||
if name of candidate starts with deviceName then
|
||||
click candidate
|
||||
exit repeat
|
||||
end if
|
||||
end repeat
|
||||
delay 0.4
|
||||
click menu item targetOrientation of menu 1 of menu item "Orientation" of menu 1 of menu bar item "Device" of menu bar 1
|
||||
end tell
|
||||
end tell
|
||||
end run
|
||||
APPLESCRIPT
|
||||
sleep 1
|
||||
}
|
||||
|
||||
run_capture() {
|
||||
local name="$1"
|
||||
local language="$2"
|
||||
local device_id
|
||||
device_id="$(find_device "$name")"
|
||||
if [[ -z "$device_id" ]]; then
|
||||
@@ -69,14 +45,29 @@ run_capture() {
|
||||
xcrun simctl bootstatus "$device_id" -b
|
||||
if [[ "$name" == iPad* ]]; then
|
||||
xcrun devicectl device orientation set --device "$device_id" landscapeLeft >/dev/null
|
||||
elif pgrep -x Simulator >/dev/null; then
|
||||
set_simulator_orientation "$name" "Portrait"
|
||||
fi
|
||||
xcrun simctl status_bar "$device_id" override \
|
||||
--time '9:41' --batteryState charged --batteryLevel 100 --wifiBars 3 --cellularBars 4
|
||||
local test_log="$derived_data/${device_id}-capture.log"
|
||||
local region apple_locale
|
||||
case "$language" in
|
||||
en) region=US; apple_locale=en_US ;;
|
||||
zh-Hans) region=CN; apple_locale=zh_CN ;;
|
||||
zh-Hant) region=TW; apple_locale=zh_TW ;;
|
||||
esac
|
||||
local localized_xctestrun="$derived_data/Build/Products/Marketing-TFI-$language.xctestrun"
|
||||
cp "$marketing_xctestrun" "$localized_xctestrun"
|
||||
/usr/libexec/PlistBuddy \
|
||||
-c "Add :TFITests:EnvironmentVariables:TERMIND_MARKETING_LOCALE string $language" \
|
||||
-c "Set :TFITests:TestLanguage $language" \
|
||||
-c "Set :TFITests:TestRegion $region" \
|
||||
-c 'Add :TFITests:CommandLineArguments:0 string -AppleLanguages' \
|
||||
-c "Add :TFITests:CommandLineArguments:1 string ($language)" \
|
||||
-c 'Add :TFITests:CommandLineArguments:2 string -AppleLocale' \
|
||||
-c "Add :TFITests:CommandLineArguments:3 string $apple_locale" \
|
||||
"$localized_xctestrun"
|
||||
local test_log="$derived_data/${device_id}-$language-capture.log"
|
||||
xcodebuild \
|
||||
-xctestrun "$marketing_xctestrun" \
|
||||
-xctestrun "$localized_xctestrun" \
|
||||
-destination "id=$device_id" \
|
||||
-only-testing:TFITests/MobileMarketingScreenshotTests \
|
||||
test-without-building > "$test_log" 2>&1 &
|
||||
@@ -129,7 +120,7 @@ run_capture() {
|
||||
local expected
|
||||
expected="$(expected_capture_count)"
|
||||
if [[ "$captures" -ne "$expected" ]]; then
|
||||
print -u2 "Expected $expected captures for $name, received $captures. See $test_log"
|
||||
print -u2 "Expected $expected captures for $name ($language), received $captures. See $test_log"
|
||||
exit 70
|
||||
fi
|
||||
}
|
||||
@@ -168,10 +159,14 @@ if [[ -n "$marketing_scenes" ]]; then
|
||||
fi
|
||||
|
||||
if [[ ",$marketing_devices," == *,iphone,* ]]; then
|
||||
run_capture "iPhone 17 Pro Max"
|
||||
for language in en zh-Hans zh-Hant; do
|
||||
run_capture "iPhone 17 Pro Max" "$language"
|
||||
done
|
||||
fi
|
||||
if [[ ",$marketing_devices," == *,ipad,* ]]; then
|
||||
run_capture "iPad Pro 13-inch (M5)"
|
||||
for language in en zh-Hans zh-Hant; do
|
||||
run_capture "iPad Pro 13-inch (M5)" "$language"
|
||||
done
|
||||
fi
|
||||
|
||||
print "Marketing screenshots written to $output_directory"
|
||||
@@ -12,7 +12,12 @@ import UIKit
|
||||
/// Opt-in marketing captures. Fixtures are entirely in memory and never open a network connection.
|
||||
@Suite(.serialized) @MainActor
|
||||
struct MobileMarketingScreenshotTests {
|
||||
private let languages = ["en", "zh-Hans", "zh-Hant"]
|
||||
private var languages: [String] {
|
||||
if let language = ProcessInfo.processInfo.environment["TERMIND_MARKETING_LOCALE"] {
|
||||
return [language]
|
||||
}
|
||||
return ["en", "zh-Hans", "zh-Hant"]
|
||||
}
|
||||
|
||||
@Test func renderMarketingScenes() async throws {
|
||||
guard ProcessInfo.processInfo.environment["TERMIND_MARKETING_SCREENSHOTS"] == "1" else { return }
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import AppKit
|
||||
import SwiftData
|
||||
import SwiftUI
|
||||
import TermindApplication
|
||||
import TermindPersistence
|
||||
import Testing
|
||||
|
||||
@testable import Termind
|
||||
@@ -12,57 +8,25 @@ import Testing
|
||||
@MainActor
|
||||
struct MacMarketingScreenshotTests {
|
||||
@Test
|
||||
func captureWorkspaceInSystemWindow() async throws {
|
||||
func captureDemoInFocusedSystemWindow() async throws {
|
||||
let environment = ProcessInfo.processInfo.environment
|
||||
guard environment["TERMIND_MAC_MARKETING_SCREENSHOTS"] == "1" else { return }
|
||||
|
||||
let argument = environment["TERMIND_MAC_MARKETING_LOCALE"] ?? "en"
|
||||
let language: AppLanguage
|
||||
switch argument {
|
||||
case "zh-Hans": language = .simplifiedChinese
|
||||
case "zh-Hant": language = .traditionalChinese
|
||||
default: language = .english
|
||||
let configuration = try #require(ScreenshotDemoConfiguration.active)
|
||||
// Capture the production WindowGroup. A second RootView would mount
|
||||
// persistent terminal and transcript surfaces in two hierarchies.
|
||||
for _ in 0..<100 {
|
||||
if NSApp.windows.contains(where: { $0.isVisible && $0.canBecomeKey }) { break }
|
||||
try await Task.sleep(for: .milliseconds(50))
|
||||
}
|
||||
|
||||
let configuration = ScreenshotDemoConfiguration(scene: .workspace, language: language)
|
||||
let hosts = HostLibraryModel.demo(language: language)
|
||||
let app = AppModel(
|
||||
hosts: hosts,
|
||||
ai: .demo(),
|
||||
proAccess: .unrestrictedForTesting(),
|
||||
onboarding: OnboardingController(automaticPresentationEnabled: false),
|
||||
serverMetrics: configuration.makeMetricsModel(hosts: hosts)
|
||||
)
|
||||
let container = try TermindPersistence.makeContainer(inMemory: true)
|
||||
let root = RootView(window: configuration.makeWindow(hosts: hosts))
|
||||
.environment(app)
|
||||
.environment(\.locale, language.locale)
|
||||
.modelContainer(container)
|
||||
let controller = NSHostingController(rootView: root)
|
||||
let window = NSWindow(
|
||||
contentRect: NSRect(origin: .zero, size: configuration.windowSize),
|
||||
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
|
||||
backing: .buffered,
|
||||
defer: false
|
||||
)
|
||||
window.isReleasedWhenClosed = false
|
||||
window.isRestorable = false
|
||||
let window = try #require(NSApp.windows.first(where: { $0.isVisible && $0.canBecomeKey }))
|
||||
window.contentMinSize = .zero
|
||||
window.titlebarAppearsTransparent = true
|
||||
window.titlebarSeparatorStyle = .none
|
||||
window.appearance = NSAppearance(named: .aqua)
|
||||
window.contentViewController = controller
|
||||
window.setFrame(NSRect(origin: .zero, size: configuration.windowSize), display: true)
|
||||
window.center()
|
||||
defer {
|
||||
window.orderOut(nil)
|
||||
window.contentViewController = nil
|
||||
app.shutdownForTermination()
|
||||
}
|
||||
|
||||
controller.view.layoutSubtreeIfNeeded()
|
||||
window.contentView?.layoutSubtreeIfNeeded()
|
||||
try await Task.sleep(for: .seconds(1))
|
||||
controller.view.layoutSubtreeIfNeeded()
|
||||
window.contentView?.layoutSubtreeIfNeeded()
|
||||
|
||||
for _ in 0..<100 {
|
||||
NSRunningApplication.current.activate(options: [.activateAllWindows])
|
||||
@@ -77,12 +41,22 @@ import Testing
|
||||
Issue.record("Marketing window could not become the focused key window")
|
||||
return
|
||||
}
|
||||
if configuration.scene == .hosts {
|
||||
window.initialFirstResponder = nil
|
||||
window.makeFirstResponder(nil)
|
||||
}
|
||||
// Allow the active appearance to render before the external capture.
|
||||
try await Task.sleep(for: .milliseconds(300))
|
||||
try #require(NSApp.isActive && window.isKeyWindow)
|
||||
try #require(window.backingScaleFactor == 2)
|
||||
|
||||
let control = FileManager.default.temporaryDirectory.appending(path: "marketing-capture-control")
|
||||
try FileManager.default.createDirectory(at: control, withIntermediateDirectories: true)
|
||||
let token = UUID().uuidString
|
||||
let ready = control.appending(path: "ready")
|
||||
try "workspace\t\(argument)\t\(window.windowNumber)\t\(token)\tactive\tkey\n".write(
|
||||
let scene = configuration.scene.rawValue
|
||||
let language = configuration.language.rawValue
|
||||
try "\(scene)\t\(language)\t\(window.windowNumber)\t\(token)\tactive\tkey\n".write(
|
||||
to: ready, atomically: true, encoding: .utf8)
|
||||
FileHandle.standardOutput.write(
|
||||
Data("TERMIND_MAC_MARKETING_READY \(ready.path)\n".utf8)
|
||||
@@ -91,6 +65,7 @@ import Testing
|
||||
let acknowledgement = control.appending(path: "ack-\(token)")
|
||||
for _ in 0..<600 {
|
||||
if FileManager.default.fileExists(atPath: acknowledgement.path) {
|
||||
#expect(NSApp.isActive && window.isKeyWindow, "Capture window lost focus before acknowledgement")
|
||||
try? FileManager.default.removeItem(at: acknowledgement)
|
||||
try? FileManager.default.removeItem(at: ready)
|
||||
return
|
||||
|
||||
@@ -1770,7 +1770,7 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = TFI/TFI.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 22;
|
||||
CURRENT_PROJECT_VERSION = 23;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
ICLOUD_CONTAINER_ENVIRONMENT = Development;
|
||||
INFOPLIST_FILE = TFI/Info.plist;
|
||||
@@ -2043,7 +2043,7 @@
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_ENTITLEMENTS = TFI/TFI.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 22;
|
||||
CURRENT_PROJECT_VERSION = 23;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
ICLOUD_CONTAINER_ENVIRONMENT = Production;
|
||||
INFOPLIST_FILE = TFI/Info.plist;
|
||||
@@ -2282,7 +2282,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 29;
|
||||
CURRENT_PROJECT_VERSION = 30;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
@@ -2319,7 +2319,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 29;
|
||||
CURRENT_PROJECT_VERSION = 30;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
|
||||
Reference in new issue
Block a user