Skip to content
Draft
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
15 changes: 15 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ dev rn e2e ios --tags checkout
dev android e2e --tags launch,checkout
```

Apple Pay is an explicit Swift-only profile and must be requested by itself:

```bash
dev swift e2e --tags apple-pay
```

The command checks that the generated Swift `Storefront.xcconfig` contains a
nonblank Apple Pay merchant identifier before it builds. Normal runs exclude the
`apple-pay` tag, so wallet configuration cannot leak into unrelated runs.

Both options match **any** listed tag, because that is how Maestro filters.
`--tags launch,checkout` runs the launch tests and the checkout tests.
`--exclude-tags` skips tests carrying any listed tag. `config.yaml` quarantines
Expand All @@ -56,6 +66,7 @@ enforces it.
| Cost tier | `smoke`, `full` | Exactly one per test |
| Quarantine | `flaky`, `wip` | Excluded by default, in `config.yaml` |
| Platform capability | `ios-only`, `android-only` | Needs a `# Platform capability:` comment |
| Feature | `apple-pay` | Explicit opt-in profile |

A platform tag marks a capability only one platform has, such as Apple Pay. It
must never mark a test that is merely not ported yet.
Expand Down Expand Up @@ -148,11 +159,15 @@ ruby e2e/scripts/e2e_matrix_to_browserstack_run_plan count
- `tests/shared/launch-smoke.yaml` is the shared launch smoke test.
- `tests/shared/checkout-present-and-close.yaml` seeds a cart through the control
link, presents checkout, closes it, and asserts dismissal.
- `tests/shared/checkout-apple-pay.yaml` presents, closes, and reopens the Apple Pay
sheet on the Swift iOS sample.
- `tests/react-native/checkout-guest.yaml` composes the React Native guest
checkout smoke test from those subflows.
- `tests/react-native/checkout-hardcoded-buyer-identity.yaml` verifies checkout
from a bootstrapped cart with hardcoded buyer identity.
- `scripts/run_local_e2e` builds and installs any of the four local targets.
- `scripts/check_swift_apple_pay_config` fails before an Apple Pay build when the
generated Swift merchant identifier is missing or blank.
- `scripts/run_maestro` is their single Maestro invocation. It holds the
environment contract and target-specific test-file selection in one place.
- `config/matrix.yml`, `lib/e2e_matrix_to_browserstack_run_plan.rb`, and
Expand Down
2 changes: 2 additions & 0 deletions e2e/config/matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ tags:
# so they cannot run on the Swift and Kotlin rows. Drop this entry when the shared
# ordering tests replace them.
- full
# Wallet tests require an explicitly configured, provisioned application row.
- apple-pay
applications:
- id: react-native-ios
target: react-native
Expand Down
14 changes: 14 additions & 0 deletions e2e/flows/checkout/close-apple-pay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
appId: ${E2E_APP_ID}
---
- tapOn:
id: dismiss
- waitForAnimationToEnd
- extendedWaitUntil:
notVisible:
id: payment-sheet
timeout: 10000
- extendedWaitUntil:
visible:
id: apple-pay-button
enabled: true
timeout: 10000
10 changes: 10 additions & 0 deletions e2e/flows/checkout/present-apple-pay.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
appId: ${E2E_APP_ID}
---
- tapOn:
id: apple-pay-button
enabled: true
- waitForAnimationToEnd
- extendedWaitUntil:
visible:
id: payment-sheet
timeout: 10000
43 changes: 43 additions & 0 deletions e2e/scripts/check_swift_apple_pay_config
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/env bash

set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
CONFIG_PATH="${1:-${REPO_ROOT}/platforms/swift/Samples/CheckoutKitSwiftDemo/Storefront.xcconfig}"
KEY="APPLE_PAY_MERCHANT_IDENTIFIER"

if [ "$#" -gt 1 ]; then
echo "Usage: e2e/scripts/check_swift_apple_pay_config [xcconfig-path]" >&2
exit 1
fi

