Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions Tests/ScriptsTests/test_release_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,6 @@ def test_testflight_beta_distribution_workflow_uses_distribution_script(self):

def test_app_store_screenshot_upload_workflow_uses_safe_defaults(self):
workflow = self.read(".github/workflows/upload-app-store-screenshots.yml")
script = self.read("scripts/upload-app-store-screenshots.py")

self.assertIn("name: Upload App Store Screenshots", workflow)
self.assertIn("default: true", workflow)
Expand All @@ -922,14 +921,6 @@ def test_app_store_screenshot_upload_workflow_uses_safe_defaults(self):
self.assertIn("- watch", workflow)
self.assertIn("- visionpro", workflow)
self.assertIn("- tvos", workflow)
self.assertIn('"ipad": "IOS"', script)
self.assertIn('"watch": "IOS"', script)
self.assertIn('"tvos": "TV_OS"', script)
self.assertIn("APP_IPAD_PRO_3GEN_129", script)
self.assertIn("APP_WATCH_ULTRA", script)
self.assertIn("APP_APPLE_VISION_PRO", script)
self.assertIn("APP_APPLE_TV", script)
self.assertIn("context-panel-appstore-5-glance-detail-redacted.png", script)

def test_app_store_review_workflow_supports_prepare_only(self):
workflow = self.read(".github/workflows/submit-app-store-review.yml")
Expand Down
133 changes: 80 additions & 53 deletions Tests/ScriptsTests/test_upload_app_store_screenshots.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import sys
import tempfile
import unittest
from collections import Counter
from pathlib import Path
from typing import Any
from unittest import mock


MODULE_PATH = Path(__file__).resolve().parents[2] / "scripts" / "upload-app-store-screenshots.py"
SCREENSHOT_ROOT = MODULE_PATH.parents[1] / "Resources" / "AppStore" / "Screenshots"
SPEC = importlib.util.spec_from_file_location("upload_app_store_screenshots", MODULE_PATH)
if SPEC is None or SPEC.loader is None:
raise RuntimeError(f"Unable to load {MODULE_PATH}")
Expand Down Expand Up @@ -354,70 +357,94 @@ def test_infers_platform_for_single_named_set(self):
self.assertEqual(upload_app_store_screenshots.inferred_platform(["visionpro"], None), "VISION_OS")
self.assertEqual(upload_app_store_screenshots.inferred_platform(["tvos"], None), "TV_OS")

def test_macos_approved_set_contains_all_five_approved_screenshots(self):
macos_assets = upload_app_store_screenshots.APPROVED_SETS["macos"]
def test_approved_sets_have_valid_platform_assets(self):
approved_sets = upload_app_store_screenshots.APPROVED_SETS
platform_for_set = upload_app_store_screenshots.PLATFORM_FOR_SET
display_types_by_platform = upload_app_store_screenshots.FULL_PLATFORM_DISPLAY_TYPES
expected_display_types = {
"macos": {"APP_DESKTOP"},
"iphone": {"APP_IPHONE_61"},
"iphone65": {"APP_IPHONE_65"},
"ipad": {"APP_IPAD_PRO_3GEN_129"},
"watch": {"APP_WATCH_ULTRA"},
"ios": display_types_by_platform["IOS"],
"visionpro": {"APP_APPLE_VISION_PRO"},
"tvos": {"APP_APPLE_TV"},
}

self.assertEqual(len(macos_assets), 5)
self.assertEqual({asset.display_type for asset in macos_assets}, {"APP_DESKTOP"})
self.assertEqual(
[asset.path.name for asset in macos_assets],
[
"context-panel-appstore-1-app.png",
"context-panel-appstore-2-widget.png",
"context-panel-appstore-3-app-dark-current.png",
"context-panel-appstore-4-widgets-live-redacted.png",
"context-panel-appstore-5-glance-detail-redacted.png",
],
)
self.assertEqual(set(approved_sets), set(platform_for_set))
self.assertEqual(set(approved_sets), set(expected_display_types))
full_platforms = set()
for set_name, assets in approved_sets.items():
with self.subTest(set_name=set_name):
self.assertTrue(assets)
paths = [asset.path for asset in assets]
self.assertEqual(len(paths), len(set(paths)))
self.assertTrue(all((SCREENSHOT_ROOT / path).is_file() for path in paths))

