feat(ui): external services tab with Azure DevOps PAT, draft PR button and link in company repos

This commit is contained in:
mika kuns
2026-10-01 12:13:57 +02:00
parent fed0b6ac51
commit 4976ded7ae
9 changed files with 247 additions and 15 deletions
@@ -0,0 +1,46 @@
using ClaudeDo.Ui.ViewModels.Islands;
using Xunit;
namespace ClaudeDo.Ui.Tests.ViewModels;
/// <summary>Detail pane in a company repo: approve becomes "create draft PR" / "update PR", and the PR link only exists once there is a PR.</summary>
public sealed class MergeSectionCompanyRepoTests
{
private sealed class Worker : StubWorkerClient { }
private static MergeSectionViewModel Vm() => new(new Worker(), services: null!);
[Fact]
public void PrivateRepo_ShowsOnlyApproveAndMerge()
{
var vm = Vm();
Assert.True(vm.ShowApproveMerge);
Assert.False(vm.ShowCreatePr);
Assert.False(vm.ShowUpdatePr);
Assert.False(vm.OpenPullRequestCommand.CanExecute(null));
}
[Fact]
public void CompanyRepo_WithoutPr_OffersCreateDraftPr()
{
var vm = Vm();
vm.IsCompanyRepo = true;
Assert.False(vm.ShowApproveMerge);
Assert.True(vm.ShowCreatePr);
Assert.False(vm.ShowUpdatePr);
}
[Fact]
public void CompanyRepo_WithPr_OffersUpdateAndLink()
{
var vm = Vm();
vm.IsCompanyRepo = true;
vm.PullRequestUrl = "https://srv/Coll/Proj/_git/Repo/pullrequest/5";
Assert.False(vm.ShowCreatePr);
Assert.True(vm.ShowUpdatePr);
Assert.True(vm.OpenPullRequestCommand.CanExecute(null));
}
}
@@ -34,6 +34,44 @@ public sealed class TicketSettingsTabViewModelTests : IDisposable
public List<string?> SavedFallbacks { get; } = new();
public override Task<TicketSettingsDto?> GetTicketSettingsAsync() => Task.FromResult(Settings);
public override Task SetTicketFallbackListAsync(string? listId) { SavedFallbacks.Add(listId); return Task.CompletedTask; }
public List<string> SavedAzdoTokens { get; } = new();
public AzureDevOpsConnectionDto? AzdoConnection { get; set; }
public override Task<AzureDevOpsSettingsDto?> GetAzureDevOpsSettingsAsync() => Task.FromResult<AzureDevOpsSettingsDto?>(new(false));
public override Task SetAzureDevOpsTokenAsync(string token) { SavedAzdoTokens.Add(token); return Task.CompletedTask; }
public override Task<AzureDevOpsConnectionDto?> TestAzureDevOpsConnectionAsync() => Task.FromResult(AzdoConnection);
}
[Fact]
public async Task Save_writes_a_typed_azdo_token_once_and_never_keeps_it_in_the_field()
{
var worker = new Worker { Settings = new TicketSettingsDto("http://api", true, null) };
var vm = new TicketSettingsTabViewModel(worker, new TestDbFactory(NewContext));
await vm.LoadAsync();
Assert.False(vm.AzdoTokenSet);
vm.AzdoToken = "azdo-pat";
await vm.SaveAsync();
await vm.SaveAsync();
Assert.Equal(["azdo-pat"], worker.SavedAzdoTokens);
Assert.Equal("", vm.AzdoToken);
Assert.True(vm.AzdoTokenSet);
}
[Fact]
public async Task Azdo_test_reports_the_server_error_verbatim()
{
var worker = new Worker
{
Settings = new TicketSettingsDto("http://api", true, null),
AzdoConnection = new AzureDevOpsConnectionDto(false, null, null, "Azure DevOps 401: PAT abgelaufen"),
};
var vm = new TicketSettingsTabViewModel(worker, new TestDbFactory(NewContext));
await vm.LoadAsync();
await vm.TestAzdoConnectionCommand.ExecuteAsync(null);
Assert.Equal("Azure DevOps 401: PAT abgelaufen", vm.AzdoStatusMessage);
}
[Fact]