diff --git a/src/tests/Browser/Admin/UserFinancesTest.php b/src/tests/Browser/Admin/UserFinancesTest.php index 44ac156b..0ff4bd73 100644 --- a/src/tests/Browser/Admin/UserFinancesTest.php +++ b/src/tests/Browser/Admin/UserFinancesTest.php @@ -1,322 +1,325 @@ getTestUser('john@kolab.org'); $wallet = $john->wallets()->first(); $wallet->discount()->dissociate(); $wallet->balance = 0; $wallet->save(); $wallet->setSettings(['mollie_id' => null, 'stripe_id' => null]); } /** * Test Finances tab (and transactions) */ public function testFinances(): void { // Assert Jack's Finances tab $this->browse(function (Browser $browser) { $jack = $this->getTestUser('jack@kolab.org'); $wallet = $jack->wallets()->first(); $wallet->transactions()->delete(); $wallet->setSetting('stripe_id', 'abc'); $page = new UserPage($jack->id); $browser->visit(new Home()) ->submitLogon('jeroen@jeroen.jeroen', 'jeroen', true) ->on(new Dashboard()) ->visit($page) ->on($page) ->assertSeeIn('@nav #tab-finances', 'Finances') ->with('@user-finances', function (Browser $browser) { $browser->waitUntilMissing('.app-loader') ->assertSeeIn('.card-title:first-child', 'Account balance') ->assertSeeIn('.card-title:first-child .text-success', '0,00 CHF') ->with('form', function (Browser $browser) { $browser->assertElementsCount('.row', 2) ->assertSeeIn('.row:nth-child(1) label', 'Discount') ->assertSeeIn('.row:nth-child(1) #discount span', 'none') ->assertSeeIn('.row:nth-child(2) label', 'Stripe ID') ->assertSeeIn('.row:nth-child(2) a', 'abc'); }) ->assertSeeIn('h2:nth-of-type(2)', 'Transactions') ->with('table', function (Browser $browser) { $browser->assertMissing('tbody') ->assertSeeIn('tfoot td', "There are no transactions for this account."); }) ->assertMissing('table + button'); }); }); // Assert John's Finances tab (with discount, and debit) $this->browse(function (Browser $browser) { $john = $this->getTestUser('john@kolab.org'); $page = new UserPage($john->id); $discount = Discount::where('code', 'TEST')->first(); $wallet = $john->wallet(); $wallet->transactions()->delete(); $wallet->discount()->associate($discount); $wallet->debit(2010); $wallet->save(); // Create test transactions $transaction = Transaction::create([ 'user_email' => 'jeroen@jeroen.jeroen', 'object_id' => $wallet->id, 'object_type' => Wallet::class, 'type' => Transaction::WALLET_CREDIT, 'amount' => 100, 'description' => 'Payment', ]); $transaction->created_at = Carbon::now()->subMonth(); $transaction->save(); // Click the managed-by link on Jack's page $browser->click('@user-info #manager a') ->on($page) ->with('@user-finances', function (Browser $browser) { $browser->waitUntilMissing('.app-loader') ->assertSeeIn('.card-title:first-child', 'Account balance') ->assertSeeIn('.card-title:first-child .text-danger', '-20,10 CHF') ->with('form', function (Browser $browser) { $browser->assertElementsCount('.row', 1) ->assertSeeIn('.row:nth-child(1) label', 'Discount') ->assertSeeIn('.row:nth-child(1) #discount span', '10% - Test voucher'); }) ->assertSeeIn('h2:nth-of-type(2)', 'Transactions') ->with('table', function (Browser $browser) { $browser->assertElementsCount('tbody tr', 2) ->assertMissing('tfoot'); if (!$browser->isPhone()) { $browser->assertSeeIn('tbody tr:last-child td.email', 'jeroen@jeroen.jeroen'); } }); }); }); // Now we go to Ned's info page, he's a controller on John's wallet $this->browse(function (Browser $browser) { $ned = $this->getTestUser('ned@kolab.org'); + $wallet = $ned->wallets()->first(); + $wallet->balance = 0; + $wallet->save(); $page = new UserPage($ned->id); $browser->click('@nav #tab-users') ->click('@user-users tbody tr:nth-child(4) td:first-child a') ->on($page) ->with('@user-finances', function (Browser $browser) { $browser->waitUntilMissing('.app-loader') ->assertSeeIn('.card-title:first-child', 'Account balance') ->assertSeeIn('.card-title:first-child .text-success', '0,00 CHF') ->with('form', function (Browser $browser) { $browser->assertElementsCount('.row', 1) ->assertSeeIn('.row:nth-child(1) label', 'Discount') ->assertSeeIn('.row:nth-child(1) #discount span', 'none'); }) ->assertSeeIn('h2:nth-of-type(2)', 'Transactions') ->with('table', function (Browser $browser) { $browser->assertMissing('tbody') ->assertSeeIn('tfoot td', "There are no transactions for this account."); }) ->assertMissing('table + button'); }); }); } /** * Test editing wallet discount * * @depends testFinances */ public function testWalletDiscount(): void { $this->browse(function (Browser $browser) { $john = $this->getTestUser('john@kolab.org'); $browser->visit(new UserPage($john->id)) ->pause(100) ->waitUntilMissing('@user-finances .app-loader') ->click('@user-finances #discount button') // Test dialog content, and closing it with Cancel button ->with(new Dialog('#discount-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Account discount') ->assertFocused('@body select') ->assertSelected('@body select', '') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Submit') ->click('@button-cancel'); }) ->assertMissing('#discount-dialog') ->click('@user-finances #discount button') // Change the discount ->with(new Dialog('#discount-dialog'), function (Browser $browser) { $browser->click('@body select') ->click('@body select option:nth-child(2)') ->click('@button-action'); }) ->assertToast(Toast::TYPE_SUCCESS, 'User wallet updated successfully.') ->assertSeeIn('#discount span', '10% - Test voucher') ->click('@nav #tab-subscriptions') ->with('@user-subscriptions', function (Browser $browser) { $browser->assertSeeIn('table tbody tr:nth-child(1) td:last-child', '3,99 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', '0,00 CHF/month¹') ->assertSeeIn('table tbody tr:nth-child(3) td:last-child', '4,99 CHF/month¹') ->assertSeeIn('table + .hint', '¹ applied discount: 10% - Test voucher'); }) // Change back to 'none' ->click('@nav #tab-finances') ->click('@user-finances #discount button') ->with(new Dialog('#discount-dialog'), function (Browser $browser) { $browser->click('@body select') ->click('@body select option:nth-child(1)') ->click('@button-action'); }) ->assertToast(Toast::TYPE_SUCCESS, 'User wallet updated successfully.') ->assertSeeIn('#discount span', 'none') ->click('@nav #tab-subscriptions') ->with('@user-subscriptions', function (Browser $browser) { $browser->assertSeeIn('table tbody tr:nth-child(1) td:last-child', '4,44 CHF/month') ->assertSeeIn('table tbody tr:nth-child(2) td:last-child', '0,00 CHF/month') ->assertSeeIn('table tbody tr:nth-child(3) td:last-child', '5,55 CHF/month') ->assertMissing('table + .hint'); }); }); } /** * Test awarding/penalizing a wallet * * @depends testFinances */ public function testBonusPenalty(): void { $this->browse(function (Browser $browser) { $john = $this->getTestUser('john@kolab.org'); $browser->visit(new UserPage($john->id)) ->waitFor('@user-finances #button-award') ->click('@user-finances #button-award') // Test dialog content, and closing it with Cancel button ->with(new Dialog('#oneoff-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Add a bonus to the wallet') ->assertFocused('@body input#oneoff_amount') ->assertSeeIn('@body label[for="oneoff_amount"]', 'Amount') ->assertvalue('@body input#oneoff_amount', '') ->assertSeeIn('@body label[for="oneoff_description"]', 'Description') ->assertvalue('@body input#oneoff_description', '') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Submit') ->click('@button-cancel'); }) ->assertMissing('#oneoff-dialog'); // Test bonus $browser->click('@user-finances #button-award') ->with(new Dialog('#oneoff-dialog'), function (Browser $browser) { // Test input validation for a bonus $browser->type('@body #oneoff_amount', 'aaa') ->type('@body #oneoff_description', '') ->click('@button-action') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->assertVisible('@body #oneoff_amount.is-invalid') ->assertVisible('@body #oneoff_description.is-invalid') ->assertSeeIn( '@body #oneoff_amount + span + .invalid-feedback', 'The amount must be a number.' ) ->assertSeeIn( '@body #oneoff_description + .invalid-feedback', 'The description field is required.' ); // Test adding a bonus $browser->type('@body #oneoff_amount', '12.34') ->type('@body #oneoff_description', 'Test bonus') ->click('@button-action') ->assertToast(Toast::TYPE_SUCCESS, 'The bonus has been added to the wallet successfully.'); }) ->assertMissing('#oneoff-dialog') ->assertSeeIn('@user-finances .card-title span.text-success', '12,34 CHF') ->waitUntilMissing('.app-loader') ->with('table', function (Browser $browser) { $browser->assertElementsCount('tbody tr', 3) ->assertMissing('tfoot') ->assertSeeIn('tbody tr:first-child td.description', 'Bonus: Test bonus') ->assertSeeIn('tbody tr:first-child td.price', '12,34 CHF'); if (!$browser->isPhone()) { $browser->assertSeeIn('tbody tr:first-child td.email', 'jeroen@jeroen.jeroen'); } }); $this->assertSame(1234, $john->wallets()->first()->balance); // Test penalty $browser->click('@user-finances #button-penalty') // Test dialog content, and closing it with Cancel button ->with(new Dialog('#oneoff-dialog'), function (Browser $browser) { $browser->assertSeeIn('@title', 'Add a penalty to the wallet') ->assertFocused('@body input#oneoff_amount') ->assertSeeIn('@body label[for="oneoff_amount"]', 'Amount') ->assertvalue('@body input#oneoff_amount', '') ->assertSeeIn('@body label[for="oneoff_description"]', 'Description') ->assertvalue('@body input#oneoff_description', '') ->assertSeeIn('@button-cancel', 'Cancel') ->assertSeeIn('@button-action', 'Submit') ->click('@button-cancel'); }) ->assertMissing('#oneoff-dialog') ->click('@user-finances #button-penalty') ->with(new Dialog('#oneoff-dialog'), function (Browser $browser) { // Test input validation for a penalty $browser->type('@body #oneoff_amount', '') ->type('@body #oneoff_description', '') ->click('@button-action') ->assertToast(Toast::TYPE_ERROR, 'Form validation error') ->assertVisible('@body #oneoff_amount.is-invalid') ->assertVisible('@body #oneoff_description.is-invalid') ->assertSeeIn( '@body #oneoff_amount + span + .invalid-feedback', 'The amount field is required.' ) ->assertSeeIn( '@body #oneoff_description + .invalid-feedback', 'The description field is required.' ); // Test adding a penalty $browser->type('@body #oneoff_amount', '12.35') ->type('@body #oneoff_description', 'Test penalty') ->click('@button-action') ->assertToast(Toast::TYPE_SUCCESS, 'The penalty has been added to the wallet successfully.'); }) ->assertMissing('#oneoff-dialog') ->assertSeeIn('@user-finances .card-title span.text-danger', '-0,01 CHF'); $this->assertSame(-1, $john->wallets()->first()->balance); }); } }