diff --git a/.github/workflows/ios-tests.yml b/.github/workflows/ios-tests.yml index 38cda2cd86ae22..b60f2c8c5efeb9 100644 --- a/.github/workflows/ios-tests.yml +++ b/.github/workflows/ios-tests.yml @@ -77,13 +77,11 @@ jobs: name: ios-build-output path: build - # The runner's Node is older than the repository requires, and - # npx enforces that requirement when it starts Playground. - - name: Use desired version of Node.js - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + - name: Set up PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 with: - node-version-file: .nvmrc - package-manager-cache: false + php-version: '8.4' + coverage: none - name: Install xcodegen run: brew install xcodegen @@ -105,5 +103,7 @@ jobs: if: ${{ always() }} with: name: ios-test-results - path: test/ios/build/results.xcresult + path: | + test/ios/build/results.xcresult + test/ios/build/server.log if-no-files-found: ignore diff --git a/test/ios/README.md b/test/ios/README.md index 75a4ab4f09af57..51f6cc3734f9d6 100644 --- a/test/ios/README.md +++ b/test/ios/README.md @@ -4,7 +4,7 @@ XCUITest suites that drive Safari on an iOS simulator, for behavior only touch gestures show, such as selecting text with the selection handles. Run them on macOS with Xcode and [xcodegen](https://github.com/yonaskolb/XcodeGen) -installed, on the Node version in `.nvmrc`, after building the plugin: +installed and PHP 8 with SQLite, after building the plugin: ```bash nvm use @@ -12,9 +12,9 @@ npm run build ./test/ios/run.sh ``` -The script serves the checkout with WordPress Playground, boots a simulator -and runs `xcodebuild test`. Screenshots of failures are in -`test/ios/build/results.xcresult`. +The script installs WordPress with SQLite, serves it and the checkout with +PHP's built-in server, boots a simulator and runs `xcodebuild test`. +Screenshots of failures are in `test/ios/build/results.xcresult`. The tests read the software keyboard, so the simulator must show one. A simulator that has ever had a hardware keyboard connected keeps hiding the diff --git a/test/ios/SafariTests/SelectionTests.swift b/test/ios/SafariTests/SelectionTests.swift index a3b7c4b391c01d..4d8f9ddcb6bcda 100644 --- a/test/ios/SafariTests/SelectionTests.swift +++ b/test/ios/SafariTests/SelectionTests.swift @@ -28,8 +28,8 @@ final class SelectionTests: XCTestCase { } func testHandleExtendsSelectionIntoPreviousParagraph() throws { - // A new post starting with this content: the blueprint passes the - // content argument of post-new.php through unescaped. + // A new post starting with this content: run.sh passes the content + // argument of post-new.php through unescaped. let base = ProcessInfo.processInfo.environment[ "WP_BASE_URL" ] ?? "http://127.0.0.1:9400" var url = URLComponents( string: base + "/wp-admin/post-new.php" )! url.queryItems = [ URLQueryItem( diff --git a/test/ios/blueprint.json b/test/ios/blueprint.json deleted file mode 100644 index 4d4207776110c6..00000000000000 --- a/test/ios/blueprint.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "https://playground.wordpress.net/blueprint-schema.json", - "login": true, - "steps": [ - { - "step": "runPHP", - "code": " array( 'welcomeGuide' => false, 'fullscreenMode' => false ), '_modified' => gmdate( 'c' ) ) );" - }, - { - "step": "writeFile", - "path": "/wordpress/wp-content/mu-plugins/new-post-content.php", - "data": "/dev/null || true -# WordPress runs in Playground: PHP compiled to WebAssembly, no Docker. +# WordPress runs on PHP's built-in server with SQLite, set up fresh on +# every run. step "Starting WordPress" -PLAYGROUND_LOG=$( mktemp ) -# npx runs outside the checkout: the checkout's npm settings refuse -# Playground's install scripts. ROOT="$PWD" -( cd "$( mktemp -d )" && exec npx --yes @wp-playground/cli@3.1.53 server \ - --auto-mount="$ROOT" \ - --blueprint="$ROOT/test/ios/blueprint.json" \ - --port "$PORT" ) > "$PLAYGROUND_LOG" 2>&1 & -PLAYGROUND_PID=$! -trap 'kill "$PLAYGROUND_PID" 2>/dev/null || true' EXIT -# Playground prints "Ready!" once it listens and the blueprint has run; the -# server answers earlier, before the plugin is active. -until grep -q "Ready!" "$PLAYGROUND_LOG"; do - kill -0 "$PLAYGROUND_PID" 2>/dev/null || { cat "$PLAYGROUND_LOG"; echo "WordPress did not start"; exit 1; } +CACHE="$ROOT/test/ios/build/cache" +WP_DIR="$ROOT/test/ios/build/wordpress" +mkdir -p "$CACHE" +step "Downloading WP-CLI and SQLite" +# A failed download leaves no file behind, so the next run tries again. +fetch() { [ -f "$2" ] || { curl -sSLf -o "$2.part" "$1" && mv "$2.part" "$2"; }; } +fetch https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar "$CACHE/wp-cli.phar" +fetch https://downloads.wordpress.org/plugin/sqlite-database-integration.latest-stable.zip "$CACHE/sqlite.zip" +# Without the JIT: compiling WordPress takes longer than running it. +wp() { php -d memory_limit=512M -d opcache.jit=disable "$CACHE/wp-cli.phar" --path="$WP_DIR" --quiet "$@"; } +rm -rf "$WP_DIR" +# The same WordPress as the end-to-end tests: a GitHub repository, with +# an optional #branch, from .wp-env.test.json. +CORE=$( python3 -c ' +import json, sys +repo, _, branch = json.load( open( ".wp-env.test.json" ) )[ "core" ].partition( "#" ) +if repo.count( "/" ) != 1: + sys.exit( "Unsupported core in .wp-env.test.json: " + repo ) +print( repo, branch ) +' ) +read -r REPO BRANCH <<< "$CORE" +step "Downloading WordPress from $REPO" +git clone --quiet --depth 1 ${BRANCH:+--branch "$BRANCH"} "https://github.com/$REPO.git" "$WP_DIR" +unzip -q "$CACHE/sqlite.zip" -d "$WP_DIR/wp-content/plugins" +SQLITE="$WP_DIR/wp-content/plugins/sqlite-database-integration" +sed -e "s#{SQLITE_IMPLEMENTATION_FOLDER_PATH}#$SQLITE#" -e "s#{SQLITE_PLUGIN}#sqlite-database-integration/load.php#" \ + "$SQLITE/db.copy" > "$WP_DIR/wp-content/db.php" +step "Installing WordPress" +wp config create --dbname=wordpress --dbuser=wordpress --dbpass=wordpress --skip-check +# No requests to other sites: a fresh site's first admin page otherwise +# checks wordpress.org for updates. +wp config set WP_HTTP_BLOCK_EXTERNAL true --raw +wp core install --url="$WP_BASE_URL" --title="iOS tests" --admin_user=admin --admin_password=password --admin_email=admin@example.com --skip-email +step "Activating the plugin" +ln -s "$ROOT" "$WP_DIR/wp-content/plugins/gutenberg" +wp plugin activate gutenberg +# The editor opens without the welcome guide or fullscreen mode. +wp user meta update 1 wp_persisted_preferences --format=json \ + "{\"core/edit-post\":{\"welcomeGuide\":false,\"fullscreenMode\":false},\"_modified\":\"$( date -u +%Y-%m-%dT%H:%M:%S+00:00 )\"}" +mkdir -p "$WP_DIR/wp-content/mu-plugins" +# A new post starts with the content argument of post-new.php, unescaped. +cat > "$WP_DIR/wp-content/mu-plugins/new-post-content.php" <<'PHP' + "$WP_DIR/wp-content/mu-plugins/login.php" <<'PHP' + "$ROOT/test/ios/build/server.log" 2>&1 & +SERVER_PID=$! +# The server's workers are its child processes: stop them too. +trap 'pkill -P "$SERVER_PID" 2>/dev/null; kill "$SERVER_PID" 2>/dev/null || true' EXIT +until curl -sf -o /dev/null "$WP_BASE_URL/wp-login.php"; do + kill -0 "$SERVER_PID" 2>/dev/null || { cat "$ROOT/test/ios/build/server.log"; echo "WordPress did not start"; exit 1; } sleep 1 done -cat "$PLAYGROUND_LOG" # The editor page shows whether the plugin replaces core's bundles, which # it only does when its build exists. Its first request also does the # one-time work of a fresh site before the test's clock starts. step "Loading the editor once" -# Playground logs the request in through a redirect, so cookies must be -# kept between the hops: -b "" holds them in memory. +# -b "" keeps the login cookie between redirects, in memory. if ! EDITOR_HTML=$( curl -sSL --fail -b "" "$WP_BASE_URL/wp-admin/post-new.php" ); then echo "The editor did not load from $WP_BASE_URL" + tail -40 "$ROOT/test/ios/build/server.log" exit 1 fi if ! grep -q "/build/scripts/rich-text/" <<< "$EDITOR_HTML"; then @@ -77,8 +135,18 @@ xcrun simctl bootstatus "$UDID" -b # Safari's tips cover the page on a fresh simulator, and a tap then only # closes the tip. xcrun simctl spawn "$UDID" defaults write com.apple.mobilesafari com.apple.TipKit.HideAllTips -bool YES -# Playground logs a browser in once and remembers that in a cookie, which -# outlives the server: start Safari without cookies from an earlier run. +# Safari's first launch on a fresh simulator takes most of a minute, and +# the simulator then loses the test's first tap. Launch it once here. +step "Starting Safari once" +xcrun simctl openurl "$UDID" "$WP_BASE_URL/wp-login.php?warm-up" +for _ in $( seq 1 120 ); do + grep -q "wp-login.php?warm-up" "$ROOT/test/ios/build/server.log" && break + sleep 1 +done +grep -q "wp-login.php?warm-up" "$ROOT/test/ios/build/server.log" || + { echo "Safari did not open the page"; exit 1; } +# The login is remembered in a cookie, which outlives the server: start +# Safari without cookies from an earlier run. xcrun simctl terminate "$UDID" com.apple.mobilesafari 2>/dev/null || true SAFARI_DATA=$( xcrun simctl get_app_container "$UDID" com.apple.mobilesafari data ) if [[ -n "$SAFARI_DATA" && -d "$SAFARI_DATA" ]]; then