whisper-money/tests/Browser/AccountsPageTest.php

281 lines
8.4 KiB
PHP

<?php
use App\Enums\AccountType;
use App\Models\Account;
use App\Models\AccountBalance;
use App\Models\Bank;
use App\Models\LoanDetail;
use App\Models\RealEstateDetail;
use App\Models\User;
use function Pest\Laravel\actingAs;
it('can view accounts page', function () {
$user = User::factory()->onboarded()->create();
actingAs($user);
$page = visit('/accounts');
$page->assertSee('Accounts')
->assertSee('View and manage your bank accounts')
->assertNoJavascriptErrors();
});
it('shows empty state when no accounts exist', function () {
$user = User::factory()->onboarded()->create();
actingAs($user);
$page = visit('/accounts');
$page->assertSee('Accounts')
->waitForText('No accounts found')
->assertNoJavascriptErrors();
});
it('shows account cards for existing accounts', function () {
$user = User::factory()->onboarded()->create();
$bank = Bank::factory()->create(['name' => 'Test Bank', 'logo' => null]);
Account::factory()->create([
'user_id' => $user->id,
'bank_id' => $bank->id,
'name' => 'My Checking',
'type' => AccountType::Checking,
'currency_code' => 'USD',
]);
actingAs($user);
$page = visit('/accounts');
$page->navigate('/accounts', ['waitUntil' => 'domcontentloaded'])->wait(2);
$page->assertSee('Accounts')
->waitForText('My Checking')
->assertSee('Test Bank')
->assertNoJavascriptErrors();
});
it('shows multiple accounts grouped by type', function () {
$user = User::factory()->onboarded()->create();
$bank = Bank::factory()->create(['name' => 'Bank One', 'logo' => null]);
Account::factory()->create([
'user_id' => $user->id,
'bank_id' => $bank->id,
'name' => 'Daily Checking',
'type' => AccountType::Checking,
'currency_code' => 'USD',
]);
Account::factory()->create([
'user_id' => $user->id,
'bank_id' => $bank->id,
'name' => 'Rainy Day Savings',
'type' => AccountType::Savings,
'currency_code' => 'USD',
]);
actingAs($user);
$page = visit('/accounts');
$page->navigate('/accounts', ['waitUntil' => 'domcontentloaded'])->wait(2);
$page->assertSee('Accounts')
->waitForText('Daily Checking')
->assertSee('Rainy Day Savings')
->assertNoJavascriptErrors();
});
it('can navigate to account details page', function () {
$user = User::factory()->onboarded()->create();
$bank = Bank::factory()->create(['name' => 'Nav Bank', 'logo' => null]);
Account::factory()->create([
'user_id' => $user->id,
'bank_id' => $bank->id,
'name' => 'Navigable Account',
'type' => AccountType::Checking,
'currency_code' => 'USD',
]);
actingAs($user);
$page = visit('/accounts');
$page->navigate('/accounts', ['waitUntil' => 'domcontentloaded'])->wait(2);
$page->waitForText('Navigable Account')
->click('Details →')
->wait(2)
->assertSee('Update balance')
->assertSee('Nav Bank')
->assertNoJavascriptErrors();
});
it('can update and view balance history from account details page', function () {
$user = User::factory()->onboarded()->create();
$user->update(['currency_code' => 'EUR']);
$bank = Bank::factory()->create(['name' => 'Balance Bank', 'logo' => null]);
$account = Account::factory()->create([
'user_id' => $user->id,
'bank_id' => $bank->id,
'name' => 'Tracked Checking',
'type' => AccountType::Checking,
'currency_code' => 'EUR',
]);
AccountBalance::factory()->create([
'account_id' => $account->id,
'balance_date' => '2024-01-01',
'balance' => 10000000,
]);
actingAs($user);
$page = visit('/accounts');
$page->navigate('/accounts', ['waitUntil' => 'domcontentloaded'])->wait(2);
$page->waitForText('Tracked Checking')
->click('Details →')
->wait(2)
->assertSee('Import balances')
->click('Update balance')
->wait(1)
->fill('#balance-amount', '120000.00')
->click('#balance-date')
->fill('#balance-date', '2024-02-01')
->click('button[type="submit"]:has-text("Save")')
->wait(2)
->click('button[aria-label="More options"]')
->wait(0.5)
->click('See balances')
->wait(2)
->assertSee('Balance History')
->assertSee('Feb 1, 2024')
->assertSee('Jan 1, 2024')
->assertNoJavascriptErrors();
$this->assertDatabaseHas('account_balances', [
'account_id' => $account->id,
'balance_date' => '2024-02-01',
'balance' => 12000000,
]);
});
it('can link and unlink a loan from the real estate details page', function () {
$user = User::factory()->onboarded()->create();
$bank = Bank::factory()->create(['name' => 'Property Bank', 'logo' => null]);
$loanBank = Bank::factory()->create(['name' => 'Mortgage Bank', 'logo' => null]);
$property = Account::factory()->create([
'user_id' => $user->id,
'bank_id' => null,
'name' => 'Beach Condo',
'type' => AccountType::RealEstate,
'currency_code' => 'EUR',
]);
AccountBalance::factory()->create([
'account_id' => $property->id,
'balance_date' => '2024-01-01',
'balance' => 30000000,
]);
RealEstateDetail::factory()->create([
'account_id' => $property->id,
'property_type' => 'residential',
'address' => 'Ocean Avenue 1',
'linked_loan_account_id' => null,
]);
$loan = Account::factory()->create([
'user_id' => $user->id,
'bank_id' => $loanBank->id,
'name' => 'Beach Mortgage',
'type' => AccountType::Loan,
'currency_code' => 'EUR',
]);
LoanDetail::factory()->create([
'account_id' => $loan->id,
'annual_interest_rate' => 3.1,
'loan_term_months' => 300,
'original_amount' => 22000000,
'start_date' => '2024-01-01',
]);
AccountBalance::factory()->create([
'account_id' => $loan->id,
'balance_date' => '2024-01-01',
'balance' => 20000000,
]);
actingAs($user);
$page = visit('/accounts');
$page->navigate('/accounts', ['waitUntil' => 'domcontentloaded'])->wait(2);
$page->waitForText('Beach Condo')
->click('Details →')
->wait(2)
->assertSee('Property Details')
->assertDontSee('Linked Mortgage / Loan')
->click('Edit')
->wait(1)
->assertSee('Edit Property Details')
->click('xpath=//label[contains(., "Linked Mortgage / Loan")]/following::button[@role="combobox"][1]')
->wait(0.5)
->click('[role="option"]:has-text("Beach Mortgage")')
->wait(0.5)
->click('button[type="submit"]:has-text("Save")')
->wait(2)
->assertSee('Linked Mortgage / Loan')
->assertSee('Beach Mortgage (Mortgage Bank)')
->click('Edit')
->wait(1)
->click('xpath=//label[contains(., "Linked Mortgage / Loan")]/following::button[@role="combobox"][1]')
->wait(0.5)
->click('[role="option"]:has-text("No linked loan")')
->wait(0.5)
->click('button[type="submit"]:has-text("Save")')
->wait(2)
->assertDontSee('Beach Mortgage (Mortgage Bank)')
->assertNoJavascriptErrors();
$detail = RealEstateDetail::query()->where('account_id', $property->id)->first();
expect($detail)->not->toBeNull();
expect($detail->fresh()->linked_loan_account_id)->toBeNull();
});
it('does not show other users accounts', function () {
$user = User::factory()->onboarded()->create();
$otherUser = User::factory()->onboarded()->create();
$bank = Bank::factory()->create(['name' => 'Shared Bank', 'logo' => null]);
Account::factory()->create([
'user_id' => $user->id,
'bank_id' => $bank->id,
'name' => 'My Own Account',
'type' => AccountType::Checking,
'currency_code' => 'USD',
]);
Account::factory()->create([
'user_id' => $otherUser->id,
'bank_id' => $bank->id,
'name' => 'Someone Elses Account',
'type' => AccountType::Checking,
'currency_code' => 'USD',
]);
actingAs($user);
$page = visit('/accounts');
$page->navigate('/accounts', ['waitUntil' => 'domcontentloaded'])->wait(2);
$page->waitForText('My Own Account')
->assertDontSee('Someone Elses Account')
->assertNoJavascriptErrors();
});