diff --git a/.buildkite/cache-builder.yml b/.buildkite/cache-builder.yml index 3e11fb963796..405b5c32eae3 100644 --- a/.buildkite/cache-builder.yml +++ b/.buildkite/cache-builder.yml @@ -7,7 +7,7 @@ common_params: # Common plugin settings to use with the `plugins` key. - &common_plugins - - automattic/a8c-ci-toolkit#2.17.0 + - automattic/a8c-ci-toolkit#2.18.1 - automattic/git-s3-cache#1.1.4: bucket: "a8c-repo-mirrors" repo: "automattic/wordpress-ios/" diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 2db9e6d19145..ac9e016e4030 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -2,7 +2,7 @@ common_params: # Common plugin settings to use with the `plugins` key. - &common_plugins - - automattic/a8c-ci-toolkit#2.17.0 + - automattic/a8c-ci-toolkit#2.18.1 - automattic/git-s3-cache#1.1.4: bucket: "a8c-repo-mirrors" repo: "automattic/wordpress-ios/" diff --git a/.buildkite/release-builds.yml b/.buildkite/release-builds.yml index 1003df4508be..53447e466cdc 100644 --- a/.buildkite/release-builds.yml +++ b/.buildkite/release-builds.yml @@ -4,7 +4,7 @@ common_params: # Common plugin settings to use with the `plugins` key. - &common_plugins - - automattic/a8c-ci-toolkit#2.17.0 + - automattic/a8c-ci-toolkit#2.18.1 - automattic/git-s3-cache#1.1.4: bucket: "a8c-repo-mirrors" repo: "automattic/wordpress-ios/" diff --git a/Gutenberg/cocoapods_helpers.rb b/Gutenberg/cocoapods_helpers.rb index bb673c6ca789..856305a41f43 100644 --- a/Gutenberg/cocoapods_helpers.rb +++ b/Gutenberg/cocoapods_helpers.rb @@ -2,6 +2,7 @@ # Helpers and configurations for integrating Gutenberg in Jetpack and WordPress via CocoaPods. +require 'json' require_relative './version' DEFAULT_GUTENBERG_LOCATION = File.join(__dir__, '..', '..', 'gutenberg-mobile') @@ -29,36 +30,37 @@ RNCMaskedView RNCClipboard RNFastImage - React-jsc ].freeze def gutenberg_pod(config: GUTENBERG_CONFIG) + # We check local_gutenberg first because it should take precedence, being an override set by the user. + return gutenberg_local_pod if should_use_local_gutenberg + options = config - # We check local_gutenberg first because it should take precedence, being an override set by the user. - if should_use_local_gutenberg - options = { path: local_gutenberg_path } + id = options[:tag] || options[:commit] - raise "Could not find Gutenberg pod at #{options[:path]}. You can configure the path using the #{LOCAL_GUTENBERG_KEY} environment variable." unless File.exist?(options[:path]) + # Notice there's no period at the end of the message as CocoaPods will add it. + raise 'Neither tag nor commit to use for Gutenberg found' unless id + + pod 'Gutenberg', podspec: "https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-#{id}.podspec" +end - puts "[Gutenberg] Installing pods using local Gutenberg version from #{local_gutenberg_path}" +def gutenberg_local_pod + options = { path: local_gutenberg_path } - react_native_path = require_react_native_helpers!(gutenberg_path: local_gutenberg_path) + raise "Could not find Gutenberg pod at #{options[:path]}. You can configure the path using the #{LOCAL_GUTENBERG_KEY} environment variable." unless File.exist?(options[:path]) - use_react_native! path: react_native_path + puts "[Gutenberg] Installing pods using local Gutenberg version from #{local_gutenberg_path}" - pod 'Gutenberg', options - pod 'RNTAztecView', options + react_native_path = require_react_native_helpers!(gutenberg_path: local_gutenberg_path) - gutenberg_dependencies(options: options) - else - id = options[:tag] || options[:commit] + use_react_native! path: react_native_path - # Notice there's no period at the end of the message as CocoaPods will add it. - raise 'Neither tag nor commit to use for Gutenberg found' unless id + pod 'Gutenberg', options + pod 'RNTAztecView', options - pod 'Gutenberg', podspec: "https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-#{id}.podspec" - end + gutenberg_dependencies(options: options) end def gutenberg_dependencies(options:) @@ -66,6 +68,7 @@ def gutenberg_dependencies(options:) return if options.key?(:tag) || options.key?(:commit) podspec_prefix = options[:path] + gutenberg_path = options[:path] raise "Unexpected Gutenberg dependencies configuration '#{options}'" if podspec_prefix.nil? @@ -74,28 +77,31 @@ def gutenberg_dependencies(options:) computed_dependencies = DEPENDENCIES.dup - # Use a custom RNReanimated version while we coordinate a fix upstream - computed_dependencies.delete('RNReanimated') - pod 'RNReanimated', git: 'https://github.com/wordpress-mobile/react-native-reanimated', branch: 'mokagio/fix-custom-node_modules-bypass-multiple-versions-check-2.17.0' + react_native_version = react_native_version!(gutenberg_path: gutenberg_path) + # We need to apply a workaround for the RNReanimated library when using React Native 0.71+. + apply_rnreanimated_workaround!(dependencies: computed_dependencies, gutenberg_path: gutenberg_path) unless react_native_version[1] < 71 computed_dependencies.each do |pod_name| pod pod_name, podspec: "#{podspec_prefix}/#{pod_name}.#{podspec_extension}" end end -def gutenberg_pre_install - return unless should_use_local_gutenberg +def apply_rnreanimated_workaround!(dependencies:, gutenberg_path:) + # RNReanimated needs React-jsc + # Reference: https://github.com/WordPress/gutenberg/pull/51386/commits/9538f8eaf73dfacef17382e1ab7feda40231061f + dependencies.push('React-jsc') - raise "[Gutenberg] Could not find local Gutenberg at given path #{local_gutenberg_path}" unless File.exist?(local_gutenberg_path) + # Use a custom RNReanimated version while we coordinate a fix upstream + dependencies.delete('RNReanimated') # This is required to workaround an issue with RNReanimated after upgrading to version 2.17.0 - rn_node_modules = File.join(local_gutenberg_path, 'node_modules') - + rn_node_modules = File.join(gutenberg_path, '..', 'gutenberg', 'node_modules') raise "Could not find node modules at given path #{rn_node_modules}" unless File.exist? rn_node_modules ENV['REACT_NATIVE_NODE_MODULES_DIR'] = rn_node_modules - puts "[Gutenberg] Set REACT_NATIVE_NODE_MODULES_DIR env var for RNReanimated to #{rn_node_modules}" + + pod 'RNReanimated', git: 'https://github.com/wordpress-mobile/react-native-reanimated', branch: 'wp-fork-2.17.0' end def gutenberg_post_install(installer:) @@ -105,7 +111,7 @@ def gutenberg_post_install(installer:) react_native_path = require_react_native_helpers!(gutenberg_path: local_gutenberg_path) - puts "[Gutenberg] Running Gunberg post install hook (RN path: #{react_native_path})" + puts "[Gutenberg] Running Gutenberg post install hook (RN path: #{react_native_path})" # It seems like React Native prepends $PWD to the path internally in the post install hook. # When using an absolute path, we get this error, notice the duplicated "/Users/gio/Developer/a8c/wpios": @@ -155,3 +161,14 @@ def react_native_path!(gutenberg_path:) react_native_path end + +def react_native_version!(gutenberg_path:) + react_native_path = react_native_path!(gutenberg_path: gutenberg_path) + package_json_path = File.join(react_native_path, 'package.json') + package_json_content = File.read(package_json_path) + package_json_version = JSON.parse(package_json_content)['version'] + + raise "[Gutenberg] Could not find React native version at #{react_native_path}" unless package_json_version + + package_json_version.split('.').map(&:to_i) +end diff --git a/Gutenberg/version.rb b/Gutenberg/version.rb index 371376554142..b4a0c8c4394e 100644 --- a/Gutenberg/version.rb +++ b/Gutenberg/version.rb @@ -11,8 +11,8 @@ # # LOCAL_GUTENBERG=../my-gutenberg-fork bundle exec pod install GUTENBERG_CONFIG = { - # commit: '' - tag: 'v1.100.0-alpha1' + commit: 'd2eab790e3c4333a059f920ce6c4ced5b9c11112' + # tag: 'v1.100.0-alpha1' } GITHUB_ORG = 'wordpress-mobile' diff --git a/Podfile b/Podfile index f2e5d35e9c0c..d0faa34c292c 100644 --- a/Podfile +++ b/Podfile @@ -317,9 +317,6 @@ end # A future version of CocoaPods may make this easier to do. See https://github.com/CocoaPods/CocoaPods/issues/7428 shared_targets = ['WordPressFlux'] pre_install do |installer| - # Note that this call is unrelated to the static vs dynamic framework shenanigan below - gutenberg_pre_install - static = [] dynamic = [] installer.pod_targets.each do |pod| diff --git a/Podfile.lock b/Podfile.lock index c30bb71e0981..448c3625c4c8 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -40,7 +40,7 @@ PODS: - AppAuth/Core (~> 1.6) - GTMSessionFetcher/Core (< 3.0, >= 1.5) - GTMSessionFetcher/Core (1.7.2) - - Gutenberg (1.99.0) + - Gutenberg (1.99.1) - JTAppleCalendar (8.0.3) - Kanvas (1.4.4) - MediaEditor (1.2.2): @@ -150,7 +150,7 @@ DEPENDENCIES: - FSInteractiveMap (from `https://github.com/wordpress-mobile/FSInteractiveMap.git`, tag `0.2.0`) - Gifu (= 3.2.0) - Gridicons (~> 1.1.0) - - Gutenberg (from `https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-v1.100.0-alpha1.podspec`) + - Gutenberg (from `https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-d2eab790e3c4333a059f920ce6c4ced5b9c11112.podspec`) - JTAppleCalendar (~> 8.0.2) - Kanvas (~> 1.4.4) - MediaEditor (>= 1.2.2, ~> 1.2) @@ -227,7 +227,7 @@ EXTERNAL SOURCES: :git: https://github.com/wordpress-mobile/FSInteractiveMap.git :tag: 0.2.0 Gutenberg: - :podspec: https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-v1.100.0-alpha1.podspec + :podspec: https://cdn.a8c-ci.services/gutenberg-mobile/Gutenberg-d2eab790e3c4333a059f920ce6c4ced5b9c11112.podspec CHECKOUT OPTIONS: FSInteractiveMap: @@ -254,7 +254,7 @@ SPEC CHECKSUMS: Gridicons: 17d660b97ce4231d582101b02f8280628b141c9a GTMAppAuth: 0ff230db599948a9ad7470ca667337803b3fc4dd GTMSessionFetcher: 5595ec75acf5be50814f81e9189490412bad82ba - Gutenberg: c570bf8955cbe174af06bec982811f9483bbe74f + Gutenberg: 2e88cc1ba60ff2c46dffed6d94aaf3758cbfc646 JTAppleCalendar: 932cadea40b1051beab10f67843451d48ba16c99 Kanvas: f932eaed3d3f47aae8aafb6c2d27c968bdd49030 MediaEditor: d08314cfcbfac74361071a306b4bc3a39b3356ae @@ -288,6 +288,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 3a8e508ab1d9dd22dc038df6c694466414e037ba ZIPFoundation: ae5b4b813d216d3bf0a148773267fff14bd51d37 -PODFILE CHECKSUM: 3e728a64a014f4eb4d75b54d470f9ee4b66e26d8 +PODFILE CHECKSUM: b995ca3ba695e70fd1f2f728800a419cf418f6ec COCOAPODS: 1.12.1 diff --git a/WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift b/WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift index b0272f6fe174..bfe10257977b 100644 --- a/WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift +++ b/WordPress/UITestsFoundation/Screens/Editor/BlockEditorScreen.swift @@ -57,9 +57,7 @@ public class BlockEditorScreen: ScreenObject { public func enterTextInTitle(text: String) -> BlockEditorScreen { let titleView = app.otherElements["Post title. Empty"].firstMatch // Uses a localized string XCTAssert(titleView.waitForExistence(timeout: 3), "Title View does not exist!") - - titleView.tap() - titleView.typeText(text) + type(text: text, in: titleView) return self } @@ -72,7 +70,7 @@ public class BlockEditorScreen: ScreenObject { addBlock("Paragraph block") let paragraphView = app.otherElements["Paragraph Block. Row 1. Empty"].textViews.element(boundBy: 0) - paragraphView.typeText(text) + type(text: text, in: paragraphView) return self } @@ -368,4 +366,44 @@ public class BlockEditorScreen: ScreenObject { editorCloseButton.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0)).tap() return try BlockEditorScreen() } + + // This could be moved as an XCUIApplication method via an extension if needed elsewhere. + private func type(text: String, in element: XCUIElement) { + // A simple implementation here would be: + // + // element.tap() + // element.typeText(text) + // + // But as of a recent (but not pinpointed at the time of writing) Gutenberg update, that is not enough. + // The test would fail with: Neither element nor any descendant has keyboard focus. + // (E.g.: https://buildkite.com/automattic/wordpress-ios/builds/15598) + // + // The following is a convoluted but seemingly robust approach that bypasses the keyboard by using the pasteboard instead. + UIPasteboard.general.string = text + + // Safety check + XCTAssertTrue(element.waitForExistence(timeout: 1)) + + element.doubleTap() + + let pasteButton = app.menuItems["Paste"] + + if pasteButton.waitForExistence(timeout: 1) == false { + // Drill in hierarchy looking for it + var found = false + element.descendants(matching: .any).enumerated().forEach { e in + guard found == false else { return } + + e.element.firstMatch.doubleTap() + + if pasteButton.waitForExistence(timeout: 1) { + found = true + } + } + } + + XCTAssertTrue(pasteButton.exists, "Could not find menu item paste button.") + + pasteButton.tap() + } } diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 8e5c46a2ad67..701a17d89f38 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -20162,6 +20162,7 @@ "${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework", "${BUILT_PRODUCTS_DIR}/SentryPrivate/SentryPrivate.framework", "${PODS_XCFRAMEWORKS_BUILD_DIR}/Gutenberg/Gutenberg.framework/Gutenberg", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/Gutenberg/hermes.framework/hermes", "${PODS_XCFRAMEWORKS_BUILD_DIR}/ZendeskCommonUISDK/CommonUISDK.framework/CommonUISDK", "${PODS_XCFRAMEWORKS_BUILD_DIR}/ZendeskCoreSDK/ZendeskCoreSDK.framework/ZendeskCoreSDK", "${PODS_XCFRAMEWORKS_BUILD_DIR}/ZendeskMessagingAPISDK/MessagingAPI.framework/MessagingAPI", @@ -20175,6 +20176,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SentryPrivate.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Gutenberg.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CommonUISDK.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZendeskCoreSDK.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MessagingAPI.framework", @@ -20238,10 +20240,12 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-WordPressTest/Pods-WordPressTest-frameworks.sh", "${PODS_XCFRAMEWORKS_BUILD_DIR}/Gutenberg/Gutenberg.framework/Gutenberg", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/Gutenberg/hermes.framework/hermes", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Gutenberg.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -20765,6 +20769,7 @@ "${BUILT_PRODUCTS_DIR}/Sentry/Sentry.framework", "${BUILT_PRODUCTS_DIR}/SentryPrivate/SentryPrivate.framework", "${PODS_XCFRAMEWORKS_BUILD_DIR}/Gutenberg/Gutenberg.framework/Gutenberg", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/Gutenberg/hermes.framework/hermes", "${PODS_XCFRAMEWORKS_BUILD_DIR}/ZendeskCommonUISDK/CommonUISDK.framework/CommonUISDK", "${PODS_XCFRAMEWORKS_BUILD_DIR}/ZendeskCoreSDK/ZendeskCoreSDK.framework/ZendeskCoreSDK", "${PODS_XCFRAMEWORKS_BUILD_DIR}/ZendeskMessagingAPISDK/MessagingAPI.framework/MessagingAPI", @@ -20778,6 +20783,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sentry.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SentryPrivate.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Gutenberg.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CommonUISDK.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ZendeskCoreSDK.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MessagingAPI.framework",