if [ ! -f "$CONFIG_PATH" ]; then
echo "Apple Pay E2E requires ${KEY} in ${CONFIG_PATH}, but that file does not exist." >&2
echo "Configure the Swift sample storefront before retrying." >&2
exit 1
fi

# Check the assignment without returning its value to the shell or printing it.
if ! awk -v key="$KEY" '
/^[[:space:]]*\/\// || /^[[:space:]]*#/ || /^[[:space:]]*$/ { next }
$0 !~ /=/ { next }
{
line = $0
candidate = line
sub(/^[[:space:]]*/, "", candidate)
sub(/[[:space:]]*=.*/, "", candidate)

if (candidate == key) {
sub(/^[^=]*=/, "", line)
sub(/^[[:space:]]*/, "", line)
sub(/[[:space:]]*$/, "", line)
found = 1
exit(line == "" ? 1 : 0)
}
}
END { if (!found) exit 1 }
' "$CONFIG_PATH"; then
echo "Apple Pay E2E requires a nonblank ${KEY} assignment in ${CONFIG_PATH}." >&2
echo "Configure the Swift sample storefront before retrying." >&2
exit 1
fi
32 changes: 31 additions & 1 deletion e2e/scripts/run_local_e2e
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ Usage: $DEV_COMMAND [--tags TAG[,TAG...]] [--exclude-tags TAG[,TAG...]]

Runs the shared and $TARGET_DESCRIPTION-specific Maestro tests. Tags come from the
taxonomy: journey (launch, cart, checkout, account), cost tier (smoke, full),
quarantine (flaky, wip) and platform capability (ios-only, android-only).
quarantine (flaky, wip), platform capability (ios-only, android-only), and
feature (apple-pay).

Options:
--tags TAG[,TAG...] Run only tests carrying any of these tags.
Expand Down Expand Up @@ -119,6 +120,28 @@ parse_maestro_tag_args() {
done
}

select_run_profile() {
case ",${INCLUDE_TAGS}," in
*",apple-pay,"*)
# Other targets stay on the normal profile, where run_maestro excludes
# Apple Pay. Only the Swift iOS sample can opt into this profile.
[ "$TARGET" = "swift-ios" ] || return 0

if [ "$INCLUDE_TAGS" != "apple-pay" ]; then
echo "--tags apple-pay must be requested by itself" >&2
return 1
fi
case ",${EXCLUDE_TAGS}," in
*",apple-pay,"*)
echo "apple-pay cannot be both included and excluded" >&2
return 1
;;
esac
RUN_PROFILE="swift-apple-pay"
;;
esac
}

