First upload

This commit is contained in:
Nikolai Fesenko
2025-02-02 13:37:56 +01:00
commit 8d227c9191
3281 changed files with 362319 additions and 0 deletions

View File

@@ -0,0 +1,289 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
declare(strict_types=1);
namespace OxidEsales\PayPalModule\Tests\Codeception\Acceptance;
use OxidEsales\Facts\Facts;
use Codeception\Util\Fixtures;
use OxidEsales\EshopCommunity\Internal\Framework\Module\Setup\Exception\ModuleSetupException;
use OxidEsales\PayPalModule\Tests\Codeception\AcceptanceTester;
use OxidEsales\PayPalModule\Tests\Codeception\Page\PayPalLogin;
use OxidEsales\Codeception\Module\Translation\Translator;
use OxidEsales\Codeception\Step\Basket;
use OxidEsales\Codeception\Page\Checkout\ThankYou;
use OxidEsales\Codeception\Page\Checkout\PaymentCheckout;
use OxidEsales\Codeception\Page\Checkout\OrderCheckout;
use OxidEsales\Codeception\Page\Checkout\Basket as BasketCheckout;
abstract class BaseCest
{
public function _before(AcceptanceTester $I): void
{
$this->activateModules();
$I->clearShopCache();
$I->updateConfigInDatabase('blUseStock', false, 'bool');
$I->updateConfigInDatabase('bl_perfLoadPrice', true, 'bool');
$I->updateConfigInDatabase('iNewBasketItemMessage', false, 'bool');
$I->updateConfigInDatabase('blOEPayPalFinalizeOrderOnPayPal', false, 'bool');
$I->updateConfigInDatabase('sOEPayPalTransactionMode', 'Sale', 'str');
$I->haveInDatabase('oxobject2payment', Fixtures::get('paymentMethod'));
$I->haveInDatabase('oxobject2payment', Fixtures::get('paymentCountry'));
$this->ensureShopUserData($I);
}
public function _after(AcceptanceTester $I): void
{
$this->ensureShopUserData($I);
$I->updateConfigInDatabase('blShowNetPrice', false, 'bool');
$I->deleteFromDatabase('oxorder', ['OXORDERNR >=' => '2']);
$I->deleteFromDatabase('oxuserbaskets', ['OXTITLE >=' => 'savedbasket']);
$I->resetCookie('sid');
$I->resetCookie('sid_key');
}
protected function getShopUrl(): string
{
$facts = new Facts();
return $facts->getShopUrl();
}
/**
* Activates modules
*/
protected function activateModules(int $shopId = 1): void
{
$testConfig = new \OxidEsales\TestingLibrary\TestConfig();
$modulesToActivate = $testConfig->getModulesToActivate();
if ($modulesToActivate) {
$serviceCaller = new \OxidEsales\TestingLibrary\ServiceCaller();
$serviceCaller->setParameter('modulestoactivate', $modulesToActivate);
try {
$serviceCaller->callService('ModuleInstaller', $shopId);
} catch (ModuleSetupException $e) {
// this may happen if the module is already active,
// we can ignore this
}
}
}
protected function ensureShopUserData(AcceptanceTester $I): void
{
$toBeUpdated = $_ENV['sBuyerLogin'];
if (0 < $I->grabNumRecords('oxuser', ['oxusername' => Fixtures::get('userName')])) {
$toBeUpdated = Fixtures::get('userName');
$I->deleteFromDatabase('oxuser', ['oxusername' => $_ENV['sBuyerLogin']]);
}
if (0 < $I->grabNumRecords('oxnewssubscribed', ['oxemail' => $_ENV['sBuyerLogin']])) {
$I->deleteFromDatabase('oxnewssubscribed', ['oxemail' => $_ENV['sBuyerLogin']]);
}
$I->updateInDatabase(
'oxuser',
[
'oxusername' => Fixtures::get('userName'),
'oxcity' => Fixtures::get('details')['oxcity'],
'oxstreet' => Fixtures::get('details')['oxstreet'],
'oxstreetnr' => Fixtures::get('details')['oxstreetnr'],
'oxzip' => Fixtures::get('details')['oxzip'],
'oxfname' => Fixtures::get('details')['firstname'],
'oxlname' => Fixtures::get('details')['lastname'],
'oxcountryid' => 'a7c40f631fc920687.20179984' //DE
],
[
'oxusername' => $toBeUpdated
]
);
$I->updateInDatabase(
'oxuser',
[
'oxpassword' => '$2y$10$b186f117054b700a89de9uXDzfahkizUucitfPov3C2cwF5eit2M2',
'oxpasssalt' => 'b186f117054b700a89de929ce90c6aef'
],
[
'oxusername' => Fixtures::get('userName')
]
);
$I->seeInDatabase('oxuser', ['oxusername' => Fixtures::get('userName')]);
$I->dontSeeInDatabase('oxuser', ['oxusername' => $_ENV['sBuyerLogin']]);
}
protected function setUserDataSameAsPayPal(AcceptanceTester $I, bool $removePassword = false): void
{
$I->updateInDatabase(
'oxuser',
[
'oxusername' => $_ENV['sBuyerLogin'],
'oxfname' => $_ENV['sBuyerFirstName'],
'oxlname' => $_ENV['sBuyerLastName'],
'oxstreet' => 'ESpachstr.',
'oxstreetnr' => '1',
'oxzip' => '79111',
'oxcity' => 'Freiburg'
],
[
'oxusername' => Fixtures::get('userName')
]
);
if ($removePassword) {
$this->removePassword($I);
}
}
protected function removePassword(AcceptanceTester $I): void
{
$I->updateInDatabase(
'oxuser',
[
'oxpassword' => '',
'oxpasssalt' => ''
],
[
'oxusername' => $_ENV['sBuyerLogin']
]
);
}
protected function setUserNameSameAsPayPal(AcceptanceTester $I): void
{
$I->updateInDatabase(
'oxuser',
[
'oxusername' => $_ENV['sBuyerLogin'],
],
[
'oxusername' => Fixtures::get('userName')
]
);
}
protected function proceedToPaymentStep(AcceptanceTester $I, string $userName = null): void
{
$userName = $userName ?: Fixtures::get('userName');
$home = $I->openShop()
->loginUser($userName, Fixtures::get('userPassword'));
$I->waitForText(Translator::translate('HOME'));
//add product to basket and start checkout
$this->fillBasket($I);
$this->fromBasketToPayment($I);
}
protected function fromBasketToPayment(AcceptanceTester $I): void
{
$I->amOnPage('/en/cart');
$basketPage = new BasketCheckout($I);
$basketPage->goToNextStep()
->goToNextStep();
$I->see(Translator::translate('PAYMENT_METHOD'));
}
protected function proceedToBasketStep(AcceptanceTester $I, string $userName = null, bool $logMeIn = true): void
{
$userName = $userName ?: Fixtures::get('userName');
$home = $I->openShop();
if ($logMeIn) {
$home->loginUser($userName, Fixtures::get('userPassword'));
}
$I->waitForText(Translator::translate('HOME'));
//add product to basket and start checkout
$this->fillBasket($I);
$I->seeElement('//input[@name="paypalExpressCheckoutButtonECS"]');
}
protected function fillBasket(AcceptanceTester $I): void
{
//add product to basket and start checkout
$product = Fixtures::get('product');
$basket = new Basket($I);
$basket->addProductToBasketAndOpenBasket($product['id'], $product['amount'], 'basket');
$I->see(Translator::translate('CONTINUE_TO_NEXT_STEP'));
}
protected function finalizeOrder(AcceptanceTester $I): string
{
$paymentPage = new PaymentCheckout($I);
$paymentPage->goToNextStep()
->submitOrder();
$thankYouPage = new ThankYou($I);
return $thankYouPage->grabOrderNumber();
}
protected function finalizeOrderInOrderStep(AcceptanceTester $I): string
{
$orderPage = new OrderCheckout($I);
$orderPage->submitOrder();
$thankYouPage = new ThankYou($I);
return $thankYouPage->grabOrderNumber();
}
protected function approvePayPalTransaction(AcceptanceTester $I, string $addParams = ''): string
{
//workaround to approve the transaction on PayPal side
$loginPage = new PayPalLogin($I);
$loginPage->openPayPalApprovalPage($I, $addParams);
$token = $loginPage->getToken();
$loginPage->approveStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
return $token;
}
protected function approveAnonymousPayPalTransaction(AcceptanceTester $I, string $addParams = ''): string
{
//workaround to approve the transaction on PayPal side
$loginPage = new PayPalLogin($I);
$loginPage->openPayPalApprovalPageAsAnonymousUser($I, $addParams);
$token = $loginPage->getToken();
$loginPage->approveStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
return $token;
}
protected function openOrderPayPal(AcceptanceTester $I, string $orderNumber): void
{
$adminPanel = $I->loginAdmin();
$orders = $adminPanel->openOrders();
$I->waitForDocumentReadyState();
$orders->find($orders->orderNumberInput, $orderNumber);
$I->selectListFrame();
$I->click("//a[contains(@href, '#oepaypalorder_paypal')]", 'edit');
$I->selectEditFrame();
}
}

View File

@@ -0,0 +1,101 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception\Acceptance;
use OxidEsales\Codeception\Step\ProductNavigation;
use OxidEsales\PayPalModule\Tests\Codeception\AcceptanceTester;
use Codeception\Util\Fixtures;
use OxidEsales\Codeception\Module\Translation\Translator;
/**
* @group oepaypal
* @group oepaypal_standard
* @group oepaypal_buttons
*
* Tests for checkout with regular payment method 'oxidpaypal'
*/
class ButtonPlacementCest extends BaseCest
{
public function _after(AcceptanceTester $I): void
{
$I->activatePaypalModule();
parent::_after($I);
}
/**
* @group oepaypal_deactivated
*/
public function deactivatedModuleDoesNoHarm(AcceptanceTester $I): void
{
$I->wantToTest('that the shop is safe with installed but inactive PayPal Module');
$I->deactivatePaypalModule();
$I->openShop();
$I->waitForText(Translator::translate('HOME'));
$productNavigation = new ProductNavigation($I);
$productNavigation->openProductDetailsPage(Fixtures::get('product')['id']);
$I->dontSeeElement("#paypalExpressCheckoutDetailsButton");
$home = $I->openShop()
->loginUser(Fixtures::get('userName'), Fixtures::get('userPassword'));
$I->waitForText(Translator::translate('HOME'));
$this->fillBasket($I);
$I->dontSeeElement('//input[@name="paypalExpressCheckoutButtonECS"]');
$home->openMiniBasket();
$I->dontSeeElement('#paypalExpressCheckoutMiniBasketImage');
$this->fromBasketToPayment($I);
$I->dontSeeElement('#payment_oxidpaypal');
}
public function seeButtonsInExpectedLocations(AcceptanceTester $I): void
{
$I->wantToTest('that all PayPal checkout buttons are shown');
$I->openShop();
$I->waitForText(Translator::translate('HOME'));
$productNavigation = new ProductNavigation($I);
$productNavigation->openProductDetailsPage(Fixtures::get('product')['id']);
$I->seeElement("#paypalExpressCheckoutDetailsButton");
$home = $I->openShop()
->loginUser(Fixtures::get('userName'), Fixtures::get('userPassword'));
$I->waitForText(Translator::translate('HOME'));
//add product to basket and start checkout
$this->fillBasket($I);
$I->seeElement('//input[@name="paypalExpressCheckoutButtonECS"]');
$home->openMiniBasket();
$I->seeElement('#paypalExpressCheckoutMiniBasketImage');
$this->fromBasketToPayment($I);
$I->seeElement('#payment_oxidpaypal');
}
}

View File

