diff --git a/src/Admin/Provisioning.php b/src/Admin/Provisioning.php index 95c37095..6d395c12 100644 --- a/src/Admin/Provisioning.php +++ b/src/Admin/Provisioning.php @@ -19,15 +19,33 @@ use Plausible\Analytics\WP\Integrations\WooCommerce; class Provisioning { + const CUSTOM_PROPERTIES = [ + 'cart_total', + 'cart_total_items', + 'id', + 'name', + 'price', + 'product_id', + 'product_name', + 'quantity', + 'shipping', + 'subtotal', + 'subtotal_tax', + 'tax_class', + 'total', + 'total_tax', + 'variation_id', + ]; + /** - * @var ClientFactory + * @var Client $client */ - private $client_factory; + public $client; /** - * @var Client $client + * @var ClientFactory */ - private $client; + private $client_factory; /** * @var string[] $custom_event_goals @@ -90,9 +108,7 @@ private function init() { add_action( 'update_option_plausible_analytics_settings', [ $this, 'create_shared_link' ], 10, 2 ); add_action( 'update_option_plausible_analytics_settings', [ $this, 'maybe_create_goals' ], 10, 2 ); - add_action( 'update_option_plausible_analytics_settings', [ $this, 'maybe_create_woocommerce_funnel' ], 10, 2 ); add_action( 'update_option_plausible_analytics_settings', [ $this, 'maybe_delete_goals' ], 11, 2 ); - add_action( 'update_option_plausible_analytics_settings', [ $this, 'maybe_delete_woocommerce_goals' ], 11, 2 ); add_action( 'update_option_plausible_analytics_settings', [ $this, 'maybe_create_custom_properties' ], 11, 2 ); } @@ -179,7 +195,7 @@ public function create_goal_request( $name, $type = 'CustomEvent', $currency = ' * * @return void */ - private function create_goals( $goals ) { + public function create_goals( $goals ) { if ( empty( $goals ) ) { return; // @codeCoverageIgnore } @@ -253,7 +269,7 @@ public function maybe_create_woocommerce_funnel( $old_settings, $settings ) { * @return void * @codeCoverageIgnore Because this method should be mocked in tests if needed. */ - private function create_funnel( $name, $steps ) { + public function create_funnel( $name, $steps ) { $create_request = new Client\Model\FunnelCreateRequest( [ 'funnel' => [ @@ -365,7 +381,7 @@ public function maybe_delete_woocommerce_goals( $old_settings, $settings ) { * @return false|mixed * @codeCoverageIgnore Because it can't be unit tested. */ - private function array_search_contains( $string, $haystack ) { + public function array_search_contains( $string, $haystack ) { if ( preg_match( '/\([A-Z]*?\)/', $string ) ) { $string = preg_replace( '/ \([A-Z]*?\)/', '', $string ); } @@ -410,8 +426,8 @@ public function maybe_create_custom_properties( $old_settings, $settings ) { /** * Create Custom Properties for WooCommerce integration. */ - if ( Helpers::is_enhanced_measurement_enabled( 'revenue', $enhanced_measurements ) && Integrations::is_wc_active() ) { - foreach ( WooCommerce::CUSTOM_PROPERTIES as $property ) { + if ( Helpers::is_enhanced_measurement_enabled( 'revenue', $enhanced_measurements ) && ( Integrations::is_wc_active() || Integrations::is_edd_active() ) ) { + foreach ( self::CUSTOM_PROPERTIES as $property ) { $properties[] = new Client\Model\CustomProp( [ 'custom_prop' => [ 'key' => $property ] ] ); } } diff --git a/src/Admin/Provisioning/Integrations.php b/src/Admin/Provisioning/Integrations.php new file mode 100644 index 00000000..7ec94e88 --- /dev/null +++ b/src/Admin/Provisioning/Integrations.php @@ -0,0 +1,110 @@ +provisioning = new Provisioning(); + + $this->init(); + } + + /** + * Action & filter hooks. + * + * We use Dependency Injection to prevent circular dependency. + * + * @return void + * @codeCoverageIgnore This is merely a wrapper to load classes. No need to test. + */ + private function init() { + new Integrations\WooCommerce( $this ); + new Integrations\EDD( $this ); + } + + /** + * @param array $event_goals + * @param string $funnel_name + * + * @return void + * @codeCoverageIgnore We don't want to test the API. + */ + public function create_integration_funnel( $event_goals, $funnel_name ) { + $goals = []; + + foreach ( $event_goals as $event_key => $event_goal ) { + // Don't add this goal to the funnel. Create it separately instead. + if ( $event_key === 'remove-from-cart' ) { + $this->provisioning->create_goals( [ $this->provisioning->create_goal_request( $event_goal ) ] ); + + continue; + } + + if ( $event_key === 'purchase' ) { + if ( \Plausible\Analytics\WP\Integrations::is_edd_active() ) { + $currency = edd_get_currency(); + } else { + $currency = get_woocommerce_currency(); + } + + $goals[] = $this->provisioning->create_goal_request( $event_goal, 'Revenue', $currency ); + + continue; + } + + if ( $event_key === 'view-product' ) { + $path = preg_replace( '/^.*?\//', '', $event_goal ); + $goals[] = $this->provisioning->create_goal_request( $event_goal, 'Pageview', null, '/' . $path ); + + continue; + } + + $goals[] = $this->provisioning->create_goal_request( $event_goal ); + } + + $this->provisioning->create_funnel( $funnel_name, $goals ); + } + + /** + * Deletes the integration-specific goals using the stored goal IDs. + * + * @param object $integration The integration object containing event goals to be deleted. + * + * @return void + * @codeCoverageIgnore Because we don't want to test the API. + */ + public function delete_integration_goals( $integration ) { + $goals = get_option( 'plausible_analytics_enhanced_measurements_goal_ids', [] ); + + foreach ( $goals as $id => $name ) { + $key = $this->provisioning->array_search_contains( $name, $integration->event_goals ); + + if ( $key ) { + $this->provisioning->client->delete_goal( $id ); + + unset( $goals[ $id ] ); + } + } + + // Refresh the stored IDs in the DB. + update_option( 'plausible_analytics_enhanced_measurements_goal_ids', $goals ); + } +} diff --git a/src/Admin/Provisioning/Integrations/EDD.php b/src/Admin/Provisioning/Integrations/EDD.php new file mode 100644 index 00000000..20fac0cb --- /dev/null +++ b/src/Admin/Provisioning/Integrations/EDD.php @@ -0,0 +1,82 @@ +integrations = $integrations; + + $this->init(); + } + + /** + * Action and filter hooks. + * + * @return void + */ + private function init() { + add_action( 'update_option_plausible_analytics_settings', [ $this, 'maybe_create_edd_funnel' ], 10, 2 ); + add_action( 'update_option_plausible_analytics_settings', [ $this, 'maybe_delete_edd_goals' ], 11, 2 ); + } + + /** + * Creates an EDD purchase funnel if enhanced measurement is enabled and EDD is active. + * + * @param array $old_settings The previous settings before the update. + * @param array $settings The updated settings array. + * + * @return void + * + * @codeCoverageIgnore Because it interacts with the Plugins API + */ + public function maybe_create_edd_funnel( $old_settings, $settings ) { + if ( ! Helpers::is_enhanced_measurement_enabled( 'revenue', $settings[ 'enhanced_measurements' ] ) || ! Integrations::is_edd_active() ) { + return; // @codeCoverageIgnore + } + + $edd = new Integrations\EDD( false ); + + $this->integrations->create_integration_funnel( $edd->event_goals, __( 'EDD Purchase Funnel', 'plausible-analytics' ) ); + } + + /** + * * Delete all custom EDD event goals if Revenue setting is disabled. The funnel is deleted when the minimum + * * required no. of goals is no longer met. + * + * @param array $old_settings The previous settings before the update. + * @param array $settings The current updated settings. + * + * @return void + * + * @codeCoverageIgnore Because it interacts with the Plugins API. + */ + public function maybe_delete_edd_goals( $old_settings, $settings ) { + $enhanced_measurements = array_filter( $settings[ 'enhanced_measurements' ] ); + + if ( Helpers::is_enhanced_measurement_enabled( 'revenue', $enhanced_measurements ) ) { + return; + } + + $edd_integration = new Integrations\EDD( false ); + + $this->integrations->delete_integration_goals( $edd_integration ); + } +} diff --git a/src/Admin/Provisioning/Integrations/WooCommerce.php b/src/Admin/Provisioning/Integrations/WooCommerce.php new file mode 100644 index 00000000..caa1de78 --- /dev/null +++ b/src/Admin/Provisioning/Integrations/WooCommerce.php @@ -0,0 +1,84 @@ +integrations = $integrations; + + $this->init(); + } + + /** + * Action & filters hooks. + * + * @return void + */ + private function init() { + add_action( 'update_option_plausible_analytics_settings', [ $this, 'maybe_create_woocommerce_funnel' ], 10, 2 ); + add_action( 'update_option_plausible_analytics_settings', [ $this, 'maybe_delete_woocommerce_goals' ], 11, 2 ); + } + + /** + * Checks whether the WooCommerce funnel should be created based on the provided settings + * and creates the funnel if the conditions are met. + * + * @param array $old_settings The previous settings before the update. + * @param array $settings The updated settings to check for enhanced measurement and WooCommerce integration. + * + * @return void + * + * @codeCoverageIgnore Because it interacts with the Plugins API. + */ + public function maybe_create_woocommerce_funnel( $old_settings, $settings ) { + if ( ! Helpers::is_enhanced_measurement_enabled( 'revenue', $settings[ 'enhanced_measurements' ] ) || ! Integrations::is_wc_active() ) { + return; // @codeCoverageIgnore + } + + $woocommerce = new Integrations\WooCommerce( false ); + + $this->integrations->create_integration_funnel( $woocommerce->event_goals, __( 'Woo Purchase Funnel', 'plausible-analytics' ) ); + } + + /** + * Delete all custom WooCommerce event goals if Revenue setting is disabled. The funnel is deleted when the minimum + * required no. of goals is no longer met. + * + * @param $old_settings + * @param $settings + * + * @return void + * + * @codeCoverageIgnore Because we don't want to test if the API is working. + */ + public function maybe_delete_woocommerce_goals( $old_settings, $settings ) { + $enhanced_measurements = array_filter( $settings[ 'enhanced_measurements' ] ); + + // Setting is enabled, no need to continue. + if ( Helpers::is_enhanced_measurement_enabled( 'revenue', $enhanced_measurements ) ) { + return; + } + + $woo_integration = new Integrations\WooCommerce( false ); + + $this->integrations->delete_integration_goals( $woo_integration ); + } +} diff --git a/src/Integrations.php b/src/Integrations.php index 0e2c035d..11992a78 100644 --- a/src/Integrations.php +++ b/src/Integrations.php @@ -31,9 +31,10 @@ private function init() { // Easy Digital Downloads if ( self::is_edd_active() ) { - // new Integrations\EDD(); + new Integrations\EDD(); } + // Form Plugins if ( self::is_form_submit_active() ) { new Integrations\FormSubmit(); } @@ -60,9 +61,6 @@ public static function is_edd_active() { * @return mixed|null */ public static function is_form_submit_active() { - return apply_filters( - 'plausible_analytics_integrations_form_submit', - Helpers::is_enhanced_measurement_enabled( 'form-completions' ) - ); + return apply_filters( 'plausible_analytics_integrations_form_submit', Helpers::is_enhanced_measurement_enabled( 'form-completions' ) ); } } diff --git a/src/Integrations/EDD.php b/src/Integrations/EDD.php new file mode 100644 index 00000000..c0c9d77a --- /dev/null +++ b/src/Integrations/EDD.php @@ -0,0 +1,197 @@ +path . $uri; + } else { + $uri = '/' . $uri; + } + + $this->event_goals = [ + 'view-product' => sprintf( __( 'Visit %s*', 'plausible-analytics' ), $uri ), + 'add-to-cart' => __( 'EDD Add to Cart', 'plausible-analytics' ), + 'remove-from-cart' => __( 'EDD Remove from Cart', 'plausible-analytics' ), + 'checkout' => __( 'EDD Start Checkout', 'plausible-analytics' ), + 'purchase' => __( 'EDD Complete Purchase', 'plausible-analytics' ), + ]; + + $this->init( $init ); + } + + /** + * Action & filter hooks. + * + * @param $init + * + * @return void + */ + private function init( $init ) { + if ( ! $init ) { + return; + } + + add_action( 'edd_post_add_to_cart', [ $this, 'track_add_to_cart' ], 10, 3 ); + add_action( 'edd_pre_remove_from_cart', [ $this, 'track_remove_cart_item' ], 10 ); + add_action( 'edd_before_purchase_form', [ $this, 'track_entered_checkout' ] ); + add_action( 'edd_complete_purchase', [ $this, 'track_purchase' ], 10, 2 ); + } + + /** + * Tracks the "add to cart" event with relevant product and cart data. + * + * @param int $download_id The ID of the product being added to the cart. + * @param array $options Optional data associated with the product being added. + * @param array $items The current items in the cart. + * + * @return void + */ + public function track_add_to_cart( $download_id, $options, $items ) { + $download = new \EDD_Download( $download_id ); + + if ( $download->ID === 0 ) { + return; + } + + $quantity = array_filter( + $items, + function ( $item ) use ( $download_id ) { + return $item[ 'id' ] === $download_id; + } + ); + $quantity = reset( $quantity )[ 'quantity' ] ?? 1; + + $props = apply_filters( + 'plausible_analytics_edd_add_to_cart_custom_properties', + [ + 'product_name' => edd_get_download_name( $download_id ), + 'product_id' => $download_id, + 'quantity' => $quantity, + 'price' => edd_get_download_price( $download_id ), + 'tax_class' => edd_get_cart_tax_rate(), + 'cart_total_items' => edd_get_cart_quantity(), + 'cart_total' => edd_get_cart_total(), + ] + ); + + $proxy = new Proxy( false ); + + $proxy->do_request( $this->event_goals[ 'add-to-cart' ], null, null, $props ); + } + + /** + * Tracks the removal of an item from the cart, updates cart contents and triggers an event to log this action. + * + * @param string|int $key The key of the item in the cart to be removed. + * + * @return void + */ + public function track_remove_cart_item( $key ) { + $cart_contents = edd_get_cart_contents(); + $item_removed_from_cart = $cart_contents[ $key ] ?? []; + $product = null; + + if ( empty( $item_removed_from_cart ) ) { + return; + } + + unset( $cart_contents[ $key ] ); + + if ( isset( $item_removed_from_cart[ 'id' ] ) ) { + $product = new \EDD_Download( $item_removed_from_cart[ 'id' ] ); + } + + if ( ! $product ) { + return; + } + + $total_removed_from_cart = edd_get_cart_total() - ( $product->get_price() * $item_removed_from_cart[ 'quantity' ] ); + + $props = apply_filters( + 'plausible_analytics_edd_remove_cart_item_custom_properties', + [ + 'product_name' => $product->get_name(), + 'product_id' => $item_removed_from_cart[ 'id' ], + 'quantity' => $item_removed_from_cart[ 'quantity' ], + 'cart_total_items' => count( $cart_contents ), + 'cart_total' => $total_removed_from_cart, + ] + ); + + $proxy = new Proxy( false ); + + $proxy->do_request( $this->event_goals[ 'remove-from-cart' ], null, null, $props ); + } + + /** + * Tracks the "entered checkout" event with relevant cart data. + * + * @return void + */ + public function track_entered_checkout() { + // Just to make sure we're where we're supposed to be. + if ( ! edd_is_checkout() ) { + return; + } + + $props = apply_filters( + 'plausible_analytics_edd_entered_checkout_custom_properties', + [ + 'subtotal' => edd_get_cart_subtotal(), + 'tax' => edd_get_cart_tax(), + 'total' => edd_get_cart_total(), + ] + ); + + $proxy = new Proxy( false ); + + $proxy->do_request( $this->event_goals[ 'checkout' ], null, null, $props ); + } + + /** + * Tracks the "purchase" event with relevant order and payment data. + * + * @param int $order_id The unique identifier of the order. + * @param object $payment The payment object containing details like total amount and currency. + * + * @return void + */ + public function track_purchase( $order_id, $payment ) { + $props = apply_filters( + 'plausible_analytics_edd_purchase_custom_properties', + [ + 'revenue' => [ + 'amount' => number_format( (float) $payment->total, 2 ), + 'currency' => $payment->currency, + ], + ] + ); + + $proxy = new Proxy( false ); + + $proxy->do_request( $this->event_goals[ 'purchase' ], null, null, $props ); + } +} diff --git a/src/Integrations/WooCommerce.php b/src/Integrations/WooCommerce.php index b72eecbc..5bc7149d 100644 --- a/src/Integrations/WooCommerce.php +++ b/src/Integrations/WooCommerce.php @@ -9,6 +9,7 @@ namespace Plausible\Analytics\WP\Integrations; +use Plausible\Analytics\WP\Admin\Provisioning; use Plausible\Analytics\WP\Integrations; use Plausible\Analytics\WP\Proxy; use WC_Cart; @@ -17,24 +18,6 @@ class WooCommerce { const PURCHASE_TRACKED_META_KEY = '_plausible_analytics_purchase_tracked'; - const CUSTOM_PROPERTIES = [ - 'cart_total', - 'cart_total_items', - 'id', - 'name', - 'price', - 'product_id', - 'product_name', - 'quantity', - 'shipping', - 'subtotal', - 'subtotal_tax', - 'tax_class', - 'total', - 'total_tax', - 'variation_id', - ]; - /** * @var array Custom Event Goals used to track Events in WooCommerce. */ @@ -46,8 +29,16 @@ class WooCommerce { * @codeCoverageIgnore */ public function __construct( $init = true ) { + $uri = wc_get_permalink_structure()[ 'product_base' ]; + + if ( is_multisite() ) { + $uri = get_blog_details()->path . $uri; + } else { + $uri = '/' . $uri; + } + $this->event_goals = [ - 'view-product' => __( 'Visit /product*', 'plausible-analytics' ), + 'view-product' => sprintf( __( 'Visit %s*', 'plausible-analytics' ), $uri ), 'add-to-cart' => __( 'Woo Add to Cart', 'plausible-analytics' ), 'remove-from-cart' => __( 'Woo Remove from Cart', 'plausible-analytics' ), 'checkout' => __( 'Woo Start Checkout', 'plausible-analytics' ), @@ -244,7 +235,7 @@ public function track_add_to_cart( $product, $add_to_cart_data ) { */ private function clean_data( $product ) { foreach ( $product as $key => $value ) { - if ( ! in_array( $key, self::CUSTOM_PROPERTIES ) ) { + if ( ! in_array( $key, Provisioning::CUSTOM_PROPERTIES ) ) { unset( $product[ $key ] ); } } @@ -292,6 +283,12 @@ public function track_remove_cart_item( $cart_item_key, $cart ) { } /** + * Tracks when a user enters the checkout process and sends event data to Plausible Analytics. + * + * This method checks if the current page is the checkout page. If it is, it collects relevant + * cart information like subtotal, shipping, tax, and total, applies filters to allow custom properties, + * encodes the data as JSON, and generates a JavaScript snippet to send the data to Plausible Analytics. + * * @return void */ public function track_entered_checkout() { diff --git a/src/Plugin.php b/src/Plugin.php index 84f87d57..2bbaa97e 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -38,6 +38,7 @@ public function register_services() { new Admin\Actions(); new Admin\Module(); new Admin\Provisioning(); + new Admin\Provisioning\Integrations(); } new Integrations(); diff --git a/src/Proxy.php b/src/Proxy.php index dd8b8ca9..dd1c7dde 100644 --- a/src/Proxy.php +++ b/src/Proxy.php @@ -80,10 +80,7 @@ private function init( $init ) { $settings = []; - if ( array_key_exists( 'option_name', $_POST ) && - $_POST[ 'option_name' ] == 'proxy_enabled' && - array_key_exists( 'option_value', $_POST ) && - $_POST[ 'option_value' ] == 'on' ) { + if ( array_key_exists( 'option_name', $_POST ) && $_POST[ 'option_name' ] == 'proxy_enabled' && array_key_exists( 'option_value', $_POST ) && $_POST[ 'option_value' ] == 'on' ) { $settings[ 'proxy_enabled' ] = 'on'; // @codeCoverageIgnore } @@ -115,7 +112,10 @@ public function do_request( $name = 'pageview', $domain = '', $url = '', $props 'u' => $url ?: wp_get_referer(), ]; - if ( ! empty( $props ) ) { + // Revenue events use a different approach. + if ( isset( $props[ 'revenue' ] ) ) { + $body[ 'revenue' ] = reset( $props ); // @codeCoverageIgnore + } elseif ( ! empty( $props ) ) { $body[ 'p' ] = $props; // @codeCoverageIgnore } diff --git a/tests/integration/Integrations/WooCommerceTest.php b/tests/integration/Integrations/WooCommerceTest.php index b7c3ca70..ab1b8bd3 100644 --- a/tests/integration/Integrations/WooCommerceTest.php +++ b/tests/integration/Integrations/WooCommerceTest.php @@ -18,6 +18,7 @@ class WooCommerceTest extends TestCase { */ public function testTrackEnteredCheckout() { when( 'is_checkout' )->justReturn( true ); + when( 'wc_get_permalink_structure' )->justReturn( [ 'product_base' => 'product' ] ); $cart_mock = $this->getMockBuilder( 'WC_Cart' )->setMethods( [ @@ -49,6 +50,8 @@ public function testTrackEnteredCheckout() { * @return void */ public function testTrackPurchase() { + when( 'wc_get_permalink_structure' )->justReturn( [ 'product_base' => 'product' ] ); + $class = new WooCommerce( false ); $mock = $this->getMockBuilder( 'WC_Order' )->setMethods( [ diff --git a/tests/integration/IntegrationsTest.php b/tests/integration/IntegrationsTest.php index f1ce43df..6873e23b 100644 --- a/tests/integration/IntegrationsTest.php +++ b/tests/integration/IntegrationsTest.php @@ -10,56 +10,55 @@ use function Brain\Monkey\Functions\when; class IntegrationsTest extends TestCase { + public function testInit() { + add_filter( 'plausible_analytics_integrations_woocommerce', '__return_true' ); + add_filter( 'plausible_analytics_integrations_edd', '__return_true' ); + add_filter( 'plausible_analytics_integrations_form_submit', '__return_true' ); + + when( 'wc_get_permalink_structure' )->justReturn( [ 'product_base' => 'product' ] ); + + new Integrations(); + + remove_filter( 'plausible_analytics_integrations_woocommerce', '__return_true' ); + remove_filter( 'plausible_analytics_integrations_edd', '__return_true' ); + remove_filter( 'plausible_analytics_integrations_form_submit', '__return_true' ); + + $this->assertTrue( class_exists( '\Plausible\Analytics\WP\Integrations\WooCommerce' ) ); + $this->assertTrue( class_exists( '\Plausible\Analytics\WP\Integrations\EDD' ) ); + $this->assertTrue( class_exists( '\Plausible\Analytics\WP\Integrations\FormSubmit' ) ); + } + /** * Tests whether the WooCommerce integration is currently active. - * - * This method temporarily applies a filter to enable revenue tracking functionality, - * mocks the function_exists call to simulate the existence of WooCommerce functions, - * verifies the active state of the WooCommerce integration through an assertion, - * and finally removes the applied filter. */ public function testIsWcActive() { - add_filter( 'plausible_analytics_settings', [ $this, 'enableRevenue' ] ); + add_filter( 'plausible_analytics_integrations_woocommerce', '__return_true' ); // WC is already mocked. $this->assertTrue( Integrations::is_wc_active() ); - remove_filter( - 'plausible_analytics_settings', - [ $this, 'enableRevenue' ] - ); + remove_filter( 'plausible_analytics_integrations_woocommerce', '__return_true' ); } /** * Tests if the Easy Digital Downloads (EDD) integration is active. - * - * This method applies a temporary filter to enable revenue tracking, mocks - * the existence of required functions using a stub, and asserts the active - * state of the EDD integration. It then removes the applied filter after testing. */ public function testIsEddActive() { - add_filter( 'plausible_analytics_settings', [ $this, 'enableRevenue' ] ); - - $edd_mock = $this->getMockBuilder( 'Easy_Digital_Downloads' )->getMock(); - when( 'EDD' )->justReturn( $edd_mock ); + add_filter( 'plausible_analytics_integrations_edd', '__return_true' ); $this->assertTrue( Integrations::is_edd_active() ); - remove_filter( 'plausible_analytics_settings', [ $this, 'enableRevenue' ] ); + remove_filter( 'plausible_analytics_integrations_edd', '__return_true' ); } /** * Determines if the form submission functionality is currently active. - * - * This method temporarily applies a filter to enable form completions, - * verifies the active state of the form submission through an assertion, - * and then removes the applied filter. */ public function isFormSubmitActive() { - add_filter( 'plausible_analytics_settings', [ $this, 'enableFormCompletions' ] ); + add_filter( 'plausible_analytics_integrations_form_submit', '__return_true' ); $this->assertTrue( Integrations::is_form_submit_active() ); - remove_filter( 'plausible_analytics_settings', [ $this, 'enableFormCompletions' ] ); + remove_filter( 'plausible_analytics_integrations_form_submit', '__return_true' ); } }