diff --git a/src/resources/vue/Wallet.vue b/src/resources/vue/Wallet.vue index 1b506553..97290317 100644 --- a/src/resources/vue/Wallet.vue +++ b/src/resources/vue/Wallet.vue @@ -1,446 +1,446 @@ diff --git a/src/tests/Browser/PaymentMollieTest.php b/src/tests/Browser/PaymentMollieTest.php index 99241290..be1fd720 100644 --- a/src/tests/Browser/PaymentMollieTest.php +++ b/src/tests/Browser/PaymentMollieTest.php @@ -1,313 +1,313 @@ deleteTestUser('payment-test@kolabnow.com'); } /** * {@inheritDoc} */ public function tearDown(): void { $this->deleteTestUser('payment-test@kolabnow.com'); parent::tearDown(); } /** * Test the payment process * * @group mollie */ public function testPayment(): void { $user = $this->getTestUser('payment-test@kolabnow.com', [ 'password' => 'simple123', ]); $this->browse(function (Browser $browser) use ($user) { $browser->visit(new Home()) ->submitLogon('payment-test@kolabnow.com', 'simple123', true, ['paymentProvider' => 'mollie']) ->on(new Dashboard()) ->click('@links .link-wallet') ->on(new WalletPage()) ->assertSeeIn('@main button', 'Add credit') ->click('@main button') ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Top up your wallet') - ->waitFor('#payment-method-selection #creditcard') - ->waitFor('#payment-method-selection #paypal') - ->waitFor('#payment-method-selection #banktransfer') - ->click('#creditcard'); + ->waitFor('#payment-method-selection .link-creditcard svg') + ->waitFor('#payment-method-selection .link-paypal svg') + ->waitFor('#payment-method-selection .link-banktransfer svg') + ->click('#payment-method-selection .link-creditcard'); }) ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Top up your wallet') ->assertFocused('#amount') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Continue') // Test error handling ->type('@body #amount', 'aaa') ->click('@button-action') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->assertSeeIn('#amount + span + .invalid-feedback', 'The amount must be a number.') // Submit valid data ->type('@body #amount', '12.34') // Note we use double click to assert it does not create redundant requests ->click('@button-action') ->click('@button-action'); }) ->on(new PaymentMollie()) ->assertSeeIn('@title', $user->tenant->title . ' Payment') ->assertSeeIn('@amount', 'CHF 12.34'); $this->assertSame(1, $user->wallets()->first()->payments()->count()); // Looks like the Mollie testing mode is limited. // We'll select credit card method and mark the payment as paid // We can't do much more, we have to trust Mollie their page works ;) // For some reason I don't get the method selection form, it // immediately jumps to the next step. Let's detect that if ($browser->element('@methods')) { $browser->click('@methods button.grid-button-creditcard') ->waitFor('button.form__button'); } $browser->click('@status-table input[value="paid"]') ->click('button.form__button'); // Now it should redirect back to wallet page and in background // use the webhook to update payment status (and balance). // Looks like in test-mode the webhook is executed before redirect // so we can expect balance updated on the wallet page $browser->waitForLocation('/wallet') ->on(new WalletPage()) ->assertSeeIn('@main .card-title', 'Account balance 12,34 CHF'); }); } /** * Test the auto-payment setup process * * @group mollie */ public function testAutoPaymentSetup(): void { $user = $this->getTestUser('payment-test@kolabnow.com', [ 'password' => 'simple123', ]); $this->browse(function (Browser $browser) use ($user) { $browser->visit(new Home()) ->submitLogon('payment-test@kolabnow.com', 'simple123', true, ['paymentProvider' => 'mollie']) ->on(new Dashboard()) ->click('@links .link-wallet') ->on(new WalletPage()) ->assertMissing('@body #mandate-form .alert') ->click('@main #mandate-form button') ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Set up auto-payment') - ->waitFor('#payment-method-selection #creditcard') - ->assertMissing('#payment-method-selection #paypal') - ->assertMissing('#payment-method-selection #banktransfer') - ->click('#creditcard'); + ->waitFor('#payment-method-selection .link-creditcard svg') + ->assertMissing('#payment-method-selection .link-paypal') + ->assertMissing('#payment-method-selection .link-banktransfer') + ->click('#payment-method-selection .link-creditcard'); }) ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Set up auto-payment') ->assertSeeIn('@body label[for="mandate_amount"]', 'Fill up by') ->assertValue('@body #mandate_amount', PaymentProvider::MIN_AMOUNT / 100) ->assertSeeIn('@body label[for="mandate_balance"]', 'when account balance is below') // phpcs:ignore ->assertValue('@body #mandate_balance', '0') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Continue') // Test error handling ->type('@body #mandate_amount', 'aaa') ->type('@body #mandate_balance', '-1') ->click('@button-action') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->assertVisible('@body #mandate_amount.is-invalid') ->assertVisible('@body #mandate_balance.is-invalid') ->assertSeeIn('#mandate_amount + span + .invalid-feedback', 'The amount must be a number.') ->assertSeeIn('#mandate_balance + span + .invalid-feedback', 'The balance must be at least 0.') ->type('@body #mandate_amount', 'aaa') ->type('@body #mandate_balance', '0') ->click('@button-action') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->assertVisible('@body #mandate_amount.is-invalid') ->assertMissing('@body #mandate_balance.is-invalid') ->assertSeeIn('#mandate_amount + span + .invalid-feedback', 'The amount must be a number.') ->assertMissing('#mandate_balance + span + .invalid-feedback') // Submit valid data ->type('@body #mandate_amount', '100') ->type('@body #mandate_balance', '0') // Note we use double click to assert it does not create redundant requests ->click('@button-action') ->click('@button-action'); }) ->on(new PaymentMollie()) ->assertSeeIn('@title', $user->tenant->title . ' Auto-Payment Setup') ->assertMissing('@amount') ->submitValidCreditCard() ->waitForLocation('/wallet') ->visit('/wallet?paymentProvider=mollie') ->waitFor('#mandate-info') ->assertPresent('#mandate-info p:first-child') ->assertSeeIn( '#mandate-info p:first-child', 'Auto-payment is set to fill up your account by 100 CHF ' . 'every time your account balance gets under 0 CHF.' ) ->assertSeeIn( '#mandate-info p:nth-child(2)', 'Mastercard (**** **** **** 6787)' ) ->assertMissing('@body .alert'); $this->assertSame(1, $user->wallets()->first()->payments()->count()); }); // Test updating (disabled) auto-payment $this->browse(function (Browser $browser) use ($user) { $wallet = $user->wallets()->first(); $wallet->setSetting('mandate_disabled', 1); $browser->refresh() ->on(new WalletPage()) ->waitFor('#mandate-info') ->assertSeeIn( '#mandate-info .disabled-mandate', 'The configured auto-payment has been disabled' ) ->assertSeeIn('#mandate-info button.btn-primary', 'Change auto-payment') ->click('#mandate-info button.btn-primary') ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Update auto-payment') ->assertSeeIn( '@body form .disabled-mandate', 'The auto-payment is disabled.' ) ->assertValue('@body #mandate_amount', '100') ->assertValue('@body #mandate_balance', '0') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Submit') // Test error handling ->type('@body #mandate_amount', 'aaa') ->click('@button-action') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->assertVisible('@body #mandate_amount.is-invalid') ->assertSeeIn('#mandate_amount + span + .invalid-feedback', 'The amount must be a number.') // Submit valid data ->type('@body #mandate_amount', '50') ->click('@button-action'); }) ->waitUntilMissing('#payment-dialog') ->assertToast(Toast::TYPE_SUCCESS, 'The auto-payment has been updated.') // make sure the "disabled" text isn't there ->assertMissing('#mandate-info .disabled-mandate') ->click('#mandate-info button.btn-primary') ->assertMissing('form .disabled-mandate') ->click('button.modal-cancel'); }); // Test deleting auto-payment $this->browse(function (Browser $browser) { $browser->on(new WalletPage()) ->waitFor('#mandate-info') ->assertSeeIn('#mandate-info * button.btn-danger', 'Cancel auto-payment') ->assertVisible('#mandate-info * button.btn-danger') ->click('#mandate-info * button.btn-danger') ->assertToast(Toast::TYPE_SUCCESS, 'The auto-payment has been removed.') ->assertVisible('#mandate-form') ->assertMissing('#mandate-info'); }); // Test pending and failed mandate $this->browse(function (Browser $browser) { $browser->on(new WalletPage()) ->assertMissing('@body #mandate-form .alert') ->click('@main #mandate-form button') ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Set up auto-payment') - ->waitFor('#payment-method-selection #creditcard') - ->click('#creditcard'); + ->waitFor('#payment-method-selection .link-creditcard') + ->click('#payment-method-selection .link-creditcard'); }) ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Set up auto-payment') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Continue') // Submit valid data ->type('@body #mandate_amount', '100') ->type('@body #mandate_balance', '0') ->click('@button-action'); }) ->on(new PaymentMollie()) ->submitValidCreditCard('open') ->waitForLocation('/wallet') ->visit('/wallet?paymentProvider=mollie') ->on(new WalletPage()) ->assertSeeIn( '#mandate-info .alert-warning', 'The setup of the automatic payment is still in progress.' ) // Delete the mandate ->click('#mandate-info * button.btn-danger') ->assertToast(Toast::TYPE_SUCCESS, 'The auto-payment has been removed.') ->assertMissing('@body #mandate-form .alert') // Create a new mandate ->click('@main #mandate-form button') ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Set up auto-payment') - ->waitFor('#payment-method-selection #creditcard') - ->click('#creditcard'); + ->waitFor('#payment-method-selection .link-creditcard') + ->click('#payment-method-selection .link-creditcard'); }) ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Set up auto-payment') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Continue') // Submit valid data ->type('@body #mandate_amount', '100') ->type('@body #mandate_balance', '0') ->click('@button-action'); }) ->on(new PaymentMollie()) ->submitValidCreditCard('failed') ->waitForLocation('/wallet') ->visit('/wallet?paymentProvider=mollie') ->on(new WalletPage()) ->waitFor('#mandate-form .alert-danger') ->assertSeeIn( '#mandate-form .alert-danger', 'The setup of automatic payments failed. Restart the process to enable' ) ->click('@main button') ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->waitFor('#mandate-form') ->assertMissing('#mandate-info'); }); }); } } diff --git a/src/tests/Browser/PaymentStripeTest.php b/src/tests/Browser/PaymentStripeTest.php index 9ddc0c1b..ce0db9e0 100644 --- a/src/tests/Browser/PaymentStripeTest.php +++ b/src/tests/Browser/PaymentStripeTest.php @@ -1,234 +1,234 @@ deleteTestUser('payment-test@kolabnow.com'); } /** * {@inheritDoc} */ public function tearDown(): void { $this->deleteTestUser('payment-test@kolabnow.com'); parent::tearDown(); } /** * Test the payment process * * @group stripe */ public function testPayment(): void { $user = $this->getTestUser('payment-test@kolabnow.com', [ 'password' => 'simple123', ]); $this->browse(function (Browser $browser) use ($user) { $browser->visit(new Home()) ->submitLogon('payment-test@kolabnow.com', 'simple123', true, ['paymentProvider' => 'stripe']) ->on(new Dashboard()) ->click('@links .link-wallet') ->on(new WalletPage()) ->assertSeeIn('@main button', 'Add credit') ->click('@main button') ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Top up your wallet') - ->waitFor('#payment-method-selection #creditcard') - ->waitFor('#payment-method-selection #paypal') - ->assertMissing('#payment-method-selection #banktransfer') - ->click('#creditcard'); + ->waitFor('#payment-method-selection .link-creditcard svg') + ->waitFor('#payment-method-selection .link-paypal svg') + ->assertMissing('#payment-method-selection .link-banktransfer svg') + ->click('#payment-method-selection .link-creditcard'); }) ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Top up your wallet') ->assertFocused('#amount') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Continue') // Test error handling ->type('@body #amount', 'aaa') ->click('@button-action') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->assertSeeIn('#amount + span + .invalid-feedback', 'The amount must be a number.') // Submit valid data ->type('@body #amount', '12.34') // Note we use double click to assert it does not create redundant requests ->click('@button-action') ->click('@button-action'); }) ->on(new PaymentStripe()) ->assertSeeIn('@title', $user->tenant->title . ' Payment') ->assertSeeIn('@amount', 'CHF 12.34') ->assertValue('@email-input', $user->email) ->submitValidCreditCard(); // Now it should redirect back to wallet page and in background // use the webhook to update payment status (and balance). // Looks like in test-mode the webhook is executed before redirect // so we can expect balance updated on the wallet page $browser->waitForLocation('/wallet', 30) // need more time than default 5 sec. ->on(new WalletPage()) ->assertSeeIn('@main .card-title', 'Account balance 12,34 CHF'); }); } /** * Test the auto-payment setup process * * @group stripe */ public function testAutoPaymentSetup(): void { $user = $this->getTestUser('payment-test@kolabnow.com', [ 'password' => 'simple123', ]); // Test creating auto-payment $this->browse(function (Browser $browser) use ($user) { $browser->visit(new Home()) ->submitLogon('payment-test@kolabnow.com', 'simple123', true, ['paymentProvider' => 'stripe']) ->on(new Dashboard()) ->click('@links .link-wallet') ->on(new WalletPage()) ->assertMissing('@body #mandate-form .alert') ->click('@main #mandate-form button') ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Set up auto-payment') - ->waitFor('#payment-method-selection #creditcard') - ->assertMissing('#payment-method-selection #paypal') - ->assertMissing('#payment-method-selection #banktransfer') - ->click('#creditcard'); + ->waitFor('#payment-method-selection .link-creditcard') + ->assertMissing('#payment-method-selection .link-paypal') + ->assertMissing('#payment-method-selection .link-banktransfer') + ->click('#payment-method-selection .link-creditcard'); }) ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Set up auto-payment') ->assertSeeIn('@body label[for="mandate_amount"]', 'Fill up by') ->assertValue('@body #mandate_amount', PaymentProvider::MIN_AMOUNT / 100) ->assertSeeIn('@body label[for="mandate_balance"]', 'when account balance is below') // phpcs:ignore ->assertValue('@body #mandate_balance', '0') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Continue') // Test error handling ->type('@body #mandate_amount', 'aaa') ->type('@body #mandate_balance', '-1') ->click('@button-action') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->assertVisible('@body #mandate_amount.is-invalid') ->assertVisible('@body #mandate_balance.is-invalid') ->assertSeeIn('#mandate_amount + span + .invalid-feedback', 'The amount must be a number.') ->assertSeeIn('#mandate_balance + span + .invalid-feedback', 'The balance must be at least 0.') ->type('@body #mandate_amount', 'aaa') ->type('@body #mandate_balance', '0') ->click('@button-action') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->assertVisible('@body #mandate_amount.is-invalid') ->assertMissing('@body #mandate_balance.is-invalid') ->assertSeeIn('#mandate_amount + span + .invalid-feedback', 'The amount must be a number.') ->assertMissing('#mandate_balance + span + .invalid-feedback') // Submit valid data ->type('@body #mandate_amount', '100') ->type('@body #mandate_balance', '0') // Note we use double click to assert it does not create redundant requests ->click('@button-action') ->click('@button-action'); }) ->on(new PaymentStripe()) ->assertMissing('@title') ->assertMissing('@amount') ->assertValue('@email-input', $user->email) ->submitValidCreditCard() ->waitForLocation('/wallet', 30) // need more time than default 5 sec. ->visit('/wallet?paymentProvider=stripe') ->waitFor('#mandate-info') ->assertPresent('#mandate-info p:first-child') ->assertSeeIn( '#mandate-info p:first-child', 'Auto-payment is set to fill up your account by 100 CHF ' . 'every time your account balance gets under 0 CHF.' ) ->assertSeeIn( '#mandate-info p:nth-child(2)', 'Visa (**** **** **** 4242)' ) ->assertMissing('@body .alert'); }); // Test updating (disabled) auto-payment $this->browse(function (Browser $browser) use ($user) { $wallet = $user->wallets()->first(); $wallet->setSetting('mandate_disabled', 1); $browser->refresh() ->on(new WalletPage()) ->waitFor('#mandate-info') ->assertSeeIn( '#mandate-info .disabled-mandate', 'The configured auto-payment has been disabled' ) ->assertSeeIn('#mandate-info button.btn-primary', 'Change auto-payment') ->click('#mandate-info button.btn-primary') ->with(new Dialog('@payment-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Update auto-payment') ->assertSeeIn( '@body form .disabled-mandate', 'The auto-payment is disabled.' ) ->assertValue('@body #mandate_amount', '100') ->assertValue('@body #mandate_balance', '0') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Submit') // Test error handling ->type('@body #mandate_amount', 'aaa') ->click('@button-action') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->assertVisible('@body #mandate_amount.is-invalid') ->assertSeeIn('#mandate_amount + span + .invalid-feedback', 'The amount must be a number.') // Submit valid data ->type('@body #mandate_amount', '50') ->click('@button-action'); }) ->waitUntilMissing('#payment-dialog') ->assertToast(Toast::TYPE_SUCCESS, 'The auto-payment has been updated.') // make sure the "disabled" text isn't there ->assertMissing('#mandate-info .disabled-mandate') ->click('#mandate-info button.btn-primary') ->assertMissing('form .disabled-mandate') ->click('button.modal-cancel'); }); // Test deleting auto-payment $this->browse(function (Browser $browser) { $browser->on(new WalletPage()) ->waitFor('#mandate-info') ->assertSeeIn('#mandate-info * button.btn-danger', 'Cancel auto-payment') ->assertVisible('#mandate-info * button.btn-danger') ->click('#mandate-info * button.btn-danger') ->assertToast(Toast::TYPE_SUCCESS, 'The auto-payment has been removed.') ->assertVisible('#mandate-form') ->assertMissing('#mandate-info'); }); } }