onboarded()->create(); $bank = Bank::factory()->create(['name' => 'Learn Bank']); $fuel = Category::factory()->create(['user_id' => $user->id, 'name' => 'Fuel']); $groceries = Category::factory()->create(['user_id' => $user->id, 'name' => 'Groceries']); $account = Account::factory()->create([ 'user_id' => $user->id, 'bank_id' => $bank->id, 'name' => 'Learn Account', 'currency_code' => 'EUR', 'type' => 'checking', ]); // A supermarket purchase the AI mislabeled as Fuel — the exact complaint. Transaction::factory()->plaintext()->create([ 'user_id' => $user->id, 'account_id' => $account->id, 'category_id' => $fuel->id, 'category_source' => CategorySource::Ai, 'ai_confidence' => 0.9, 'creditor_name' => 'Mercadona', 'description' => 'Mercadona compra', 'amount' => -4300, ]); actingAs($user); $page = visit('/transactions'); $page->assertSee('Transactions') ->waitForText('Mercadona compra', 10) ->click('[data-testid="row-category-select"]') ->wait(1) ->waitForText('Groceries', 5) ->click('Groceries') ->waitForText('similar transactions will be categorized automatically', 5) ->assertNoJavascriptErrors(); expect( AutomationRule::query() ->origin(RuleOrigin::Correction) ->where('action_category_id', $groceries->id) ->exists() )->toBeTrue(); // Undo removes the learned rule (the correction itself stays applied). $page->click('Undo')->wait(2); expect(AutomationRule::query()->origin(RuleOrigin::Correction)->exists())->toBeFalse(); });