Snapshot of main at 9f39a6e1e (2026-11-02)
This commit is contained in:
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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user