metro_running() {
curl --silent --fail http://localhost:8081/status | grep -q "packager-status:running"
}
Expand Down Expand Up @@ -245,10 +268,16 @@ main() {

INCLUDE_TAGS=""
EXCLUDE_TAGS=""
RUN_PROFILE="normal"
METRO_PID=""
E2E_ENV_FILE=""

parse_maestro_tag_args "$@"
select_run_profile

if [ "$RUN_PROFILE" = "swift-apple-pay" ]; then
"$REPO_ROOT/e2e/scripts/check_swift_apple_pay_config"
fi

"$REPO_ROOT/e2e/scripts/maestro_bin" >/dev/null
trap cleanup EXIT
Expand All @@ -258,6 +287,7 @@ main() {
build_and_install

E2E_DEVICE_ID="$DEVICE_ID" \
E2E_RUN_PROFILE="$RUN_PROFILE" \
"$REPO_ROOT/e2e/scripts/run_maestro" \
"$PLATFORM" "$APP_ID" "$READY_MARKER" "$INCLUDE_TAGS" "$EXCLUDE_TAGS" "$TEST_NAMESPACE"
}
Expand Down
29 changes: 29 additions & 0 deletions e2e/scripts/run_maestro
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ INCLUDE_TAGS="${4:-}"
EXCLUDE_TAGS="${5:-}"
TEST_NAMESPACE="${6:?test namespace is required}"
DEVICE_ID="${E2E_DEVICE_ID:-}"
RUN_PROFILE="${E2E_RUN_PROFILE:-normal}"

E2E_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$E2E_ROOT"
Expand Down Expand Up @@ -43,6 +44,34 @@ exclude_tag() {

REQUESTED_TAGS="$INCLUDE_TAGS"

# Normal runs must never select Apple Pay accidentally. The Swift local runner opts in
# only after confirming that its generated app configuration contains a merchant ID.
case "$RUN_PROFILE" in
normal)
exclude_tag "apple-pay"
;;
swift-apple-pay)
if [ "$PLATFORM" != "ios" ] || [ "$APP_ID" != "com.shopify.checkoutkit.swiftdemo" ]; then
echo "run_maestro: swift-apple-pay requires the Swift iOS sample app" >&2
exit 1
fi
if [ "$INCLUDE_TAGS" != "apple-pay" ]; then
echo "run_maestro: swift-apple-pay requires --tags apple-pay by itself" >&2
exit 1
fi
case ",${EXCLUDE_TAGS}," in
*",apple-pay,"*)
echo "run_maestro: swift-apple-pay cannot exclude apple-pay" >&2
exit 1
;;
esac
;;
*)
echo "run_maestro: profile must be normal or swift-apple-pay, got '${RUN_PROFILE}'" >&2
exit 1
;;
esac

# Every local target executes the shared tests folder. A platform capability tag marks
# a test that cannot run on the other platform, so derive the mandatory exclusion from
# the platform instead of repeating it in four wrappers.
Expand Down
71 changes: 71 additions & 0 deletions e2e/test/check_swift_apple_pay_config_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# frozen_string_literal: true

require "minitest/autorun"
require "open3"
require "tempfile"

class CheckSwiftApplePayConfigTest < Minitest::Test
REPO_ROOT = File.expand_path("../..", __dir__)
SCRIPT = File.join(REPO_ROOT, "e2e", "scripts", "check_swift_apple_pay_config")
SYNTHETIC_IDENTIFIER = "merchant.com.example.e2e"

def test_script_is_executable_bash
assert File.executable?(SCRIPT)

_output, error, status = Open3.capture3("bash", "-n", SCRIPT)

assert status.success?, error
end

def test_missing_config_fails_without_printing_a_value
Tempfile.create("missing-swift-storefront") do |file|
path = file.path
file.close
File.unlink(path)

output, error, status = Open3.capture3(SCRIPT, path)

refute status.success?
assert_empty output
assert_includes error, "APPLE_PAY_MERCHANT_IDENTIFIER"
refute_includes error, SYNTHETIC_IDENTIFIER
end
end

def test_missing_or_blank_assignment_fails
[
"STOREFRONT_DOMAIN = example.invalid\n",
"APPLE_PAY_MERCHANT_IDENTIFIER = \n"
].each do |contents|
with_config(contents) do |path|
output, error, status = Open3.capture3(SCRIPT, path)

refute status.success?
assert_empty output
assert_includes error, "nonblank APPLE_PAY_MERCHANT_IDENTIFIER"
refute_includes error, SYNTHETIC_IDENTIFIER
end
end
end

def test_nonblank_assignment_succeeds_without_printing_the_value
with_config("APPLE_PAY_MERCHANT_IDENTIFIER = #{SYNTHETIC_IDENTIFIER}\n") do |path|
output, error, status = Open3.capture3(SCRIPT, path)

assert status.success?, error
assert_empty output
assert_empty error
refute_includes "#{output}#{error}", SYNTHETIC_IDENTIFIER
end
end

private

