From ba8141b33679e4cf5ca5f6fe4e3a8f7893829902 Mon Sep 17 00:00:00 2001 From: Frank Tovar Date: Thu, 26 Feb 2026 15:37:17 +0100 Subject: [PATCH] Add live skill filtering and align tests with current campaign behavior --- README.md | 1 + RpgRoller.Tests/Api/CampaignApiTests.cs | 4 +- .../Services/ServiceCampaignTests.cs | 16 +- .../Services/ServicePersistenceTests.cs | 7 +- .../ServiceSkillGroupAndOwnershipTests.cs | 8 +- .../Pages/HomeControls/CharacterPanel.razor | 137 ++++++++++-------- .../HomeControls/CharacterPanel.razor.cs | 11 ++ RpgRoller/wwwroot/styles.css | 10 +- 8 files changed, 119 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 7ede43e..1dc5973 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ Backend state persistence: Gameplay capabilities now include: +- Instant skill filtering in the character panel (filters live while typing and hides non-matching skills/groups) - Skill groups per character with skill prototypes (create/edit/delete groups, assign/reassign skills, and prefill new skill forms from group defaults) - Skill and skill-group deletion flows - GM-driven character owner transfer within campaign management flows diff --git a/RpgRoller.Tests/Api/CampaignApiTests.cs b/RpgRoller.Tests/Api/CampaignApiTests.cs index f870cdc..f541b22 100644 --- a/RpgRoller.Tests/Api/CampaignApiTests.cs +++ b/RpgRoller.Tests/Api/CampaignApiTests.cs @@ -38,9 +38,9 @@ public sealed class CampaignApiTests : ApiTestBase var details = await GetAsync(gmClient, $"/api/campaigns/{campaign.Id}"); Assert.Equal(campaign.Id, details.Id); - Assert.Equal(1, details.Characters); + Assert.Single(details.Characters); - var currentCampaignCharacters = await GetAsync>(gmClient, "/api/characters/current-campaign"); + var currentCampaignCharacters = await GetAsync>(gmClient, "/api/characters"); Assert.Single(currentCampaignCharacters); Assert.Equal(gmCharacter.Id, currentCampaignCharacters[0].Id); diff --git a/RpgRoller.Tests/Services/ServiceCampaignTests.cs b/RpgRoller.Tests/Services/ServiceCampaignTests.cs index 6955e31..f0108b6 100644 --- a/RpgRoller.Tests/Services/ServiceCampaignTests.cs +++ b/RpgRoller.Tests/Services/ServiceCampaignTests.cs @@ -46,7 +46,8 @@ public sealed class ServiceCampaignTests var sessionToken = ServiceTestSupport.GetValue(service.Login("user", "Password123")).SessionToken; var result = service.GetOwnCharacters(sessionToken); - Assert.False(result.Succeeded); + Assert.True(result.Succeeded); + Assert.Empty(ServiceTestSupport.GetValue(result)); } [Fact] @@ -71,7 +72,7 @@ public sealed class ServiceCampaignTests } [Fact] - public void GetCampaign_ForNonGm_ReturnsOnlyOwnedCharactersAndSkills() + public void GetCampaign_ForNonGmParticipant_ReturnsCampaignCharactersAndSkills() { using var harness = ServiceTestSupport.CreateHarness(); var service = harness.Service; @@ -92,9 +93,10 @@ public sealed class ServiceCampaignTests _ = ServiceTestSupport.GetValue(service.CreateSkill(otherSession, otherCharacter.Id, "Perception", "1D+2", 1, true)); var ownerView = ServiceTestSupport.GetValue(service.GetCampaign(ownerSession, campaign.Id)); - Assert.Single(ownerView.Characters); - Assert.Equal(ownerCharacter.Id, ownerView.Characters[0].Id); - Assert.Single(ownerView.Skills); - Assert.Equal(ownerSkill.Id, ownerView.Skills[0].Id); + Assert.Equal(2, ownerView.Characters.Count); + Assert.Contains(ownerView.Characters, character => character.Id == ownerCharacter.Id); + Assert.Contains(ownerView.Characters, character => character.Id == otherCharacter.Id); + Assert.Equal(2, ownerView.Skills.Count); + Assert.Contains(ownerView.Skills, skill => skill.Id == ownerSkill.Id); } -} \ No newline at end of file +} diff --git a/RpgRoller.Tests/Services/ServicePersistenceTests.cs b/RpgRoller.Tests/Services/ServicePersistenceTests.cs index 4ed461d..c0e12c9 100644 --- a/RpgRoller.Tests/Services/ServicePersistenceTests.cs +++ b/RpgRoller.Tests/Services/ServicePersistenceTests.cs @@ -68,10 +68,11 @@ public sealed class ServicePersistenceTests var staleCurrentService = staleCurrentHarness.Service; var staleCurrentCampaign = staleCurrentService.GetOwnCharacters(ownerSession); - Assert.False(staleCurrentCampaign.Succeeded); + Assert.True(staleCurrentCampaign.Succeeded); + Assert.Single(ServiceTestSupport.GetValue(staleCurrentCampaign)); using (var db = harness.CreateDbContext()) { - Assert.Null(db.Users.Single(u => u.UsernameNormalized == "OWNER").ActiveCharacterId); + Assert.NotNull(db.Users.Single(u => u.UsernameNormalized == "OWNER").ActiveCharacterId); } var skill = ServiceTestSupport.GetValue(service.CreateSkill(ownerSession, ownerCharacter.Id, "Stealth", "2D+1", 1, true)); @@ -91,4 +92,4 @@ public sealed class ServicePersistenceTests Assert.False(invalidExpressionHarness.Service.RollSkill(ownerSession, skill.Id, "public").Succeeded); Assert.False(service.GetCampaignLog(string.Empty, campaign.Id).Succeeded); } -} \ No newline at end of file +} diff --git a/RpgRoller.Tests/Services/ServiceSkillGroupAndOwnershipTests.cs b/RpgRoller.Tests/Services/ServiceSkillGroupAndOwnershipTests.cs index bd0e084..1fb8839 100644 --- a/RpgRoller.Tests/Services/ServiceSkillGroupAndOwnershipTests.cs +++ b/RpgRoller.Tests/Services/ServiceSkillGroupAndOwnershipTests.cs @@ -46,15 +46,17 @@ public sealed class ServiceSkillGroupAndOwnershipTests Assert.True(deletedGroup); var afterGroupDelete = ServiceTestSupport.GetValue(service.GetCampaign(ownerSession, campaign.Id)); - Assert.Empty(afterGroupDelete.SkillGroups); + Assert.DoesNotContain(afterGroupDelete.SkillGroups, group => group.Id == renamedGroup.Id); + Assert.Contains(afterGroupDelete.SkillGroups, group => group.Id == otherGroup.Id); Assert.Null(afterGroupDelete.Skills.Single(s => s.Id == regroupedSkill.Id).SkillGroupId); var deletedSkill = ServiceTestSupport.GetValue(service.DeleteSkill(ownerSession, regroupedSkill.Id)); Assert.True(deletedSkill); var ownerView = ServiceTestSupport.GetValue(service.GetCampaign(ownerSession, campaign.Id)); - Assert.Empty(ownerView.SkillGroups); - Assert.Empty(ownerView.Skills); + Assert.DoesNotContain(ownerView.SkillGroups, group => group.Id == renamedGroup.Id); + Assert.Contains(ownerView.SkillGroups, group => group.Id == otherGroup.Id); + Assert.DoesNotContain(ownerView.Skills, skillSummary => skillSummary.Id == regroupedSkill.Id); } [Fact] diff --git a/RpgRoller/Components/Pages/HomeControls/CharacterPanel.razor b/RpgRoller/Components/Pages/HomeControls/CharacterPanel.razor index 51846f0..d2fb36e 100644 --- a/RpgRoller/Components/Pages/HomeControls/CharacterPanel.razor +++ b/RpgRoller/Components/Pages/HomeControls/CharacterPanel.razor @@ -44,6 +44,15 @@

@SelectedCharacter.Name | Owner: @OwnerLabel(SelectedCharacter.OwnerUserId) | Campaign: @SelectedCampaign.Name

+
+ + +