From 9ada189c9d99448768538d7bfccf39e72c5d23ba Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 4 Nov 2022 09:30:13 +0200 Subject: [PATCH 01/18] Improve cart subscriptions check Make compatible with plugins like "All products for subscriptions" --- modules/ppcp-subscription/src/Helper/SubscriptionHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php index cb4521f37..5d4984081 100644 --- a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php +++ b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php @@ -50,7 +50,7 @@ class SubscriptionHelper { if ( ! isset( $item['data'] ) || ! is_a( $item['data'], WC_Product::class ) ) { continue; } - if ( $item['data']->is_type( 'subscription' ) || $item['data']->is_type( 'subscription_variation' ) ) { + if ( WC_Subscriptions_Product::is_subscription( $item['data'] ) ) { return true; } } From 745c226f7745b76cafe7b2e63919649460627953 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 15 Dec 2022 08:57:49 +0200 Subject: [PATCH 02/18] Hide product button when subscription mode chosen via plugin --- .../modules/ContextBootstrap/SingleProductBootstap.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index f336c0a81..272495abb 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -43,9 +43,9 @@ class SingleProductBootstap { } shouldRender() { - - return document.querySelector('form.cart') !== null && !this.priceAmountIsZero(); - + return document.querySelector('form.cart') !== null + && !this.priceAmountIsZero() + && !this.isSubscriptionMode(); } priceAmount() { @@ -74,6 +74,11 @@ class SingleProductBootstap { return !price || price === 0; } + isSubscriptionMode() { + // Check "All products for subscriptions" plugin. + return document.querySelector('.wcsatt-options-product .subscription-option input[type="radio"]:checked') !== null; + } + render() { const actionHandler = new SingleProductActionHandler( this.gateway, From 4bd39c9f95ed88de91cda860b77675c072a12f9a Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 15 Dec 2022 09:41:36 +0200 Subject: [PATCH 03/18] Fix/refactor product button handling --- .../SingleProductActionHandler.js | 14 ------- .../ContextBootstrap/SingleProductBootstap.js | 40 ++++++++++--------- .../modules/Helper/ButtonsToggleListener.js | 5 ++- .../js/modules/Renderer/MessageRenderer.js | 9 ----- .../resources/js/modules/Renderer/Renderer.js | 18 --------- 5 files changed, 26 insertions(+), 60 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js index b94864604..4296b1045 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js @@ -9,31 +9,17 @@ class SingleProductActionHandler { constructor( config, updateCart, - showButtonCallback, - hideButtonCallback, formElement, errorHandler ) { this.config = config; this.updateCart = updateCart; - this.showButtonCallback = showButtonCallback; - this.hideButtonCallback = hideButtonCallback; this.formElement = formElement; this.errorHandler = errorHandler; } configuration() { - - if ( this.hasVariations() ) { - const observer = new ButtonsToggleListener( - this.formElement.querySelector('.single_add_to_cart_button'), - this.showButtonCallback, - this.hideButtonCallback - ); - observer.init(); - } - return { createOrder: this.createOrder(), onApprove: onApprove(this, this.errorHandler), diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index 272495abb..42edf7a5d 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -1,5 +1,7 @@ import UpdateCart from "../Helper/UpdateCart"; import SingleProductActionHandler from "../ActionHandler/SingleProductActionHandler"; +import {hide, show, setVisible} from "../Helper/Hiding"; +import ButtonsToggleListener from "../Helper/ButtonsToggleListener"; class SingleProductBootstap { constructor(gateway, renderer, messages, errorHandler) { @@ -12,10 +14,10 @@ class SingleProductBootstap { handleChange() { - if (!this.shouldRender()) { - this.renderer.hideButtons(this.gateway.hosted_fields.wrapper); - this.renderer.hideButtons(this.gateway.button.wrapper); - this.messages.hideMessages(); + const shouldRender = this.shouldRender(); + setVisible(this.gateway.button.wrapper, shouldRender); + setVisible(this.gateway.messages.wrapper, shouldRender); + if (!shouldRender) { return; } @@ -23,7 +25,6 @@ class SingleProductBootstap { } init() { - const form = document.querySelector('form.cart'); if (!form) { return; @@ -32,14 +33,27 @@ class SingleProductBootstap { form.addEventListener('change', this.handleChange.bind(this)); this.mutationObserver.observe(form, {childList: true, subtree: true}); + const buttonObserver = new ButtonsToggleListener( + form.querySelector('.single_add_to_cart_button'), + () => { + show(this.gateway.button.wrapper); + show(this.gateway.messages.wrapper); + this.messages.renderWithAmount(this.priceAmount()) + }, + () => { + hide(this.gateway.button.wrapper); + hide(this.gateway.messages.wrapper); + }, + ); + buttonObserver.init(); + if (!this.shouldRender()) { - this.renderer.hideButtons(this.gateway.hosted_fields.wrapper); - this.messages.hideMessages(); + hide(this.gateway.button.wrapper); + hide(this.gateway.messages.wrapper); return; } this.render(); - } shouldRender() { @@ -86,16 +100,6 @@ class SingleProductBootstap { this.gateway.ajax.change_cart.endpoint, this.gateway.ajax.change_cart.nonce, ), - () => { - this.renderer.showButtons(this.gateway.button.wrapper); - this.renderer.showButtons(this.gateway.hosted_fields.wrapper); - this.messages.renderWithAmount(this.priceAmount()) - }, - () => { - this.renderer.hideButtons(this.gateway.button.wrapper); - this.renderer.hideButtons(this.gateway.hosted_fields.wrapper); - this.messages.hideMessages(); - }, document.querySelector('form.cart'), this.errorHandler, ); diff --git a/modules/ppcp-button/resources/js/modules/Helper/ButtonsToggleListener.js b/modules/ppcp-button/resources/js/modules/Helper/ButtonsToggleListener.js index bed5afcda..add1ee28a 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/ButtonsToggleListener.js +++ b/modules/ppcp-button/resources/js/modules/Helper/ButtonsToggleListener.js @@ -14,6 +14,9 @@ class ButtonsToggleListener { init() { + if (!this.element) { + return; + } const config = { attributes : true }; const callback = () => { if (this.element.classList.contains('disabled')) { @@ -33,4 +36,4 @@ class ButtonsToggleListener { } } -export default ButtonsToggleListener; \ No newline at end of file +export default ButtonsToggleListener; diff --git a/modules/ppcp-button/resources/js/modules/Renderer/MessageRenderer.js b/modules/ppcp-button/resources/js/modules/Renderer/MessageRenderer.js index f7409c683..a07e81d21 100644 --- a/modules/ppcp-button/resources/js/modules/Renderer/MessageRenderer.js +++ b/modules/ppcp-button/resources/js/modules/Renderer/MessageRenderer.js @@ -53,14 +53,5 @@ class MessageRenderer { } return true; } - - hideMessages() { - const domElement = document.querySelector(this.config.wrapper); - if (! domElement ) { - return false; - } - domElement.style.display = 'none'; - return true; - } } export default MessageRenderer; diff --git a/modules/ppcp-button/resources/js/modules/Renderer/Renderer.js b/modules/ppcp-button/resources/js/modules/Renderer/Renderer.js index f07df8fe4..d5d295f23 100644 --- a/modules/ppcp-button/resources/js/modules/Renderer/Renderer.js +++ b/modules/ppcp-button/resources/js/modules/Renderer/Renderer.js @@ -99,24 +99,6 @@ class Renderer { return this.renderedSources.has(wrapper + fundingSource ?? ''); } - hideButtons(element) { - const domElement = document.querySelector(element); - if (! domElement ) { - return false; - } - domElement.style.display = 'none'; - return true; - } - - showButtons(element) { - const domElement = document.querySelector(element); - if (! domElement ) { - return false; - } - domElement.style.display = 'block'; - return true; - } - disableCreditCardFields() { this.creditCardRenderer.disableFields(); } From d5f54abd946b5a0aa85344d5225169d8034a10e4 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 21 Dec 2022 17:00:38 +0200 Subject: [PATCH 04/18] Support grouped layout of sub mode plugin --- .../js/modules/ContextBootstrap/SingleProductBootstap.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index 42edf7a5d..6c0a03283 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -90,7 +90,8 @@ class SingleProductBootstap { isSubscriptionMode() { // Check "All products for subscriptions" plugin. - return document.querySelector('.wcsatt-options-product .subscription-option input[type="radio"]:checked') !== null; + return document.querySelector('.wcsatt-options-product:not(.wcsatt-options-product--hidden) .subscription-option input[type="radio"]:checked') !== null + || document.querySelector('.wcsatt-options-prompt-label-subscription input[type="radio"]:checked') !== null; // grouped } render() { From 4ae82b704ae5539adcf14c1e60131df5e121d455 Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 15 Feb 2023 16:03:49 +0200 Subject: [PATCH 05/18] Make SmartButton more reusable --- modules/ppcp-button/resources/js/button.js | 18 +-------- .../js/modules/Helper/ScriptLoading.js | 24 ++++++++++++ .../src/Assets/DisabledSmartButton.php | 23 ++++++++--- .../ppcp-button/src/Assets/SmartButton.php | 39 ++++++++++++------- .../src/Assets/SmartButtonInterface.php | 18 +++++++-- 5 files changed, 82 insertions(+), 40 deletions(-) create mode 100644 modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js diff --git a/modules/ppcp-button/resources/js/button.js b/modules/ppcp-button/resources/js/button.js index 8dc519a6c..d5a7dc5ee 100644 --- a/modules/ppcp-button/resources/js/button.js +++ b/modules/ppcp-button/resources/js/button.js @@ -6,7 +6,6 @@ import PayNowBootstrap from "./modules/ContextBootstrap/PayNowBootstrap"; import Renderer from './modules/Renderer/Renderer'; import ErrorHandler from './modules/ErrorHandler'; import CreditCardRenderer from "./modules/Renderer/CreditCardRenderer"; -import dataClientIdAttributeHandler from "./modules/DataClientIdAttributeHandler"; import MessageRenderer from "./modules/Renderer/MessageRenderer"; import Spinner from "./modules/Helper/Spinner"; import { @@ -19,6 +18,7 @@ import {isChangePaymentPage} from "./modules/Helper/Subscriptions"; import FreeTrialHandler from "./modules/ActionHandler/FreeTrialHandler"; import FormSaver from './modules/Helper/FormSaver'; import FormValidator from "./modules/Helper/FormValidator"; +import {loadPaypalScript} from "./modules/Helper/ScriptLoading"; // TODO: could be a good idea to have a separate spinner for each gateway, // but I think we care mainly about the script loading, so one spinner should be enough. @@ -255,24 +255,10 @@ document.addEventListener( hideOrderButtonIfPpcpGateway(); }); - const script = document.createElement('script'); - script.addEventListener('load', (event) => { + loadPaypalScript(PayPalCommerceGateway, () => { bootstrapped = true; bootstrap(); }); - script.setAttribute('src', PayPalCommerceGateway.button.url); - Object.entries(PayPalCommerceGateway.script_attributes).forEach( - (keyValue) => { - script.setAttribute(keyValue[0], keyValue[1]); - } - ); - - if (PayPalCommerceGateway.data_client_id.set_attribute) { - dataClientIdAttributeHandler(script, PayPalCommerceGateway.data_client_id); - return; - } - - document.body.appendChild(script); }, ); diff --git a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js new file mode 100644 index 000000000..c5742ab19 --- /dev/null +++ b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js @@ -0,0 +1,24 @@ +import dataClientIdAttributeHandler from "../DataClientIdAttributeHandler"; + +export const loadPaypalScript = (config, onLoaded) => { + if (typeof paypal !== 'undefined') { + onLoaded(); + return; + } + + const script = document.createElement('script'); + script.addEventListener('load', onLoaded); + script.setAttribute('src', config.url); + Object.entries(config.script_attributes).forEach( + (keyValue) => { + script.setAttribute(keyValue[0], keyValue[1]); + } + ); + + if (config.data_client_id.set_attribute) { + dataClientIdAttributeHandler(script, config.data_client_id); + return; + } + + document.body.appendChild(script); +} diff --git a/modules/ppcp-button/src/Assets/DisabledSmartButton.php b/modules/ppcp-button/src/Assets/DisabledSmartButton.php index 9fe537163..04cee3eab 100644 --- a/modules/ppcp-button/src/Assets/DisabledSmartButton.php +++ b/modules/ppcp-button/src/Assets/DisabledSmartButton.php @@ -24,12 +24,16 @@ class DisabledSmartButton implements SmartButtonInterface { } /** - * Enqueues necessary scripts. - * - * @return bool + * Whether the scripts should be loaded. */ - public function enqueue(): bool { - return true; + public function should_load(): bool { + return false; + } + + /** + * Enqueues necessary scripts. + */ + public function enqueue(): void { } /** @@ -41,4 +45,13 @@ class DisabledSmartButton implements SmartButtonInterface { return false; } + + /** + * The configuration for the smart buttons. + * + * @return array + */ + public function script_data(): array { + return array(); + } } diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 725c6e559..747d91047 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -506,17 +506,25 @@ class SmartButton implements SmartButtonInterface { } /** - * Enqueues the script. - * - * @return bool - * @throws NotFoundException When a setting was not found. + * Whether the scripts should be loaded. */ - public function enqueue(): bool { + public function should_load(): bool { $buttons_enabled = $this->settings->has( 'enabled' ) && $this->settings->get( 'enabled' ); if ( ! is_checkout() && ! $buttons_enabled ) { return false; } + return true; + } + + /** + * Enqueues the scripts. + */ + public function enqueue(): void { + if ( ! $this->should_load() ) { + return; + } + $load_script = false; if ( is_checkout() && $this->settings->has( 'dcc_enabled' ) && $this->settings->get( 'dcc_enabled' ) ) { $load_script = true; @@ -554,10 +562,9 @@ class SmartButton implements SmartButtonInterface { wp_localize_script( 'ppcp-smart-button', 'PayPalCommerceGateway', - $this->localize_script() + $this->script_data() ); } - return true; } /** @@ -751,18 +758,22 @@ class SmartButton implements SmartButtonInterface { } /** - * The localized data for the smart button. + * The configuration for the smart buttons. * * @return array * @throws NotFoundException If a setting hasn't been found. */ - private function localize_script(): array { + public function script_data(): array { global $wp; $is_free_trial_cart = $this->is_free_trial_cart(); + $url_params = $this->url_params(); + $this->request_data->enqueue_nonce_fix(); $localize = array( + 'url' => add_query_arg( $url_params, 'https://www.paypal.com/sdk/js' ), + 'url_params' => $url_params, 'script_attributes' => $this->attributes(), 'data_client_id' => array( 'set_attribute' => ( is_checkout() && $this->dcc_is_enabled() ) || $this->can_save_vault_token(), @@ -809,7 +820,6 @@ class SmartButton implements SmartButtonInterface { 'wrapper' => '#ppc-button-' . PayPalGateway::ID, 'mini_cart_wrapper' => '#ppc-button-minicart', 'cancel_wrapper' => '#ppcp-cancel', - 'url' => $this->url(), 'mini_cart_style' => array( 'layout' => $this->style_for_context( 'layout', 'mini-cart' ), 'color' => $this->style_for_context( 'color', 'mini-cart' ), @@ -916,12 +926,12 @@ class SmartButton implements SmartButtonInterface { } /** - * The JavaScript SDK url to load. + * The JavaScript SDK url parameters. * - * @return string + * @return array * @throws NotFoundException If a setting was not found. */ - private function url(): string { + private function url_params(): array { $intent = ( $this->settings->has( 'intent' ) ) ? $this->settings->get( 'intent' ) : 'capture'; $product_intent = $this->subscription_helper->current_product_is_subscription() ? 'authorize' : $intent; $other_context_intent = $this->subscription_helper->cart_contains_subscription() ? 'authorize' : $intent; @@ -993,8 +1003,7 @@ class SmartButton implements SmartButtonInterface { $params['enable-funding'] = implode( ',', array_unique( $enable_funding ) ); } - $smart_button_url = add_query_arg( $params, 'https://www.paypal.com/sdk/js' ); - return $smart_button_url; + return $params; } /** diff --git a/modules/ppcp-button/src/Assets/SmartButtonInterface.php b/modules/ppcp-button/src/Assets/SmartButtonInterface.php index a574bc649..be5f5d015 100644 --- a/modules/ppcp-button/src/Assets/SmartButtonInterface.php +++ b/modules/ppcp-button/src/Assets/SmartButtonInterface.php @@ -22,11 +22,14 @@ interface SmartButtonInterface { public function render_wrapper(): bool; /** - * Enqueues the necessary scripts. - * - * @return bool + * Whether the scripts should be loaded. */ - public function enqueue(): bool; + public function should_load(): bool; + + /** + * Enqueues the necessary scripts. + */ + public function enqueue(): void; /** * Whether the running installation could save vault tokens or not. @@ -34,4 +37,11 @@ interface SmartButtonInterface { * @return bool */ public function can_save_vault_token(): bool; + + /** + * The configuration for the smart buttons. + * + * @return array + */ + public function script_data(): array; } From 5dd0931c0fe428e9221feaedb7a554985fc9e8ac Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 15 Feb 2023 16:50:34 +0200 Subject: [PATCH 06/18] Hide cart buttons when intent changed --- .../modules/ContextBootstrap/CartBootstap.js | 21 +++++ modules/ppcp-button/services.php | 7 ++ .../ppcp-button/src/Assets/SmartButton.php | 4 + modules/ppcp-button/src/ButtonModule.php | 10 +++ .../src/Endpoint/CartScriptParamsEndpoint.php | 80 +++++++++++++++++++ 5 files changed, 122 insertions(+) create mode 100644 modules/ppcp-button/src/Endpoint/CartScriptParamsEndpoint.php diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js index ebc00bfdc..0051ecb20 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js @@ -1,4 +1,5 @@ import CartActionHandler from '../ActionHandler/CartActionHandler'; +import {setVisible} from "../Helper/Hiding"; class CartBootstrap { constructor(gateway, renderer, errorHandler) { @@ -16,6 +17,26 @@ class CartBootstrap { jQuery(document.body).on('updated_cart_totals updated_checkout', () => { this.render(); + + fetch( + this.gateway.ajax.cart_script_params.endpoint, + { + method: 'GET', + credentials: 'same-origin', + } + ) + .then(result => result.json()) + .then(result => { + if (! result.success) { + return; + } + + const newParams = result.data; + const reloadRequired = this.gateway.url_params.intent !== newParams.intent; + + // TODO: should reload the script instead + setVisible(this.gateway.button.wrapper, !reloadRequired) + }); }); } diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index 6e8fe8a18..53e01f193 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Button; +use WooCommerce\PayPalCommerce\Button\Endpoint\CartScriptParamsEndpoint; use WooCommerce\PayPalCommerce\Button\Helper\CheckoutFormSaver; use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint; use WooCommerce\PayPalCommerce\Button\Validation\CheckoutFormValidator; @@ -220,6 +221,12 @@ return array( $container->get( 'woocommerce.logger.woocommerce' ) ); }, + 'button.endpoint.cart-script-params' => static function ( ContainerInterface $container ): CartScriptParamsEndpoint { + return new CartScriptParamsEndpoint( + $container->get( 'button.smart-button' ), + $container->get( 'woocommerce.logger.woocommerce' ) + ); + }, 'button.helper.three-d-secure' => static function ( ContainerInterface $container ): ThreeDSecure { $logger = $container->get( 'woocommerce.logger.woocommerce' ); return new ThreeDSecure( $logger ); diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 747d91047..d6ce0d2fd 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken; use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint; +use WooCommerce\PayPalCommerce\Button\Endpoint\CartScriptParamsEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\ChangeCartEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\CreateOrderEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\DataClientIdEndpoint; @@ -809,6 +810,9 @@ class SmartButton implements SmartButtonInterface { 'endpoint' => \WC_AJAX::get_endpoint( ValidateCheckoutEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ValidateCheckoutEndpoint::nonce() ), ), + 'cart_script_params' => array( + 'endpoint' => \WC_AJAX::get_endpoint( CartScriptParamsEndpoint::ENDPOINT ), + ), ), 'enforce_vault' => $this->has_subscriptions(), 'can_save_vault_token' => $this->can_save_vault_token(), diff --git a/modules/ppcp-button/src/ButtonModule.php b/modules/ppcp-button/src/ButtonModule.php index 573c7012c..db3e67245 100644 --- a/modules/ppcp-button/src/ButtonModule.php +++ b/modules/ppcp-button/src/ButtonModule.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Button; +use WooCommerce\PayPalCommerce\Button\Endpoint\CartScriptParamsEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; @@ -177,6 +178,15 @@ class ButtonModule implements ModuleInterface { $endpoint->handle_request(); } ); + + add_action( + 'wc_ajax_' . CartScriptParamsEndpoint::ENDPOINT, + static function () use ( $container ) { + $endpoint = $container->get( 'button.endpoint.cart-script-params' ); + assert( $endpoint instanceof CartScriptParamsEndpoint ); + $endpoint->handle_request(); + } + ); } /** diff --git a/modules/ppcp-button/src/Endpoint/CartScriptParamsEndpoint.php b/modules/ppcp-button/src/Endpoint/CartScriptParamsEndpoint.php new file mode 100644 index 000000000..ee976cac3 --- /dev/null +++ b/modules/ppcp-button/src/Endpoint/CartScriptParamsEndpoint.php @@ -0,0 +1,80 @@ +smart_button = $smart_button; + $this->logger = $logger; + } + + /** + * Returns the nonce. + * + * @return string + */ + public static function nonce(): string { + return self::ENDPOINT; + } + + /** + * Handles the request. + * + * @return bool + */ + public function handle_request(): bool { + try { + $script_data = $this->smart_button->script_data(); + + wp_send_json_success( $script_data['url_params'] ); + + return true; + } catch ( Throwable $error ) { + $this->logger->error( "CartScriptParamsEndpoint execution failed. {$error->getMessage()} {$error->getFile()}:{$error->getLine()}" ); + + wp_send_json_error(); + return false; + } + } +} From 6a0592f58be61790bdffb56fe1a73aaf5284f10f Mon Sep 17 00:00:00 2001 From: Alex P Date: Wed, 15 Feb 2023 16:52:16 +0200 Subject: [PATCH 07/18] Remove useless dcc check in cart --- .../resources/js/modules/ContextBootstrap/CartBootstap.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js index 0051ecb20..bd4242e07 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js @@ -41,9 +41,7 @@ class CartBootstrap { } shouldRender() { - return document.querySelector(this.gateway.button.wrapper) !== - null || document.querySelector(this.gateway.hosted_fields.wrapper) !== - null; + return document.querySelector(this.gateway.button.wrapper) !== null; } render() { From 0fd45c7fffab54024dccd1e6d2e90253a557367c Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 24 Feb 2023 15:17:40 +0100 Subject: [PATCH 08/18] Add playwright --- .env.sample | 1 + .gitignore | 3 +++ package.json | 10 ++++------ playwright.config.js | 14 ++++++++++++++ tests/playwright/example.spec.js | 6 ++++++ yarn.lock | 30 ++++++++++++++++++++++++++++++ 6 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 .env.sample create mode 100644 playwright.config.js create mode 100644 tests/playwright/example.spec.js diff --git a/.env.sample b/.env.sample new file mode 100644 index 000000000..a34939eb4 --- /dev/null +++ b/.env.sample @@ -0,0 +1 @@ +BASEURL="http://example.com" diff --git a/.gitignore b/.gitignore index 8fa78b2c6..009d9c219 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ modules/ppcp-wc-gateway/assets/css .env .env.e2e auth.json +/test-results/ +/playwright-report/ +/tests/playwright/.cache/ diff --git a/package.json b/package.json index 1fa2df72e..16effe908 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "author": "WooCommerce", "scripts": { "postinstall": "run-s install:modules:* && run-s build:modules", - "install:modules:ppcp-button": "cd modules/ppcp-button && yarn install", "install:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn install", "install:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn install", @@ -16,7 +15,6 @@ "install:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn install", "install:modules:ppcp-compat": "cd modules/ppcp-compat && yarn install", "install:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn install", - "build:modules:ppcp-button": "cd modules/ppcp-button && yarn run build", "build:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn run build", "build:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn run build", @@ -26,7 +24,6 @@ "build:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run build", "build:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run build", "build:modules": "run-p build:modules:*", - "watch:modules:ppcp-button": "cd modules/ppcp-button && yarn run watch", "watch:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn run watch", "watch:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn run watch", @@ -36,7 +33,6 @@ "watch:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run watch", "watch:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run watch", "watch:modules": "run-p watch:modules:*", - "ddev:setup": "ddev start && ddev orchestrate", "ddev:start": "ddev start", "ddev:stop": "ddev stop", @@ -55,7 +51,6 @@ "ddev:xdebug-on": "ddev xdebug", "ddev:xdebug-off": "ddev xdebug", "ddev:build-package": "ddev yarn build", - "prebuild": "rm -rf ./vendor && find . -name 'node_modules' -type d -maxdepth 3 -exec rm -rf {} +", "build": "composer install --no-dev && yarn install && yarn run archive", "prearchive": "rm -rf $npm_package_name.zip", @@ -67,7 +62,10 @@ "wp_org_slug": "woocommerce-paypal-payments" }, "dependencies": { + "dotenv": "^16.0.3", "npm-run-all": "^4.1.5" }, - "devDependencies": {} + "devDependencies": { + "@playwright/test": "^1.31.1" + } } diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 000000000..b2a2d5a08 --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,14 @@ +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +require('dotenv').config(); + +const config = { + timeout: 30000, + use: { + baseURL: process.env.BASEURL, + }, +}; + +module.exports = config; diff --git a/tests/playwright/example.spec.js b/tests/playwright/example.spec.js new file mode 100644 index 000000000..903559648 --- /dev/null +++ b/tests/playwright/example.spec.js @@ -0,0 +1,6 @@ +const { test, expect } = require('@playwright/test'); + +test('has ngrok url', async ({page, baseURL}) => { + await page.goto('/'); + await expect(page).toHaveURL(baseURL); +}); diff --git a/yarn.lock b/yarn.lock index 862d1ba46..89ead66f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,21 @@ # yarn lockfile v1 +"@playwright/test@^1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.31.1.tgz#39d6873dc46af135f12451d79707db7d1357455d" + integrity sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA== + dependencies: + "@types/node" "*" + playwright-core "1.31.1" + optionalDependencies: + fsevents "2.3.2" + +"@types/node@*": + version "18.14.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.1.tgz#90dad8476f1e42797c49d6f8b69aaf9f876fc69f" + integrity sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -75,6 +90,11 @@ define-properties@^1.1.3, define-properties@^1.1.4: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +dotenv@^16.0.3: + version "16.0.3" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" + integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -126,6 +146,11 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +fsevents@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -422,6 +447,11 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== +playwright-core@1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.31.1.tgz#4deeebbb8fb73b512593fe24bea206d8fd85ff7f" + integrity sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ== + read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" From 1a0c819434d0a68078455cd32c6c643a47883496 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 24 Feb 2023 15:24:53 +0100 Subject: [PATCH 09/18] Update playwright ignore paths --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 009d9c219..022598da2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,6 @@ modules/ppcp-wc-gateway/assets/css .env .env.e2e auth.json -/test-results/ -/playwright-report/ +/tests/playwright/test-results/ +/tests/playwright/playwright-report/ /tests/playwright/.cache/ From fa87e19618b8211fcd48164a6074e57a842dd6c9 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 24 Feb 2023 15:28:48 +0100 Subject: [PATCH 10/18] Exclude playwright config file from archive --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 16effe908..192dc56ae 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "prearchive": "rm -rf $npm_package_name.zip", "archive": "zip -r $npm_package_name.zip . -x **.git/\\* **node_modules/\\*", "postarchive": "yarn run archive:cleanup && rm -rf $npm_package_name && unzip $npm_package_name.zip -d $npm_package_name && rm $npm_package_name.zip && zip -r $npm_package_name.zip $npm_package_name && rm -rf $npm_package_name", - "archive:cleanup": "zip -d $npm_package_name.zip .env* .ddev/\\* \\*.idea/\\* .editorconfig tests/\\* .github/\\* .psalm/\\* wordpress_org_assets/\\* \\*.DS_Store \\*README.md \\*.gitattributes \\*.gitignore \\*composer.json \\*composer.lock patchwork.json phpunit.xml.dist .phpunit.result.cache phpcs.xml* psalm*.xml* \\*.babelrc \\*package.json \\*webpack.config.js \\*yarn.lock \\*.travis.yml" + "archive:cleanup": "zip -d $npm_package_name.zip .env* .ddev/\\* \\*.idea/\\* .editorconfig tests/\\* .github/\\* .psalm/\\* wordpress_org_assets/\\* \\*.DS_Store \\*README.md \\*.gitattributes \\*.gitignore \\*composer.json \\*composer.lock patchwork.json phpunit.xml.dist .phpunit.result.cache phpcs.xml* psalm*.xml* playwright.config.js \\*.babelrc \\*package.json \\*webpack.config.js \\*yarn.lock \\*.travis.yml\\" }, "config": { "wp_org_slug": "woocommerce-paypal-payments" From c84d10eb62f3096e9795025a89d3fada4bf884a9 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 24 Feb 2023 15:37:35 +0100 Subject: [PATCH 11/18] Add test dir config --- playwright.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/playwright.config.js b/playwright.config.js index b2a2d5a08..2b3c3a244 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -5,6 +5,7 @@ require('dotenv').config(); const config = { + testDir: './tests/playwright', timeout: 30000, use: { baseURL: process.env.BASEURL, From a121373be03d9015635a301037ffd620ce34fa40 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 24 Feb 2023 15:48:14 +0100 Subject: [PATCH 12/18] Add basic documentation --- tests/playwright/README.md | 14 ++++++++++++++ tests/playwright/example.spec.js | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 tests/playwright/README.md diff --git a/tests/playwright/README.md b/tests/playwright/README.md new file mode 100644 index 000000000..48e70b36a --- /dev/null +++ b/tests/playwright/README.md @@ -0,0 +1,14 @@ +# Playwright Testing + +## Local Environment Variables +Allows using environment variables inside the tests. + +- Duplicate `.env.sample` and rename it as `.env`, set values and add new variables if needed. + +## Run Tests +``` +$ npx playwright test +$ npx playwright test example.spec.js --headed +$ npx playwright test example.spec.js --debug +$ npx playwright test -g "Test name here" +``` diff --git a/tests/playwright/example.spec.js b/tests/playwright/example.spec.js index 903559648..789a68e19 100644 --- a/tests/playwright/example.spec.js +++ b/tests/playwright/example.spec.js @@ -1,6 +1,6 @@ const { test, expect } = require('@playwright/test'); -test('has ngrok url', async ({page, baseURL}) => { +test('has title', async ({ page }) => { await page.goto('/'); - await expect(page).toHaveURL(baseURL); + await expect(page).toHaveTitle(/WooCommerce PayPal Payments/); }); From f174e8b8d61f6841ad7d6b3121433340cdb78f1e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 24 Feb 2023 15:55:04 +0100 Subject: [PATCH 13/18] Move playwright ignores to its own directory --- .gitignore | 3 --- tests/playwright/.gitignore | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 tests/playwright/.gitignore diff --git a/.gitignore b/.gitignore index 022598da2..8fa78b2c6 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,3 @@ modules/ppcp-wc-gateway/assets/css .env .env.e2e auth.json -/tests/playwright/test-results/ -/tests/playwright/playwright-report/ -/tests/playwright/.cache/ diff --git a/tests/playwright/.gitignore b/tests/playwright/.gitignore new file mode 100644 index 000000000..ee8266b18 --- /dev/null +++ b/tests/playwright/.gitignore @@ -0,0 +1,3 @@ +test-results/ +playwright-report/ +.cache/ From 9d460605a8e7d2ec8f894ffe87971ab59af85256 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 24 Feb 2023 17:22:03 +0100 Subject: [PATCH 14/18] Add basic tests --- .env.sample | 7 +++ tests/playwright/example.spec.js | 6 --- tests/playwright/place-order.spec.js | 77 ++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 6 deletions(-) delete mode 100644 tests/playwright/example.spec.js create mode 100644 tests/playwright/place-order.spec.js diff --git a/.env.sample b/.env.sample index a34939eb4..d29ddcf5f 100644 --- a/.env.sample +++ b/.env.sample @@ -1 +1,8 @@ BASEURL="http://example.com" + +CUSTOMER_EMAIL="customer@example.com" +CUSTOMER_PASSWORD="password" + +CREDIT_CARD_NUMBER="1234567890" +CREDIT_CARD_EXPIRATION="01/2042" +CREDIT_CARD_CVV="123" diff --git a/tests/playwright/example.spec.js b/tests/playwright/example.spec.js deleted file mode 100644 index 789a68e19..000000000 --- a/tests/playwright/example.spec.js +++ /dev/null @@ -1,6 +0,0 @@ -const { test, expect } = require('@playwright/test'); - -test('has title', async ({ page }) => { - await page.goto('/'); - await expect(page).toHaveTitle(/WooCommerce PayPal Payments/); -}); diff --git a/tests/playwright/place-order.spec.js b/tests/playwright/place-order.spec.js new file mode 100644 index 000000000..2a28fbd2c --- /dev/null +++ b/tests/playwright/place-order.spec.js @@ -0,0 +1,77 @@ +require('dotenv').config(); + +const {test, expect} = require('@playwright/test'); +const { + CUSTOMER_EMAIL, + CUSTOMER_PASSWORD, + CREDIT_CARD_NUMBER, + CREDIT_CARD_EXPIRATION, + CREDIT_CARD_CVV +} = process.env; + +async function fillCheckoutForm(page) { + await page.fill('#billing_first_name', 'John'); + await page.fill('#billing_last_name', 'Doe'); + await page.selectOption('select#billing_country', 'DE'); + await page.fill('#billing_address_1', 'Badensche Str. 24'); + await page.fill('#billing_postcode', '10715'); + await page.fill('#billing_city', '10715'); + await page.fill('#billing_phone', '1234567890'); + await page.fill('#billing_email', CUSTOMER_EMAIL); +} + +test('PayPal button place order from product page', async ({page}) => { + + await page.goto('/product/product/'); + + const [popup] = await Promise.all([ + page.waitForEvent('popup'), + page.frameLocator('.component-frame').locator('[data-funding-source="paypal"]').click(), + ]); + await popup.waitForLoadState(); + + await popup.click("text=Log in"); + await popup.fill('#email', CUSTOMER_EMAIL); + await popup.locator('#btnNext').click(); + await popup.fill('#password', CUSTOMER_PASSWORD); + await popup.locator('#btnLogin').click(); + + await popup.locator('#payment-submit-btn').click(); + await fillCheckoutForm(page); + + await Promise.all([ + page.waitForNavigation(), + page.locator('#place_order').click(), + ]); + + const title = await page.locator('.entry-title'); + await expect(title).toHaveText('Order received'); +}); + +test('ACDC', async ({page}) => { + + await page.goto('/product/product/'); + await page.locator('.single_add_to_cart_button').click(); + + await page.goto('/checkout/'); + await fillCheckoutForm(page); + + await page.click("text=Credit Cards"); + + const creditCardNumber = page.frameLocator('#braintree-hosted-field-number').locator('#credit-card-number'); + await creditCardNumber.fill(CREDIT_CARD_NUMBER); + + const expirationDate = page.frameLocator('#braintree-hosted-field-expirationDate').locator('#expiration'); + await expirationDate.fill(CREDIT_CARD_EXPIRATION); + + const cvv = page.frameLocator('#braintree-hosted-field-cvv').locator('#cvv'); + await cvv.fill(CREDIT_CARD_CVV); + + await Promise.all([ + page.waitForNavigation(), + page.locator('.ppcp-dcc-order-button').click(), + ]); + + const title = await page.locator('.entry-title'); + await expect(title).toHaveText('Order received'); +}); From e9eba09ebb359cebd8bfa31a5004d42cf3088e80 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 27 Feb 2023 11:39:30 +0100 Subject: [PATCH 15/18] Use existing e2e environment config file --- .env.e2e.example | 9 +++++++++ .env.sample | 8 -------- playwright.config.js | 6 +----- tests/playwright/README.md | 2 +- tests/playwright/place-order.spec.js | 6 ++---- 5 files changed, 13 insertions(+), 18 deletions(-) delete mode 100644 .env.sample diff --git a/.env.e2e.example b/.env.e2e.example index 02ffdb0bc..6caba9f7c 100644 --- a/.env.e2e.example +++ b/.env.e2e.example @@ -1 +1,10 @@ PPCP_E2E_WP_DIR=${ROOT_DIR}/.ddev/wordpress + +BASEURL="http://example.com" + +CUSTOMER_EMAIL="customer@example.com" +CUSTOMER_PASSWORD="password" + +CREDIT_CARD_NUMBER="1234567890" +CREDIT_CARD_EXPIRATION="01/2042" +CREDIT_CARD_CVV="123" diff --git a/.env.sample b/.env.sample deleted file mode 100644 index d29ddcf5f..000000000 --- a/.env.sample +++ /dev/null @@ -1,8 +0,0 @@ -BASEURL="http://example.com" - -CUSTOMER_EMAIL="customer@example.com" -CUSTOMER_PASSWORD="password" - -CREDIT_CARD_NUMBER="1234567890" -CREDIT_CARD_EXPIRATION="01/2042" -CREDIT_CARD_CVV="123" diff --git a/playwright.config.js b/playwright.config.js index 2b3c3a244..081c1c0f8 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -1,8 +1,4 @@ -/** - * Read environment variables from file. - * https://github.com/motdotla/dotenv - */ -require('dotenv').config(); +require('dotenv').config({ path: '.env.e2e' }); const config = { testDir: './tests/playwright', diff --git a/tests/playwright/README.md b/tests/playwright/README.md index 48e70b36a..cd8e56468 100644 --- a/tests/playwright/README.md +++ b/tests/playwright/README.md @@ -3,7 +3,7 @@ ## Local Environment Variables Allows using environment variables inside the tests. -- Duplicate `.env.sample` and rename it as `.env`, set values and add new variables if needed. +- Duplicate `.env.e2e.example` and rename it as `.env.e2e`, set values and add new variables if needed. ## Run Tests ``` diff --git a/tests/playwright/place-order.spec.js b/tests/playwright/place-order.spec.js index 2a28fbd2c..c778446da 100644 --- a/tests/playwright/place-order.spec.js +++ b/tests/playwright/place-order.spec.js @@ -1,5 +1,3 @@ -require('dotenv').config(); - const {test, expect} = require('@playwright/test'); const { CUSTOMER_EMAIL, @@ -20,7 +18,7 @@ async function fillCheckoutForm(page) { await page.fill('#billing_email', CUSTOMER_EMAIL); } -test('PayPal button place order from product page', async ({page}) => { +test('PayPal button place order from Product page', async ({page}) => { await page.goto('/product/product/'); @@ -48,7 +46,7 @@ test('PayPal button place order from product page', async ({page}) => { await expect(title).toHaveText('Order received'); }); -test('ACDC', async ({page}) => { +test('Advanced Credit and Debit Card (ACDC) place order from Checkout page', async ({page}) => { await page.goto('/product/product/'); await page.locator('.single_add_to_cart_button').click(); From d31a9a982b2a46079e3eabc4c33f3b1094d2be66 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 27 Feb 2023 16:38:49 +0100 Subject: [PATCH 16/18] Add `ci` tag for running non headed tests, add command to `package.json` --- package.json | 3 ++- tests/playwright/README.md | 1 + tests/playwright/place-order.spec.js | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 192dc56ae..beb9217ce 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,8 @@ "ddev:composer-update": "ddev composer update && ddev composer update --lock", "ddev:unit-tests": "ddev exec phpunit", "ddev:e2e-tests": "cp -n .env.e2e.example .env.e2e && ddev php tests/e2e/PHPUnit/setup.php && ddev exec phpunit -c tests/e2e/phpunit.xml.dist", - "ddev:test": "yarn run ddev:unit-tests && yarn run ddev:e2e-tests", + "ddev:pw-tests": "npx playwright test --grep @ci", + "ddev:test": "yarn run ddev:unit-tests && yarn run ddev:e2e-tests && yarn run ddev:pw-tests", "ddev:lint": "yarn ddev:phpcs && yarn ddev:psalm", "ddev:phpcs": "ddev exec phpcs --parallel=8 -s", "ddev:psalm": "ddev exec psalm --show-info=false --threads=8 --diff", diff --git a/tests/playwright/README.md b/tests/playwright/README.md index cd8e56468..d9b337d78 100644 --- a/tests/playwright/README.md +++ b/tests/playwright/README.md @@ -8,6 +8,7 @@ Allows using environment variables inside the tests. ## Run Tests ``` $ npx playwright test +$ npx playwright test --grep @ci $ npx playwright test example.spec.js --headed $ npx playwright test example.spec.js --debug $ npx playwright test -g "Test name here" diff --git a/tests/playwright/place-order.spec.js b/tests/playwright/place-order.spec.js index c778446da..ff24ffe96 100644 --- a/tests/playwright/place-order.spec.js +++ b/tests/playwright/place-order.spec.js @@ -46,7 +46,7 @@ test('PayPal button place order from Product page', async ({page}) => { await expect(title).toHaveText('Order received'); }); -test('Advanced Credit and Debit Card (ACDC) place order from Checkout page', async ({page}) => { +test('Advanced Credit and Debit Card (ACDC) place order from Checkout page @ci', async ({page}) => { await page.goto('/product/product/'); await page.locator('.single_add_to_cart_button').click(); From 9876fc67fa3e9859af02921a798641d7621f6839 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 27 Feb 2023 17:35:23 +0100 Subject: [PATCH 17/18] Use `ddev exec npx` to avoid installing on host --- package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index beb9217ce..fa4b97a33 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,10 @@ "ddev:composer-update": "ddev composer update && ddev composer update --lock", "ddev:unit-tests": "ddev exec phpunit", "ddev:e2e-tests": "cp -n .env.e2e.example .env.e2e && ddev php tests/e2e/PHPUnit/setup.php && ddev exec phpunit -c tests/e2e/phpunit.xml.dist", - "ddev:pw-tests": "npx playwright test --grep @ci", - "ddev:test": "yarn run ddev:unit-tests && yarn run ddev:e2e-tests && yarn run ddev:pw-tests", + "ddev:pw-install": "ddev exec npx playwright install --with-deps", + "ddev:pw-tests-ci": "ddev exec npx playwright test --grep @ci", + "ddev:pw-tests-headed": "ddev exec npx playwright test --headed", + "ddev:test": "yarn run ddev:unit-tests && yarn run ddev:e2e-tests && yarn run ddev:pw-tests-ci", "ddev:lint": "yarn ddev:phpcs && yarn ddev:psalm", "ddev:phpcs": "ddev exec phpcs --parallel=8 -s", "ddev:psalm": "ddev exec psalm --show-info=false --threads=8 --diff", From 2b1363671b2c3ace14ede2d1bd77c61e1a9a3322 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 28 Feb 2023 09:44:44 +0100 Subject: [PATCH 18/18] Add ddev url to env config file --- .env.e2e.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.e2e.example b/.env.e2e.example index 6caba9f7c..079cdb856 100644 --- a/.env.e2e.example +++ b/.env.e2e.example @@ -1,6 +1,6 @@ PPCP_E2E_WP_DIR=${ROOT_DIR}/.ddev/wordpress -BASEURL="http://example.com" +BASEURL="https://woocommerce-paypal-payments.ddev.site" CUSTOMER_EMAIL="customer@example.com" CUSTOMER_PASSWORD="password"