platform = platform_for_set[set_name]
allowed_display_types = display_types_by_platform[platform]
actual_display_types = {asset.display_type for asset in assets}
self.assertLessEqual(actual_display_types, allowed_display_types)
self.assertEqual(actual_display_types, expected_display_types[set_name])
if actual_display_types == allowed_display_types:
full_platforms.add(platform)

counts = Counter(asset.display_type for asset in assets)
self.assertTrue(
all(
0 < count <= upload_app_store_screenshots.MAX_SCREENSHOTS_PER_DISPLAY_TYPE
for count in counts.values()
)
)

def test_ipad_approved_set_contains_current_approved_screenshots(self):
ipad_assets = upload_app_store_screenshots.APPROVED_SETS["ipad"]
self.assertEqual(full_platforms, set(display_types_by_platform))

self.assertEqual(len(ipad_assets), 2)
self.assertEqual({asset.display_type for asset in ipad_assets}, {"APP_IPAD_PRO_3GEN_129"})
self.assertEqual(
[asset.path.name for asset in ipad_assets],
[
"ipad-12-9-app-light-synced.png",
"ipad-12-9-app-dark-synced.png",
],
)
def test_device_specific_approved_sets_do_not_reuse_screenshot_paths(self):
approved_sets = upload_app_store_screenshots.APPROVED_SETS
leaf_set_names = ("macos", "iphone", "iphone65", "ipad", "watch", "visionpro", "tvos")
paths = [asset.path for set_name in leaf_set_names for asset in approved_sets[set_name]]

def test_tvos_approved_set_contains_current_approved_screenshots(self):
tvos_assets = upload_app_store_screenshots.APPROVED_SETS["tvos"]
self.assertEqual(len(paths), len(set(paths)))

self.assertEqual(len(tvos_assets), 3)
self.assertEqual({asset.display_type for asset in tvos_assets}, {"APP_APPLE_TV"})
self.assertEqual(
[asset.path.name for asset in tvos_assets],
[
"tvos-1-runway-full-detail.png",
"tvos-2-provider-hide-account-names.png",
"tvos-3-runway-percentages-only.png",
],
)

def test_ios_approved_set_contains_only_current_approved_display_types(self):
ios_assets = upload_app_store_screenshots.APPROVED_SETS["ios"]
def test_ios_approved_set_composes_the_device_specific_sets_in_upload_order(self):
approved_sets = upload_app_store_screenshots.APPROVED_SETS

self.assertEqual(len(ios_assets), 12)
self.assertEqual(
{asset.display_type for asset in ios_assets},
{"APP_IPHONE_61", "APP_IPHONE_65", "APP_IPAD_PRO_3GEN_129", "APP_WATCH_ULTRA"},
expected = sum(
(approved_sets[set_name] for set_name in ("iphone", "iphone65", "ipad", "watch")),
(),
)

def test_iphone65_approved_set_contains_current_approved_screenshots(self):
iphone65_assets = upload_app_store_screenshots.APPROVED_SETS["iphone65"]
self.assertEqual(approved_sets["ios"], expected)

def test_selected_assets_preserves_configured_set_and_asset_order(self):
configured_sets = {
"first": (
upload_app_store_screenshots.ScreenshotAsset("DISPLAY_A", Path("first-a.png")),
upload_app_store_screenshots.ScreenshotAsset("DISPLAY_A", Path("first-b.png")),
),
"second": (
upload_app_store_screenshots.ScreenshotAsset("DISPLAY_B", Path("second.png")),
),
}

with mock.patch.object(upload_app_store_screenshots, "APPROVED_SETS", configured_sets):
assets = upload_app_store_screenshots.selected_assets(
["second", "first"], Path("approved-root")
)

self.assertEqual(len(iphone65_assets), 4)
self.assertEqual({asset.display_type for asset in iphone65_assets}, {"APP_IPHONE_65"})
self.assertEqual(
[asset.path.name for asset in iphone65_assets],
assets,
[
"iphone-6-5-app-light-synced.png",
"iphone-6-5-app-dark-synced.png",
"iphone-6-5-widget-light-home.png",
"iphone-6-5-widget-dark-home.png",
upload_app_store_screenshots.ScreenshotAsset(
"DISPLAY_B", Path("approved-root/second.png")
),
upload_app_store_screenshots.ScreenshotAsset(
"DISPLAY_A", Path("approved-root/first-a.png")
),
upload_app_store_screenshots.ScreenshotAsset(
"DISPLAY_A", Path("approved-root/first-b.png")
),
],
)

Expand Down