Files
wp-graphql-woocommerce/tests/functional/ElementorCompatibilityCest.php
Geoff TaylorandGitHub 9ef79563ac fix: Elementor breaks transfer-session endpoint (#1004)
* fix: Elementor breaks transfer-session endpoint with 500 error

Elementor's LandingPages module creates a WP_Query during `init` which
fires `pre_get_posts` before WooCommerce session is initialized. Our
resolve_request handler ran on this early query and called
WC()->session->get_customer_id() on null, causing a fatal error.

Fix: Guard resolve_request to only run on the main front-end query and
bail if WC session is not yet initialized. Also add Elementor to the
test environment and add functional tests that reproduce the issue.

Closes #945

* chore: Add @param docblock for resolve_request $query parameter

* devops: Remove unused resolvers and add session transaction manager tests

Remove Coupon_Connection_Resolver and Customer_Connection_Resolver
which had 0% coverage and were never instantiated.

Add SessionTransactionManagerTest covering did_transaction_expire
edge cases and next_transaction invalid/expired queue handling.
2026-03-30 22:58:32 -04:00

139 lines
4.2 KiB
PHP

<?php
use WPGraphQL\WooCommerce\Vendor\Firebase\JWT\JWT;
use WPGraphQL\WooCommerce\Vendor\Firebase\JWT\Key;
/**
* Tests that the Protected Router's transfer-session endpoint works
* when Elementor is active with an "entire website" template.
*
* @see https://github.com/wp-graphql/wp-graphql-woocommerce/issues/945
*/
class ElementorCompatibilityCest {
private $product_catalog;
public function _before( FunctionalTester $I ) {
if ( ! defined( 'GRAPHQL_WOOCOMMERCE_SECRET_KEY' ) ) {
define( 'GRAPHQL_WOOCOMMERCE_SECRET_KEY', 'testestestestestestestestestest!!' );
}
// Enable authorizing URL fields for nonce handling.
$I->setWooGraphQLSetting(
'enable_authorizing_url_fields',
[
'cart_url' => 'cart_url',
'checkout_url' => 'checkout_url',
'account_url' => 'account_url',
'add_payment_method_url' => 'add_payment_method_url',
]
);
$this->product_catalog = $I->getCatalog();
// Activate Elementor.
activate_plugin( 'elementor/elementor.php' );
// Create an Elementor template with "entire website" condition.
$template_id = $I->havePostInDatabase(
[
'post_type' => 'elementor_library',
'post_title' => 'Entire Site Template',
'post_status' => 'publish',
'meta_input' => [
'_elementor_template_type' => 'page',
'_elementor_conditions' => [ 'include/general' ],
'_elementor_data' => '[]',
'_elementor_edit_mode' => 'builder',
],
]
);
// Set Elementor's active kit and conditions cache.
$I->haveOptionInDatabase( 'elementor_active_kit', $template_id );
}
public function _after( FunctionalTester $I ) {
deactivate_plugins( 'elementor/elementor.php' );
}
/**
* Helper: Starts a guest session by adding a product to the cart.
*/
private function startNewSession( FunctionalTester $I ): array {
$success = $I->addToCart(
[
'clientMutationId' => 'someId',
'productId' => $this->product_catalog['t-shirt'],
'quantity' => 1,
]
);
$session_token = $I->grabHttpHeader( 'woocommerce-session' );
return [
'key' => $I->lodashGet( $success, 'data.addToCart.cartItem.key' ),
'session_token' => $session_token,
];
}
/**
* Helper: Decodes the session token and returns the customer_id (session_id).
*/
private function getSessionId( string $session_token ): string {
JWT::$leeway = 60;
$token_data = JWT::decode( $session_token, new Key( GRAPHQL_WOOCOMMERCE_SECRET_KEY, 'HS256' ) );
return $token_data->data->customer_id;
}
/**
* Test that the transfer-session endpoint still works when Elementor is
* active with an "entire website" template applied.
*/
public function testTransferSessionWorksWithElementorActive( FunctionalTester $I ) {
$session_data = $this->startNewSession( $I );
$session_id = $this->getSessionId( $session_data['session_token'] );
$query = 'query { customer { checkoutNonce } }';
$success = $I->sendGraphQLRequest(
$query,
null,
[ 'woocommerce-session' => "Session {$session_data['session_token']}" ]
);
$checkout_nonce = $I->lodashGet( $success, 'data.customer.checkoutNonce' );
$I->assertNotEmpty( $checkout_nonce, 'checkoutNonce should be returned.' );
$I->stopFollowingRedirects();
$wp_url = getenv( 'WORDPRESS_URL' );
$I->amOnUrl( "{$wp_url}/transfer-session?session_id={$session_id}&_wc_checkout={$checkout_nonce}" );
$I->seeResponseCodeIs( 302 );
$I->followRedirect();
$I->seeInCurrentUrl( '/checkout' );
$I->startFollowingRedirects();
}
/**
* Test that the transfer-session endpoint does not return a 500 error
* when Elementor is active, even with an invalid nonce.
*/
public function testTransferSessionDoesNotReturn500WithElementor( FunctionalTester $I ) {
$session_data = $this->startNewSession( $I );
$session_id = $this->getSessionId( $session_data['session_token'] );
$I->stopFollowingRedirects();
$wp_url = getenv( 'WORDPRESS_URL' );
$I->amOnUrl( "{$wp_url}/transfer-session?session_id={$session_id}&_wc_checkout=invalid_nonce" );
// Should redirect to home (302), NOT 500.
$I->seeResponseCodeIs( 302 );
$I->followRedirect();
$I->dontSeeInCurrentUrl( '/checkout' );
$I->startFollowingRedirects();
}
}