def with_config(contents)
Tempfile.create(["swift-storefront", ".xcconfig"]) do |file|
file.write(contents)
file.flush
yield file.path
end
end
end
10 changes: 8 additions & 2 deletions e2e/test/e2e_matrix_to_browserstack_run_plan_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,14 @@ def test_runs_carry_default_tags_and_the_other_platform_exclusion
android_run = run_for("kotlin-android")

assert_equal ["launch", "checkout"], ios_run.fetch("include_tags")
assert_equal ["flaky", "wip", "full", "android-only"], ios_run.fetch("exclude_tags")
assert_equal ["flaky", "wip", "full", "ios-only"], android_run.fetch("exclude_tags")
assert_equal ["flaky", "wip", "full", "apple-pay", "android-only"], ios_run.fetch("exclude_tags")
assert_equal ["flaky", "wip", "full", "apple-pay", "ios-only"], android_run.fetch("exclude_tags")
end

def test_apple_pay_is_excluded_from_every_application_by_default
plan.expand.each do |run|
assert_includes run.fetch("exclude_tags"), "apple-pay", run.fetch("application_id")
end
end

def test_an_application_overrides_the_default_tags
Expand Down
3 changes: 2 additions & 1 deletion e2e/test/maestro_test_tags_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ class MaestroTestTagsTest < Minitest::Test
COST_TIER_TAGS = ["smoke", "full"].freeze
QUARANTINE_TAGS = ["flaky", "wip"].freeze
PLATFORM_TAGS = ["ios-only", "android-only"].freeze
KNOWN_TAGS = (JOURNEY_TAGS + COST_TIER_TAGS + QUARANTINE_TAGS + PLATFORM_TAGS).freeze
FEATURE_TAGS = ["apple-pay"].freeze
KNOWN_TAGS = (JOURNEY_TAGS + COST_TIER_TAGS + QUARANTINE_TAGS + PLATFORM_TAGS + FEATURE_TAGS).freeze

def test_files
Dir.glob("tests/**/*.yaml", base: E2E_ROOT).sort
Expand Down
57 changes: 57 additions & 0 deletions e2e/test/run_local_e2e_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,63 @@ def test_maestro_runs_shared_and_target_specific_test_files
refute_includes swift_arguments, "."
end

def selected_profile(target, include_tags, exclude_tags = "")
output, error, status = Open3.capture3(
"bash",
"-c",
<<~'SH',
source "$1"
configure_target "$2"
INCLUDE_TAGS="$3"
EXCLUDE_TAGS="$4"
RUN_PROFILE="normal"
select_run_profile
printf '%s\n' "$RUN_PROFILE"
SH
"run-local-e2e-test",
RUNNER,
target,
include_tags,
exclude_tags
)

[output.lines.map(&:chomp), error, status]
end

def test_swift_apple_pay_selects_its_profile
selected, error, status = selected_profile("swift-ios", "apple-pay")

assert status.success?, error
assert_equal ["swift-apple-pay"], selected
end

def test_swift_apple_pay_rejects_mixed_or_excluded_tags
_selected, error, status = selected_profile("swift-ios", "apple-pay,checkout")
refute status.success?
assert_includes error, "must be requested by itself"

_selected, error, status = selected_profile("swift-ios", "apple-pay", "apple-pay")
refute status.success?
assert_includes error, "cannot be both included and excluded"
end

def test_non_swift_target_stays_on_the_normal_profile
selected, error, status = selected_profile("kotlin-android", "apple-pay")

assert status.success?, error
assert_equal ["normal"], selected
end

def test_apple_pay_config_is_checked_before_the_swift_build
script = File.read(RUNNER)
check_index = script.index('"$REPO_ROOT/e2e/scripts/check_swift_apple_pay_config"')
build_index = script.index(" build_and_install\n", check_index)

refute_nil check_index
refute_nil build_index
assert_operator check_index, :<, build_index
end

def test_target_specific_and_old_named_runners_are_removed
REMOVED_RUNNERS.each do |path|
refute_path_exists File.join(REPO_ROOT, path)
Expand Down
Loading
Loading