Snapshot of main at 9f39a6e1e (2026-11-02)

This commit is contained in:
Ronald Huynen committed 2026-11-02 10:41:47 +01:00
commit 6ccf1792d7
2580 files changed
+1040202

No files matched your search

@@ -0,0 +1,315 @@
<?php
namespace Tests\Feature;
use App\Http\Livewire\Pay;
use App\Models\Account;
use App\Models\Transaction;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Namu\WireChat\Enums\ConversationType;
use Namu\WireChat\Enums\MessageType;
use Namu\WireChat\Models\Conversation;
use Namu\WireChat\Models\Message;
use Tests\TestCase;
/**
* Tests the "new recipient" confirmation modal shown in the Pay view when the
* active profile has never had a chat conversation or payment with the
* selected to-account holder.
*
* @group payment
*/
class PayNewRecipientConfirmationTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
\DB::table('transaction_types')->insertOrIgnore([
['id' => 1, 'name' => 'worked_hours', 'label' => 'Worked Hours', 'icon' => 'clock'],
['id' => 6, 'name' => 'migration', 'label' => 'Migration', 'icon' => 'arrow-path'],
]);
}
private function actingAsUserWithAccount(): array
{
$user = User::factory()->create();
$account = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $user->id,
'limit_min' => -1000,
'limit_max' => 1000,
]);
$this->actingAs($user, 'web');
session(['activeProfileType' => User::class]);
session(['activeProfileId' => $user->id]);
return [$user, $account];
}
private function toAccountDetails(User $recipient, Account $recipientAccount): array
{
return [
'accountId' => $recipientAccount->id,
'accountName' => $recipientAccount->name,
'holderId' => $recipient->id,
'holderType' => User::class,
'holderName' => $recipient->name,
'holderLocation' => 'Testville',
'holderPhoto' => $recipient->profile_photo_path,
];
}
/** @test */
public function selecting_a_recipient_with_no_history_opens_the_confirmation_modal()
{
[$user, ] = $this->actingAsUserWithAccount();
$recipient = User::factory()->create();
$recipientAccount = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $recipient->id,
'limit_min' => -1000,
'limit_max' => 1000,
]);
$response = Livewire::test(Pay::class)
->dispatch('toAccountDetails', $this->toAccountDetails($recipient, $recipientAccount));
$response->assertSet('isNewRecipientModalOpen', true);
$response->assertSet('toAccountId', null);
}
/** @test */
public function selecting_a_recipient_with_a_prior_transaction_skips_the_modal()
{
[$user, $userAccount] = $this->actingAsUserWithAccount();
$recipient = User::factory()->create();
$recipientAccount = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $recipient->id,
'limit_min' => -1000,
'limit_max' => 1000,
]);
Transaction::factory()->create([
'from_account_id' => $userAccount->id,
'to_account_id' => $recipientAccount->id,
'transaction_type_id' => 1,
]);
$response = Livewire::test(Pay::class)
->dispatch('toAccountDetails', $this->toAccountDetails($recipient, $recipientAccount));
$response->assertSet('isNewRecipientModalOpen', false);
$response->assertSet('toAccountId', $recipientAccount->id);
}
/** @test */
public function selecting_a_recipient_with_a_prior_transaction_in_reverse_direction_skips_the_modal()
{
[$user, $userAccount] = $this->actingAsUserWithAccount();
$recipient = User::factory()->create();
$recipientAccount = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $recipient->id,
'limit_min' => -1000,
'limit_max' => 1000,
]);
Transaction::factory()->create([
'from_account_id' => $recipientAccount->id,
'to_account_id' => $userAccount->id,
'transaction_type_id' => 1,
]);
$response = Livewire::test(Pay::class)
->dispatch('toAccountDetails', $this->toAccountDetails($recipient, $recipientAccount));
$response->assertSet('isNewRecipientModalOpen', false);
$response->assertSet('toAccountId', $recipientAccount->id);
}
private function createPrivateConversation(User $user, User $recipient): Conversation
{
$conversation = new Conversation();
$conversation->type = ConversationType::PRIVATE;
$conversation->save();
$conversation->participants()->create([
'participantable_type' => User::class,
'participantable_id' => $user->id,
'role' => \Namu\WireChat\Enums\ParticipantRole::PARTICIPANT,
]);
$conversation->participants()->create([
'participantable_type' => User::class,
'participantable_id' => $recipient->id,
'role' => \Namu\WireChat\Enums\ParticipantRole::PARTICIPANT,
]);
return $conversation;
}
/** @test */
public function selecting_a_recipient_who_only_sent_messages_without_a_reply_still_shows_the_modal()
{
[$user, ] = $this->actingAsUserWithAccount();
$recipient = User::factory()->create();
$recipientAccount = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $recipient->id,
'limit_min' => -1000,
'limit_max' => 1000,
]);
$conversation = $this->createPrivateConversation($user, $recipient);
// Recipient messaged the active profile, but the active profile never replied.
Message::create([
'body' => 'Hello there',
'sendable_type' => User::class,
'sendable_id' => $recipient->id,
'conversation_id' => $conversation->id,
'type' => MessageType::TEXT,
]);
$response = Livewire::test(Pay::class)
->dispatch('toAccountDetails', $this->toAccountDetails($recipient, $recipientAccount));
$response->assertSet('isNewRecipientModalOpen', true);
$response->assertSet('toAccountId', null);
}
/** @test */
public function selecting_a_recipient_the_active_profile_previously_messaged_skips_the_modal()
{
[$user, ] = $this->actingAsUserWithAccount();
$recipient = User::factory()->create();
$recipientAccount = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $recipient->id,
'limit_min' => -1000,
'limit_max' => 1000,
]);
$conversation = $this->createPrivateConversation($user, $recipient);
Message::create([
'body' => 'Hi, thanks for reaching out',
'sendable_type' => User::class,
'sendable_id' => $user->id,
'conversation_id' => $conversation->id,
'type' => MessageType::TEXT,
]);
$response = Livewire::test(Pay::class)
->dispatch('toAccountDetails', $this->toAccountDetails($recipient, $recipientAccount));
$response->assertSet('isNewRecipientModalOpen', false);
$response->assertSet('toAccountId', $recipientAccount->id);
}
/** @test */
public function internal_transfer_to_own_profile_skips_the_modal()
{
[$user, $userAccount] = $this->actingAsUserWithAccount();
$secondOwnAccount = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $user->id,
'limit_min' => -1000,
'limit_max' => 1000,
]);
$details = [
'accountId' => $secondOwnAccount->id,
'accountName' => $secondOwnAccount->name,
'holderId' => $user->id,
'holderType' => User::class,
'holderName' => $user->name,
'holderLocation' => 'Testville',
'holderPhoto' => $user->profile_photo_path,
];
$response = Livewire::test(Pay::class)
->dispatch('toAccountDetails', $details);
$response->assertSet('isNewRecipientModalOpen', false);
$response->assertSet('toAccountId', $secondOwnAccount->id);
}
/** @test */
public function confirming_new_recipient_applies_the_pending_selection()
{
[$user, ] = $this->actingAsUserWithAccount();
$recipient = User::factory()->create();
$recipientAccount = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $recipient->id,
'limit_min' => -1000,
'limit_max' => 1000,
]);
$response = Livewire::test(Pay::class)
->dispatch('toAccountDetails', $this->toAccountDetails($recipient, $recipientAccount))
->call('confirmNewRecipient');
$response->assertSet('isNewRecipientModalOpen', false);
$response->assertSet('toAccountId', $recipientAccount->id);
$response->assertSet('toHolderName', $recipient->name);
}
/** @test */
public function cancelling_new_recipient_clears_the_pending_selection()
{
[$user, ] = $this->actingAsUserWithAccount();
$recipient = User::factory()->create();
$recipientAccount = Account::factory()->create([
'accountable_type' => User::class,
'accountable_id' => $recipient->id,
'limit_min' => -1000,
'limit_max' => 1000,
]);
$response = Livewire::test(Pay::class)
->dispatch('toAccountDetails', $this->toAccountDetails($recipient, $recipientAccount))
->call('cancelNewRecipient');
$response->assertSet('isNewRecipientModalOpen', false);
$response->assertSet('toAccountId', null);
$response->assertSet('pendingToAccountDetails', null);
$response->assertDispatched('focus-to-account-search');
$response->assertDispatchedTo(\App\Http\Livewire\ToAccount::class, 'resetForm');
}
/** @test */
public function unrecognised_holder_type_is_treated_as_unfamiliar_without_error()
{
[$user, ] = $this->actingAsUserWithAccount();
$details = [
'accountId' => 999999,
'accountName' => 'Fake Account',
'holderId' => 1,
'holderType' => 'App\\Models\\NotARealModel',
'holderName' => 'Fake Holder',
'holderLocation' => 'Nowhere',
'holderPhoto' => null,
];
$response = Livewire::test(Pay::class)
->dispatch('toAccountDetails', $details);
$response->assertSet('isNewRecipientModalOpen', true);
}
}