@@ -0,0 +1,300 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception\Acceptance;
use Codeception\Example;
use Codeception\Util\Fixtures;
use OxidEsales\Codeception\Page\Checkout\ThankYou;
use OxidEsales\Codeception\Step\Basket;
use OxidEsales\Codeception\Step\ProductNavigation;
use OxidEsales\Codeception\Page\Checkout\PaymentCheckout;
use OxidEsales\PayPalModule\Tests\Codeception\AcceptanceTester;
use OxidEsales\Codeception\Module\Translation\Translator;
/**
* @group oepaypal
* @group oepaypal_standard
* @group oepaypal_callback_controller
*/
class CallbackControllerCest extends BaseCest
{
/**
* @dataProvider providerCallBackTests
*/
public function testCallbackResponse(AcceptanceTester $I, Example $data): void
{
$I->wantToTest('callback response depending on session information');
$home = $I->openShop();
$I->waitForText(Translator::translate('HOME'));
if ($data['doLogInUser']) {
$home->loginUser(Fixtures::get('userName'), Fixtures::get('userPassword'));
}
if ($data['doFillBasket']) {
$this->fillBasket($I);
}
$token = (string) $I->grabValueFrom('//input[@name="stoken"]');
$sid = (string) $I->grabCookie('sid');
//Check callback
$callbackUrl = $this->getCallbackUrl($I, $sid, $token, $data['payPalData']);
$result = $this->getCallbackResponse($callbackUrl, $sid);
$this->assertResults($I, $data['expected'], $result);
}
/**
* Data provider.
*
* @return array
*/
protected function providerCallBackTests()
{
$data = [];
//Test case that callback is provided a session id but the basket related to that SID is empty and PP data empty.
$data['CallbackEmptyBasketNoPayPalData'] = [
'payPalData' => [],
'expected' => [
'METHOD' => 'CallbackResponse',
'NO_SHIPPING_OPTION_DETAILS' => 1],
'doLogInUser' => false,
'doFillBasket' => true
];
//Test case that callback data from PayPal is incomplete. Basket filled.
$data['LoggedInUserEmptyBasketUnknownCountry'] = [
'payPalData' => [
'FIRSTNAME' => 'gerName',
'LASTNAME' => 'gerlastname',
'SHIPTONAME' => "Testuser",
'SHIPTOSTREET' => 'Musterstr. 123',
'SHIPTOCITY' => 'Musterstadt',
'SHIPTOZIP' => '79098'
],
'expected' => [
'METHOD' => 'CallbackResponse',
'NO_SHIPPING_OPTION_DETAILS' => 1
],
'doLogInUser' => true,
'doFillBasket' => true
];
//Test case that user enters different address in PayPal for country that has PP attached in shop
$data['CallbackOkForLoggedInUserGetDelSet'] = [
'payPalData' => [
'FIRSTNAME' => 'gerName',
'LASTNAME' => 'gerlastname',
'SHIPTONAME' => "Testuser",
'SHIPTOSTREET' => 'Universitätsring 1',
'SHIPTOCITY' => 'Wien',
'SHIPTOZIP' => '1010',
'SHIPTOCOUNTRY' => 'AT',
'SHIPTOCOUNTRYCODE' => 'AT',
'SHIPTOCOUNTRYNAME' => 'Austria'],
'expected' => [
'METHOD' => 'CallbackResponse',
'L_SHIPPINGOPTIONNAME0' => 'Standard',
'L_SHIPPINGOPTIONLABEL0' => 'Preis',
'L_SHIPPINGOPTIONAMOUNT0' => '6.90',
'L_SHIPPINGOPTIONISDEFAULT0' => 'true',
'L_TAXAMT0' => '0.00',
'L_SHIPPINGOPTIONNAME1' => 'Beispiel+Set1%3A+UPS+48+Std.',
'L_SHIPPINGOPTIONLABEL1' => 'Preis',
'L_SHIPPINGOPTIONAMOUNT1' => '9.90',
'L_SHIPPINGOPTIONISDEFAULT1' => 'false',
'L_TAXAMT1' => '0.00',
'L_SHIPPINGOPTIONNAME2' => 'Beispiel+Set1%3A+UPS+24+Std.+Express',
'L_SHIPPINGOPTIONLABEL2' => 'Preis',
'L_SHIPPINGOPTIONAMOUNT2' => '12.90',
'L_SHIPPINGOPTIONISDEFAULT2' => 'false',
'L_TAXAMT2' => '0.00',
'OFFERINSURANCEOPTION' => 'false'],
'doLogInUser' => true,
'doFillBasket' => true
];
//Test case that callback data from PayPal is complete. Basket empty.
$data['LoggedInUserFilledBasketUnknownCountry'] = [
'payPalData' => [
'FIRSTNAME' => 'gerName',
'LASTNAME' => 'gerlastname',
'SHIPTONAME' => "Testuser",
'SHIPTOSTREET' => 'Musterstr. 123',
'SHIPTOCITY' => 'Musterstadt',
'SHIPTOZIP' => '79098',
'SHIPTOCOUNTRY' => 'DE',
'SHIPTOCOUNTRYCODE' => 'DE',
'SHIPTOCOUNTRYNAME' => 'Germany'
],
'expected' => [
'METHOD' => 'CallbackResponse',
'L_SHIPPINGOPTIONNAME0' => 'Standard',
'L_SHIPPINGOPTIONLABEL0' => 'Preis',
'L_SHIPPINGOPTIONAMOUNT0' => '0.00',
'L_SHIPPINGOPTIONISDEFAULT0' => 'true',
'L_TAXAMT0' => '0.00',
'L_SHIPPINGOPTIONNAME1' => 'Beispiel+Set1%3A+UPS+48+Std.',
'L_SHIPPINGOPTIONLABEL1' => 'Preis',
'L_SHIPPINGOPTIONAMOUNT1' => '0.00',
'L_SHIPPINGOPTIONISDEFAULT1' => 'false',
'L_TAXAMT1' => '0.00',
'OFFERINSURANCEOPTION' => 'false'
],
'doLogInUser' => true,
'doFillBasket' => false
];
//Test case that callback data from PayPal is complete. Basket filled.
$data['CallbackOkForLoggedInUserGermany'] = [
'payPalData' => [
'FIRSTNAME' => 'gerName',
'LASTNAME' => 'gerlastname',
'SHIPTONAME' => "Testuser",
'SHIPTOSTREET' => 'Musterstr. 123',
'SHIPTOCITY' => 'Musterstadt',
'SHIPTOZIP' => '79098',
'SHIPTOCOUNTRY' => 'DE',
'SHIPTOCOUNTRYCODE' => 'DE',
'SHIPTOCOUNTRYNAME' => 'Germany'
],
'expected' => [
'METHOD' => 'CallbackResponse',
'L_SHIPPINGOPTIONNAME0' => 'Standard',
'L_SHIPPINGOPTIONLABEL0' => 'Preis',
'L_SHIPPINGOPTIONAMOUNT0' => '0.00',
'L_SHIPPINGOPTIONISDEFAULT0' => 'true',
'L_TAXAMT0' => '0.00',
'L_SHIPPINGOPTIONNAME1' => 'Beispiel+Set1%3A+UPS+48+Std.',
'L_SHIPPINGOPTIONLABEL1' => 'Preis',
'L_SHIPPINGOPTIONAMOUNT1' => '9.90',
'L_SHIPPINGOPTIONISDEFAULT1' => 'false',
'L_TAXAMT1' => '0.00',
'OFFERINSURANCEOPTION' => 'false'
],
'doLogInUser' => true,
'doFillBasket' => true
];
//Test case that user enters different address in PayPal for country that has no PP attached in shop
$data['CallbackOkForLoggedInUserChange'] = [
'payPalData' => [
'FIRSTNAME' => 'gerName',
'LASTNAME' => 'gerlastname',
'SHIPTONAME' => "Testuser",
'SHIPTOSTREET' => 'Musterstr. 123',
'SHIPTOCITY' => 'Antwerp',
'SHIPTOZIP' => '2000',
'SHIPTOCOUNTRY' => 'BE',
'SHIPTOCOUNTRYCODE' => 'BE',
'SHIPTOCOUNTRYNAME' => 'Belgien'
],
'expected' => [
'METHOD' => 'CallbackResponse',
'NO_SHIPPING_OPTION_DETAILS' => 1
],
'doLogInUser' => true,
'doFillBasket' => true
];
return $data;
}
/**
* Test helper to assemble call back URl from PayPal to shop.
*
* @param string $sid Session id.
* @param string $token Rtoken.
* @param array $paypalData Optional paypal data.
* @param int $language Optional language id.
*
* @return string
*/
private function getCallbackUrl(AcceptanceTester $I, $sid, $token, $paypalData = [], $language = 0)
{
$callbackUrl = $I->getShopUrl() . 'index.php?';
$data = [
'lang' => $language,
'sid' => $sid,
'rtoken' => $token,
'shp' => 1,
'cl' => 'oepaypalexpresscheckoutdispatcher',
'fnc' => 'processCallBack'
];
$data = array_merge($data, $paypalData);
foreach ($data as $key => $value) {
$callbackUrl .= '&' . $key . '=' . urlencode($value);
}
return $callbackUrl;
}
/**
* Get response from callback as array.
*
* @param array $requestParameters
*
* @return array
*/
private function getCallbackResponse($callbackUrl, $sid)
{
$result = [];
$curlHandler = oxNew(\OxidEsales\Eshop\Core\Curl::class);
$curlHandler->setUrl($callbackUrl);
$cookieString = 'sid=' . $sid . '; sid_key=oxid;';
$curlHandler->setOption('CURLOPT_COOKIE', $cookieString);
$curlHandler->setOption('CURLOPT_COOKIESESSION', true);
$curlHandler->setOption('CURLOPT_USERAGENT', 'test user agent');
$raw = $curlHandler->execute();
$tmp = explode('&', $raw);
if (is_array($tmp) && !empty($tmp)) {
foreach ($tmp as $keyValue) {
$sub = explode('=', $keyValue);
$result[$sub[0]] = $sub[1];
}
}
return $result;
}
/**
* Test helper to check results.
*
* @param array $toBeAsserted
* @param array $result
*/
private function assertResults(AcceptanceTester $I, $toBeAsserted, $result)
{
foreach ($toBeAsserted as $key => $expected) {
$I->assertTrue(array_key_exists($key, $result), "Key '{$key}' missing in result array.");
$I->assertEquals($expected, $result[$key], "Value '{$expected}' for key '{$key}' not as expected.");
}
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception\Acceptance;
use Codeception\Util\Fixtures;
use OxidEsales\Codeception\Page\Checkout\ThankYou;
use OxidEsales\Codeception\Step\Basket;
use OxidEsales\PayPalModule\Tests\Codeception\AcceptanceTester;
use OxidEsales\PayPalModule\Tests\Codeception\Page\PayPalLogin;
use OxidEsales\Codeception\Module\Translation\Translator;
/**
* @group oepaypal
* @group oepaypal_standard
* @group oepaypal_capture_and_refund
*/
class CaptureAndRefundCest extends BaseCest
{
/**
* @param AcceptanceTester $I
*
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_captureandrefund
*/
public function orderCaptureAndRefundAmount(AcceptanceTester $I)
{
$I->updateConfigInDatabase('sOEPayPalTransactionMode', 'Authorization', 'str');
$I->updateConfigInDatabase('blOEPayPalFinalizeOrderOnPayPal', true, 'bool');
$basket = new Basket($I);
$basketItem = Fixtures::get('product');
//add Product to basket
$basket->addProductToBasket($basketItem['id'], $basketItem['amount']);
$I->openShop()->seeMiniBasketContains([$basketItem], $basketItem['price'], (string) $basketItem['amount']);
$I->waitForElementVisible("#paypalExpressCheckoutMiniBasketImage", 10);
$I->click("#paypalExpressCheckoutMiniBasketImage");
$loginPage = new PayPalLogin($I);
$loginPage->loginAndCheckout($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
$thankYouPage = new ThankYou($I);
$orderNumber = $thankYouPage->grabOrderNumber();
$order = [
'order_number' => (int) $orderNumber,
'payment_method' => 'PayPal',
'capture_amount' => '55,55',
'capture_type' => 'NotComplete',
'refund_amount' => '49,50',
'refund_type' => 'Partial',
];
$paypalOrder = $I->openAdminOrder((int) $orderNumber);
$paypalOrder->captureAmount($order['capture_amount'], $order['capture_type']);
$I->dontSee($paypalOrder->captureErrorText, $paypalOrder->errorBox);
$I->see(Translator::translate('OEPAYPAL_CAPTURE'), $paypalOrder->lastHistoryRowAction);
$I->see('55.55', $paypalOrder->lastHistoryRowAmount);
$paypalOrder->refundAmount($order['refund_amount'], $order['refund_type']);
$I->wait(1);
$I->dontSee($paypalOrder->refundErrorText, $paypalOrder->errorBox);
$I->see(Translator::translate('OEPAYPAL_REFUND'), $paypalOrder->lastHistoryRowAction);
$I->see('49.50', $paypalOrder->lastHistoryRowAmount);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace OxidEsales\PayPalModule\Tests\Codeception\Acceptance;
use Codeception\Example;
use Codeception\Util\Fixtures;
use OxidEsales\Codeception\Step\Basket;
use OxidEsales\Codeception\Module\Translation\Translator;
use OxidEsales\PayPalModule\Tests\Codeception\AcceptanceTester;
use OxidEsales\PayPalModule\Tests\Codeception\Page\PayPalLogin;
use \Codeception\Util\Locator;
/**
* @group oepaypal
* @group oepaypal_standard
* @group oepaypal_checkout_redirect
* @group oepaypal_checkout_finalizeonpaypal
*/
class CheckoutRedirectCest extends BaseCest
{
/**
* @group checkoutFrontend
* @group paypal_external
* @group paypal_buyerlogin
*
* @example { "setting": false, "expectedEndText": "MESSAGE_SUBMIT_BOTTOM" }
* @example { "setting": true, "expectedEndText": "THANK_YOU_FOR_ORDER" }
*
* @param AcceptanceTester $I
*/
public function checkRedirectOnCheckout(AcceptanceTester $I, Example $example)
{
$I->wantToTest('redirect to finalize order on successful PayPal checkout during express checkout');
$I->updateConfigInDatabase('blOEPayPalFinalizeOrderOnPayPal', $example['setting'], 'bool');
$basket = new Basket($I);
$basketItem = Fixtures::get('product');
$basket->addProductToBasket($basketItem['id'], $basketItem['amount']);
$I->openShop()->seeMiniBasketContains([$basketItem], $basketItem['price'], (string) $basketItem['amount']);
$I->openShop()->openMiniBasket();
$paypalButton = Locator::find(
'input',
['id' => 'paypalExpressCheckoutMiniBasketImage']
);
$I->waitForElementVisible($paypalButton, 5);
$I->click($paypalButton);
$paypalPage = new PaypalLogin($I);
$paypalUserEmail = $_ENV['sBuyerLogin'];
$paypalUserPassword = $_ENV['sBuyerPassword'];
$paypalPage->loginAndCheckout($paypalUserEmail, $paypalUserPassword);
$I->waitForText(Translator::translate($example['expectedEndText']), 10);
}
}

View File

@@ -0,0 +1,279 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception\Acceptance;
use Codeception\Util\Fixtures;
use OxidEsales\Codeception\Page\Checkout\ThankYou;
use OxidEsales\Codeception\Step\Basket;
use OxidEsales\Codeception\Step\ProductNavigation;
use OxidEsales\Codeception\Page\Checkout\PaymentCheckout;
use OxidEsales\Codeception\Page\Checkout\Basket as BasketCheckout;
use OxidEsales\PayPalModule\Tests\Codeception\AcceptanceTester;
use OxidEsales\PayPalModule\Tests\Codeception\Page\Checkout\OrderCheckout;
use OxidEsales\PayPalModule\Tests\Codeception\Page\PayPalLogin;
use OxidEsales\Codeception\Module\Translation\Translator;
/**
* @group oepaypal
* @group oepaypal_standard
* @group oepaypal_express_checkout
*
* Tests for checkout with payment process started via paypal button
*/
class ExpressCheckoutCest extends BaseCest
{
/**
* @group oepaypal_mandatory_test_with_graphql
*/
public function checkoutWithPaypalExpressWithNotYetExistingShopAccount(AcceptanceTester $I)
{
$I->wantToTest('express checkout from minibasket button. Customer is not logged in and account does not exist in shop');
$I->updateConfigInDatabase('sOEPayPalTransactionMode', 'Authorization', 'str');
$I->updateConfigInDatabase('blOEPayPalFinalizeOrderOnPayPal', true, 'bool');
$basket = new Basket($I);
$basketItem = Fixtures::get('product');
//add Product to basket
$basket->addProductToBasket($basketItem['id'], $basketItem['amount']);
$I->openShop()->seeMiniBasketContains([$basketItem], $basketItem['price'], (string) $basketItem['amount']);
$I->waitForElementVisible("#paypalExpressCheckoutMiniBasketImage", 10);
$I->click("#paypalExpressCheckoutMiniBasketImage");
$loginPage = new PayPalLogin($I);
$loginPage->loginAndCheckout($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
$thankYouPage = new ThankYou($I);
$orderNumber = $thankYouPage->grabOrderNumber();
$I->assertGreaterThan(1, $orderNumber);
$I->seeInDataBase(
'oxorder',
[
'OXORDERNR' => $orderNumber,
'OXTOTALORDERSUM' => '119.6',
'OXBILLFNAME' => $_ENV['sBuyerFirstName'],
'OXBILLCITY' => 'Freiburg',
'OXDELCITY' => ''
]
);
//Order was only authorized, so it should not yet be marked as paid
$oxPaid = $I->grabFromDatabase('oxorder', 'oxpaid', ['OXORDERNR' => $orderNumber]);
$I->assertStringStartsWith('0000-00-00', $oxPaid);
}
/**
* NOTE: this test relies on the shipping cost callback NOT being accessible by PayPal
*
* @group oepaypal_will_fail_with_public_url
*/
public function testExpressCheckoutFromDetailsButtonWhenNotLoggedInWithExistingShopAccount(AcceptanceTester $I): void
{
$I->wantToTest('checkout from details page with empty cart. Customer is not logged in to existing account in shop.');
//Only use name will be same as PayPal. Addresses will be different.
$this->setUserDataSameAsPayPal($I);
$I->openShop();
$I->waitForText(Translator::translate('HOME'));
$productNavigation = new ProductNavigation($I);
$productNavigation->openProductDetailsPage(Fixtures::get('product')['id']);
$I->seeElement("#paypalExpressCheckoutDetailsButton");
$I->click("#paypalExpressCheckoutDetailsButton");
$loginPage = new PayPalLogin($I);
$loginPage->approveExpressPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
//we are below shipping cost free limit and did not enable callback
//so we need to reapprove the order with PayPal
$I->see(Translator::translate('OEPAYPAL_ORDER_TOTAL_HAS_CHANGED'));
$I->seeElement('//input[@name="paypalExpressCheckoutButtonECS"]');
$I->click('//input[@name="paypalExpressCheckoutButtonECS"]');
$loginPage = new PayPalLogin($I);
$loginPage->approveExpressPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
$orderCheckout = new OrderCheckout($I);
$orderCheckout->submitOrder();
$thankYouPage = new ThankYou($I);
$orderNumber = $thankYouPage->grabOrderNumber();
$I->assertGreaterThan(1, $orderNumber);
$I->seeInDataBase(
'oxorder',
[
'OXORDERNR' => $orderNumber,
'OXTOTALORDERSUM' => Fixtures::get('totalordersum_ecswithshipping'),
'OXBILLFNAME' => $_ENV['sBuyerFirstName'],
'OXBILLCITY' => 'Freiburg',
]
);
//Order was captured, so it should be marked as paid
$oxPaid = $I->grabFromDatabase('oxorder', 'oxpaid', ['OXORDERNR' => $orderNumber]);
$I->assertStringStartsWith(date('Y-m-d'), $oxPaid);
}
public function testExpressCheckoutFromDetailsButtonWhenNotLoggedInWithExistingSameDataShopAccount(AcceptanceTester $I): void
{
$I->wantToTest('checkout from details page with empty cart. Customer is not logged in to existing account in shop.');
//Only use name will be same as PayPal. Addresses will be different.
$this->setUserNameSameAsPayPal($I);
$I->openShop();
$I->waitForText(Translator::translate('HOME'));
$productNavigation = new ProductNavigation($I);
$productNavigation->openProductDetailsPage(Fixtures::get('product')['id']);
$I->seeElement("#paypalExpressCheckoutDetailsButton");
$I->click("#paypalExpressCheckoutDetailsButton");
$loginPage = new PayPalLogin($I);
$loginPage->approveExpressPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
//Only email address is found in shop but address does not match
$I->see(Translator::translate('OEPAYPAL_ERROR_USER_ADDRESS'));
//So let's log in and try again
$basketCheckout = new BasketCheckout($I);
$basketCheckout->loginUser($_ENV['sBuyerLogin'], Fixtures::get('userPassword'));
$I->seeElement('//input[@name="paypalExpressCheckoutButtonECS"]');
$I->click('//input[@name="paypalExpressCheckoutButtonECS"]');
$loginPage = new PayPalLogin($I);
$loginPage->approveExpressPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
$orderCheckout = new OrderCheckout($I);
$orderCheckout->submitOrder();
$thankYouPage = new ThankYou($I);
$orderNumber = $thankYouPage->grabOrderNumber();
$I->assertGreaterThan(1, $orderNumber);
$I->seeInDataBase(
'oxorder',
[
'OXORDERNR' => $orderNumber,
'OXTOTALORDERSUM' => Fixtures::get('totalordersum_ecswithshipping'),
'OXBILLFNAME' => Fixtures::get('details')['firstname'],
'OXBILLCITY' => Fixtures::get('details')['oxcity']
]
);
//Order was captured, so it should be marked as paid
$oxPaid = $I->grabFromDatabase('oxorder', 'oxpaid', ['OXORDERNR' => $orderNumber]);
$I->assertStringStartsWith(date('Y-m-d'), $oxPaid);
}
public function testExpressCheckoutFromDetailsButton(AcceptanceTester $I): void
{
$I->wantToTest('checkout from details page with prefilled cart. Customer is logged in. PayPal and shop data are different.');
$this->proceedToBasketStep($I);
$productNavigation = new ProductNavigation($I);
$productNavigation->openProductDetailsPage(Fixtures::get('product')['id']);
$I->seeElement("#paypalExpressCheckoutDetailsButton");
$I->click("#paypalExpressCheckoutDetailsButton");
$I->see(substr(sprintf(Translator::translate('OEPAYPAL_SAME_ITEM_QUESTION'), 1), 0, 30));
$I->seeElement("#actionAddToBasketAndGoToCheckout");
$I->click("#actionAddToBasketAndGoToCheckout");
$loginPage = new PayPalLogin($I);
$loginPage->approveExpressPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
$orderCheckout = new OrderCheckout($I);
$orderCheckout->submitOrder();
$thankYouPage = new ThankYou($I);
$orderNumber = $thankYouPage->grabOrderNumber();
$I->assertGreaterThan(1, $orderNumber);
$I->seeInDataBase(
'oxorder',
[
'OXORDERNR' => $orderNumber,
'OXTOTALORDERSUM' => Fixtures::get('totalordersum_ecsdetails'),
'OXBILLFNAME' => Fixtures::get('details')['firstname'],
'OXBILLCITY' => Fixtures::get('details')['oxcity'],
'OXDELCITY' => ''
]
);
//Order was captured, so it should be marked as paid
$oxPaid = $I->grabFromDatabase('oxorder', 'oxpaid', ['OXORDERNR' => $orderNumber]);
$I->assertStringStartsWith(date('Y-m-d'), $oxPaid);
}
/**
* NOTE: this test relies on the shipping cost callback NOT being accessible by PayPal
*
* @group oepaypal_will_fail_without_public_url
* @group oepaypal_express_checkout_callback
*/
public function testExpressCheckoutWithCallback(AcceptanceTester $I): void
{
$I->wantToTest('checkout from details page with empty cart. Customer has no account in shop.');
$I->openShop();
$I->waitForText(Translator::translate('HOME'));
$productNavigation = new ProductNavigation($I);
$productNavigation->openProductDetailsPage(Fixtures::get('product')['id']);
$I->seeElement("#paypalExpressCheckoutDetailsButton");
$I->click("#paypalExpressCheckoutDetailsButton");
$loginPage = new PayPalLogin($I);
$loginPage->approveExpressPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
$orderCheckout = new OrderCheckout($I);
$orderCheckout->submitOrder();
$thankYouPage = new ThankYou($I);
$orderNumber = $thankYouPage->grabOrderNumber();
$I->assertGreaterThan(1, $orderNumber);
$I->seeInDataBase(
'oxorder',
[
'OXORDERNR' => $orderNumber,
'OXTOTALORDERSUM' => Fixtures::get('totalordersum_ecswithshipping'),
'OXBILLFNAME' => $_ENV['sBuyerFirstName'],
'OXBILLCITY' => 'Freiburg',
]
);
//Order was captured, so it should be marked as paid
$oxPaid = $I->grabFromDatabase('oxorder', 'oxpaid', ['OXORDERNR' => $orderNumber]);
$I->assertStringStartsWith(date('Y-m-d'), $oxPaid);
}
}

View File

@@ -0,0 +1,258 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception\Acceptance\GraphQL;
use OxidEsales\Eshop\Core\Registry;
use OxidEsales\PayPalModule\Tests\Codeception\Acceptance\BaseCest;
use OxidEsales\PayPalModule\Tests\Codeception\AcceptanceTester;
use Codeception\Util\Fixtures;
/**
* @group oepaypal
* @group oepaypal_graphql
* @group oepaypal_graphql_basketpayment
*/
class BasketPaymentWithGraphqlCest extends BaseCest
{
use GraphqlCheckoutTrait;
use GraphqlExpressCheckoutTrait;
public function _before(AcceptanceTester $I): void
{
if (!($I->checkGraphBaseActive() && $I->checkGraphStorefrontActive())) {
$I->markTestSkipped('GraphQL modules are not active');
}
parent::_before($I);
$I->updateInDatabase(
'oxuser',
[
'oxpassword' => '$2y$10$b186f117054b700a89de9uXDzfahkizUucitfPov3C2cwF5eit2M2',
'oxpasssalt' => 'b186f117054b700a89de929ce90c6aef'
],
[
'oxusername' => $I->getDemoUserName()
]
);
$I->updateConfigInDatabase('blPerfNoBasketSaving', false, 'bool');
$this->enablePayments();
}
public function _after(AcceptanceTester $I): void
{
$this->enablePayments();
parent::_after($I);
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
* @group paypal_graphql_express
*/
public function testPaypalBasketPayments(AcceptanceTester $I): void
{
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare standard basket
$basketId = $this->createBasket($I, 'pp_cart');
$result = $this->getBasketPaymentIds($I, $basketId);
$I->assertSame(
$this->getPaymentsArray(true),
$result
);
//prepare pp express basket
$basketId = $this->prepareExpressBasket($I, 'pp_express_cart');
$result = $this->getBasketPaymentIds($I, $basketId);
$I->assertSame(
$this->getPaymentsArray(true),
$result
);
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
* @group paypal_graphql_express
*/
public function testBasketPaymentsStandardPaymentTurnedOff(AcceptanceTester $I): void
{
$this->disableStandardPayment();
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare standard basket
$basketId = $this->createBasket($I, 'pp_cart_no_standart_payment');
$result = $this->getBasketPaymentIds($I, $basketId);
$I->assertSame(
$this->getPaymentsArray(),
$result
);
//prepare pp express basket
$basketId = $this->prepareExpressBasket($I, 'pp_express_cart_no_standart_payment');
$result = $this->getBasketPaymentIds($I, $basketId);
$I->assertSame(
$this->getPaymentsArray(true),
$result
);
$this->enableStandardPayment();
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
* @group paypal_graphql_express
*/
public function testBasketPaymentsExpressPaymentTurnedOff(AcceptanceTester $I): void
{
$this->disableExpressPayment();
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword());
//prepare standard basket
$basketId = $this->createBasket($I, 'pp_cart_no_express_payment');
$result = $this->getBasketPaymentIds($I, $basketId);
$I->assertSame(
$this->getPaymentsArray(true),
$result
);
//prepare pp express basket
$result = $this->prepareExpressBasket($I, 'pp_express_cart_no_express_payments');
$I->assertSame("Payment method 'oxidpaypal' is unavailable!", $result);
$this->enableExpressPayment();
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
* @group paypal_graphql_express
*/
public function testBasketPaymentsTurnedOff(AcceptanceTester $I): void
{
$this->disablePayments();
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare standard basket
$basketId = $this->createBasket($I, 'pp_cart_no_pp_payments');
$result = $this->getBasketPaymentIds($I, $basketId);
$I->assertSame(
$this->getPaymentsArray(),
$result
);
//prepare pp express basket
$result = $this->prepareExpressBasket($I, 'pp_express_cart_no_express_payments');
$I->assertSame("Payment method 'oxidpaypal' is unavailable!", $result);
$this->enablePayments();
}
private function prepareExpressBasket(AcceptanceTester $I, string $basketTitle): string
{
$basketId = $this->createBasket($I, $basketTitle);
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 4);
//Enable pp express process
$result = $this->paypalExpressApprovalProcess(
$I,
$basketId
);
if(isset($result['errors'])) {
return $result['errors'][0]['message'];
} else {
return $basketId;
}
}
private function getPaymentsArray(bool $includePaypal = false): array
{
$availablePayments = [
'oxidinvoice' => 'oxidinvoice',
'oxidpayadvance' => 'oxidpayadvance',
'oxiddebitnote' => 'oxiddebitnote',
'oxidcashondel' => 'oxidcashondel',
];
if ($includePaypal === true) {
$availablePayments = ['oxidpaypal' => 'oxidpaypal'] + $availablePayments;
}
return $availablePayments;
}
private function enablePayments(): void
{
$this->enableExpressPayment();
$this->enableStandardPayment();
}
private function disablePayments(): void
{
$this->disableExpressPayment();
$this->disableStandardPayment();
}
private function enableStandardPayment(): void
{
Registry::getConfig()->saveShopConfVar('bool', 'blOEPayPalStandardCheckout', true, null, 'module:oepaypal');
}
private function enableExpressPayment(): void
{
Registry::getConfig()->saveShopConfVar('bool', 'blOEPayPalExpressCheckout', true, null, 'module:oepaypal');
}
private function disableStandardPayment(): void
{
Registry::getConfig()->saveShopConfVar('bool', 'blOEPayPalStandardCheckout', false, null, 'module:oepaypal');
}
private function disableExpressPayment(): void
{
Registry::getConfig()->saveShopConfVar('bool', 'blOEPayPalExpressCheckout', false, null, 'module:oepaypal');
}
}

View File

@@ -0,0 +1,586 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception\Acceptance\GraphQL;
use OxidEsales\GraphQL\Storefront\Basket\Exception\BasketAccessForbidden;
use OxidEsales\GraphQL\Storefront\Country\Exception\CountryNotFound;
use OxidEsales\PayPalModule\GraphQL\Exception\BasketCommunication;
use OxidEsales\PayPalModule\Tests\Codeception\Acceptance\BaseCest;
use OxidEsales\PayPalModule\Tests\Codeception\AcceptanceTester;
use Codeception\Util\Fixtures;
use OxidEsales\PayPalModule\Tests\Codeception\Page\PayPalLogin;
use OxidEsales\PayPalModule\GraphQL\Exception\PaymentValidation;
use OxidEsales\PayPalModule\GraphQL\Exception\BasketValidation;
/**
* @group oepaypal
* @group oepaypal_graphql
* @group oepaypal_graphql_checkout
*/
class CheckoutWithGraphqlCest extends BaseCest
{
private const EXPIRED_TOKEN = 'EC-20P17490LV1421614';
use GraphqlCheckoutTrait;
public function _before(AcceptanceTester $I): void
{
if (!($I->checkGraphBaseActive() && $I->checkGraphStorefrontActive())) {
$I->markTestSkipped('GraphQL modules are not active');
}
parent::_before($I);
$I->updateConfigInDatabase('blPerfNoBasketSaving', false, 'bool');
$I->updateInDatabase(
'oxuser',
[
'oxpassword' => '$2y$10$b186f117054b700a89de9uXDzfahkizUucitfPov3C2cwF5eit2M2',
'oxpasssalt' => 'b186f117054b700a89de929ce90c6aef'
],
[
'oxusername' => $I->getDemoUserName()
]
);
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphql(AcceptanceTester $I)
{
$I->wantToTest('placing an order successfully with PayPal via graphql');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//query basket payments
$basketPayments = $this->getBasketPaymentIds($I, $basketId);
$I->assertArrayHasKey(Fixtures::get('payment_id'), $basketPayments);
//Get token and approval url, make customer approve the payment
$approvalDetails = $this->paypalApprovalProcess($I, $basketId);
$I->amOnUrl($approvalDetails['data']['paypalApprovalProcess']['communicationUrl']);
$loginPage = new PayPalLogin($I);
$loginPage->approveGraphqlStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
//place the order
$result = $this->placeOrder($I, $basketId);
$orderId = $result['data']['placeOrder']['id'];
$I->assertNotEmpty($orderId);
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlNewUserWithoutInvoiceAddress(AcceptanceTester $I)
{
$I->wantToTest('placing an order with PayPal via graphql for newly registered user without invoice address');
//register customer via graphql, he's in 'oxidnotyetordered' group.
$username = 'newPayPalUser@oxid-esales.com';
$password = 'useruser';
$this->registerCustomer($I, $username, $password);
//log in to graphql
$I->loginToGraphQLApi($username, $password);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
//this is as far as we get in this case because of missing invoice country
$shippingId = Fixtures::get('shipping')['standard'];
$result = $this->setBasketDeliveryMethod($I, $basketId, $shippingId);
$expectedException = CountryNotFound::byId('');
$I->assertStringContainsString($expectedException->getMessage(), $result);
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlUseDeliveryAddress(AcceptanceTester $I)
{
$I->wantToTest('placing an order successfully with PayPal via graphql using a delivery address');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_two');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryAddress($I, $basketId, $this->createDeliveryAddress($I));
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//Get token and approval url, make customer approve the payment
$approvalDetails = $this->paypalApprovalProcess($I, $basketId);
$I->amOnUrl($approvalDetails['data']['paypalApprovalProcess']['communicationUrl']);
$loginPage = new PayPalLogin($I);
$loginPage->approveGraphqlStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
//place the order
$result = $this->placeOrder($I, $basketId);
$orderId = $result['data']['placeOrder']['id'];
$I->assertNotEmpty($orderId);
$orderDetails = $this->getLatestOrderFromOrderHistory($I);
$I->assertSame($orderId, $orderDetails['id']);
$I->assertNotEmpty($orderDetails['invoiceAddress']);
$I->assertNotEmpty($orderDetails['deliveryAddress']);
$I->assertNotEquals($orderDetails['invoiceAddress']['lastName'], $orderDetails['deliveryAddress']['lastName']);
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlChangeBasketContentsAfterApproval(AcceptanceTester $I)
{
$I->wantToTest('placing an order with PayPal via graphql fails if basket contents was changed after PP approval');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//Get token and approval url, make customer approve the payment
$approvalDetails = $this->paypalApprovalProcess($I, $basketId);
$I->amOnUrl($approvalDetails['data']['paypalApprovalProcess']['communicationUrl']);
$loginPage = new PayPalLogin($I);
$loginPage->approveGraphqlStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
//change basket contents
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 1);
//place the order
$result = $this->placeOrder($I, $basketId);
$expectedException = BasketValidation::basketChange($basketId);
$I->assertStringContainsString($expectedException->getMessage(), $result['errors'][0]['message']);
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlChangeDeliveryAddressToNonPayPalCountryAfterApproval(AcceptanceTester $I)
{
$I->wantToTest('placing an order with PayPal via graphql fails if delivery address was changed after PP approval to unsupported country');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//Get token and approval url, make customer approve the payment
$approvalDetails = $this->paypalApprovalProcess($I, $basketId);
$I->amOnUrl($approvalDetails['data']['paypalApprovalProcess']['communicationUrl']);
$loginPage = new PayPalLogin($I);
$loginPage->approveGraphqlStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
//We change delivery address to country (Belgium) which is not assigned to oxidstandard delivery set.
$this->setBasketDeliveryAddress($I, $basketId, $this->createDeliveryAddress($I, 'a7c40f632e04633c9.47194042'));
//place the order
$result = $this->placeOrder($I, $basketId);
$expectedException = BasketValidation::basketAddressChange($basketId);
$I->assertStringContainsString($expectedException->getMessage(), $result['errors'][0]['message']);
//PayPal check runs before shop check. Here's what shop would find:
//$I->assertStringContainsString("Delivery set 'oxidstandard' is unavailable!", $result['errors'][0]['message']);
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlChangeDeliveryAddressAfterApproval(AcceptanceTester $I)
{
$I->wantToTest('placing an order with PayPal via graphql fails if delivery address was changed after PP approval');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//Get token and approval url, make customer approve the payment
$approvalDetails = $this->paypalApprovalProcess($I, $basketId);
$I->amOnUrl($approvalDetails['data']['paypalApprovalProcess']['communicationUrl']);
$loginPage = new PayPalLogin($I);
$loginPage->approveGraphqlStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
//change delivery address to one where country is assigned to oxidpaypal payment method.
$this->setBasketDeliveryAddress($I, $basketId, $this->createDeliveryAddress($I));
//place the order
$result = $this->placeOrder($I, $basketId);
$expectedException = BasketValidation::basketAddressChange($basketId);
$I->assertStringContainsString($expectedException->getMessage(), $result['errors'][0]['message']);
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlFailsForNotFinishedPayPalApproval(AcceptanceTester $I)
{
$I->wantToTest('placing an order with PayPal via graphql for not finished PP approval');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//Get token and approval url, make customer log in to PayPal but not yet approve the payment
$approvalDetails = $this->paypalApprovalProcess($I, $basketId);
$I->amOnUrl($approvalDetails['data']['paypalApprovalProcess']['communicationUrl']);
$loginPage = new PayPalLogin($I);
$loginPage->loginToPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
//placing the order fails
$result = $this->placeOrder($I, $basketId);
$expectedException = BasketCommunication::notConfirmed($basketId);
$I->assertEquals($expectedException->getMessage(), $result['errors'][0]['message']);
}
/**
* @group paypal_external
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlNotConfirmed(AcceptanceTester $I)
{
$I->wantToTest('placing an order fails with PayPal via graphql not confirmed');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//Get token and approval url but do not have customer approve the transaction
$this->paypalApprovalProcess($I, $basketId);
//placing the order fails
$result = $this->placeOrder($I, $basketId);
$expectedException = BasketCommunication::notConfirmed($basketId);
$I->assertEquals($expectedException->getMessage(), $result['errors'][0]['message']);
}
/**
* @group paypal_external
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlNotStarted(AcceptanceTester $I)
{
$I->wantToTest('placing an order fails with PayPal via graphql not started');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//placing the order fails
$result = $this->placeOrder($I, $basketId);
$expectedException = BasketCommunication::notStarted($basketId);
$I->assertEquals($expectedException->getMessage(), $result['errors'][0]['message']);
}
/**
* @group paypal_external
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlEmptyBasket(AcceptanceTester $I)
{
$I->wantToTest('placing an order fails with PayPal via graphql with empty basket');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
//Get token and approval url, make customer approve the payment
$approvalDetails = $this->paypalApprovalProcess($I, $basketId);
$expectedException = PaymentValidation::paymentMethodIsNotPaypal();
$I->assertEquals($expectedException->getMessage(), $approvalDetails['errors'][0]['message']);
}
/**
* @group paypal_external
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlEmptyBasketDeliverySet(AcceptanceTester $I)
{
$I->wantToTest('placing an order fails with PayPal via graphql with empty basket');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//Get token and approval url, make customer approve the payment
$approvalDetails = $this->paypalApprovalProcess($I, $basketId);
$I->assertStringContainsString(
//TODO: use Codeception Translator when it is possible to switch the language:
// to German 'OEPAYPAL_RESPONSE_FROM_PAYPAL'
'Fehlermeldung von PayPal',
$approvalDetails['errors'][0]['debugMessage']
);
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlOtherPaymentMethod(AcceptanceTester $I)
{
$I->wantToTest('placing an order fails with PayPal via graphql and payment method changed after token');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//Get token and approval url, make customer approve the payment
$approvalDetails = $this->paypalApprovalProcess($I, $basketId);
$I->amOnUrl($approvalDetails['data']['paypalApprovalProcess']['communicationUrl']);
$loginPage = new PayPalLogin($I);
$loginPage->approveGraphqlStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
//change payment method
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id_other'));
//place the order
//TODO: should the token be reset when the payment method is changed to non-paypal?
$result = $this->placeOrder($I, $basketId);
$orderId = $result['data']['placeOrder']['id'];
$I->assertNotEmpty($orderId);
}
/**
* @group paypal_external
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlExpiredToken(AcceptanceTester $I)
{
$I->wantToTest('placing an order fails with PayPal via graphql and expired token');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//we just set the token manually
$I->updateInDatabase('oxuserbaskets',
['OEPAYPAL_PAYMENT_TOKEN' => self::EXPIRED_TOKEN],
['OXID' => $basketId]
);
//place the order
$result = $this->placeOrder($I, $basketId);
$I->assertStringContainsString(
//TODO: use Codeception Translator when it is possible to switch the language:
// to German 'OEPAYPAL_RESPONSE_FROM_PAYPAL'
'Fehlermeldung von PayPal',
$result['errors'][0]['debugMessage']
);
}
/**
* @group paypal_external
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlNoPaymentMethodSet(AcceptanceTester $I)
{
$I->wantToTest('placing an order fails with PayPal via graphql and payment method not set');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
//Get token and approval url, make customer approve the payment
$approvalDetails = $this->paypalApprovalProcess($I, $basketId);
$expectedException = PaymentValidation::paymentMethodIsNotPaypal();
$I->assertEquals($expectedException->getMessage(), $approvalDetails['errors'][0]['message']);
}
/**
* @group paypal_external
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlGetTokenStatusExpired(AcceptanceTester $I)
{
$I->wantToTest('get token status for PayPal via graphql for expired token');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
$result = $this->paypalTokenStatus($I, self::EXPIRED_TOKEN);
$I->assertStringContainsString(
//TODO: use Codeception Translator when it is possible to switch the language:
// to German 'OEPAYPAL_RESPONSE_FROM_PAYPAL'
'Fehlermeldung von PayPal',
$result['errors'][0]['debugMessage']
);
}
/**
* @group paypal_external
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlGetTokenStatusForValidToken(AcceptanceTester $I)
{
$I->wantToTest('get token status for PayPal via graphql for valid token');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//Get token and approval url
$approvalDetails = $this->paypalApprovalProcess($I, $basketId);
//token is valid but not yet approved
$result = $this->paypalTokenStatus($I, $approvalDetails['data']['paypalApprovalProcess']['token']);
$I->assertFalse($result['data']['paypalTokenStatus']['tokenApproved']);
//make customer login to paypal but cancel
$I->amOnUrl($approvalDetails['data']['paypalApprovalProcess']['communicationUrl']);
$loginPage = new PayPalLogin($I);
$loginPage->loginToPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
//token is not approved
$result = $this->paypalTokenStatus($I, $approvalDetails['data']['paypalApprovalProcess']['token']);
$I->assertFalse($result['data']['paypalTokenStatus']['tokenApproved']);
//make customer approve the payment
$I->amOnUrl($approvalDetails['data']['paypalApprovalProcess']['communicationUrl']);
$loginPage = new PayPalLogin($I);
$loginPage->approveGraphqlStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
//token is approved
$result = $this->paypalTokenStatus($I, $approvalDetails['data']['paypalApprovalProcess']['token']);
$I->assertTrue($result['data']['paypalTokenStatus']['tokenApproved']);
}
/**
* @group paypal_external
* @group paypal_buyerlogin
* @group paypal_checkout
* @group paypal_graphql
*/
public function checkoutWithGraphqlPlaceOrderWhichDoesNotBelongToYou(AcceptanceTester $I)
{
$I->wantToTest('placing an order with PayPal via graphql which does not belong to you');
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
//prepare basket
$basketId = $this->createBasket($I, 'my_cart_one');
$this->addProductToBasket($I, $basketId, Fixtures::get('product')['id'], 2);
$this->setBasketDeliveryMethod($I, $basketId, Fixtures::get('shipping')['standard']);
$this->setBasketPaymentMethod($I, $basketId, Fixtures::get('payment_id'));
//Get token and approval url, make customer approve the payment
$approvalDetails = $this->paypalApprovalProcess($I, $basketId);
$I->amOnUrl($approvalDetails['data']['paypalApprovalProcess']['communicationUrl']);
$loginPage = new PayPalLogin($I);
$loginPage->approveGraphqlStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
$I->logoutFromGraphQLApi();
$I->haveInDatabase('oxuser', $I->getExistingUserData());
$I->haveInDatabase('oxobject2group', Fixtures::get('usergroups'));
$I->loginToGraphQLApi($I->getExistingUserName(), $I->getExistingUserPassword(), 0);
//place the order
$result = $this->placeOrder($I, $basketId);
$I->assertStringContainsString(
BasketAccessForbidden::byAuthenticatedUser()->getMessage(),
$result['errors'][0]['message']
);
$I->logoutFromGraphQLApi();
$I->loginToGraphQLApi($I->getDemoUserName(), $I->getExistingUserPassword(), 0);
$result = $this->placeOrder($I, $basketId);
$orderId = $result['data']['placeOrder']['id'];
$I->assertNotEmpty($orderId);
}
}

View File

@@ -0,0 +1,406 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception\Acceptance\GraphQL;
use OxidEsales\Eshop\Core\Registry as EshopRegistry;
use OxidEsales\PayPalModule\Tests\Codeception\AcceptanceTester;
use Codeception\Util\HttpCode;
use TheCodingMachine\GraphQLite\Types\ID;
trait GraphqlCheckoutTrait
{
protected function getGQLResponse(
AcceptanceTester $I,
string $query,
array $variables = []
): array {
$I->sendGQLQuery($query, $variables);
$I->seeResponseCodeIs(HttpCode::OK);
$I->seeResponseIsJson();
return $I->grabJsonResponseAsArray();
}
protected function createBasket(AcceptanceTester $I, string $basketTitle): string
{
$variables = [
'title' => $basketTitle,
];
$query = '
mutation ($title: String!){
basketCreate(basket: {title: $title}) {
id
}
}
';
$result = $this->getGQLResponse($I, $query, $variables);
return $result['data']['basketCreate']['id'];
}
protected function addProductToBasket(AcceptanceTester $I, string $basketId, string $productId, float $amount): array
{
$variables = [
'basketId' => $basketId,
'productId' => $productId,
'amount' => $amount,
];
$mutation = '
mutation ($basketId: ID!, $productId: ID!, $amount: Float! ) {
basketAddItem(
basketId: $basketId,
productId: $productId,
amount: $amount
) {
id
items {
id
product {
id
}
amount
}
}
}
';
$result = $this->getGQLResponse($I, $mutation, $variables);
return $result['data']['basketAddItem']['items'];
}
protected function setBasketDeliveryMethod(
AcceptanceTester $I,
string $basketId,
string $deliverySetId
): string {
$variables = [
'basketId' => new ID($basketId),
'deliveryId' => new ID($deliverySetId),
];
$mutation = '
mutation ($basketId: ID!, $deliveryId: ID!) {
basketSetDeliveryMethod(
basketId: $basketId,
deliveryMethodId: $deliveryId
) {
deliveryMethod {
id
}
}
}
';
$result = $this->getGQLResponse($I, $mutation, $variables);
if (isset($result['errors'])) {
return (string) $result['errors'][0]['message'];
}
return (string) $result['data']['basketSetDeliveryMethod']['deliveryMethod']['id'];
}
protected function setBasketPaymentMethod(AcceptanceTester $I, string $basketId, string $paymentId): string
{
$variables = [
'basketId' => new ID($basketId),
'paymentId' => new ID($paymentId),
];
$mutation = '
mutation ($basketId: ID!, $paymentId: ID!) {
basketSetPayment(
basketId: $basketId,
paymentId: $paymentId
) {
id
}
}
';
$result = $this->getGQLResponse($I, $mutation, $variables);
return $result['data']['basketSetPayment']['id'];
}
protected function placeOrder(AcceptanceTester $I, string $basketId, ?bool $termsAndConditions = null): array
{
//now actually place the order
$variables = [
'basketId' => new ID($basketId),
'confirmTermsAndConditions' => $termsAndConditions,
];
$mutation = '
mutation ($basketId: ID!, $confirmTermsAndConditions: Boolean) {
placeOrder(
basketId: $basketId
confirmTermsAndConditions: $confirmTermsAndConditions
) {
id
orderNumber
}
}
';
return $this->getGQLResponse($I, $mutation, $variables);
}
protected function paypalApprovalProcess(AcceptanceTester $I, string $basketId): array
{
$variables = [
'basketId' => $basketId,
'returnUrl' => EshopRegistry::getConfig()->getShopUrl()
];
$mutation = '
query ($basketId: ID!, $returnUrl: String!) {
paypalApprovalProcess(
basketId: $basketId,
returnUrl: $returnUrl,
cancelUrl: ""
displayBasketInPayPal: true
) {
token
communicationUrl
}
}
';
$result = $this->getGQLResponse($I, $mutation, $variables);
return $result;
}
protected function paypalTokenStatus(AcceptanceTester $I, string $token): array
{
$variables = [
'token' => $token
];
$mutation = '
query ($token: String!) {
paypalTokenStatus(
paypalToken: $token
) {
token
tokenApproved
}
}
';
$result = $this->getGQLResponse($I, $mutation, $variables);
return $result;
}
protected function createDeliveryAddress(AcceptanceTester $I, string $countryId = 'a7c40f631fc920687.20179984'): string
{
$variables = [
'countryId' => new ID($countryId),
];
$mutation = 'mutation ($countryId: ID!) {
customerDeliveryAddressAdd(deliveryAddress: {
salutation: "MRS",
firstName: "Marlene",
lastName: "Musterlich",
additionalInfo: "protected delivery",
street: "Bertoldstrasse",
streetNumber: "48",
zipCode: "79098",
city: "Freiburg",
countryId: $countryId}
){
id
}
}
';
$result = $this->getGQLResponse($I, $mutation, $variables);
return $result['data']['customerDeliveryAddressAdd']['id'];
}
protected function setBasketDeliveryAddress(
AcceptanceTester $I,
string $basketId,
string $deliveryAddressId
): array
{
$variables = [
'basketId' => $basketId,
'deliveryAddressId' => $deliveryAddressId,
];
$mutation = '
mutation ($basketId: ID!, $deliveryAddressId: ID!) {
basketSetDeliveryAddress(basketId: $basketId, deliveryAddressId: $deliveryAddressId) {
deliveryAddress {
id
}
}
}';
return $this->getGQLResponse($I, $mutation, $variables);
}
protected function getLatestOrderFromOrderHistory(AcceptanceTester $I): array
{
$mutation = '
query {
customer {
id
orders(
pagination: {limit: 1, offset: 0}
){
id
orderNumber
invoiceNumber
invoiced
cancelled
ordered
paid
updated
cost {
total
voucher
discount
}
vouchers {
id
}
invoiceAddress {
firstName
lastName
street
city
}
deliveryAddress {
firstName
lastName
street
city
country {
id
}
}
}
}
}
';
$result = $this->getGQLResponse($I, $mutation);
return $result['data']['customer']['orders'][0];
}
protected function getBasketPaymentIds(AcceptanceTester $I, string $basketId): array
{
$variables = [
'basketId' => new ID($basketId)
];
$query = 'query ($basketId: ID!) {
basketPayments(basketId:$basketId){
id
}
}';
$raw = $this->getGQLResponse($I, $query, $variables);
$result = [];
foreach ($raw['data']['basketPayments'] as $sub) {
$result[$sub['id']] = $sub['id'];
}
return $result;
}
protected function registerCustomer(AcceptanceTester $I, string $email, string $password = 'useruser'): array
{
$variables = [
'email' => $email,
'password' => $password,
'name',
];
$mutation = 'mutation ($email: String!, $password: String!) {
customerRegister (
customer: {
email: $email,
password: $password
}
) {
id
email
}
}';
$result = $this->getGQLResponse($I, $mutation, $variables);
return $result['data']['customerRegister'];
}
protected function setInvoiceAddress(AcceptanceTester $I): array
{
$variables = [
'firstName' => 'Test',
'lastName' => 'Registered',
'street' => 'Landstraße',
'streetNumber' => '66',
'zipCode' => '22547',
'city' => 'Hamburg',
'countryId' => new ID('a7c40f631fc920687.20179984'),
];
$mutation = 'mutation (
$firstName: String!,
$lastName: String!,
$street: String!,
$streetNumber: String!,
$zipCode: String!,
$city: String!,
$countryId: ID!
) {
customerInvoiceAddressSet (
invoiceAddress: {
firstName: $firstName,
lastName: $lastName,
street: $street,
streetNumber: $streetNumber
zipCode: $zipCode,
city: $city,
countryId: $countryId
})
{
firstName
lastName
}
}';
$result = $this->getGQLResponse($I, $mutation, $variables);
return $result['data']['customerInvoiceAddressSet'];
}
}

View File

@@ -0,0 +1,128 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception\Acceptance\GraphQL;
use OxidEsales\Eshop\Core\Registry as EshopRegistry;
use OxidEsales\PayPalModule\Tests\Codeception\AcceptanceTester;
use TheCodingMachine\GraphQLite\Types\ID;
trait GraphqlExpressCheckoutTrait
{
protected function paypalExpressApprovalProcess(
AcceptanceTester $I,
string $basketId
): array
{
$variables = [
'basketId' => $basketId,
'returnUrl' => EshopRegistry::getConfig()->getShopUrl(),
'cancelUrl' => EshopRegistry::getConfig()->getShopUrl()
];
$mutation = '
query ($basketId: ID!, $returnUrl: String!, $cancelUrl: String!) {
paypalExpressApprovalProcess(
basketId: $basketId,
returnUrl: $returnUrl,
cancelUrl: $cancelUrl,
displayBasketInPayPal: true
) {
token
communicationUrl
}
}
';
return $this->getGQLResponse($I, $mutation, $variables);
}
protected function removeItemFromBasket(
AcceptanceTester $I,
string $basketId,
string $itemId,
int $amount = 1
): array
{
$variables = [
'basketId' => $basketId,
'itemId' => $itemId,
'amount' => $amount
];
$mutation = 'mutation ($basketId: ID!, $itemId: ID!, $amount: Int!) {
basketRemoveItem(
basketId: $basketId,
itemId: $itemId,
amount: $amount
) {
id
}
}';
return $this->getGQLResponse($I, $mutation, $variables);
}
protected function addVoucherToBasket(
AcceptanceTester $I,
string $basketId,
string $voucherNumber
) : array
{
$variables = [
'basketId' => $basketId,
'voucherNumber' => $voucherNumber
];
$mutation = 'mutation ($basketId: ID!, $voucherNumber: String!) {
basketAddVoucher (
basketId: $basketId,
voucherNumber: $voucherNumber
) {
id
}
}';
return $this->getGQLResponse($I, $mutation, $variables);
}
protected function removeVoucherFromBasket(
AcceptanceTester $I,
string $basketId,
string $voucherId
) : array
{
$variables = [
'basketId' => $basketId,
'voucherId' => $voucherId
];
$mutation = 'mutation ($basketId: String!, $voucherId: String!) {
basketRemoveVoucher (
basketId: $basketId,
voucherId: $voucherId
) {
id
}
}';
return $this->getGQLResponse($I, $mutation, $variables);
}
}

View File

@@ -0,0 +1,554 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
declare(strict_types=1);
namespace OxidEsales\PayPalModule\Tests\Codeception\Acceptance;
use OxidEsales\Codeception\Module\Translation\Translator;
use Codeception\Util\Fixtures;
use OxidEsales\Codeception\Step\Basket;
use OxidEsales\Codeception\Step\ProductNavigation;
use OxidEsales\PayPalModule\Tests\Codeception\AcceptanceTester;
/**
* @group oepaypal
* @group oepaypal_standard
* @group oepaypal_installment_banners
*/
class InstallmentBannersCest extends BaseCest
{
public function _after(AcceptanceTester $I): void
{
$I->activateFlowTheme();
$I->clearShopCache();
parent::_after($I);
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
* @group installment_banners_paypal_search
*/
public function searchPageBannerInBruttoMode(AcceptanceTester $I)
{
$I->wantToTest('PayPal installment banner on search page in brutto mode');
$I->updateConfigInDatabase('oePayPalBannersSearchResultsPage', false, 'bool');
$I->updateConfigInDatabase('iNewBasketItemMessage', false, 'bool');
$basketItem = Fixtures::get('product');
$homePage = $I->openShop();
$basket = new Basket($I);
$basket->addProductToBasket($basketItem['id'], (int)$basketItem['amount']);
$homePage
->seeMiniBasketContains([$basketItem], $basketItem['price'], (string) $basketItem['amount'])
->searchFor("3503");
$I->dontSeeElementInDOM('#paypal-installment-banner-container');
//Check installment banner body in Flow theme
$I->updateConfigInDatabase('oePayPalBannersSearchResultsPage', true, 'bool');
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(119.6);
// Check banner visibility when oePayPalBannersHideAll setting is set to true
$I->updateConfigInDatabase('oePayPalBannersHideAll', true, 'bool');
$I->reloadPage();
$I->dontSeeElementInDOM('#paypal-installment-banner-container');
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
* @group installment_banners_paypal_search
*/
public function searchPageBannerInNettoMode(AcceptanceTester $I)
{
$I->wantToTest('PayPal installment banner on search page in netto mode');
$I->updateConfigInDatabase('blShowNetPrice', true, 'bool');
$I->updateConfigInDatabase('iNewBasketItemMessage', false, 'bool');
$product = Fixtures::get('product');
$product['price'] = '100,52 €';
$homePage = $I->openShop();
$basket = new Basket($I);
$basket->addProductToBasket($product['id'], (int)$product['amount']);
$homePage
->seeMiniBasketContains([$product], $product['price'], (string) $product['amount'])
->searchFor($product['title']);
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(100.52);
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
* @group installment_banners_paypal_checkout
*/
public function checkoutPageBannerInBruttoMode(AcceptanceTester $I)
{
$I->wantToTest('PayPal installment banner on checkout page in brutto mode');
$I->haveInDatabase('oxuser', $I->getExistingUserData());
$I->updateConfigInDatabase('oePayPalBannersCheckoutPage', false, 'bool');
$I
->openShop()
->loginUser($I->getExistingUserName(), $I->getExistingUserPassword());
// 0. Prepare basket
$basket = new Basket($I);
$basketPage = $basket->addProductToBasketAndOpenBasket(Fixtures::get('product')['id'], 1, 'basket');
// 1. Basket overview
$I->dontSeeElementInDOM('#paypal-installment-banner-container');
$I->updateConfigInDatabase('oePayPalBannersCheckoutPage', true, 'bool');
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(33.8);
// Check banner visibility when oePayPalBannersHideAll setting is set to true
$I->updateConfigInDatabase('oePayPalBannersHideAll', true, 'bool');
$I->reloadPage();
$I->dontSeeElementInDOM('#paypal-installment-banner-container');
// 3. Payment
$I->updateConfigInDatabase('oePayPalBannersHideAll', false, 'bool');
$I->updateConfigInDatabase('oePayPalBannersCheckoutPage', false, 'bool');
$basketPage->goToNextStep()->goToNextStep();
$I->dontSeeElementInDOM('#paypal-installment-banner-container');
$I->updateConfigInDatabase('oePayPalBannersCheckoutPage', true, 'bool');
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(33.8);
// Check banner visibility when oePayPalBannersHideAll setting is set to true
$I->updateConfigInDatabase('oePayPalBannersHideAll', true, 'bool');
$I->reloadPage();
$I->dontSeeElementInDOM('#paypal-installment-banner-container');
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
* @group installment_banners_paypal_checkout
*/
public function checkoutPageBannerInNettoMode(AcceptanceTester $I)
{
$I->wantToTest('PayPal installment banner on checkout page in netto mode');
$I->haveInDatabase('oxuser', $I->getExistingUserData());
$I->updateConfigInDatabase('blShowNetPrice', true, 'bool');
$I
->openShop()
->loginUser($I->getExistingUserName(), $I->getExistingUserPassword());
// 0. Prepare basket
$basket = new Basket($I);
$basketPage = $basket->addProductToBasketAndOpenBasket(Fixtures::get('product')['id'], 1, 'basket');
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(33.8);
$basketPage->goToNextStep()->goToNextStep();
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(33.8);
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
* @group installment_banners_paypal_category
*/
public function categoryPageBannerInBruttoMode(AcceptanceTester $I)
{
$I->wantToTest('PayPal installment banner on category page in brutto mode');
$I->updateConfigInDatabase('oePayPalBannersCategoryPage', false, 'bool');
$I->updateConfigInDatabase('iNewBasketItemMessage', false, 'bool');
$homePage = $I->openShop();
$basket = new Basket($I);
$basketItem = Fixtures::get('product');
$basket->addProductToBasket($basketItem['id'], (int)$basketItem['amount']);
$homePage
->seeMiniBasketContains([$basketItem], $basketItem['price'], (string) $basketItem['amount'])
->openCategoryPage("Kiteboarding");
$I->dontSeeElementInDOM('#paypal-installment-banner-container');
//Check installment banner body in Flow and Wave theme
$I->updateConfigInDatabase('oePayPalBannersCategoryPage', true, 'bool');
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(119.6);
// Check banner visibility when oePayPalBannersHideAll setting is set to true
$I->updateConfigInDatabase('oePayPalBannersHideAll', true, 'bool');
$I->reloadPage();
$I->dontSeeElementInDOM('#paypal-installment-banner-container');
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
* @group installment_banners_paypal_category
*/
public function categoryPageBannerInNettoMode(AcceptanceTester $I)
{
$I->wantToTest('PayPal installment banner on category page in netto mode');
$I->updateConfigInDatabase('blShowNetPrice', true, 'bool');
$I->updateConfigInDatabase('iNewBasketItemMessage', false, 'bool');
$homePage = $I->openShop();
$basket = new Basket($I);
$basketItem = Fixtures::get('product');
$basketItem['price'] = '100,52 €';
$basket->addProductToBasket($basketItem['id'], (int)$basketItem['amount']);
$homePage
->seeMiniBasketContains([$basketItem], $basketItem['price'], (string) $basketItem['amount'])
->openCategoryPage("Kiteboarding");
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(100.52);
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
*/
public function checkBannerPlaceholderAppearsOnStartPageOnlyByCorrectConfig(AcceptanceTester $I)
{
$I->updateConfigInDatabase('oePayPalBannersStartPage', false, 'bool');
$I->openShop();
$I->dontSeeElementInDOM("#paypal-installment-banner-container");
$I->updateConfigInDatabase('oePayPalBannersStartPage', true, 'bool');
$I->clearShopCache();
$I->openShop();
$I->seeElementInDOM("#paypal-installment-banner-container");
$I->click(Translator::translate('HELP'));
$I->dontSeeElementInDOM("#paypal-installment-banner-container");
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
*/
public function checkCorrectDefaultsSentToPaypalInstallmentsOnStartPageWithEmptyBasket(AcceptanceTester $I)
{
$I->updateConfigInDatabase('oePayPalBannersStartPage', true, 'bool');
$I->openShop();
$I->checkInstallmentBannerData();
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
*/
public function checkCorrectSumSentToPaypalInstallmentsOnStartPageWithFilledBasketBrutto(AcceptanceTester $I)
{
$I->updateConfigInDatabase('oePayPalBannersStartPage', true, 'bool');
$I->updateConfigInDatabase('iNewBasketItemMessage', false, 'bool');
$I->updateConfigInDatabase('blShowNetPrice', false, 'bool');
$I->updateConfigInDatabase('iNewBasketItemMessage', false, 'bool');
$homePage = $I->openShop();
$basket = new Basket($I);
$basketItem = Fixtures::get('product');
$basket->addProductToBasket($basketItem['id'], (int) $basketItem['amount']);
$homePage->seeMiniBasketContains([$basketItem], $basketItem['price'], (string) $basketItem['amount']);
$I->checkInstallmentBannerData(119.6);
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
*/
public function checkCorrectSumSentToPaypalInstallmentsOnStartPageWithFilledBasketNetto(AcceptanceTester $I)
{
$I->updateConfigInDatabase('oePayPalBannersStartPage', true, 'bool');
$I->updateConfigInDatabase('iNewBasketItemMessage', false, 'bool');
$I->updateConfigInDatabase('blShowNetPrice', true, 'bool');
$I->updateConfigInDatabase('iNewBasketItemMessage', false, 'bool');
$homePage = $I->openShop();
$basket = new Basket($I);
$basketItem = Fixtures::get('product');
$basketItem['price'] = '100,52 €';
$basket->addProductToBasket($basketItem['id'], (int)$basketItem['amount']);
$homePage->seeMiniBasketContains([$basketItem], $basketItem['price'], (string) $basketItem['amount']);
$I->checkInstallmentBannerData(100.52);
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
* @group installment_banners_paypal_details
*/
public function productDetailsPageBannerBrutto(AcceptanceTester $I)
{
$I->wantToTest('PayPal installment banner on product details page brutto mode');
$parentProduct = Fixtures::get('parent');
$variant = Fixtures::get('variant');
$alternateVariant = Fixtures::get('alternate_variant');
$basketItem = Fixtures::get('product');
$I->updateConfigInDatabase('oePayPalBannersProductDetailsPage', false, 'bool');
$parentProductNavigation = new ProductNavigation($I);
$parentProductNavigation->openProductDetailsPage($parentProduct['id'])
->seeOnBreadCrumb(Translator::translate('YOU_ARE_HERE'));
$I->dontSee(Translator::translate('ERROR_MESSAGE_ARTICLE_ARTICLE_NOT_BUYABLE'));
$I->dontSeeElementInDOM('#paypal-installment-banner-container');
$I->updateConfigInDatabase('oePayPalBannersProductDetailsPage', true, 'bool');
$parentProductNavigation->openProductDetailsPage($parentProduct['id'])
->seeOnBreadCrumb(Translator::translate('YOU_ARE_HERE'));
$I->seePayPalInstallmentBannerInFlowAndWaveTheme($parentProduct['minBruttoPrice'], Translator::translate('YOU_ARE_HERE'));
// Check banner amount when basket is not empty
$basket = new Basket($I);
$basket->addProductToBasket($basketItem['id'], 1);
$parentProductNavigation->openProductDetailsPage($parentProduct['id'])
->seeOnBreadCrumb(Translator::translate('YOU_ARE_HERE'));
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(
$basketItem['bruttoprice_single'] + $parentProduct['minBruttoPrice'],
Translator::translate('YOU_ARE_HERE')
);
// Check banner amount when the given product is also in the basket
$basket->addProductToBasket($variant['id'], 1);
$I->waitForPageLoad();
$I->seePayPalInstallmentBannerInFlowAndWaveTheme($basketItem['bruttoprice_single'] + $variant['bruttoprice']); //check on front page
//check banner in case we open variant parent details page and have no variant selected
$parentProductNavigation->openProductDetailsPage($parentProduct['id']);
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(
$basketItem['bruttoprice_single'] + $variant['bruttoprice'],
Translator::translate('YOU_ARE_HERE')
); //check on details page
//check banner in case we open alternate variant details page, alternate variant price should be added to price
$parentProductNavigation->openProductDetailsPage($alternateVariant['id']);
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(
$basketItem['bruttoprice_single'] + $variant['bruttoprice'] + $alternateVariant['bruttoprice'],
Translator::translate('YOU_ARE_HERE')
); //check on details page */
//check banner in case we open variant details page
$parentProductNavigation->openProductDetailsPage($variant['id']);
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(
$basketItem['bruttoprice_single'] + $variant['bruttoprice'],
Translator::translate('YOU_ARE_HERE')
); //check on details page */
// Check banner visibility when oePayPalBannersHideAll setting is set to true
$I->updateConfigInDatabase('oePayPalBannersHideAll', true, 'bool');
$I->reloadPage();
$I->dontSeeElementInDOM('#paypal-installment-banner-container');
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
* @group installment_banners_paypal_details
*/
public function productDetailsPageBannerNetto(AcceptanceTester $I)
{
$I->wantToTest('PayPal installment banner on product details page in netto mode');
$I->updateConfigInDatabase('blShowNetPrice', true, 'bool');
$parentProduct = Fixtures::get('parent');
$variant = Fixtures::get('variant');
$alternateVariant = Fixtures::get('alternate_variant');
$basketItem = Fixtures::get('product');
$parentProductNavigation = new ProductNavigation($I);
$parentProductNavigation->openProductDetailsPage($parentProduct['id'])
->seeOnBreadCrumb(Translator::translate('YOU_ARE_HERE'));
$I->waitForPageLoad();
$I->seePayPalInstallmentBannerInFlowAndWaveTheme($parentProduct['minNettoPrice']);
// Check banner amount when basket is not empty
$basket = new Basket($I);
$basket->addProductToBasket($basketItem['id'], 1);
$parentProductNavigation->openProductDetailsPage($parentProduct['id'])
->seeOnBreadCrumb(Translator::translate('YOU_ARE_HERE'));
$I->seePayPalInstallmentBannerInFlowAndWaveTheme($basketItem['nettoprice_single'] + $parentProduct['minNettoPrice']);
// Check banner amount when the given product is also in the basket
$basket->addProductToBasket($variant['id'], 1);
$I->waitForPageLoad();
$I->seePayPalInstallmentBannerInFlowAndWaveTheme($basketItem['nettoprice_single'] + $variant['nettoprice']); //check on front page
//check banner in case we open variant parent details page and have no variant selected
$parentProductNavigation->openProductDetailsPage($parentProduct['id']);
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(
$basketItem['nettoprice_single'] + $variant['nettoprice'],
Translator::translate('YOU_ARE_HERE')
); //check on details page
//check banner in case we open alternate variant details page, alternate variant price should be added to price
$parentProductNavigation->openProductDetailsPage($alternateVariant['id']);
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(
$basketItem['nettoprice_single'] + $variant['nettoprice'] + $alternateVariant['nettoprice'],
Translator::translate('YOU_ARE_HERE')
); //check on details page */
//check banner in case we open variant details page
$parentProductNavigation->openProductDetailsPage($variant['id']);
$I->seePayPalInstallmentBannerInFlowAndWaveTheme(
$basketItem['nettoprice_single'] + $variant['nettoprice'],
Translator::translate('YOU_ARE_HERE')
); //check on details page */
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
* @group installment_banners_paypal_variant
*/
public function productVariantBannerBrutto(AcceptanceTester $I)
{
$I->wantToTest('PayPal installment banner for selected variant in brutto mode');
$product = Fixtures::get('parent');
$productNavigation = new ProductNavigation($I);
$productNavigation
->openProductDetailsPage($product['id'])
->selectVariant(1, 'W 30/L 30')
->selectVariant(2, 'Super Blue')
->seeProductData([
'id' => '0702-85-853-1-3',
'title' => 'Kuyichi Jeans ANNA W 30/L 30 | Super Blue',
'description' => 'Cool lady jeans by Kuyichi',
'price' => '99,90 €'
]);
$I->checkInstallmentBannerData($product['maxBruttoPrice']);
$I->activateWaveTheme();
$I->reloadPage();
$I->waitForPageLoad();
$productDetails = $productNavigation
->openProductDetailsPage($product['id']);
$this->selectWaveVariant($I, 1, 'W 30/L 30');
$this->selectWaveVariant($I, 2, 'Super Blue');
$productDetails->seeProductData([
'id' => '0702-85-853-1-3',
'title' => 'Kuyichi Jeans ANNA W 30/L 30 | Super Blue',
'description' => 'Cool lady jeans by Kuyichi',
'price' => '99,90 €'
]);
$I->checkInstallmentBannerData($product['maxBruttoPrice']);
}
/**
* @param AcceptanceTester $I
*
* @group installment_banners_paypal
* @group installment_banners_paypal_variant
*/
public function productVariantBannerWrongSelector(AcceptanceTester $I)
{
$I->wantToTest('PayPal installment banner with wrong selector');
$I->updateConfigInDatabase('oePayPalBannersProductDetailsPageSelector', '.non-existing-css-selector', 'str');
$product = Fixtures::get('parent');
$productNavigation = new ProductNavigation($I);
$productNavigation
->openProductDetailsPage($product['id'])
->seeProductData([
'id' => '3570',
'title' => 'Kuyichi Jeans ANNA',
'description' => 'Cool lady jeans by Kuyichi',
'price' => 'from 92,90 € *'
])
->selectVariant(1, "W 30/L 30", "W 30/L 30")
->selectVariant(2, "Blue", "W 30/L 30, Blue")
->seeProductData([
'id' => '0702-85-853-1-1',
'title' => 'Kuyichi Jeans ANNA W 30/L 30 | Blue',
'description' => 'Cool lady jeans by Kuyichi',
'price' => '99,90 €'
]);
$I->activateWaveTheme();
$I->reloadPage();
$I->waitForPageLoad();
$productDetails = $productNavigation
->openProductDetailsPage($product['id'])
->seeProductData([
'id' => '3570',
'title' => 'Kuyichi Jeans ANNA',
'description' => 'Cool lady jeans by Kuyichi',
'price' => 'from 92,90 € *'
]);
$this->selectWaveVariant($I, 1, 'W 30/L 30');
$this->selectWaveVariant($I, 2, 'Blue');
$productDetails->seeProductData([
'id' => '0702-85-853-1-1',
'title' => 'Kuyichi Jeans ANNA W 30/L 30 | Blue',
'description' => 'Cool lady jeans by Kuyichi',
'price' => '99,90 €'
]);
}
/**
* @param AcceptanceTester $I
* @param int $variant The position of the variant.
* @param string $variantValue The value of the variant.
*/
private function selectWaveVariant(AcceptanceTester $I, int $variant, string $variantValue)
{
$variantSelection = '/descendant::button[@class="btn btn-outline-dark btn-sm dropdown-toggle"][%s]';
$variantOpenSelection = '//ul[@class="dropdown-menu vardrop"]';
$I->click(sprintf($variantSelection, $variant));
$I->click($variantValue);
if ($I->see($variantValue)) {
$I->click($variantValue);
}
$I->waitForElementNotVisible($variantOpenSelection);
$I->waitForPageLoad();
$I->see($variantValue);
}
}

View File

@@ -0,0 +1,163 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception\Acceptance;
use Codeception\Util\Fixtures;
use OxidEsales\Codeception\Page\Checkout\ThankYou;
use OxidEsales\Codeception\Step\Basket;
use OxidEsales\Codeception\Page\Checkout\PaymentCheckout;
use OxidEsales\PayPalModule\Tests\Codeception\AcceptanceTester;
use OxidEsales\PayPalModule\Tests\Codeception\Page\PayPalLogin;
use OxidEsales\Codeception\Module\Translation\Translator;
/**
* @group oepaypal
* @group oepaypal_standard
* @group oepaypal_standard_checkout
*
* Tests for checkout with regular payment method 'oxidpaypal'
*/
class StandardPaymentCest extends BaseCest
{
/**
* @group oepaypal_mandatory_test_with_graphql
*/
public function checkoutWithPaypalStandard(AcceptanceTester $I)
{
$I->wantToTest('checking out as logged in user with PayPal as payment method. Shop login and PayPal login mail are different.');
//order will be captured and finalized in shop
$I->updateConfigInDatabase('sOEPayPalTransactionMode', 'Sale', 'str');
$this->proceedToPaymentStep($I, Fixtures::get('userName'));
$paymentPage = new PaymentCheckout($I);
$paymentPage = $paymentPage->selectPayment('oxidpaypal');
$I->click($paymentPage->nextStepButton);
$loginPage = new PayPalLogin($I);
$orderCheckout = $loginPage->checkoutWithStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
$orderCheckout->submitOrder();
$thankYouPage = new ThankYou($I);
$orderNumber = $thankYouPage->grabOrderNumber();
$I->assertGreaterThan(1, $orderNumber);
$I->seeInDataBase(
'oxorder',
[
'OXORDERNR' => $orderNumber,
'OXTOTALORDERSUM' => '119.6',
'OXBILLFNAME' => Fixtures::get('details')['firstname'],
'OXBILLCITY' => Fixtures::get('details')['oxcity'],
'OXDELCITY' => ''
]
);
//Order was captured, so it should be marked as paid
$oxPaid = $I->grabFromDatabase('oxorder', 'oxpaid', ['OXORDERNR' => $orderNumber]);
$I->assertStringStartsWith(date('Y-m-d'), $oxPaid);
}
public function checkoutWithPaypalStandardSameUserData(AcceptanceTester $I): void
{
$I->wantToTest('checking out as logged in user with PayPal as payment method. Shop login and PayPal login mail are the same.');
//order will be only authorized
$I->updateConfigInDatabase('sOEPayPalTransactionMode', 'Authorization', 'str');
$this->setUserDataSameAsPayPal($I);
$this->proceedToPaymentStep($I, $_ENV['sBuyerLogin']);
$paymentPage = new PaymentCheckout($I);
$paymentPage = $paymentPage->selectPayment('oxidpaypal');
$I->click($paymentPage->nextStepButton);
$loginPage = new PayPalLogin($I);
$orderCheckout = $loginPage->checkoutWithStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
$orderCheckout->submitOrder();
$thankYouPage = new ThankYou($I);
$orderNumber = $thankYouPage->grabOrderNumber();
$I->assertGreaterThan(1, $orderNumber);
$orderId = $I->grabFromDatabase('oxorder', 'oxid', ['OXORDERNR' => $orderNumber]);
$I->seeInDataBase(
'oxorder',
[
'OXID' => $orderId,
'OXTOTALORDERSUM' => '119.6',
'OXBILLFNAME' => $_ENV['sBuyerFirstName'],
'OXBILLCITY' => 'Freiburg',
'OXDELCITY' => ''
]
);
//Order was only authorized, so it should not yet be marked as paid
$oxPaid = $I->grabFromDatabase('oxorder', 'oxpaid', ['OXORDERNR' => $orderNumber]);
$I->assertStringStartsWith(date('0000-00-00'), $oxPaid);
}
public function changeBasketDuringCheckout(AcceptanceTester $I)
{
$I->wantToTest('changing basket contents after payment was authorized');
$this->proceedToPaymentStep($I, Fixtures::get('userName'));
$paymentPage = new PaymentCheckout($I);
$paymentPage = $paymentPage->selectPayment('oxidpaypal');
$I->click($paymentPage->nextStepButton);
$loginPage = new PayPalLogin($I);
$loginPage->checkoutWithStandardPayPal($_ENV['sBuyerLogin'], $_ENV['sBuyerPassword']);
$I->amOnUrl($this->getShopUrl() . '/en/cart');
$product = Fixtures::get('product');
$basket = new Basket($I);
$basket->addProductToBasketAndOpenBasket($product['id'], $product['amount'], 'basket');
//finalize order in previous tab
$I->amOnUrl($this->getShopUrl() . '?cl=order');
$orderNumber = $this->finalizeOrderInOrderStep($I);
$I->assertGreaterThan(1, $orderNumber);
$shopOrderId = $I->grabFromDatabase(
'oxorder',
'oxid',
[
'OXORDERNR' => $orderNumber,
'OXTOTALORDERSUM' => '239.2'
]
);
$I->seeInDatabase(
'oepaypal_order',
[
'OEPAYPAL_ORDERID' => $shopOrderId,
'OEPAYPAL_PAYMENTSTATUS' => 'completed',
'OEPAYPAL_CAPTUREDAMOUNT' => '239.20'
]
);
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
// This is acceptance bootstrap
$helper = new \OxidEsales\Codeception\Module\FixturesHelper();
$helper->loadRuntimeFixtures(dirname(__FILE__).'/../_data/fixtures.php');
$helper->loadRuntimeFixtures(dirname(__FILE__).'/../_data/configData.php');
$dotenv = new \Symfony\Component\Dotenv\Dotenv();
$dotenv->load(__DIR__.'/../../../.env');

View File

@@ -0,0 +1,87 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
use OxidEsales\Facts\Facts;
use OxidEsales\Eshop\Core\ConfigFile;
use OxidEsales\TestingLibrary\Services\Library\DatabaseDefaultsFileGenerator;
$facts = new Facts();
$selenium_server_port = getenv('SELENIUM_SERVER_PORT');
$selenium_server_port = ($selenium_server_port) ? $selenium_server_port : '4444';
$selenium_server_host = getenv('SELENIUM_SERVER_HOST');
$selenium_server_host = ($selenium_server_host) ? : 'localhost';
$php = (getenv('PHPBIN')) ? getenv('PHPBIN') : 'php';
return [
'SHOP_URL' => $facts->getShopUrl(),
'SHOP_SOURCE_PATH' => $facts->getSourcePath(),
'VENDOR_PATH' => $facts->getVendorPath(),
'DB_NAME' => $facts->getDatabaseName(),
'DB_USERNAME' => $facts->getDatabaseUserName(),
'DB_PASSWORD' => $facts->getDatabasePassword(),
'DB_HOST' => $facts->getDatabaseHost(),
'DB_PORT' => $facts->getDatabasePort(),
'DUMP_PATH' => getTestDataDumpFilePath(),
'MYSQL_CONFIG_PATH' => getMysqlConfigPath(),
'MODULE_DUMP_PATH' => getModuleTestDataDumpFilePath(),
'SELENIUM_SERVER_PORT' => $selenium_server_port,
'SELENIUM_SERVER_HOST' => $selenium_server_host,
'BROWSER_NAME' => getenv('BROWSER_NAME') ?: 'chrome',
'PHP_BIN' => $php,
];
function getModuleTestDataDumpFilePath()
{
return __DIR__ . '/../_data/dump.sql';
}
function getTestDataDumpFilePath()
{
return getShopTestPath() . '/Codeception/_data/dump.sql';
}
function getShopSuitePath($facts)
{
$testSuitePath = getenv('TEST_SUITE');
if (!$testSuitePath) {
$testSuitePath = $facts->getShopRootPath() . '/tests';
}
return $testSuitePath;
}
function getShopTestPath()
{
$facts = new Facts();
$shopTestPath = getShopSuitePath($facts);
return $shopTestPath;
}
function getMysqlConfigPath()
{
$facts = new Facts();
$configFile = new ConfigFile($facts->getSourcePath() . '/config.inc.php');
$generator = new DatabaseDefaultsFileGenerator($configFile);
return $generator->generate();
}

View File

@@ -0,0 +1,170 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
declare(strict_types=1);
namespace OxidEsales\PayPalModule\Tests\Codeception\Module;
use Codeception\Lib\Interfaces\DependsOnModule;
use Codeception\Module;
use Codeception\Module\REST;
use InvalidArgumentException;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Token\Parser;
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
use OxidEsales\EshopCommunity\Internal\Framework\Module\Setup\Bridge\ModuleActivationBridgeInterface;
use PHPUnit\Framework\AssertionFailedError;
class GraphQLHelper extends Module implements DependsOnModule
{
/** @var REST */
private $rest;
/**
* @return array|mixed
*/
public function _depends()
{
return [REST::class => 'Codeception\Module\REST is required'];
}
public function _inject(REST $rest): void
{
$this->rest = $rest;
}
public function getRest(): REST
{
return $this->rest;
}
public function sendGQLQuery(
string $query,
?array $variables = null,
int $language = 0,
int $shopId = 1,
array $additionalParameters = []
): void {
$uri = '/graphql?lang=' . $language . '&shp=' . $shopId;
foreach ($additionalParameters as $key => $value) {
$uri .= '&' . $key . '=' . $value;
}
$this->rest->haveHTTPHeader('Content-Type', 'application/json');
$this->rest->sendPOST($uri, [
'query' => $query,
'variables' => $variables,
]);
}
public function loginToGraphQLApi(string $username, string $password, int $shopId = 1): void
{
$this->logoutFromGraphQLApi();
$query = 'query ($username: String!, $password: String!) { token (username: $username, password: $password) }';
$variables = [
'username' => $username,
'password' => $password,
];
$this->sendGQLQuery($query, $variables, 0, $shopId);
$this->rest->seeResponseIsJson();
$this->seeResponseContainsValidJWTToken();
$this->rest->amBearerAuthenticated($this->grabTokenFromResponse());
}
public function anonymousLoginToGraphQLApi(int $shopId = 1): void
{
$this->logoutFromGraphQLApi();
$query = 'query{token}';
$this->sendGQLQuery($query, [], 0, $shopId);
$this->rest->seeResponseIsJson();
$this->seeResponseContainsValidJWTToken();
$this->rest->amBearerAuthenticated($this->grabTokenFromResponse());
}
public function logoutFromGraphQLApi(): void
{
$this->rest->deleteHeader('Authorization');
}
public function grabJsonResponseAsArray(): array
{
return json_decode($this->rest->grabResponse(), true);
}
public function grabTokenFromResponse(): string
{
return $this->grabJsonResponseAsArray()['data']['token'];
}
public function seeResponseContainsValidJWTToken(): void
{
$token = $this->grabTokenFromResponse();
try {
(new Parser(new JoseEncoder()))->parse($token);
} catch (InvalidArgumentException $e) {
throw new AssertionFailedError(sprintf('Not a valid JWT token: %s', $token));
}
}
public function extractSidFromResponseCookies(): string
{
$cookieHeaders = $this->rest->grabHttpHeader('Set-Cookie', false);
$sid = '';
foreach ($cookieHeaders as $value) {
preg_match('/^(sid=)([a-z0-9]*);/', $value, $matches);
if (isset($matches[2])) {
$sid = $matches[2];
break;
}
}
return $sid;
}
public function checkGraphBaseActive(): bool
{
$moduleActivation = ContainerFactory::getInstance()
->getContainer()
->get(ModuleActivationBridgeInterface::class);
return (bool) $moduleActivation->isActive('oe_graphql_base', 1);
}
public function checkGraphStorefrontActive(): bool
{
$moduleActivation = ContainerFactory::getInstance()
->getContainer()
->get(ModuleActivationBridgeInterface::class);
return (bool) $moduleActivation->isActive('oe_graphql_storefront', 1);
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
return [
'paymentMethod' => [
'OXID' => '660f0f63epaypald63a78af0a4b44',
'OXPAYMENTID' => 'oxidpaypal',
'OXOBJECTID' => 'oxidstandard',
'OXTYPE' => 'oxdelset'
],
'paymentCountry' => [
'OXID' => '2c0879ca0d9b6d56273064bd6627a8f6',
'OXPAYMENTID' => 'oxidpaypal',
'OXOBJECTID' => 'a7c40f631fc920687.20179984',
'OXTYPE' => 'oxcountry'
],
'adminData' => [
'OXID' => 'codeceptionadmin',
'OXRIGHTS' => 'malladmin',
'OXUSERNAME' => 'admin@myo3-shop.com',
'OXPASSWORD' => '6cb4a34e1b66d3445108cd91b67f98b9',
'OXPASSSALT' => '6631386565336161636139613634663766383538633566623662613036636539',
'OXCUSTNR' => 1,
'OXCOMPANY' => 'Your Company Name',
'OXFNAME' => 'John',
'OXLNAME' => 'Doe',
'OXSTREET' => 'Maple Street',
'OXSTREETNR' => '2425',
'OXCITY' => 'Any City',
'OXCOUNTRYID' => 'a7c40f631fc920687.20179984',
'OXZIP' => '9041',
'OXFON' => '217-8918712',
'OXFAX' => '217-8918713',
'OXSAL' => 'MR',
'OXBONI' => 1000,
'OXCREATE' => '2003-01-01 00:00:00',
'OXREGISTER' => '2003-01-01 00:00:00',
'OXTIMESTAMP' => '2021-01-26 15:57:46',
]
];

View File

@@ -0,0 +1,48 @@
SET @@session.sql_mode = '';
#Users demodata
REPLACE INTO `oxuser` SET
OXID = 'paypaluser',
OXACTIVE = 1,
OXRIGHTS = 'user',
OXSHOPID = 1,
OXUSERNAME = 'paypaluser@oxid-esales.dev',
OXPASSWORD = '$2y$10$tJd1YkFr2y4kUmojqa6NPuHrcMzZmxc9mh4OWQcLONfHg4WXzbtlu',
OXPASSSALT = '',
OXFNAME = 'TestUserName',
OXLNAME = 'TestUserSurname',
OXSTREET = 'Musterstr.šÄßüл',
OXSTREETNR = '12',
OXCITY = 'City',
OXZIP = '12345',
OXCOUNTRYID = 'a7c40f631fc920687.20179984',
OXBIRTHDATE = '1985-02-05 14:42:42',
OXCREATE = '2021-02-05 14:42:42',
OXREGISTER = '2021-02-05 14:42:42';
REPLACE INTO `oxuser` SET
OXID = 'defaultuser',
OXACTIVE = 1,
OXRIGHTS = 'user',
OXSHOPID = 1,
OXUSERNAME = 'defaultuser@oxid-esales.dev',
OXPASSWORD = '$2y$10$tJd1YkFr2y4kUmojqa6NPuHrcMzZmxc9mh4OWQcLONfHg4WXzbtlu',
OXPASSSALT = '',
OXFNAME = 'UserName',
OXLNAME = 'UserSurname',
OXSTREET = 'MeineStrasse',
OXSTREETNR = '12',
OXCITY = 'Hamburg',
OXZIP = '22001',
OXCOUNTRYID = 'a7c40f631fc920687.20179984',
OXBIRTHDATE = '1985-02-05 14:42:42',
OXCREATE = '2021-02-05 14:42:42',
OXREGISTER = '2021-02-05 14:42:42';
REPLACE INTO `oxuser` (`OXID`, `OXACTIVE`, `OXRIGHTS`, `OXSHOPID`, `OXUSERNAME`, `OXPASSWORD`, `OXPASSSALT`, `OXCUSTNR`, `OXUSTID`, `OXCOMPANY`, `OXFNAME`, `OXLNAME`, `OXSTREET`, `OXSTREETNR`, `OXADDINFO`, `OXCITY`, `OXCOUNTRYID`, `OXSTATEID`, `OXZIP`, `OXFON`, `OXFAX`, `OXSAL`, `OXBONI`, `OXCREATE`, `OXREGISTER`, `OXPRIVFON`, `OXMOBFON`, `OXBIRTHDATE`, `OXURL`, `OXUPDATEKEY`, `OXUPDATEEXP`, `OXPOINTS`) VALUES
('oxdefaultadmin', 1, 'malladmin', 1, 'admin', 'e3a8a383819630e42d9ef90be2347ea70364b5efbb11dfc59adbf98487e196fffe4ef4b76174a7be3f2338581e507baa61c852b7d52f4378e21bd2de8c1efa5e', '61646D696E61646D696E61646D696E', 1, '', 'Your Company Name', 'John', 'Doe', 'Maple Street', '2425', '', 'Any City', 'a7c40f631fc920687.20179984', '', '9041', '217-8918712', '217-8918713', 'MR', 1000, '2003-01-01 00:00:00', '2003-01-01 00:00:00', '', '', '0000-00-00', '', '', 0, 0);
REPLACE INTO `oxobject2payment` (`OXID`, `OXPAYMENTID`, `OXOBJECTID`, `OXTYPE`) VALUES ('660b8f058paypal6ada9ee7586d946ef', 'oxidpaypal', 'a7c40f6320aeb2ec2.72885259', 'oxcountry');
REPLACE INTO `oxobject2payment` (`OXID`, `OXPAYMENTID`, `OXOBJECTID`, `OXTYPE`) VALUES ('26586trf09oiu927b50ed832f76feed4', 'oxidpaypal', '1b842e732a23255b1.91207750', 'oxdelset');
REPLACE INTO `oxobject2payment` (`OXID`, `OXPAYMENTID`, `OXOBJECTID`, `OXTYPE`) VALUES ('26586trf09oiu927b50ed832f76feeg5', 'oxidpaypal', '1b842e732a23255b1.91207751', 'oxdelset');

View File

@@ -0,0 +1,168 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
return [
'adminUser' => [
"userId" => "admin",
"userLoginName" => "admin",
"userPassword" => "admin",
"userName" => "John",
"userLastName" => "Doe",
],
'userPassword' => 'useruser',
'defaultUserName' => 'defaultuser@oxid-esales.dev',
'defaultUserFirstName' => 'UserName',
'userName' => 'paypaluser@oxid-esales.dev',
'demoUserName' => 'user@oxid-esales.com',
'details' => [
'firstname' => 'TestUserName',
'lastname' => 'TestUserSurname',
'oxcity' => 'Hamburg',
'oxstreet' => 'Hauptstr.',
'oxstreetnr' => '13',
'oxzip' => '22547'
],
'totalordersum_ecsdetails' => 149.5,
'totalordersum_ecswithshipping' => 33.80,
// This user will be created and will be available in ce|pe|ee demodata
'user' => [
'oxid' => 'pptestuser',
'oxactive' => 1,
'oxrights' => 'user',
'oxshopid' => 1,
'oxusername' => 'testing_account@oxid-esales.dev',
'oxpassword' => '$2y$10$lj90Q/CaB0IB8PZemQW4Xu1/EWvAhkW9SOZ1Sr3JBx8DOmd3qz7bu',
'oxfname' => 'User',
'oxlname' => 'User',
'oxstreet' => 'Street',
'oxstreetnr' => 'Street Number',
'oxzip' => 'ZIP',
'oxcity' => 'City',
'oxcountryid' => 'a7c40f631fc920687.20179984',
'oxboni' => '600',
'oxcreate' => date("Y-m-d H:i:s"),
'oxregister' => date("Y-m-d H:i:s"),
'oxbirthdate' => date("Y-m-d"),
'oxpasssalt' => ''
],
'usergroups' => [
'OXID' => 'pptestusergroups',
'oxobjectid' => 'pptestuser',
'oxgroupsid' => 'oxidcustomer'
],
// This product is available in ce|pe|ee demodata
'product' => [
'id' => 'dc5ffdf380e15674b56dd562a7cb6aec',
'title' => 'Kuyichi leather belt JEVER',
'amount' => 4,
'price' => '119,60 €',
'bruttoprice_single' => '29.9',
'nettoprice_single' => '25.13'
],
'parent' => [
'id' => '531b537118f5f4d7a427cdb825440922',
'maxNettoPrice' => 83.95,
'maxBruttoPrice' => 99.9,
'minNettoPrice' => 78.07,
'minBruttoPrice' => 92.9,
],
'variant' => [
'id' => '6b6e0bb9f2b8b5f070f91593073b4555',
'bruttoprice' => '99.9',
'nettoprice' => '83.95'
],
'alternate_variant' => [
'id' => '6b65295a7fe5fa6faaa2f0ac3f9b0f80',
'bruttoprice' => '109.9',
'nettoprice' => '92.35'
],
'shipping' => [
'standard' => 'oxidstandard'
],
'payment_id' => 'oxidpaypal',
'payment_id_other' => 'oxidcashondel',
'oxvoucherseries' => [
'OXID' => 'ppgserie1',
'OXSERIENR' => 'ppgserie1',
'OXDISCOUNT' => '10',
'OXDISCOUNTTYPE' => 'absolute',
'OXBEGINDATE' => '2000-01-01',
'OXENDDATE' => '2050-12-31',
'OXSERIEDESCRIPTION' => 'PPG test voucher',
'OXALLOWOTHERSERIES' => 1
],
'oxvoucherseries_ee' => [
'OXID' => 'ppgserie1',
'OXMAPID' => '6543',
'OXSERIENR' => 'ppgserie1',
'OXDISCOUNT' => '10',
'OXDISCOUNTTYPE' => 'absolute',
'OXBEGINDATE' => '2000-01-01',
'OXENDDATE' => '2050-12-31',
'OXSERIEDESCRIPTION' => 'PPG test voucher',
'OXALLOWOTHERSERIES' => 1
],
'oxvoucherseries2shop' => [
'OXSHOPID' => '1',
'OXMAPOBJECTID' => '6543'
],
'oxvouchers' => [
'OXID' => 'ppgvoucher1',
'OXVOUCHERNR' => 'ppgvoucher1',
'OXVOUCHERSERIEID' => 'ppgserie1'
],
'oxdiscount' => [
'OXID' => 'ppgdiscount',
'OXSHOPID' => 1,
'OXACTIVE' => 1,
'OXACTIVEFROM' => '2020-12-01 00:00:00',
'OXACTIVETO' => '2099-01-01 00:00:00',
'OXTITLE' => 'ppgdiscount for product',
'OXTITLE_1' => 'ppgdiscount for product',
'OXAMOUNT' => 1,
'OXAMOUNTTO' => 999999,
'OXADDSUMTYPE' => 'abs',
'OXADDSUM' => 9.9
],
'oxdiscount_ee' => [
'OXID' => 'ppgdiscount',
'OXMAPID' => '7654',
'OXSHOPID' => 1,
'OXACTIVE' => 1,
'OXACTIVEFROM' => '2020-12-01 00:00:00',
'OXACTIVETO' => '2099-01-01 00:00:00',
'OXTITLE' => 'ppgdiscount for product',
'OXTITLE_1' => 'ppgdiscount for product',
'OXAMOUNT' => 1,
'OXAMOUNTTO' => 999999,
'OXADDSUMTYPE' => 'abs',
'OXADDSUM' => 9.9
],
'oxdiscount2shop' => [
'OXSHOPID' => '1',
'OXMAPOBJECTID' => '7654'
],
'oxobject2discount' => [
'OXID' => 'ppgdiscountrelation',
'OXDISCOUNTID' => 'ppgdiscount',
'OXOBJECTID' => 'dc5ffdf380e15674b56dd562a7cb6aec',
'OXTYPE' => 'oxarticles'
]
];

View File

@@ -0,0 +1,319 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception;
use Codeception\Util\Fixtures;
use OxidEsales\Codeception\Admin\AdminLoginPage;
use OxidEsales\Codeception\Admin\AdminPanel;
use OxidEsales\Codeception\Page\Home;
use OxidEsales\Codeception\Module\Translation\Translator;
use OxidEsales\Facts\Facts;
use OxidEsales\PayPalModule\Tests\Codeception\Admin\PayPalOrder;
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL)
*
* @SuppressWarnings(PHPMD)
*/
class AcceptanceTester extends \Codeception\Actor
{
use _generated\AcceptanceTesterActions;
protected $maxRetries = 20;
/**
* Open shop first page.
*/
public function openShop()
{
$I = $this;
$homePage = new Home($I);
$I->amOnPage($homePage->URL);
return $homePage;
}
/**
* @return \OxidEsales\Codeception\Admin\AdminPanel
*/
public function openAdminLoginPage()
{
$I = $this;
$adminPanel = new AdminLoginPage($I);
$I->amOnPage($adminPanel->URL);
return $adminPanel;
}
public function activatePaypalModule(): void
{
$rootPath = (new Facts())->getShopRootPath();
$possiblePaths = [
'/bin/oe-console',
'/vendor/bin/oe-console',
];
foreach ($possiblePaths as $path) {
if (is_file($rootPath . $path)) {
exec($rootPath . $path . ' oe:module:activate oepaypal');
return;
}
}
throw new \Exception('Could not find script "/bin/oe-console" to activate module');
}
public function deactivatePaypalModule(): void
{
$rootPath = (new Facts())->getShopRootPath();
$possiblePaths = [
'/bin/oe-console',
'/vendor/bin/oe-console',
];
foreach ($possiblePaths as $path) {
if (is_file($rootPath . $path)) {
exec($rootPath . $path . ' oe:module:deactivate oepaypal');
return;
}
}
throw new \Exception('Could not find script "/bin/oe-console" to deactivate module');
}
/**
* Switch to PayPal Installment banner iframe
* and check if body contains elements.
*/
public function seePayPalInstallmentBanner()
{
$I = $this;
$I->waitForElement("//div[contains(@id, 'paypal-installment-banner-container')]//iframe");
$I->switchToIFrame("//div[contains(@id, 'paypal-installment-banner-container')]//iframe");
$I->waitForElementVisible("//body[node()]");
// Switch back to main window, otherwise we will stay in PP banner iframe
$I->switchToIFrame();
return $this;
}
public function activateFlowTheme()
{
$I = $this;
//prepare testing with flow theme
$I->updateConfigInDatabase('sTheme', 'flow', 'str');
$I->updateConfigInDatabase('oePayPalBannersStartPageSelector', '#wrapper .row', 'str');
$I->updateConfigInDatabase('oePayPalBannersSearchResultsPageSelector', '#content .page-header .clearfix', 'str');
$I->updateConfigInDatabase('oePayPalBannersProductDetailsPageSelector', '.detailsParams', 'str');
$I->updateConfigInDatabase('oePayPalBannersPaymentPageSelector', '.checkoutSteps ~ .spacer', 'str');
}
public function activateWaveTheme()
{
$I = $this;
//prepare testing with wave theme
$I->updateConfigInDatabase('sTheme', 'wave', 'str');
$I->updateConfigInDatabase('oePayPalBannersStartPageSelector', '#wrapper .container', 'str');
$I->updateConfigInDatabase('oePayPalBannersSearchResultsPageSelector', '.page-header', 'str');
$I->updateConfigInDatabase('oePayPalBannersProductDetailsPageSelector', '#detailsItemsPager', 'str');
$I->updateConfigInDatabase('oePayPalBannersPaymentPageSelector', '.checkout-steps', 'str');
}
/**
* @param float $amount
*/
public function seePayPalInstallmentBannerInFlowAndWaveTheme(float $amount = 0, $breadCrumbText = '')
{
$I = $this;
//Check installment banner body in Flow theme
$I->reloadPage();
$I->waitForPageLoad();
if ($breadCrumbText) {
$I->see($breadCrumbText);
}
$I->dontSee(Translator::translate('ERROR_MESSAGE_ARTICLE_ARTICLE_NOT_BUYABLE'));
$I->seePayPalInstallmentBanner();
$I->checkInstallmentBannerData($amount);
//Check installment banner body in Wave theme
$this->activateWaveTheme();
$I->reloadPage();
$I->waitForPageLoad();
if ($breadCrumbText) {
$I->see($breadCrumbText);
}
$I->dontSee(Translator::translate('ERROR_MESSAGE_ARTICLE_ARTICLE_NOT_BUYABLE'));
$I->seePayPalInstallmentBanner();
$I->checkInstallmentBannerData($amount);
}
/**
* @param float $amount
* @param string $ratio
* @param string $currency
*/
public function checkInstallmentBannerData(float $amount = 0, string $ratio = '20x1', string $currency = 'EUR')
{
$I = $this;
$onloadMethod = $I->executeJS("return PayPalMessage.toString()");
$I->assertRegExp($this->prepareMessagePartRegex(sprintf("amount: %s", $amount)), $onloadMethod);
$I->assertRegExp($this->prepareMessagePartRegex(sprintf("ratio: '%s'", $ratio)), $onloadMethod);
$I->assertRegExp($this->prepareMessagePartRegex(sprintf("currency: '%s'", $currency)), $onloadMethod);
}
/**
* @return array
*/
public function getExistingUserData(): array
{
return Fixtures::get('user');
}
/**
* @return string
*/
public function getExistingUserName(): string
{
return $this->getExistingUserData()['oxusername'];
}
/**
* @return string
*/
public function getExistingUserPassword(): string
{
return Fixtures::get('userPassword');
}
/**
* @return string
*/
public function getDemoUserName(): string
{
return Fixtures::get('demoUserName');
}
/**
* Wrap the message part in message required conditions
*
* @param string $part
* @return string
*/
protected function prepareMessagePartRegex($part)
{
return "/paypal.Messages\(\{[^}\)]*{$part}/";
}
public function openAdmin(): AdminLoginPage
{
$I = $this;
$adminLogin = new AdminLoginPage($I);
$I->amOnPage($adminLogin->URL);
return $adminLogin;
}
public function loginAdmin(): AdminPanel
{
$adminPage = $this->openAdmin();
$admin = Fixtures::get('adminUser');
return $adminPage->login($admin['userLoginName'], $admin['userPassword']);
}
public function getShopUrl(): string
{
$facts = new Facts();
return $facts->getShopUrl();
}
public function switchToLastWindow()
{
$I = $this;
$I->executeInSelenium(function (\Facebook\WebDriver\Remote\RemoteWebDriver $webdriver) {
$handles=$webdriver->getWindowHandles();
$last_window = end($handles);
$webdriver->switchTo()->window($last_window);
$size = new \Facebook\WebDriver\WebDriverDimension(1920, 1280);
$webdriver->manage()->window()->setSize($size);
});
}
/**
* @param AcceptanceTester $I
* @param int $orderNumber
*
* @return PayPalOrder
* @throws \Exception
*/
public function openAdminOrder(int $orderNumber, int $retry = 0)
{
$I = $this;
if ($retry >= $this->maxRetries) {
$I->makeScreenshot();
$I->makeHtmlSnapshot();
$I->markTestIncomplete('Did not manage to open the PayPal order tab');
}
$adminLoginPage = $I->openAdminLoginPage();
$adminUser = Fixtures::get('adminUser');
$adminPanel = $adminLoginPage->login($adminUser['userLoginName'], $adminUser['userPassword']);
$ordersList = $adminPanel->openOrders($adminPanel);
$ordersList->searchByOrderNumber($orderNumber);
$I->wait(1);
if ($I->seePageHasElement('//a[text()="' . $orderNumber . '"]')) {
$I->retryClick('//a[text()="' . $orderNumber . '"]');
} elseif ($I->seePageHasElement('//a[text()="PayPal"]')) {
$I->retryClick('//a[text()="PayPal"]');
} else {
$this->openAdminOrder($orderNumber, ++$retry);
}
$I->wait(1);
$paypalOrder = new PayPalOrder($I);
if (!$I->seePageHasElement($paypalOrder->paypalTab)) {
$this->openAdminOrder($orderNumber, ++$retry);
}
$I->retryClick($paypalOrder->paypalTab);
$I->selectEditFrame();
if (!$I->seePageHasElement($paypalOrder->captureButton)) {
$this->openAdminOrder($orderNumber, ++$retry);
}
return $paypalOrder;
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception\Admin;
use OxidEsales\Codeception\Page\Page;
/**
* Class Orders
* @package OxidEsales\PayPalModule\Tests\Codeception\Admin
*/
class PayPalOrder extends Page
{
public $paypalTab = '//a[@href="#oepaypalorder_paypal"]';
public $captureButton = '#captureButton';
public $amountSelect = '.amountSelect';
public $captureAmountInput = '#captureAmountInput';
public $pendingStatusCheckbox = '#pendingStatusCheckbox';
public $editForm = '[name=myedit]';
public $refundButton = '#refundButton0';
public $refundAmountInput = '#refundAmountInput';
public $errorBox = '.errorbox';
public $captureErrorText = 'Error message from PayPal: Amount is not valid';
public $refundErrorText = 'Error message from PayPal: The partial refund amount is not valid';
public $lastHistoryRowAction = '//*[@id="historyTable"]/tbody/tr[2]/td[2]';
public $lastHistoryRowAmount = '//*[@id="historyTable"]/tbody/tr[2]/td[3]';
/**
* Capture order
*
* @param $amount
* @param string $type
* @return $this
*/
public function captureAmount($amount, $type = 'Complete')
{
$I = $this->user;
$I->waitForElement($this->captureButton, 10);
$I->click($this->captureButton);
$I->selectOption($this->amountSelect, $type);
if ($type !== 'Complete') {
$I->fillField($this->captureAmountInput, $amount);
$I->click($this->pendingStatusCheckbox);
}
$I->submitForm($this->editForm, []);
$I->waitForElementNotVisible($this->editForm, 30);
return $this;
}
/**
* Refund amount
*
* @param $amount
* @param string $type
* @return $this
*/
public function refundAmount($amount, $type = 'Full')
{
$I = $this->user;
$I->waitForElement($this->refundButton, 10);
$I->click($this->refundButton);
$I->selectOption($this->amountSelect, $type);
if ($type !== 'Full') {
$I->fillField($this->refundAmountInput, $amount);
}
$I->submitForm($this->editForm, []);
$I->waitForElementNotVisible($this->editForm, 30);
return $this;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception\Helper;
// here you can define custom actions
// all public methods declared in helper class will be available in $I
use PHPUnit\Framework\AssertionFailedError;
class Acceptance extends \Codeception\Module
{
public function seePageHasElement($element)
{
try {
$this->getModule('WebDriver')->_findElements($element);
} catch (AssertionFailedError $f) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
namespace OxidEsales\PayPalModule\Tests\Codeception\Page\Checkout;
/**
* Class OrderCheckout
* @package OxidEsales\PayPalModule\Tests\Codeception\Page
*/
class OrderCheckout extends \OxidEsales\Codeception\Page\Checkout\OrderCheckout
{
public $confirmationButton = '#orderConfirmAgbBottom';
/**
* Clicks on submit order button.
*
* @return $this
*/
public function submitOrder()
{
$I = $this->user;
$I->waitForElementVisible($this->confirmationButton, 10);
$I->submitForm($this->confirmationButton, []);
return $this;
}
}

View File

@@ -0,0 +1,356 @@
<?php
/**
* This file is part of O3-Shop Paypal module.
*
* O3-Shop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* O3-Shop is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with O3-Shop. If not, see <http://www.gnu.org/licenses/>
*
* @copyright Copyright (c) 2022 OXID eSales AG (https://www.oxid-esales.com)
* @copyright Copyright (c) 2022 O3-Shop (https://www.o3-shop.com)
* @license https://www.gnu.org/licenses/gpl-3.0 GNU General Public License 3 (GPLv3)
*/
declare(strict_types=1);
namespace OxidEsales\PayPalModule\Tests\Codeception\Page;
use Facebook\WebDriver\Exception\ElementNotVisibleException;
use OxidEsales\Codeception\Page\Checkout\OrderCheckout;
use OxidEsales\Codeception\Page\Page;
/**
* Class PayPalLogin
* @package OxidEsales\PayPalModule\Tests\Codeception\Page
*/
class PayPalLogin extends Page
{
public $userLoginEmail = '#email';
public $oldUserLoginEmail = '#login_email';
public $userPassword = '#password';
public $oldUserPassword = '#login_password';
public $nextButton = '#btnNext';
public $loginButton = '#btnLogin';
public $oldLoginButton = '#submitLogin';
public $newConfirmButton = '#confirmButtonTop';
public $oldConfirmButton = '#continue_abovefold';
public $oneTouchNotNowLink = '#notNowLink';
public $spinner = '#spinner';
public $gdprContainer = "#gdpr-container";
public $gdprCookieBanner = "#gdprCookieBanner";
public $acceptAllPaypalCookies = "#acceptAllButton";
public $loginSection = "#loginSection";
public $oldLoginSection = "#passwordSection";
public $cancelLink = "#cancelLink";
public $returnToShop = "#cancel_return";
public $breadCrumb = "#breadCrumb";
public $paymentConfirmButton = "#payment-submit-btn";
public $globalSpinner = "//div[@data-testid='global-spinner']";
public $preloaderSpinner = "//div[@id='preloaderSpinner']";
public $paypalBannerContainer = "//div[@id='paypal-installment-banner-container']";
public $backToInputEmail = "#backToInputEmailLink";
public $errorSection = '#notifications #pageLevelErrors';
public $splitPassword = '#splitPassword';
public $splitEmail = '#splitEmail';
public $rememberedEmail = "//div[@class='profileRememberedEmail']";
/**
* @param string $userName
* @param string $userPassword
*
* @return OrderCheckout
*/
public function loginAndCheckout(string $userName, string $userPassword): OrderCheckout
{
$I = $this->user;
$usingNewLogin = true;
$this->waitForPayPalPage();
// In case we have cookie message, accept all cookies
$this->acceptAllPayPalCookies();
// new login page
if ($I->seePageHasElement($this->userLoginEmail)) {
$I->waitForElementVisible($this->userLoginEmail, 30);
$I->fillField($this->userLoginEmail, $userName);
$I->retryClick($this->nextButton);
$this->waitForPayPalPage();
$I->waitForElementVisible($this->userPassword, 10);
$I->fillField($this->userPassword, $userPassword);
$I->retryClick($this->loginButton);
}
// old login page
if ($I->seePageHasElement($this->oldUserLoginEmail)) {
$usingNewLogin = false;
$I->waitForElementVisible($this->oldUserLoginEmail, 30);
$I->fillField($this->oldUserLoginEmail, $userName);
$I->waitForElementVisible($this->oldUserPassword, 5);
$I->fillField($this->oldUserPassword, $userPassword);
$I->retryClick($this->oldLoginButton);
}
$this->waitForPayPalPage();
if ($I->seePageHasElement($this->oneTouchNotNowLink)) {
$I->retryClick($this->oneTouchNotNowLink);
}
$confirmButton = $usingNewLogin ? $this->newConfirmButton : $this->oldConfirmButton;
$I->waitForElementClickable($confirmButton, 60);
$this->waitForPayPalPage();
$I->retryClick($confirmButton);
$I->waitForDocumentReadyState();
return new OrderCheckout($I);
}
/**
* @param string $userName
* @param string $userPassword
*
* @return OrderCheckout
*/
public function checkoutWithStandardPayPal(string $userName, string $userPassword): OrderCheckout
{
$I = $this->user;
$this->loginToPayPal($userName, $userPassword);
$this->confirmPayPal();
//retry
$this->waitForSpinnerDisappearance();
$this->confirmPayPal();
return new OrderCheckout($I);
}
public function loginToPayPal(string $userName, string $userPassword): void
{
$I = $this->user;
$this->waitForPayPalPage();
$this->removeCookieConsent();
if ($I->seePageHasElement($this->splitPassword)
&& $I->seePageHasElement($this->rememberedEmail)
&& $I->seePageHasElement($this->backToInputEmail)
) {
try {
$I->seeAndClick($this->backToInputEmail);
$I->waitForDocumentReadyState();
$this->waitForSpinnerDisappearance();
$I->waitForElementNotVisible($this->backToInputEmail);
} catch(ElementNotVisibleException $e) {
//nothing to be done, element was not visible
}
}
if ($I->seePageHasElement($this->oldLoginSection)) {
$I->waitForElementVisible($this->userLoginEmail, 5);
$I->fillField($this->userLoginEmail, $userName);
if ($I->seePageHasElement($this->nextButton)) {
$I->retryClick($this->nextButton);
}
$I->waitForElementVisible($this->userPassword, 5);
$I->fillField($this->userPassword, $userPassword);
$I->retryClick($this->loginButton);
}
if ($I->seePageHasElement($this->oneTouchNotNowLink)) {
$I->retryClick($this->oneTouchNotNowLink);
}
$this->waitForSpinnerDisappearance();
$this->removeCookieConsent();
$this->waitForSpinnerDisappearance();
$I->wait(3);
}
/**
* @param string $userName
* @param string $userPassword
*/
public function approveGraphqlStandardPayPal(string $userName, string $userPassword): void
{
$I = $this->user;
$this->loginToPayPal($userName, $userPassword);
$this->confirmPayPal();
//retry
$this->waitForSpinnerDisappearance();
$this->confirmPayPal();
//we should be back to shop frontend as we sent a redirect url to paypal
$I->assertTrue($I->seePageHasElement($this->paypalBannerContainer));
}
public function confirmPayPal()
{
$I = $this->user;
$this->waitForSpinnerDisappearance();
$this->removeCookieConsent();
if ($I->seePageHasElement(substr($this->newConfirmButton, 1))) {
$I->retryClick($this->newConfirmButton);
$I->waitForDocumentReadyState();
$I->waitForElementNotVisible($this->globalSpinner, 60);
$I->wait(10);
}
if ($I->seePageHasElement("//input[@id='" . substr($this->newConfirmButton, 1) . "']")) {
$I->executeJS("document.getElementById('" . substr($this->newConfirmButton, 1) . "').click();");
$I->waitForDocumentReadyState();
$I->waitForElementNotVisible($this->globalSpinner, 60);
$I->wait(10);
}
if ($I->seePageHasElement($this->paymentConfirmButton)) {
$I->retryClick($this->paymentConfirmButton);
$I->waitForDocumentReadyState();
$I->waitForElementNotVisible($this->globalSpinner, 60);
$I->wait(10);
}
}
/**
* @param string $userName
* @param string $userPassword
*/
public function approveGraphqlExpressPayPal(string $userName, string $userPassword): void
{
$I = $this->user;
$this->approveExpressPayPal($userName, $userPassword);
//we should be back to shop frontend as we sent a redirect url to paypal
$I->seePageHasElement($this->paypalBannerContainer);
}
/**
* @param string $userName
* @param string $userPassword
*/
public function approveExpressPayPal(string $userName, string $userPassword): void
{
$I = $this->user;
$this->waitForPayPalPage();
$this->waitForSpinnerDisappearance();
$this->removeCookieConsent();
$this->loginToPayPal($userName, $userPassword);
$this->waitForSpinnerDisappearance();
$I->wait(3);
$this->confirmPayPal();
//retry
$this->waitForSpinnerDisappearance();
$this->confirmPayPal();
}
public function waitForPayPalPage(): PayPalLogin
{
$I = $this->user;
$I->waitForDocumentReadyState();
$I->waitForElementNotVisible($this->spinner, 90);
$I->wait(10);
if ($I->seePageHasElement($this->loginSection)) {
$I->retryClick('.loginRedirect a');
$I->waitForDocumentReadyState();
$this->waitForSpinnerDisappearance();
$I->waitForElementNotVisible($this->loginSection);
}
return $this;
}
/**
* Click cancel on payPal side to return to shop.
*/
public function cancelPayPal(bool $isRetry = false): void
{
$I = $this->user;
if ($I->seePageHasElement($this->cancelLink)) {
$I->amOnUrl($I->grabAttributeFrom($this->cancelLink, 'href'));
$I->waitForDocumentReadyState();
} elseif ($I->seePageHasElement($this->returnToShop)) {
$I->amOnUrl($I->grabAttributeFrom($this->returnToShop, 'href'));
$I->waitForDocumentReadyState();
}
//we should be redirected back to shop at this point
if ($I->dontSeeElement($this->breadCrumb) &&
$I->dontSeeElement(strtolower($this->breadCrumb)) &&
!$isRetry
) {
$this->cancelPayPal(true);
}
}
private function acceptAllPayPalCookies()
{
$I = $this->user;
// In case we have cookie message, accept all cookies
if ($I->seePageHasElement($this->acceptAllPaypalCookies)) {
$I->retryClick($this->acceptAllPaypalCookies);
$I->waitForElementNotVisible($this->acceptAllPaypalCookies);
}
}
private function waitForSpinnerDisappearance()
{
$I = $this->user;
$I->waitForElementNotVisible($this->preloaderSpinner, 30);
$I->waitForElementNotVisible($this->globalSpinner, 30);
$I->waitForElementNotVisible($this->spinner, 30);
}
private function removeCookieConsent()
{
$I = $this->user;
if ($I->seePageHasElement($this->gdprContainer)) {
$I->executeJS("document.getElementById('" . substr($this->gdprContainer, 1) . "').remove();");
}
if ($I->seePageHasElement($this->gdprCookieBanner)) {
$I->executeJS("document.getElementById('" . substr($this->gdprCookieBanner, 1) . "').remove();");
}
}
}

View File

@@ -0,0 +1,55 @@
# suite config
actor: AcceptanceTester
path: Acceptance
bootstrap: _bootstrap.php
modules:
enabled:
- Asserts
- REST:
url: '%SHOP_URL%'
depends: PhpBrowser
part: Json
- \OxidEsales\PayPalModule\Tests\Codeception\Helper\Acceptance
- WebDriver:
url: '%SHOP_URL%'
browser: '%BROWSER_NAME%'
port: '%SELENIUM_SERVER_PORT%'
host: '%SELENIUM_SERVER_HOST%'
window_size: 1920x1080
clear_cookies: true
- Db:
dsn: 'mysql:host=%DB_HOST%;dbname=%DB_NAME%;charset=utf8'
user: '%DB_USERNAME%'
password: '%DB_PASSWORD%'
port: '%DB_PORT%'
dump: '%DUMP_PATH%'
module_dump: '%MODULE_DUMP_PATH%'
mysql_config: '%MYSQL_CONFIG_PATH%'
populate: true # run populator before all tests
cleanup: true # run populator before each test
populator: >
%PHP_BIN% %VENDOR_PATH%/bin/reset-shop
&& mysql --defaults-file=$mysql_config --default-character-set=utf8 $dbname < $dump
&& mysql --defaults-file=$mysql_config --default-character-set=utf8 $dbname < $module_dump
initial_queries:
- "SET SESSION sql_mode = '';"
- \OxidEsales\Codeception\Module\Oxideshop:
depends:
- WebDriver
- Db
- \OxidEsales\Codeception\Module\OxideshopAdmin:
depends:
- WebDriver
- \OxidEsales\Codeception\Module\Oxideshop
- \OxidEsales\Codeception\Module\Database:
config_key: 'fq45QS09_fqyx09239QQ'
depends: Db
- \OxidEsales\Codeception\Module\Translation\TranslationsModule:
shop_path: '%SHOP_SOURCE_PATH%'
paths: 'Application/views/flow,Application/views/admin,modules/oe/oepaypal/translations,modules/oe/oepaypal/views/admin'
- \OxidEsales\Codeception\Module\OxideshopModules
- \OxidEsales\PayPalModule\Tests\Codeception\Module\GraphQLHelper:
depends:
- REST
step_decorators:
- \Codeception\Step\Retry