From c44d3db5a43fa055703c1c0c1c0c57169ad1bf45 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Tue, 18 Aug 2026 18:08:38 +0100 Subject: [PATCH 01/41] chore(release): bump version via gitversion for 1.0.8-rc1 --- src/PostIt.Tests/PostIt.Tests.csproj | 8 ++++---- src/PostIt/PostIt.Android/PostIt.Android.csproj | 8 ++++---- src/PostIt/PostIt.Browser/PostIt.Browser.csproj | 8 ++++---- src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj | 8 ++++---- src/PostIt/PostIt/PostIt.csproj | 8 ++++---- src/Yavsc.Abstract/Yavsc.Abstract.csproj | 8 ++++---- src/Yavsc.Api/Yavsc.Api.csproj | 8 ++++---- src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj | 8 ++++---- src/Yavsc.Blogs/Yavsc.Blogs.csproj | 8 ++++---- src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj | 8 ++++---- src/Yavsc.Org/Yavsc.Org.csproj | 8 ++++---- src/Yavsc.Server/Yavsc.Server.csproj | 8 ++++---- src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj | 4 ++++ src/cli/cli.csproj | 8 ++++---- 14 files changed, 56 insertions(+), 52 deletions(-) diff --git a/src/PostIt.Tests/PostIt.Tests.csproj b/src/PostIt.Tests/PostIt.Tests.csproj index 3d35d827..049ce9f2 100644 --- a/src/PostIt.Tests/PostIt.Tests.csproj +++ b/src/PostIt.Tests/PostIt.Tests.csproj @@ -6,10 +6,10 @@ false PostIt.Tests true - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 diff --git a/src/PostIt/PostIt.Android/PostIt.Android.csproj b/src/PostIt/PostIt.Android/PostIt.Android.csproj index 3820bf49..7a78afb8 100644 --- a/src/PostIt/PostIt.Android/PostIt.Android.csproj +++ b/src/PostIt/PostIt.Android/PostIt.Android.csproj @@ -12,10 +12,10 @@ apk false android-arm;android-arm64;android-x86;android-x64 - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 diff --git a/src/PostIt/PostIt.Browser/PostIt.Browser.csproj b/src/PostIt/PostIt.Browser/PostIt.Browser.csproj index a339b9f0..150daaff 100644 --- a/src/PostIt/PostIt.Browser/PostIt.Browser.csproj +++ b/src/PostIt/PostIt.Browser/PostIt.Browser.csproj @@ -4,10 +4,10 @@ Exe true enable - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 diff --git a/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj b/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj index c543c550..faef4e28 100644 --- a/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj +++ b/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj @@ -5,10 +5,10 @@ See https://docs.avaloniaui.net/docs/guides/platforms/platform-specific-code/dotnet for more details.--> net10.0 enable - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 app.manifest diff --git a/src/PostIt/PostIt/PostIt.csproj b/src/PostIt/PostIt/PostIt.csproj index d9cf96d3..bcd3b7e6 100644 --- a/src/PostIt/PostIt/PostIt.csproj +++ b/src/PostIt/PostIt/PostIt.csproj @@ -4,10 +4,10 @@ enable latest true - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 diff --git a/src/Yavsc.Abstract/Yavsc.Abstract.csproj b/src/Yavsc.Abstract/Yavsc.Abstract.csproj index 06174cf8..f22b9503 100644 --- a/src/Yavsc.Abstract/Yavsc.Abstract.csproj +++ b/src/Yavsc.Abstract/Yavsc.Abstract.csproj @@ -9,10 +9,10 @@ true true latest - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 diff --git a/src/Yavsc.Api/Yavsc.Api.csproj b/src/Yavsc.Api/Yavsc.Api.csproj index d6cceba5..19e22fa4 100644 --- a/src/Yavsc.Api/Yavsc.Api.csproj +++ b/src/Yavsc.Api/Yavsc.Api.csproj @@ -5,10 +5,10 @@ 1c73094f-959f-4211-b1a1-6a69b236c283 Yavsc.Api true - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 diff --git a/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj b/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj index ec1f7f0a..9ea7b893 100644 --- a/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj +++ b/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj @@ -7,10 +7,10 @@ Yavsc.Blogs.Tests b1a9d0d6-3f5e-4a07-9f0a-7e4d5b6c1a82 true - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 diff --git a/src/Yavsc.Blogs/Yavsc.Blogs.csproj b/src/Yavsc.Blogs/Yavsc.Blogs.csproj index 3c4bdc68..86aa173e 100644 --- a/src/Yavsc.Blogs/Yavsc.Blogs.csproj +++ b/src/Yavsc.Blogs/Yavsc.Blogs.csproj @@ -6,10 +6,10 @@ Yavsc.Blogs https://github.com/pazof/yavsc true - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 diff --git a/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj b/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj index 79a6bae1..d2a18451 100644 --- a/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj +++ b/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj @@ -9,10 +9,10 @@ true exe $(MSBuildProjectDirectory)\test.runsettings - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 diff --git a/src/Yavsc.Org/Yavsc.Org.csproj b/src/Yavsc.Org/Yavsc.Org.csproj index adb21103..060d7933 100644 --- a/src/Yavsc.Org/Yavsc.Org.csproj +++ b/src/Yavsc.Org/Yavsc.Org.csproj @@ -7,10 +7,10 @@ Yavsc true https://github.com/pazof/yavsc - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 diff --git a/src/Yavsc.Server/Yavsc.Server.csproj b/src/Yavsc.Server/Yavsc.Server.csproj index c12210a6..9ebddba8 100644 --- a/src/Yavsc.Server/Yavsc.Server.csproj +++ b/src/Yavsc.Server/Yavsc.Server.csproj @@ -7,10 +7,10 @@ true https://github.com/pazof/yavsc true - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 diff --git a/src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj b/src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj index b8fa9e64..5f919310 100644 --- a/src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj +++ b/src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj @@ -12,6 +12,10 @@ TestSdk. The consumer projects own the test execution and inherit from the shared base classes. --> + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 diff --git a/src/cli/cli.csproj b/src/cli/cli.csproj index fe627d9d..b53ea9f2 100644 --- a/src/cli/cli.csproj +++ b/src/cli/cli.csproj @@ -5,10 +5,10 @@ enable Yavsc.cli true - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+2.Branch.release-1.0.8-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1 From b613927409f68d8d88b9884ba1b2ba712f858c6e Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Tue, 18 Aug 2026 18:09:30 +0100 Subject: [PATCH 02/41] MEF --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index d1ed912a..c84cade7 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,19 @@ # Yavsc + [![The latest release made in the repository](https://forgejo.pschneider.fr/notazof/yavsc/badges/release.svg)](https://forgejo.pschneider.fr/notazof/yavsc/releases/latest) C'est une application mettant en oeuvre une prise de contact entre un demandeur de services et son éventuel prestataire associé. +# Statut actuel des actions Forgejo + +* [![Build and test](https://forgejo.pschneider.fr/notazof/yavsc/badges/workflows/buildAndTest.yml/badge.svg)](https://forgejo.pschneider.fr/notazof/yavsc/actions?workflow=buildAndTest.yml) + +* [![Release](https://forgejo.pschneider.fr/notazof/yavsc/badges/workflows/release.yml/badge.svg)]( +https://forgejo.pschneider.fr/notazof/yavsc/actions?workflow=release.yml +) + + # Statut actuel des actions GitHub * [![Build and Push Yavsc Apk](https://github.com/pazof/yavsc/actions/workflows/docker-publish-android.yml/badge.svg)](https://github.com/pazof/yavsc/actions/workflows/docker-publish-android.yml) From 1ab6e59fe2e7ba8727893652e731d34a96140db3 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Tue, 18 Aug 2026 19:18:36 +0100 Subject: [PATCH 03/41] chore(release): bump version via gitversion for 1.0.8-rc1 --- src/PostIt.Tests/PostIt.Tests.csproj | 2 +- src/PostIt/PostIt.Android/PostIt.Android.csproj | 2 +- src/PostIt/PostIt.Browser/PostIt.Browser.csproj | 2 +- src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj | 2 +- src/PostIt/PostIt/PostIt.csproj | 2 +- src/Yavsc.Abstract/Yavsc.Abstract.csproj | 2 +- src/Yavsc.Api.Client/Yavsc.Api.Client.csproj | 10 +++++----- src/Yavsc.Api/Yavsc.Api.csproj | 2 +- src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj | 2 +- src/Yavsc.Blogs/Yavsc.Blogs.csproj | 2 +- src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj | 2 +- src/Yavsc.Org/Yavsc.Org.csproj | 2 +- src/Yavsc.Server/Yavsc.Server.csproj | 2 +- src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj | 2 +- src/cli/cli.csproj | 2 +- 15 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/PostIt.Tests/PostIt.Tests.csproj b/src/PostIt.Tests/PostIt.Tests.csproj index 54c40e8c..b12c536e 100644 --- a/src/PostIt.Tests/PostIt.Tests.csproj +++ b/src/PostIt.Tests/PostIt.Tests.csproj @@ -8,7 +8,7 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/PostIt/PostIt.Android/PostIt.Android.csproj b/src/PostIt/PostIt.Android/PostIt.Android.csproj index de4e3801..b08143b4 100644 --- a/src/PostIt/PostIt.Android/PostIt.Android.csproj +++ b/src/PostIt/PostIt.Android/PostIt.Android.csproj @@ -14,7 +14,7 @@ android-arm;android-arm64;android-x86;android-x64 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/PostIt/PostIt.Browser/PostIt.Browser.csproj b/src/PostIt/PostIt.Browser/PostIt.Browser.csproj index 7a0a9ba3..8643fcc6 100644 --- a/src/PostIt/PostIt.Browser/PostIt.Browser.csproj +++ b/src/PostIt/PostIt.Browser/PostIt.Browser.csproj @@ -6,7 +6,7 @@ enable 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj b/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj index 6ab6fdac..5043da6e 100644 --- a/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj +++ b/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj @@ -7,7 +7,7 @@ enable 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/PostIt/PostIt/PostIt.csproj b/src/PostIt/PostIt/PostIt.csproj index c63771b8..163a6a77 100644 --- a/src/PostIt/PostIt/PostIt.csproj +++ b/src/PostIt/PostIt/PostIt.csproj @@ -6,7 +6,7 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/Yavsc.Abstract/Yavsc.Abstract.csproj b/src/Yavsc.Abstract/Yavsc.Abstract.csproj index 2b6d261a..7a4f83ae 100644 --- a/src/Yavsc.Abstract/Yavsc.Abstract.csproj +++ b/src/Yavsc.Abstract/Yavsc.Abstract.csproj @@ -11,7 +11,7 @@ latest 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/Yavsc.Api.Client/Yavsc.Api.Client.csproj b/src/Yavsc.Api.Client/Yavsc.Api.Client.csproj index 5376856d..ade7ca17 100644 --- a/src/Yavsc.Api.Client/Yavsc.Api.Client.csproj +++ b/src/Yavsc.Api.Client/Yavsc.Api.Client.csproj @@ -15,10 +15,10 @@ https://github.com/pazof/yavsc true - 1.0.1.0 - 1.0.1.0 - 1.0.1-5+Branch.main.Sha.0617fc6bda7151c70559d87177e2dcfb1b60995f - 1.0.1-5 + 1.1.0.0 + 1.1.0.0 + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 + 1.1.0-beta.1 @@ -26,4 +26,4 @@ - + \ No newline at end of file diff --git a/src/Yavsc.Api/Yavsc.Api.csproj b/src/Yavsc.Api/Yavsc.Api.csproj index 63019232..5672ab7a 100644 --- a/src/Yavsc.Api/Yavsc.Api.csproj +++ b/src/Yavsc.Api/Yavsc.Api.csproj @@ -7,7 +7,7 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj b/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj index 393a520c..256bdc4d 100644 --- a/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj +++ b/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj @@ -9,7 +9,7 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/Yavsc.Blogs/Yavsc.Blogs.csproj b/src/Yavsc.Blogs/Yavsc.Blogs.csproj index a7f4bea9..5c175cb5 100644 --- a/src/Yavsc.Blogs/Yavsc.Blogs.csproj +++ b/src/Yavsc.Blogs/Yavsc.Blogs.csproj @@ -8,7 +8,7 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj b/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj index 8943e869..88842cb4 100644 --- a/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj +++ b/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj @@ -11,7 +11,7 @@ $(MSBuildProjectDirectory)\test.runsettings 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/Yavsc.Org/Yavsc.Org.csproj b/src/Yavsc.Org/Yavsc.Org.csproj index 80caabbb..ccdb7f24 100644 --- a/src/Yavsc.Org/Yavsc.Org.csproj +++ b/src/Yavsc.Org/Yavsc.Org.csproj @@ -9,7 +9,7 @@ https://github.com/pazof/yavsc 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/Yavsc.Server/Yavsc.Server.csproj b/src/Yavsc.Server/Yavsc.Server.csproj index d8f0a683..4eaf6a83 100644 --- a/src/Yavsc.Server/Yavsc.Server.csproj +++ b/src/Yavsc.Server/Yavsc.Server.csproj @@ -9,7 +9,7 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj b/src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj index 81404c20..52effc72 100644 --- a/src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj +++ b/src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj @@ -14,7 +14,7 @@ --> 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 diff --git a/src/cli/cli.csproj b/src/cli/cli.csproj index 305ec0af..db5e9314 100644 --- a/src/cli/cli.csproj +++ b/src/cli/cli.csproj @@ -7,7 +7,7 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-beta.1+2.Branch.release-1.0.7-rc1.Sha.6e50967702ba9d310017c86a2d7ee636a9e94ada + 1.1.0-beta.1+1.Branch.release-1.0.8-rc1.Sha.1167169aa89e1bf25290e9a152d27b357a500ab3 1.1.0-beta.1 From 65f6ad8752cc4b5260e884d9596933ba9f40f60a Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Tue, 18 Aug 2026 21:14:52 +0100 Subject: [PATCH 04/41] cleanup --- .forgejo/workflows/buildAndTest.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.forgejo/workflows/buildAndTest.yml b/.forgejo/workflows/buildAndTest.yml index 9b3403db..0c48451a 100644 --- a/.forgejo/workflows/buildAndTest.yml +++ b/.forgejo/workflows/buildAndTest.yml @@ -24,17 +24,6 @@ on: branches: [ "main" ] jobs: - log-the-inputs: - runs-on: debian-latest - steps: - - run: | - echo "Log level: $LEVEL" - echo "Tags: $TAGS" - echo "Environment: $ENVIRONMENT" - env: - LEVEL: ${{ inputs.logLevel }} - TAGS: ${{ inputs.tags }} - build: runs-on: docker From 6695c9ed4d0223a674810904f03b1accc5a4e9ce Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Tue, 18 Aug 2026 21:25:06 +0100 Subject: [PATCH 05/41] build and test release/* --- .forgejo/workflows/buildAndTest.yml | 2 +- .forgejo/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/buildAndTest.yml b/.forgejo/workflows/buildAndTest.yml index 0c48451a..a0f3a375 100644 --- a/.forgejo/workflows/buildAndTest.yml +++ b/.forgejo/workflows/buildAndTest.yml @@ -21,7 +21,7 @@ on: push: branches: [ "main" ] pull_request: - branches: [ "main" ] + branches: [ "main", "release/*" ] jobs: build: diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index ef518037..f7173b3a 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -328,4 +328,4 @@ jobs: exit 1 fi - echo "Release publiée: $API_BASE/$GITHUB_REPOSITORY/releases/tag/$TAG" \ No newline at end of file + echo "Release publiée: $API_BASE/$GITHUB_REPOSITORY/releases/tag/$TAG" From d245d3972d23bc6ab5c3a0c20e02d5d523ad0ed7 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Tue, 18 Aug 2026 21:26:40 +0100 Subject: [PATCH 06/41] ci: retrigger Forgejo Actions on PR #37 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #37 (feat/postit-fs -> release/1.0.8-rc1) was opened before the buildAndTest.yml workflow trigger was widened to include release/*. Forgejo Actions does not re-evaluate the workflow file on its own — a push event on the PR is needed to re-trigger the CI. This empty commit is the push. From f36679aa656d7e3678db3e4c115e94f2a56d79da Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Tue, 18 Aug 2026 22:01:09 +0100 Subject: [PATCH 07/41] fix(blog): replace IApplicationUser Author with concrete BlogPostAuthorDto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit System.Text.Json cannot materialise an interface without a polymorphic converter. Until this commit, BlogPostDto.Author was typed as the abstract interface IApplicationUser, which crashed the "load posts" call in PostIt whenever the server returned a post with a populated Author object (the common case — GET /api/BlogApi). Fix: * Introduce a minimum-viable wire DTO BlogPostAuthorDto in Yavsc.Abstract.Blogspot (record: Id, UserName, Avatar). These are the only fields the client UI actually needs; the server-side ApplicationUser navigation is preserved for permission checks and authorisation. * Change IBlogPost.Author and BlogPostDto.Author from IApplicationUser to BlogPostAuthorDto? (interface change, breaking). The EF entity BlogPost keeps its full ApplicationUser navigation property and exposes IBlogPost.Author via an explicit interface implementation that projects to BlogPostAuthorDto on demand (so EF can still lazy-load the navigation without forcing an eager join on every read). * Restore the using directive that was accidentally removed when the BlogPostDto property was rewritten (needed for ICircleAuthorization in GetACL()). Regression coverage (the missing test Paul flagged): * Add BlogPostAuthorDtoTests in PostIt.Tests with four scenarios that exercise the wire shape on the client side: - A BlogPostDto JSON with a populated Author round-trips through JsonSerializer without throwing and the three fields (Id, UserName, Avatar) survive intact. - A BlogPostDto JSON with explicit "author": null deserialises with Author == null. - A BlogPostDto JSON without any Author field at all deserialises with Author == null (forward compat). - The serialised shape of BlogPostAuthorDto uses camelCase property names (matching the server's Web defaults), so the field names on the wire don't drift without a test catching it. Tests: 55/55 PostIt.Tests (+4 new), 24/24 Yavsc.Blogs.Tests, 44/44 Yavsc.Org.Tests. No regressions. Side note: yavsc.sln picks up Yavsc.Api.Client (added by 'feat/postit-acl' in 1.0.7 but never registered in the solution file until now — probably auto-added by a recent 'dotnet build' that discovered the .csproj). --- src/PostIt.Tests/BlogPostAuthorDtoTests.cs | 169 ++++++++++++++++++ src/Yavsc.Abstract/Blogspot/BlogPost.cs | 3 +- .../Blogspot/BlogPostAuthorDto.cs | 33 ++++ src/Yavsc.Abstract/Blogspot/IBlogPost.cs | 8 +- src/Yavsc.Server/Models/Blog/BlogPost.cs | 28 ++- yavsc.sln | 15 ++ 6 files changed, 251 insertions(+), 5 deletions(-) create mode 100644 src/PostIt.Tests/BlogPostAuthorDtoTests.cs create mode 100644 src/Yavsc.Abstract/Blogspot/BlogPostAuthorDto.cs diff --git a/src/PostIt.Tests/BlogPostAuthorDtoTests.cs b/src/PostIt.Tests/BlogPostAuthorDtoTests.cs new file mode 100644 index 00000000..895f220e --- /dev/null +++ b/src/PostIt.Tests/BlogPostAuthorDtoTests.cs @@ -0,0 +1,169 @@ +using System.Text.Json; +using Yavsc.Blogspot; + +namespace PostIt.Tests; + +/// +/// Round-trip tests for the wire shape of a blog post as +/// serialised by Yavsc.Blogs and consumed by PostIt. +/// +/// +/// Background: in 1.0.7, BlogPostDto.Author was typed as +/// the abstract interface IApplicationUser. System.Text.Json +/// cannot materialise an interface without a polymorphic +/// converter, so the "load posts" call from PostIt crashed when +/// the server returned a post with a populated Author +/// object. The fix replaced IApplicationUser with a thin +/// concrete DTO, BlogPostAuthorDto, embedded directly in +/// BlogPostDto.Author. +/// +/// +/// +/// These tests pin the wire shape: a JSON document with an +/// Author object must deserialise without throwing and +/// must round-trip the three fields PostIt exposes in the UI +/// (Id, UserName, Avatar). They are intentionally placed in +/// PostIt.Tests — the client-side assembly — so the +/// regression is caught at the deserialisation boundary, where +/// it actually manifested in production. +/// +/// +public class BlogPostAuthorDtoTests +{ + private static readonly JsonSerializerOptions CaseInsensitiveJson + = new() { PropertyNameCaseInsensitive = true }; + + [Fact] + public void BlogPostDto_deserialises_with_populated_author() + { + // A representative JSON shape the server would emit for + // GET /api/BlogApi. The Author object is fully populated + // — that's the shape that used to break deserialisation + // when Author was typed as the abstract IApplicationUser + // interface. + var json = """ + { + "id": 42, + "title": "Premier billet", + "article": "Contenu", + "photo": null, + "dateCreated": "2026-08-01T12:00:00Z", + "dateModified": "2026-08-02T12:00:00Z", + "userCreated": "alice", + "userModified": "alice", + "authorId": "u-alice", + "isPublished": true, + "author": { + "id": "u-alice", + "userName": "alice", + "avatar": "/avatars/alice.png" + } + } + """; + + var post = JsonSerializer.Deserialize(json, CaseInsensitiveJson); + + Assert.NotNull(post); + Assert.Equal(42, post!.Id); + Assert.Equal("Premier billet", post.Title); + Assert.Equal("u-alice", post.AuthorId); + Assert.True(post.IsPublished); + + // The actual regression coverage: Author must + // materialise as a concrete DTO, not be left null because + // of a JsonException on IApplicationUser. + Assert.NotNull(post.Author); + Assert.Equal("u-alice", post.Author!.Id); + Assert.Equal("alice", post.Author.UserName); + Assert.Equal("/avatars/alice.png", post.Author.Avatar); + } + + [Fact] + public void BlogPostDto_deserialises_when_author_is_null() + { + // The server is allowed to omit Author (the field is + // nullable on the wire — it maps to a navigation + // property that may not have been Included). The client + // must accept that shape without throwing. + var json = """ + { + "id": 7, + "title": "Sans auteur", + "article": null, + "photo": null, + "dateCreated": "2026-08-01T12:00:00Z", + "dateModified": "2026-08-01T12:00:00Z", + "userCreated": "system", + "userModified": "system", + "authorId": "system", + "isPublished": false, + "author": null + } + """; + + var post = JsonSerializer.Deserialize(json, CaseInsensitiveJson); + + Assert.NotNull(post); + Assert.Null(post!.Author); + Assert.Equal("system", post.AuthorId); + } + + [Fact] + public void BlogPostDto_deserialises_when_author_field_is_missing() + { + // Forward-compatibility: an older server that doesn't + // emit the Author field at all. Should not throw. + var json = """ + { + "id": 9, + "title": "Ancien format", + "article": "Pas d'auteur dans la charge utile", + "photo": null, + "dateCreated": "2026-07-01T12:00:00Z", + "dateModified": "2026-07-01T12:00:00Z", + "userCreated": "bob", + "userModified": "bob", + "authorId": "u-bob", + "isPublished": true + } + """; + + var post = JsonSerializer.Deserialize(json, CaseInsensitiveJson); + + Assert.NotNull(post); + Assert.Null(post!.Author); + } + + [Fact] + public void BlogPostAuthorDto_serialises_back_to_expected_json_shape() + { + // Pin the wire shape on the way out too. The server + // builds BlogPostAuthorDto from an ApplicationUser and + // PostIt receives it as JSON; if the field names + // change (e.g. case) the round-trip on the client side + // is what would silently break. + // + // The server emits camelCase (ASP.NET Core's Web + // defaults — PropertyNamingPolicy = CamelCase). We + // mirror that here so the test reflects what the wire + // actually looks like. PropertyNameCaseInsensitive on + // the client deserialiser means we don't have to + // hardcode the casing for the inbound assertions. + var author = new BlogPostAuthorDto + { + Id = "u-alice", + UserName = "alice", + Avatar = "/avatars/alice.png" + }; + + var json = JsonSerializer.Serialize(author, + new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + + using var doc = JsonDocument.Parse(json); + var root = doc.RootElement; + + Assert.True(root.TryGetProperty("id", out _)); + Assert.True(root.TryGetProperty("userName", out _)); + Assert.True(root.TryGetProperty("avatar", out _)); + } +} diff --git a/src/Yavsc.Abstract/Blogspot/BlogPost.cs b/src/Yavsc.Abstract/Blogspot/BlogPost.cs index 0f88fdf8..2406fb2a 100644 --- a/src/Yavsc.Abstract/Blogspot/BlogPost.cs +++ b/src/Yavsc.Abstract/Blogspot/BlogPost.cs @@ -1,5 +1,4 @@ using System; -using Yavsc.Abstract.Identity; using Yavsc.Abstract.Identity.Security; namespace Yavsc.Blogspot; @@ -8,7 +7,7 @@ public class BlogPostDto : IBlogPost { public string AuthorId { get; set; } - public IApplicationUser Author { get; set; } + public BlogPostAuthorDto? Author { get; set; } public string Article { get; set ; } public string Photo { get; set ; } diff --git a/src/Yavsc.Abstract/Blogspot/BlogPostAuthorDto.cs b/src/Yavsc.Abstract/Blogspot/BlogPostAuthorDto.cs new file mode 100644 index 00000000..e332822e --- /dev/null +++ b/src/Yavsc.Abstract/Blogspot/BlogPostAuthorDto.cs @@ -0,0 +1,33 @@ +namespace Yavsc.Blogspot; + +/// +/// Minimum-viable author payload embedded in . +/// +/// +/// Before this record existed, BlogPostDto.Author was typed +/// as the abstract interface IApplicationUser. The +/// interface is fine for server-side contract (we have a concrete +/// entity that implements it) but System.Text.Json cannot +/// materialise an interface without a polymorphic converter +/// configured on both ends. PostIt would crash on load-posts +/// because the JSON contained an Author object that the +/// client could not deserialise. +/// +/// +/// +/// This record is the wire shape: Id for "go to author +/// profile", UserName for "by @username", Avatar +/// for the round badge next to the title. The server-side +/// BlogPost entity (Yavsc.Server.Models.Blog) keeps +/// its full ApplicationUser navigation property for +/// permission checks and authorisation; the DTO is built on +/// demand by the controller / service layer when the post is +/// served to the wire. +/// +/// +public sealed record BlogPostAuthorDto +{ + public string Id { get; init; } = string.Empty; + public string? UserName { get; init; } + public string? Avatar { get; init; } +} diff --git a/src/Yavsc.Abstract/Blogspot/IBlogPost.cs b/src/Yavsc.Abstract/Blogspot/IBlogPost.cs index 5287685d..6090fca2 100644 --- a/src/Yavsc.Abstract/Blogspot/IBlogPost.cs +++ b/src/Yavsc.Abstract/Blogspot/IBlogPost.cs @@ -1,7 +1,6 @@ -using Yavsc.Abstract.Identity; using Yavsc.Abstract.Identity.Security; using Yavsc.Interfaces; @@ -9,6 +8,11 @@ namespace Yavsc.Blogspot { public interface IBlogPost : IBlogPostPayLoad, ICircleAuthorized, ITrackedEntity, ITitle { - IApplicationUser Author { get; } + // Typed as a concrete wire DTO (not the IApplicationUser + // interface) so System.Text.Json can materialise it on the + // client without a polymorphic converter. The server-side + // BlogPost entity implements this getter by mapping its + // ApplicationUser navigation to a BlogPostAuthorDto. + BlogPostAuthorDto? Author { get; } } } diff --git a/src/Yavsc.Server/Models/Blog/BlogPost.cs b/src/Yavsc.Server/Models/Blog/BlogPost.cs index 213910cd..be9fe7fa 100644 --- a/src/Yavsc.Server/Models/Blog/BlogPost.cs +++ b/src/Yavsc.Server/Models/Blog/BlogPost.cs @@ -107,6 +107,32 @@ namespace Yavsc.Models.Blog [NotMapped] public bool IsPublished { get; set; } - IApplicationUser IBlogPost.Author => Author; + /// + /// Explicit interface implementation of + /// . The underlying + /// navigation property is + /// (an ApplicationUser entity), but the wire + /// DTO is a thin with + /// only the fields the client UI consumes. We project + /// on demand so EF can lazy-load the navigation + /// without forcing an eager join on every read. + /// Returns null when the navigation hasn't been + /// loaded (caller should pre-Include Author if + /// they need it). + /// + BlogPostAuthorDto? IBlogPost.Author + { + get + { + var a = Author; + if (a == null) return null; + return new BlogPostAuthorDto + { + Id = a.Id, + UserName = a.UserName, + Avatar = a.Avatar + }; + } + } } } diff --git a/yavsc.sln b/yavsc.sln index fafdff89..7d972ade 100644 --- a/yavsc.sln +++ b/yavsc.sln @@ -37,6 +37,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yavsc.Blogs.Tests", "src\Ya EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yavsc.Tests.Shared", "src\Yavsc.Tests.Shared\Yavsc.Tests.Shared.csproj", "{34D1F73D-BF74-47CC-9358-9F4F221C75D7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yavsc.Api.Client", "src\Yavsc.Api.Client\Yavsc.Api.Client.csproj", "{59AF5DEA-D349-495A-BC44-FC7BD4E55099}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -215,6 +217,18 @@ Global {34D1F73D-BF74-47CC-9358-9F4F221C75D7}.Release|x64.Build.0 = Release|Any CPU {34D1F73D-BF74-47CC-9358-9F4F221C75D7}.Release|x86.ActiveCfg = Release|Any CPU {34D1F73D-BF74-47CC-9358-9F4F221C75D7}.Release|x86.Build.0 = Release|Any CPU + {59AF5DEA-D349-495A-BC44-FC7BD4E55099}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {59AF5DEA-D349-495A-BC44-FC7BD4E55099}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59AF5DEA-D349-495A-BC44-FC7BD4E55099}.Debug|x64.ActiveCfg = Debug|Any CPU + {59AF5DEA-D349-495A-BC44-FC7BD4E55099}.Debug|x64.Build.0 = Debug|Any CPU + {59AF5DEA-D349-495A-BC44-FC7BD4E55099}.Debug|x86.ActiveCfg = Debug|Any CPU + {59AF5DEA-D349-495A-BC44-FC7BD4E55099}.Debug|x86.Build.0 = Debug|Any CPU + {59AF5DEA-D349-495A-BC44-FC7BD4E55099}.Release|Any CPU.ActiveCfg = Release|Any CPU + {59AF5DEA-D349-495A-BC44-FC7BD4E55099}.Release|Any CPU.Build.0 = Release|Any CPU + {59AF5DEA-D349-495A-BC44-FC7BD4E55099}.Release|x64.ActiveCfg = Release|Any CPU + {59AF5DEA-D349-495A-BC44-FC7BD4E55099}.Release|x64.Build.0 = Release|Any CPU + {59AF5DEA-D349-495A-BC44-FC7BD4E55099}.Release|x86.ActiveCfg = Release|Any CPU + {59AF5DEA-D349-495A-BC44-FC7BD4E55099}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -235,5 +249,6 @@ Global {4D283324-6DD3-4CD1-9893-8C317772C6B5} = {CDB1BDB5-53F9-4B43-864F-60F2E74F44E2} {0E471075-DABF-40E9-98B7-1630BEF19145} = {CDB1BDB5-53F9-4B43-864F-60F2E74F44E2} {34D1F73D-BF74-47CC-9358-9F4F221C75D7} = {CDB1BDB5-53F9-4B43-864F-60F2E74F44E2} + {59AF5DEA-D349-495A-BC44-FC7BD4E55099} = {CDB1BDB5-53F9-4B43-864F-60F2E74F44E2} EndGlobalSection EndGlobal From 84f3ffa9c25120bac3f2b32555aebea4138a3ad8 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Tue, 18 Aug 2026 23:24:08 +0100 Subject: [PATCH 08/41] test(postit): pin inoperative toolbar buttons (ACL, Mes cercles, [DEV] Signature) with headless UI tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three buttons on MainPage's toolbar are reported as inoperative in the running app: ACL, Mes cercles, and [DEV] Signature. They click but no dialog / page opens. This commit adds headless UI tests that drive each button via the Avalonia headless harness (KeyPressQwerty(Enter) on a focused, x:Name'd button, per the CalculatorTests pattern in Avalonia.Samples) and asserts the post-click top of NavRoot.NavigationStack is a non-null Page. The tests fail today on every button (stack size before == after == 1): the click does not push anything. The bug is the user's real complaint — the test is now wired to catch it. To make the buttons reachable by the harness without walking the visual tree (which does not see buttons hosted inside a NavigationPage), name the two unnamed buttons: - ACL -> ManageAclButton - Mes cercles -> OpenCirclesButton ([DEV] Signature was already named OpenSignatureDevButton.) The XAML change is cosmetic; bindings and commands are untouched. The test pattern follows SessionStatusBannerTests: new MainWindow().Show(), PushAsync(MainPage), drive controls via their generated x:Name fields. --- src/PostIt.Tests/MainPageButtonsTests.cs | 207 +++++++++++++++++++++++ src/PostIt/PostIt/Views/MainPage.axaml | 8 +- 2 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 src/PostIt.Tests/MainPageButtonsTests.cs diff --git a/src/PostIt.Tests/MainPageButtonsTests.cs b/src/PostIt.Tests/MainPageButtonsTests.cs new file mode 100644 index 00000000..26636635 --- /dev/null +++ b/src/PostIt.Tests/MainPageButtonsTests.cs @@ -0,0 +1,207 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Headless; +using Avalonia.Headless.XUnit; +using Avalonia.Input; +using Avalonia.Interactivity; +using CommunityToolkit.Mvvm.Input; +using Yavsc.Api.Client; +using Yavsc.Blogspot; +using PostIt.Services; +using PostIt.ViewModels; +using PostIt.Views; + +namespace PostIt.Tests; + +/// +/// Regression coverage for the three toolbar buttons on +/// that the user reported as inoperative: +/// "ACL", "Mes cercles", and "[DEV] Signature". +/// +/// Pattern (per the Avalonia headless testing docs — +/// TestableApp.Headless.XUnit/CalculatorTests): name every +/// interactive control in the XAML with x:Name="...", then +/// in the test focus the named control and raise the click via +/// window.KeyPressQwerty(PhysicalKey.Enter, ...). This is +/// the supported path — searching the visual tree via +/// GetVisualDescendants().OfType<Button>() for a +/// button by Content text is brittle and was tried first; it does +/// not work reliably when the page is hosted inside an +/// , which wraps the +/// pushed page in an internal container that the visual-tree walk +/// does not always expose under headless. +/// +/// The assertion is on the post-click top of +/// : +/// the user's bug is "I click and the dialog / page never opens", +/// so the test fails when the click doesn't push anything onto the +/// stack. We pin γ + sniff léger — the new top must be a non-null +/// , but we do not yet assert the concrete type +/// (that would require a fully stubbed App.ServiceProvider, +/// which is the next iteration of this suite). +/// +/// Each test exercises the bit that would silently break if +/// the wiring was reverted: +/// +/// "ACL" — click with a selected post pushes a page onto +/// the stack. +/// "Mes cercles" — click pushes a page onto the stack. +/// "[DEV] Signature" — click pushes a page onto the +/// stack. +/// +/// +public class MainPageButtonsTests +{ + /// + /// Fake that throws on any + /// wire call. These tests never invoke a command that hits + /// the API — only the click → nav side of the pipeline is + /// asserted. + /// + private sealed class ThrowingApi : YavscApiClient + { + public ThrowingApi() : base( + new Settings + { + Authentication = new AuthenticationSettings + { + Authority = "https://stub.invalid", + ClientId = "stub", + Scopes = new[] { "openid" }, + }, + }, + new TokenStore(System.IO.Path.GetTempFileName())) + { } + } + + private static MainPageViewModel MakeViewModel(BlogPostDto? selectedPost = null) + { + var api = new ThrowingApi(); + var blog = new BlogApiClient(api, "http://localhost/"); + var vm = new MainPageViewModel(blog); + if (selectedPost is not null) vm.SelectedPost = selectedPost; + return vm; + } + + /// + /// Mount a real (as + /// SessionStatusBannerTests does), push a + /// with the given VM onto + /// NavRoot. PushAsync is awaited (via + /// GetAwaiter().GetResult()) so the page is on the + /// nav stack before the test tries to interact with its + /// named buttons. The window is shown so the visual tree is + /// realised and KeyPressQwerty has a real + /// to dispatch against. + /// + private static (MainWindow window, MainPage page) MountMainPage(MainPageViewModel vm) + { + var window = new MainWindow(); + var page = new MainPage { DataContext = vm }; + window.Show(); + window.NavRoot.PushAsync(page).GetAwaiter().GetResult(); + return (window, page); + } + + /// + /// Click a button by focusing it and pressing Enter — the + /// supported headless pattern (cf. CalculatorTests in the + /// Avalonia.Samples repo). Returns the nav-stack count + /// before the click so the caller can assert on the delta. + /// KeyPressQwerty is dispatched on the + /// itself — it is the that owns the + /// headless implementation, and routing the key through any + /// descendant TopLevel (e.g. one obtained via + /// TopLevel.GetTopLevel(button)) fails with a + /// NullReferenceException from the headless impl + /// because the descendant does not carry the + /// PlatformHandle the harness expects. + /// + private static int ClickAndCapture(MainWindow window, Button button) + { + var stackBefore = window.NavRoot.NavigationStack.Count; + button.Focus(); + window.KeyPressQwerty(PhysicalKey.Enter, RawInputModifiers.None); + return stackBefore; + } + + [AvaloniaFact] + public void Acl_button_click_pushes_a_page_onto_nav_stack() + { + // Arrange: a VM whose SelectedPost is non-null so + // CanManageAcl evaluates to true and the button is + // armed. + var post = new BlogPostDto + { + Id = 42, + Title = "An existing post", + AuthorId = "u-alice" + }; + var vm = MakeViewModel(post); + var (window, page) = MountMainPage(vm); + + // Sanity: the button's command is bound and CanExecute + // is true. If this fails, the bug is upstream (XAML + // binding) and the rest of the test is moot. + var aclButton = page.ManageAclButton; + Assert.NotNull(aclButton.Command); + Assert.True(aclButton.Command.CanExecute(null)); + + // Act + var stackBefore = ClickAndCapture(window, aclButton); + + // Assert γ + sniff léger: stack grew, new top is a Page. + Assert.True(window.NavRoot.NavigationStack.Count > stackBefore, + $"Click on ACL must push a new page onto the nav stack. Stack size before: {stackBefore}, after: {window.NavRoot.NavigationStack.Count}."); + var pushed = window.NavRoot.NavigationStack.Last(); + Assert.NotNull(pushed); + Assert.IsAssignableFrom(pushed); + } + + [AvaloniaFact] + public void Circles_button_click_pushes_a_page_onto_nav_stack() + { + // Arrange: OpenCircles has no CanExecute guard today — + // any click should fire it and push the page. + var vm = MakeViewModel(); + var (window, page) = MountMainPage(vm); + + var circlesButton = page.OpenCirclesButton; + Assert.NotNull(circlesButton.Command); + + // Act + var stackBefore = ClickAndCapture(window, circlesButton); + + // Assert + Assert.True(window.NavRoot.NavigationStack.Count > stackBefore, + "Click on 'Mes cercles' must push a new page onto the nav stack."); + var pushed = window.NavRoot.NavigationStack.Last(); + Assert.NotNull(pushed); + Assert.IsAssignableFrom(pushed); + } + + [AvaloniaFact] + public void Signature_dev_button_click_pushes_a_page_onto_nav_stack() + { + // Arrange: the "[DEV] Signature" button uses XAML's + // Click="OpenSignatureDev" attribute, so we don't bind + // a Command here — we drive the click directly. The + // handler resolves App.ServiceProvider, which is null + // in a unit test, and early-returns; that is the + // failure mode the test pins. + var vm = MakeViewModel(); + var (window, page) = MountMainPage(vm); + + var signatureButton = page.OpenSignatureDevButton; + + // Act + var stackBefore = ClickAndCapture(window, signatureButton); + + // Assert + Assert.True(window.NavRoot.NavigationStack.Count > stackBefore, + "Click on '[DEV] Signature' must push a new page onto the nav stack."); + var pushed = window.NavRoot.NavigationStack.Last(); + Assert.NotNull(pushed); + Assert.IsAssignableFrom(pushed); + } +} diff --git a/src/PostIt/PostIt/Views/MainPage.axaml b/src/PostIt/PostIt/Views/MainPage.axaml index 0246857e..86bcf7dd 100644 --- a/src/PostIt/PostIt/Views/MainPage.axaml +++ b/src/PostIt/PostIt/Views/MainPage.axaml @@ -33,8 +33,12 @@