From 247b92f9e381440af7860c5efc00e67ecd718098 Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 14:27:52 +0200 Subject: [PATCH 01/20] Add workflows --- .github/workflows/ci.yml | 79 +++++++++++++++++++++++ .github/workflows/deploy-release.yml | 95 ++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/deploy-release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6fd3bc7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,79 @@ +name: "CI" + +on: + push: + branches: [ develop, main ] + +jobs: + build: + runs-on: windows-latest + + env: + Solution: "src/CleanMyPosts.sln" + Test_Project: "src/Tests/Tests.csproj" + FORCE_COLOR: "true" + DOTNET_LOGGING__CONSOLE__COLORBEHAVIOR: Enabled + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 9.x + + - name: Install .NET Tools (local) + run: | + dotnet new tool-manifest + dotnet tool install dotnet-reportgenerator-globaltool + dotnet tool install dotnet-sonarscanner + + - name: Restore + run: dotnet restore "${{ env.Solution }}" + + - name: Build and Test with SonarQube + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: | + ./dotnet-sonarscanner begin ` + /k:"thorstenalpers_CleanMyPosts" ` + /o:"thorstenalpers" ` + /d:sonar.token="${{ secrets.SONAR_TOKEN }}" ` + /d:sonar.host.url="https://sonarcloud.io" ` + /d:sonar.sources="src" ` + /d:sonar.tests="src/Tests" ` + /d:sonar.test.inclusions="src/Tests/**/*.cs" ` + /d:sonar.exclusions="**/*.html" ` + /d:sonar.coverageReportPaths="./TestResults/Reports/SonarQube.xml" + + dotnet build "${{ env.Solution }}" --configuration Release --no-restore + + dotnet test "${{ env.Test_Project }}" ` + --collect:"XPlat Code Coverage" ` + --results-directory ./TestResults/Tests ` + --configuration Release ` + --logger "console;verbosity=detailed" ` + --filter "TestCategory!=Long-Running" + + ./dotnet-reportgenerator ` + -reports:./TestResults/Tests/**/coverage.cobertura.xml ` + -targetdir:./TestResults/Reports ` + -reporttypes:"Html;lcov;SonarQube;Cobertura" + + ./dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + + - name: Upload to Coveralls + uses: coverallsapp/github-action@v2 + with: + path-to-lcov: ./TestResults/Reports/lcov.info + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + + - name: Upload Test Coverage Report + uses: actions/upload-artifact@v4 + with: + name: unit-test-coverage-report + path: ./TestResults/Reports diff --git a/.github/workflows/deploy-release.yml b/.github/workflows/deploy-release.yml new file mode 100644 index 0000000..43d6915 --- /dev/null +++ b/.github/workflows/deploy-release.yml @@ -0,0 +1,95 @@ +name: "Deploy Release" + +on: + workflow_dispatch: # manual trigger + +jobs: + build: + runs-on: windows-latest + + env: + Solution: "src/CleanMyPosts.sln" + UI_Project: "src/UI/UI.csproj" + Test_Project: "src/Tests/Tests.csproj" + Installer_Script: "installer/Installer.iss" + FORCE_COLOR: "true" + DOTNET_LOGGING__CONSOLE__COLORBEHAVIOR: Enabled + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 9.x + + - name: Restore + run: dotnet restore "${{ env.Solution }}" + + - name: Build + run: dotnet build "${{ env.Solution }}" --configuration Release --no-restore + + - name: Run tests + run: dotnet test "${{ env.Test_Project }}" --configuration Release --logger "console;verbosity=detailed" --filter "TestCategory!=Long-Running" + + - name: Extract Version + id: get_version + shell: pwsh + run: | + $content = Get-Content "${{ env.UI_Project }}" + if ($content -match '(.+)') { + $version = $matches[1] + echo "VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Append + } else { + throw "Version not found in project file" + } + + - name: Publish Single EXE + run: | + dotnet publish "${{ env.UI_Project }}" -c Release -r win-x64 --self-contained true ` + /p:PublishSingleFile=true ` + /p:IncludeAllContentForSelfExtract=true ` + /p:EnableCompressionInSingleFile=true ` + -o artifacts/single-exe + + - name: Install Inno Setup + run: | + choco install innosetup --yes + + - name: Build Setup EXE + run: | + iscc "/DMyAppVersion=${{ env.VERSION }}" "/DMyAppExePath=artifacts\\single-exe\\*" "${{ env.Installer_Script }}" + + - name: Copy Setup EXE to Artifacts + run: | + mkdir -p artifacts/setup + copy installer\Output\CleanMyPosts-Setup-${{ env.VERSION }}.exe artifacts\setup\ + + - name: Rename Standalone EXE for Release + run: | + Rename-Item "artifacts/single-exe/CleanMyPosts.exe" "CleanMyPosts-standalone.exe" + + - name: Configure Git Credentials + run: | + echo "https://${{ secrets.GH_APIKEY }}@github.com" > $env:USERPROFILE\.git-credentials + git config --global credential.helper store + git config --global user.name "GitHub Actions" + git config --global user.email "actions@github.com" + + - name: Create Git Tag + run: | + git tag -a "v${{ env.VERSION }}" -m "Release v${{ env.VERSION }}" + git push origin "v${{ env.VERSION }}" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + tag_name: "v${{ env.VERSION }}" + name: "CleanMyPosts ${{ env.VERSION }}" + body_path: ./release-notes/v${{ env.VERSION }}.md + files: | + artifacts/single-exe/CleanMyPosts-standalone.exe + artifacts/setup/CleanMyPosts-Setup-${{ env.VERSION }}.exe + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 26fd21818229c05250793136dcd96c780dd8a73e Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 14:28:19 +0200 Subject: [PATCH 02/20] Cleanup --- README.md | 11 +++++++++-- src/UI/Views/ShellWindow.xaml | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d240649..5fa8c25 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ -# CleanMyPosts +![Banner](./src/UI/Assets/logo.png) +[![Windows](https://img.shields.io/badge/platform-Windows-blue)](#) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=thorstenalpers_CleanMyPosts&metric=alert_status)](https://sonarcloud.io/project/issues?issueStatuses=OPEN%2CCONFIRMED&id=thorstenalpers_CleanMyPosts) +[![CI Tests](https://github.com/thorstenalpers/CleanMyPosts/actions/workflows/ci.yml/badge.svg)](https://github.com/thorstenalpers/CleanMyPosts/actions/workflows/ci.yml) +[![Coverage Status](https://coveralls.io/repos/github/thorstenalpers/CleanMyPosts/badge.svg?branch=develop)](https://coveralls.io/github/thorstenalpers/CleanMyPosts?branch=develop) +[![Star this repo](https://img.shields.io/github/stars/thorstenalpers/CleanMyPosts.svg?style=social&label=Star&maxAge=60)](https://github.com/thorstenalpers/CleanMyPosts) + > ⚠️ **Warning:** Development in progress – the application has not been released. @@ -21,7 +28,7 @@ * bookmark bar, hideable via settings -# 🧹 CleanMyPosts +--- **CleanMyPosts** is a lightweight Windows desktop application that securely deletes all tweets from your X (formerly Twitter) account in bulk. Designed for privacy-focused users, social media managers, or anyone looking to start fresh. diff --git a/src/UI/Views/ShellWindow.xaml b/src/UI/Views/ShellWindow.xaml index 17809a0..e5f2cc1 100644 --- a/src/UI/Views/ShellWindow.xaml +++ b/src/UI/Views/ShellWindow.xaml @@ -4,9 +4,9 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:fa="http://metro.mahapps.com/winfx/xaml/iconpacks/fontawesome" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:material="http://metro.mahapps.com/winfx/xaml/iconpacks/material" - xmlns:fa="http://metro.mahapps.com/winfx/xaml/iconpacks/fontawesome" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:properties="clr-namespace:CleanMyPosts.UI.Properties" xmlns:templateSelectors="clr-namespace:CleanMyPosts.UI.TemplateSelectors" From 4c80f6c4a1bedc5fc1c78abdbe4a104a96818dde Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 14:28:55 +0200 Subject: [PATCH 03/20] Add banner for readme --- src/UI/Assets/banner.png | Bin 0 -> 13301 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/UI/Assets/banner.png diff --git a/src/UI/Assets/banner.png b/src/UI/Assets/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..afbdec3ab9a14eeee34e40a60a3fa7f8e8d24a40 GIT binary patch literal 13301 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 T%}?^5e{Vj>smj(#n}z;=zqNte literal 0 HcmV?d00001 From aea125080dcf522a85b0d99ac3c25e9b9df070dc Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 14:29:16 +0200 Subject: [PATCH 04/20] Add first release notes --- release-notes/v0.0.1.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 release-notes/v0.0.1.md diff --git a/release-notes/v0.0.1.md b/release-notes/v0.0.1.md new file mode 100644 index 0000000..8bf8e03 --- /dev/null +++ b/release-notes/v0.0.1.md @@ -0,0 +1,3 @@ +### What's Changed + +* First Release From 8e743d767bb5af998fb800f53a98814beccfe938 Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 14:30:07 +0200 Subject: [PATCH 05/20] adapt script for GitHub CI/CD integration --- installer/Installer.iss | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/installer/Installer.iss b/installer/Installer.iss index 167cc16..acfb0bc 100644 --- a/installer/Installer.iss +++ b/installer/Installer.iss @@ -1,40 +1,32 @@ -; Script generated by the Inno Setup Script Wizard. -; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! - #define MyAppName "CleanMyPosts" -#define MyAppVersion "0.0.1" #define MyAppPublisher "Thorsten Alpers" #define MyAppURL "https://github.com/thorstenalpers/CleanMyPosts" #define MyAppExeName "CleanMyPosts.exe" #define MyIconPath "..\src\UI\Assets\logo.ico" -#define MyAppExePath "..\src\UI\bin\Release\net9.0-windows10.0.19041.0\win-x64\publish\*" + +; dynamically set in github actions, ifndef use local values +#ifndef MyAppVersion + #define MyAppVersion "0.0.1" +#endif +#ifndef MyAppExePath + #define MyAppExePath "..\src\UI\bin\Release\net9.0-windows10.0.19041.0\win-x64\publish\*" +#endif [Setup] -; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications. -; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) AppId={{AEE32610-58A5-4785-98B0-B651865B30D2}} AppName={#MyAppName} AppVersion={#MyAppVersion} -;AppVerName={#MyAppName} {#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} AppSupportURL={#MyAppURL} AppUpdatesURL={#MyAppURL} DefaultDirName={autopf}\{#MyAppName} UninstallDisplayIcon={app}\{#MyAppExeName} -; "ArchitecturesAllowed=x64compatible" specifies that Setup cannot run -; on anything but x64 and Windows 11 on Arm. ArchitecturesAllowed=x64compatible -; "ArchitecturesInstallIn64BitMode=x64compatible" requests that the -; install be done in "64-bit mode" on x64 or Windows 11 on Arm, -; meaning it should use the native 64-bit Program Files directory and -; the 64-bit view of the registry. ArchitecturesInstallIn64BitMode=x64compatible DisableProgramGroupPage=yes -; Uncomment the following line to run in non administrative install mode (install for current user only). PrivilegesRequired=admin -; PrivilegesRequiredOverridesAllowed=no -OutputBaseFilename=CleanMyPosts_Setup_{#MyAppVersion} +OutputBaseFilename=CleanMyPosts-Setup-{#MyAppVersion} SolidCompression=yes WizardStyle=modern SetupIconFile={#MyIconPath} @@ -49,13 +41,10 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{ Source: "{#MyAppExePath}"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "{#MyIconPath}"; DestDir: "{app}"; Flags: ignoreversion -; NOTE: Don't use "Flags: ignoreversion" on any shared system files - [Icons] Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\logo.ico" Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\logo.ico"; Tasks: desktopicon - [Run] Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent From 1c2a5f8238d70f119231198fcd2095b0779630e3 Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 14:33:26 +0200 Subject: [PATCH 06/20] Create an xml file for updater --- .github/workflows/deploy-release.yml | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-release.yml b/.github/workflows/deploy-release.yml index 43d6915..456eab2 100644 --- a/.github/workflows/deploy-release.yml +++ b/.github/workflows/deploy-release.yml @@ -54,8 +54,7 @@ jobs: -o artifacts/single-exe - name: Install Inno Setup - run: | - choco install innosetup --yes + run: choco install innosetup --yes - name: Build Setup EXE run: | @@ -68,7 +67,28 @@ jobs: - name: Rename Standalone EXE for Release run: | - Rename-Item "artifacts/single-exe/CleanMyPosts.exe" "CleanMyPosts-standalone.exe" + Rename-Item "artifacts/single-exe/CleanMyPosts.exe" "artifacts/single-exe/CleanMyPosts-standalone.exe" + + - name: Generate update.xml + shell: pwsh + run: | + $version = "${{ env.VERSION }}" + $repo = "${{ github.repository }}" + $baseUrl = "https://github.com/$repo/releases/download/v$version" + $installerUrl = "$baseUrl/CleanMyPosts-Setup-$version.exe" + $changelogUrl = "https://github.com/$repo/releases/tag/v$version" + $xmlContent = @" + + + + CleanMyPosts + $version + $installerUrl + $changelogUrl + + + "@ + $xmlContent | Set-Content -Path "artifacts/update.xml" -Encoding UTF8 - name: Configure Git Credentials run: | @@ -91,5 +111,6 @@ jobs: files: | artifacts/single-exe/CleanMyPosts-standalone.exe artifacts/setup/CleanMyPosts-Setup-${{ env.VERSION }}.exe + artifacts/update.xml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From d656e1a46b689d71d378b8c0f29da5fea6d52797 Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 14:38:17 +0200 Subject: [PATCH 07/20] Fix path to banner --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5fa8c25..ec9e0a1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Banner](./src/UI/Assets/logo.png) +![Banner](./src/UI/Assets/banner.png) [![Windows](https://img.shields.io/badge/platform-Windows-blue)](#) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=thorstenalpers_CleanMyPosts&metric=alert_status)](https://sonarcloud.io/project/issues?issueStatuses=OPEN%2CCONFIRMED&id=thorstenalpers_CleanMyPosts) From 27a0487caf7d2d5a9b5672dbfa274f8fbbaa237e Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 14:45:43 +0200 Subject: [PATCH 08/20] Fix usage of dotnet tools under windows --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6fd3bc7..cf3935e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | - ./dotnet-sonarscanner begin ` + dotnet tool run dotnet-sonarscanner begin ` /k:"thorstenalpers_CleanMyPosts" ` /o:"thorstenalpers" ` /d:sonar.token="${{ secrets.SONAR_TOKEN }}" ` @@ -58,12 +58,12 @@ jobs: --logger "console;verbosity=detailed" ` --filter "TestCategory!=Long-Running" - ./dotnet-reportgenerator ` + dotnet tool run dotnet-reportgenerator ` -reports:./TestResults/Tests/**/coverage.cobertura.xml ` -targetdir:./TestResults/Reports ` -reporttypes:"Html;lcov;SonarQube;Cobertura" - ./dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + dotnet tool run dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" - name: Upload to Coveralls uses: coverallsapp/github-action@v2 From d754170f3eb0ac151d69a7a5f3060accfa3718a0 Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 14:45:57 +0200 Subject: [PATCH 09/20] Add code coverage collection --- src/Tests/Tests.csproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Tests/Tests.csproj b/src/Tests/Tests.csproj index d62e402..f9d88ca 100644 --- a/src/Tests/Tests.csproj +++ b/src/Tests/Tests.csproj @@ -23,6 +23,14 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From c4a4655df50ddb9e05c4c6822ea7ea84f43dabb0 Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 14:48:14 +0200 Subject: [PATCH 10/20] Fix lint issues --- src/UI/App.xaml.cs | 2 +- src/UI/ViewModels/XViewModel.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/UI/App.xaml.cs b/src/UI/App.xaml.cs index 3ad67c4..98d5f60 100644 --- a/src/UI/App.xaml.cs +++ b/src/UI/App.xaml.cs @@ -116,7 +116,7 @@ private async void OnExit(object sender, ExitEventArgs e) logger.LogInformation("Application is stopping."); await _host.StopAsync(); _host.Dispose(); - Log.CloseAndFlush(); + await Log.CloseAndFlushAsync(); _host = null; } diff --git a/src/UI/ViewModels/XViewModel.cs b/src/UI/ViewModels/XViewModel.cs index 3a19a0e..422af76 100644 --- a/src/UI/ViewModels/XViewModel.cs +++ b/src/UI/ViewModels/XViewModel.cs @@ -34,7 +34,6 @@ public XViewModel(ILogger logger, _xWebViewScriptService = xWebViewScriptService ?? throw new ArgumentNullException(nameof(xWebViewScriptService)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); //_windowManagerService = windowManagerService ?? throw new ArgumentNullException(nameof(windowManagerService)); - _xWebViewScriptService = xWebViewScriptService ?? throw new ArgumentNullException(nameof(xWebViewScriptService)); _webViewHostService.NavigationCompleted += OnNavigationCompleted; _webViewHostService.WebMessageReceived += OnWebMessageReceived; } From 89c7fff9b9dfe849314e23fb6e0931573722b1a9 Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 14:59:02 +0200 Subject: [PATCH 11/20] Split tests into integration and unit tests --- .github/workflows/ci.yml | 33 +++++++++----- .github/workflows/deploy-release.yml | 10 +++-- src/CleanMyPosts.sln | 17 ++++++- src/IntegrationTests/IntegrationTests.csproj | 44 +++++++++++++++++++ src/{Tests => IntegrationTests}/PagesTests.cs | 2 +- .../SettingsViewModelTests.cs | 0 .../UnitTests.csproj} | 4 +- 7 files changed, 91 insertions(+), 19 deletions(-) create mode 100644 src/IntegrationTests/IntegrationTests.csproj rename src/{Tests => IntegrationTests}/PagesTests.cs (99%) rename src/{Tests => UnitTests}/SettingsViewModelTests.cs (100%) rename src/{Tests/Tests.csproj => UnitTests/UnitTests.csproj} (93%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf3935e..750f0e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,8 @@ jobs: env: Solution: "src/CleanMyPosts.sln" - Test_Project: "src/Tests/Tests.csproj" + UnitTest_Project: "src/UnitTests/UnitTests.csproj" + IntegrationTest_Project: "src/IntegrationTests/IntegrationTests.csproj" FORCE_COLOR: "true" DOTNET_LOGGING__CONSOLE__COLORBEHAVIOR: Enabled @@ -44,23 +45,31 @@ jobs: /d:sonar.token="${{ secrets.SONAR_TOKEN }}" ` /d:sonar.host.url="https://sonarcloud.io" ` /d:sonar.sources="src" ` - /d:sonar.tests="src/Tests" ` - /d:sonar.test.inclusions="src/Tests/**/*.cs" ` - /d:sonar.exclusions="**/*.html" ` - /d:sonar.coverageReportPaths="./TestResults/Reports/SonarQube.xml" + /d:sonar.tests="src/UnitTests;src/IntegrationTests" ` + /d:sonar.test.inclusions="**/*Tests.cs" ` + /d:sonar.coverageReportPaths="TestResults/Reports/SonarQube.xml" dotnet build "${{ env.Solution }}" --configuration Release --no-restore - dotnet test "${{ env.Test_Project }}" ` + # Run Unit Tests + dotnet test "${{ env.UnitTest_Project }}" ` --collect:"XPlat Code Coverage" ` - --results-directory ./TestResults/Tests ` + --results-directory TestResults/UnitTests ` --configuration Release ` --logger "console;verbosity=detailed" ` --filter "TestCategory!=Long-Running" + # Run Integration Tests + dotnet test "${{ env.IntegrationTest_Project }}" ` + --collect:"XPlat Code Coverage" ` + --results-directory TestResults/IntegrationTests ` + --configuration Release ` + --logger "console;verbosity=detailed" + + # Generate coverage reports dotnet tool run dotnet-reportgenerator ` - -reports:./TestResults/Tests/**/coverage.cobertura.xml ` - -targetdir:./TestResults/Reports ` + -reports:TestResults/**/coverage.cobertura.xml ` + -targetdir:TestResults/Reports ` -reporttypes:"Html;lcov;SonarQube;Cobertura" dotnet tool run dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" @@ -68,12 +77,12 @@ jobs: - name: Upload to Coveralls uses: coverallsapp/github-action@v2 with: - path-to-lcov: ./TestResults/Reports/lcov.info + path-to-lcov: TestResults/Reports/lcov.info env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} - name: Upload Test Coverage Report uses: actions/upload-artifact@v4 with: - name: unit-test-coverage-report - path: ./TestResults/Reports + name: test-coverage-report + path: TestResults/Reports diff --git a/.github/workflows/deploy-release.yml b/.github/workflows/deploy-release.yml index 456eab2..b6134bd 100644 --- a/.github/workflows/deploy-release.yml +++ b/.github/workflows/deploy-release.yml @@ -10,7 +10,8 @@ jobs: env: Solution: "src/CleanMyPosts.sln" UI_Project: "src/UI/UI.csproj" - Test_Project: "src/Tests/Tests.csproj" + UnitTest_Project: "src/UnitTests/UnitTests.csproj" + IntegrationTest_Project: "src/IntegrationTests/IntegrationTests.csproj" Installer_Script: "installer/Installer.iss" FORCE_COLOR: "true" DOTNET_LOGGING__CONSOLE__COLORBEHAVIOR: Enabled @@ -30,8 +31,11 @@ jobs: - name: Build run: dotnet build "${{ env.Solution }}" --configuration Release --no-restore - - name: Run tests - run: dotnet test "${{ env.Test_Project }}" --configuration Release --logger "console;verbosity=detailed" --filter "TestCategory!=Long-Running" + - name: Run Unit Tests + run: dotnet test "${{ env.UnitTest_Project }}" --configuration Release --logger "console;verbosity=detailed" --filter "TestCategory!=Long-Running" + + - name: Run Integration Tests + run: dotnet test "${{ env.IntegrationTest_Project }}" --configuration Release --logger "console;verbosity=detailed" - name: Extract Version id: get_version diff --git a/src/CleanMyPosts.sln b/src/CleanMyPosts.sln index e2176a9..31cbeab 100644 --- a/src/CleanMyPosts.sln +++ b/src/CleanMyPosts.sln @@ -7,7 +7,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UI", "UI\UI.csproj", "{48A1 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{A65F1C77-11C1-DC4F-0A69-6EE789D29685}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{F7B3E70B-3487-8FDC-C91B-9CA0F0F66BE0}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{F7B3E70B-3487-8FDC-C91B-9CA0F0F66BE0}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}" ProjectSection(SolutionItems) = preProject @@ -19,6 +19,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\README.md = ..\README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "IntegrationTests\IntegrationTests.csproj", "{59FDAA84-6701-32D0-9E5C-CA30D0B3B987}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + ..\.github\workflows\ci.yml = ..\.github\workflows\ci.yml + ..\.github\workflows\deploy-release.yml = ..\.github\workflows\deploy-release.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -37,10 +45,17 @@ Global {F7B3E70B-3487-8FDC-C91B-9CA0F0F66BE0}.Debug|Any CPU.Build.0 = Debug|Any CPU {F7B3E70B-3487-8FDC-C91B-9CA0F0F66BE0}.Release|Any CPU.ActiveCfg = Release|Any CPU {F7B3E70B-3487-8FDC-C91B-9CA0F0F66BE0}.Release|Any CPU.Build.0 = Release|Any CPU + {59FDAA84-6701-32D0-9E5C-CA30D0B3B987}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {59FDAA84-6701-32D0-9E5C-CA30D0B3B987}.Debug|Any CPU.Build.0 = Debug|Any CPU + {59FDAA84-6701-32D0-9E5C-CA30D0B3B987}.Release|Any CPU.ActiveCfg = Release|Any CPU + {59FDAA84-6701-32D0-9E5C-CA30D0B3B987}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {8EC462FD-D22E-90A8-E5CE-7E832BA40C5D} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C5A00240-64B9-4718-9682-317A36915078} EndGlobalSection diff --git a/src/IntegrationTests/IntegrationTests.csproj b/src/IntegrationTests/IntegrationTests.csproj new file mode 100644 index 0000000..30f6835 --- /dev/null +++ b/src/IntegrationTests/IntegrationTests.csproj @@ -0,0 +1,44 @@ + + + + net9.0-windows10.0.19041.0 + false + uap10.0.18362 + x64;x86;AnyCPU + CleanMyPosts.IntegrationTests + CleanMyPosts.IntegrationTests + enable + + + + x86 + + + + x64 + + + + AnyCPU + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/src/Tests/PagesTests.cs b/src/IntegrationTests/PagesTests.cs similarity index 99% rename from src/Tests/PagesTests.cs rename to src/IntegrationTests/PagesTests.cs index 46bcb58..5286228 100644 --- a/src/Tests/PagesTests.cs +++ b/src/IntegrationTests/PagesTests.cs @@ -13,7 +13,7 @@ namespace CleanMyPosts.Tests; -[Category("Unit")] +[Category("Integration")] public class PagesTests { private IHost _host; diff --git a/src/Tests/SettingsViewModelTests.cs b/src/UnitTests/SettingsViewModelTests.cs similarity index 100% rename from src/Tests/SettingsViewModelTests.cs rename to src/UnitTests/SettingsViewModelTests.cs diff --git a/src/Tests/Tests.csproj b/src/UnitTests/UnitTests.csproj similarity index 93% rename from src/Tests/Tests.csproj rename to src/UnitTests/UnitTests.csproj index f9d88ca..5b4e488 100644 --- a/src/Tests/Tests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -5,8 +5,8 @@ false uap10.0.18362 x64;x86;AnyCPU - CleanMyPosts.Tests - CleanMyPosts.UI + CleanMyPosts.UnitTests + CleanMyPosts.UnitTests enable From b3a7babc523c746e17bc6bb3695789ec459aac5a Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 15:00:11 +0200 Subject: [PATCH 12/20] Add newline between banner and badges --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ec9e0a1..a84844c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ ![Banner](./src/UI/Assets/banner.png) + + [![Windows](https://img.shields.io/badge/platform-Windows-blue)](#) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=thorstenalpers_CleanMyPosts&metric=alert_status)](https://sonarcloud.io/project/issues?issueStatuses=OPEN%2CCONFIRMED&id=thorstenalpers_CleanMyPosts) From 47656406d5e753121840c4f66d22dd59e3726c8e Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 15:03:20 +0200 Subject: [PATCH 13/20] Fix test --- src/UI/Properties/Resources.Designer.cs | 2 +- src/UI/Properties/Resources.resx | 2 +- src/UnitTests/SettingsViewModelTests.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/UI/Properties/Resources.Designer.cs b/src/UI/Properties/Resources.Designer.cs index e487eca..c92d32b 100644 --- a/src/UI/Properties/Resources.Designer.cs +++ b/src/UI/Properties/Resources.Designer.cs @@ -61,7 +61,7 @@ internal Resources() { } /// - /// Looks up a localized string similar to Clean My Posts. + /// Looks up a localized string similar to CleanMyPosts. /// public static string AppDisplayName { get { diff --git a/src/UI/Properties/Resources.resx b/src/UI/Properties/Resources.resx index 39e52fd..58c7ea8 100644 --- a/src/UI/Properties/Resources.resx +++ b/src/UI/Properties/Resources.resx @@ -172,7 +172,7 @@ Open or close navigation - Clean My Posts + CleanMyPosts Log diff --git a/src/UnitTests/SettingsViewModelTests.cs b/src/UnitTests/SettingsViewModelTests.cs index b9c096f..1f96e65 100644 --- a/src/UnitTests/SettingsViewModelTests.cs +++ b/src/UnitTests/SettingsViewModelTests.cs @@ -40,7 +40,7 @@ public void TestSettingsViewModel_SetCurrentVersion() Mock mockUpdateService = new(); Mock> mockLogger = new(); - Version testVersion = new(1, 2, 3, 4); + Version testVersion = new(1, 2, 3); mockApplicationInfoService.Setup(mock => mock.GetVersion()).Returns(testVersion); var settingsVm = new SettingsViewModel(mockThemeSelectorService.Object, From fecfe6c4a94c8924d58c014d134f632206e522fe Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 16:12:17 +0200 Subject: [PATCH 14/20] Make app testable --- README.md | 2 +- src/IntegrationTests/IntegrationTests.csproj | 15 +---- src/IntegrationTests/PagesTests.cs | 65 ++++--------------- src/IntegrationTests/TestHelper.cs | 58 +++++++++++++++++ src/UI/App.xaml.cs | 44 +------------ src/UI/AssemblyInfo.cs | 7 ++ src/UI/Helpers/ServiceCollectionExtensions.cs | 51 +++++++++++++++ src/UI/Models/UpdaterOptions.cs | 7 +- src/UI/Services/UpdateService.cs | 9 +-- src/UI/appsettings.json | 3 +- src/UnitTests/SettingsViewModelTests.cs | 2 +- src/UnitTests/UnitTests.csproj | 12 ---- 12 files changed, 145 insertions(+), 130 deletions(-) create mode 100644 src/IntegrationTests/TestHelper.cs create mode 100644 src/UI/AssemblyInfo.cs create mode 100644 src/UI/Helpers/ServiceCollectionExtensions.cs diff --git a/README.md b/README.md index a84844c..1b31fff 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Windows](https://img.shields.io/badge/platform-Windows-blue)](#) -[![License](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE.txt) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=thorstenalpers_CleanMyPosts&metric=alert_status)](https://sonarcloud.io/project/issues?issueStatuses=OPEN%2CCONFIRMED&id=thorstenalpers_CleanMyPosts) [![CI Tests](https://github.com/thorstenalpers/CleanMyPosts/actions/workflows/ci.yml/badge.svg)](https://github.com/thorstenalpers/CleanMyPosts/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/github/thorstenalpers/CleanMyPosts/badge.svg?branch=develop)](https://coveralls.io/github/thorstenalpers/CleanMyPosts?branch=develop) diff --git a/src/IntegrationTests/IntegrationTests.csproj b/src/IntegrationTests/IntegrationTests.csproj index 30f6835..31e1348 100644 --- a/src/IntegrationTests/IntegrationTests.csproj +++ b/src/IntegrationTests/IntegrationTests.csproj @@ -7,19 +7,8 @@ x64;x86;AnyCPU CleanMyPosts.IntegrationTests CleanMyPosts.IntegrationTests - enable - - - - x86 - - - - x64 - - - - AnyCPU + CleanMyPosts.IntegrationTests + enable diff --git a/src/IntegrationTests/PagesTests.cs b/src/IntegrationTests/PagesTests.cs index 5286228..9269c3f 100644 --- a/src/IntegrationTests/PagesTests.cs +++ b/src/IntegrationTests/PagesTests.cs @@ -1,17 +1,10 @@ -using System.Reflection; -using CleanMyPosts.Core.Contracts.Services; -using CleanMyPosts.Core.Services; -using CleanMyPosts.UI.Contracts.Services; -using CleanMyPosts.UI.Models; -using CleanMyPosts.UI.Services; +using CleanMyPosts.UI.Contracts.Services; using CleanMyPosts.UI.ViewModels; using CleanMyPosts.UI.Views; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using NUnit.Framework; -namespace CleanMyPosts.Tests; +namespace CleanMyPosts.IntegrationTests; [Category("Integration")] public class PagesTests @@ -21,31 +14,7 @@ public class PagesTests [SetUp] public void Setup() { - var appLocation = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); - _host = Host.CreateDefaultBuilder() - .ConfigureAppConfiguration(c => c.SetBasePath(appLocation)) - .ConfigureServices(ConfigureServices) - .Build(); - } - - private void ConfigureServices(HostBuilderContext context, IServiceCollection services) - { - // Core Services - services.AddSingleton(); - - // Services - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - - // ViewModels - services.AddTransient(); - services.AddTransient(); - - // Configuration - services.Configure(context.Configuration.GetSection(nameof(AppConfig))); + _host = TestHelper.SetUpHost(); } [Test] @@ -58,15 +27,11 @@ public void TestWebViewViewModelCreation() [Test] public void TestGetMainPageType() { - if (_host.Services.GetService(typeof(IPageService)) is IPageService pageService) - { - var pageType = pageService.GetPageType(typeof(XViewModel).FullName); - Assert.That(typeof(XPage), Is.EqualTo(pageType)); - } - else - { - Assert.Fail($"Can't resolve {nameof(IPageService)}"); - } + var pageService = _host.Services.GetService(typeof(IPageService)) as IPageService; + Assert.That(pageService, Is.Not.Null); + + var pageType = pageService.GetPageType(typeof(XViewModel).FullName); + Assert.That(typeof(XPage), Is.EqualTo(pageType)); } [Test] @@ -79,14 +44,10 @@ public void TestSettingsViewModelCreation() [Test] public void TestGetSettingsPageType() { - if (_host.Services.GetService(typeof(IPageService)) is IPageService pageService) - { - var pageType = pageService.GetPageType(typeof(SettingsViewModel).FullName); - Assert.That(typeof(SettingsPage), Is.EqualTo(pageType)); - } - else - { - Assert.Fail($"Can't resolve {nameof(IPageService)}"); - } + var pageService = _host.Services.GetService(typeof(IPageService)) as IPageService; + Assert.That(pageService, Is.Not.Null); + + var pageType = pageService.GetPageType(typeof(SettingsViewModel).FullName); + Assert.That(typeof(SettingsPage), Is.EqualTo(pageType)); } } diff --git a/src/IntegrationTests/TestHelper.cs b/src/IntegrationTests/TestHelper.cs new file mode 100644 index 0000000..709c45a --- /dev/null +++ b/src/IntegrationTests/TestHelper.cs @@ -0,0 +1,58 @@ +using CleanMyPosts.UI.Helpers; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace CleanMyPosts.IntegrationTests; + +internal static class TestHelper +{ + public static IHost SetUpHost() + { + JsonConvert.DefaultSettings = () => new JsonSerializerSettings + { + Converters = { new StringEnumConverter() }, + Formatting = Formatting.Indented + }; + + var cfgBuilder = new ConfigurationBuilder(); + cfgBuilder.AddInMemoryCollection(new Dictionary + { + ["UpdateSettings:AppCastUrl"] = "https://example.com/appcast.xml", + ["UpdateSettings:SecurityMode"] = "Unsafe" + }); + cfgBuilder.AddUserSecrets(); + cfgBuilder.AddEnvironmentVariables(); + var cfg = cfgBuilder.Build(); + + var host = Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((context, config) => + { + config.AddConfiguration(cfg); // reuse custom config + }) + .ConfigureServices((context, services) => + { + services.AddCleanMyPosts(cfg); // your custom DI setup + }) + .ConfigureLogging(logging => + { + logging.ClearProviders(); + logging.AddConsole(); + logging.SetMinimumLevel(LogLevel.Information); + logging.AddFilter("System.Net.Http.HttpClient", LogLevel.Warning); + logging.AddFilter("CleanMyPosts", LogLevel.Information); + + logging.AddSimpleConsole(options => + { + options.UseUtcTimestamp = true; + options.SingleLine = true; + options.ColorBehavior = Microsoft.Extensions.Logging.Console.LoggerColorBehavior.Enabled; + }); + }) + .Build(); + + return host; + } +} diff --git a/src/UI/App.xaml.cs b/src/UI/App.xaml.cs index 98d5f60..e08f676 100644 --- a/src/UI/App.xaml.cs +++ b/src/UI/App.xaml.cs @@ -1,14 +1,7 @@ using System.Windows; using System.Windows.Threading; -using CleanMyPosts.Core.Contracts.Services; -using CleanMyPosts.Core.Services; -using CleanMyPosts.UI.Contracts.Services; -using CleanMyPosts.UI.Contracts.Views; using CleanMyPosts.UI.Helpers; -using CleanMyPosts.UI.Models; -using CleanMyPosts.UI.Services; using CleanMyPosts.UI.ViewModels; -using CleanMyPosts.UI.Views; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -52,13 +45,12 @@ private async void OnStartup(object sender, StartupEventArgs e) }) .Build(); - // 🔧 LogViewModel must be resolved **after** the host is built var logViewModel = _host.Services.GetRequiredService(); Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(config) .WriteTo.Debug() - .WriteTo.LogViewModelSink(logViewModel) // custom sink + .WriteTo.LogViewModelSink(logViewModel) .CreateLogger(); await _host.StartAsync(); @@ -75,39 +67,7 @@ private async void OnStartup(object sender, StartupEventArgs e) internal static void ConfigureServices(HostBuilderContext context, IServiceCollection services) { - // Your existing registrations - services.AddHostedService(); - - services.AddSingleton(); - services.AddSingleton(); - - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - - services.AddTransient(); - services.AddTransient(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - - services.AddTransient(); - services.AddTransient(); - - services.AddTransient(); - services.AddTransient(); - - services.AddHttpClient(); - - services.Configure(context.Configuration.GetSection(nameof(AppConfig))); - services.Configure(context.Configuration.GetSection("UpdateSettings")); + services.AddCleanMyPosts(context.Configuration); } private async void OnExit(object sender, ExitEventArgs e) diff --git a/src/UI/AssemblyInfo.cs b/src/UI/AssemblyInfo.cs new file mode 100644 index 0000000..2aa049f --- /dev/null +++ b/src/UI/AssemblyInfo.cs @@ -0,0 +1,7 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("CleanMyPosts.UnitTests")] +[assembly: InternalsVisibleTo("CleanMyPosts.IntegrationTests")] + +// Moq: ILogger needs it to mock internal classes +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/UI/Helpers/ServiceCollectionExtensions.cs b/src/UI/Helpers/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..d0031f5 --- /dev/null +++ b/src/UI/Helpers/ServiceCollectionExtensions.cs @@ -0,0 +1,51 @@ +using CleanMyPosts.Core.Contracts.Services; +using CleanMyPosts.Core.Services; +using CleanMyPosts.UI.Contracts.Services; +using CleanMyPosts.UI.Contracts.Views; +using CleanMyPosts.UI.Models; +using CleanMyPosts.UI.Services; +using CleanMyPosts.UI.ViewModels; +using CleanMyPosts.UI.Views; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace CleanMyPosts.UI.Helpers; + +public static class ServiceCollectionExtensions +{ + public static void AddCleanMyPosts(this IServiceCollection services, IConfiguration configuration) + { + services.AddHostedService(); + + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddTransient(); + services.AddTransient(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddTransient(); + services.AddTransient(); + + services.AddTransient(); + services.AddTransient(); + + services.AddHttpClient(); + + services.Configure(configuration.GetSection(nameof(AppConfig))); + services.Configure(configuration.GetSection("Updater")); + } +} diff --git a/src/UI/Models/UpdaterOptions.cs b/src/UI/Models/UpdaterOptions.cs index bb6f7cf..46434d6 100644 --- a/src/UI/Models/UpdaterOptions.cs +++ b/src/UI/Models/UpdaterOptions.cs @@ -1,7 +1,10 @@ -namespace CleanMyPosts.UI.Models; +using NetSparkleUpdater.Enums; + +namespace CleanMyPosts.UI.Models; public class UpdaterOptions { public string AppCastUrl { get; set; } - public string SecurityMode { get; set; } + public SecurityMode SecurityMode { get; set; } + public string IconUri { get; set; } } \ No newline at end of file diff --git a/src/UI/Services/UpdateService.cs b/src/UI/Services/UpdateService.cs index 1c75cca..3f5bd9d 100644 --- a/src/UI/Services/UpdateService.cs +++ b/src/UI/Services/UpdateService.cs @@ -3,7 +3,6 @@ using CleanMyPosts.UI.Models; using Microsoft.Extensions.Options; using NetSparkleUpdater; -using NetSparkleUpdater.Enums; using NetSparkleUpdater.SignatureVerifiers; using NetSparkleUpdater.UI.WPF; @@ -12,15 +11,13 @@ namespace CleanMyPosts.UI.Services; public class UpdateService : IUpdateService { private readonly SparkleUpdater _sparkle; + public UpdateService(IOptions options) { - var securityMode = options.Value.SecurityMode == "Strict" ? SecurityMode.Strict : SecurityMode.Unsafe; - - var uri = new Uri("pack://application:,,,/CleanMyPosts;component/Assets/logo.ico", UriKind.Absolute); + var uri = new Uri(options.Value.IconUri, UriKind.Absolute); var imageSource = new BitmapImage(uri); - - var verifier = new DSAChecker(securityMode); + var verifier = new DSAChecker(options.Value.SecurityMode); _sparkle = new SparkleUpdater(options.Value.AppCastUrl, verifier) { UIFactory = new UIFactory(imageSource), diff --git a/src/UI/appsettings.json b/src/UI/appsettings.json index 4b3fc62..6ba13be 100644 --- a/src/UI/appsettings.json +++ b/src/UI/appsettings.json @@ -5,7 +5,8 @@ }, "Updater": { "AppCastUrl": "https://raw.githubusercontent.com/youruser/yourrepo/main/AutoUpdater.xml", - "SecurityMode": "Strict" + "SecurityMode": "Strict", + "IconUri": "pack://application:,,,/CleanMyPosts;component/Assets/logo.ico" }, "Serilog": { "MinimumLevel": { diff --git a/src/UnitTests/SettingsViewModelTests.cs b/src/UnitTests/SettingsViewModelTests.cs index 1f96e65..5d7ffa5 100644 --- a/src/UnitTests/SettingsViewModelTests.cs +++ b/src/UnitTests/SettingsViewModelTests.cs @@ -5,7 +5,7 @@ using Moq; using NUnit.Framework; -namespace CleanMyPosts.Tests; +namespace CleanMyPosts.UnitTests; [Category("Unit")] public class SettingsViewModelTests diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index 5b4e488..7815e87 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -10,18 +10,6 @@ enable - - x86 - - - - x64 - - - - AnyCPU - - all From e772f8e83e3394d293aebc7520f3466388877d55 Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 16:19:54 +0200 Subject: [PATCH 15/20] Save update.xml on its own branch --- .github/workflows/deploy-release.yml | 26 ++++++++++++++++++++++++-- src/UI/appsettings.json | 2 +- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-release.yml b/.github/workflows/deploy-release.yml index b6134bd..ff381b9 100644 --- a/.github/workflows/deploy-release.yml +++ b/.github/workflows/deploy-release.yml @@ -19,6 +19,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 # Important to fetch full history for git tag and branches - name: Install .NET uses: actions/setup-dotnet@v4 @@ -82,7 +84,7 @@ jobs: $installerUrl = "$baseUrl/CleanMyPosts-Setup-$version.exe" $changelogUrl = "https://github.com/$repo/releases/tag/v$version" $xmlContent = @" - + CleanMyPosts @@ -98,9 +100,29 @@ jobs: run: | echo "https://${{ secrets.GH_APIKEY }}@github.com" > $env:USERPROFILE\.git-credentials git config --global credential.helper store - git config --global user.name "GitHub Actions" + git config --global user.name "github-actions" git config --global user.email "actions@github.com" + - name: Push update.xml to update-feed branch + run: | + git fetch origin update-feed || echo "No update-feed branch yet" + if git show-ref --verify --quiet refs/heads/update-feed; then + git checkout update-feed + else + git checkout --orphan update-feed + git rm -rf . + fi + + cp artifacts/update.xml update.xml + git add update.xml + + if git diff --cached --quiet; then + echo "No changes in update.xml; skipping commit" + else + git commit -m "Update appcast for version ${{ env.VERSION }}" + git push origin update-feed --force + fi + - name: Create Git Tag run: | git tag -a "v${{ env.VERSION }}" -m "Release v${{ env.VERSION }}" diff --git a/src/UI/appsettings.json b/src/UI/appsettings.json index 6ba13be..dd5e0d2 100644 --- a/src/UI/appsettings.json +++ b/src/UI/appsettings.json @@ -4,7 +4,7 @@ "appPropertiesFileName": "AppProperties.json" }, "Updater": { - "AppCastUrl": "https://raw.githubusercontent.com/youruser/yourrepo/main/AutoUpdater.xml", + "AppCastUrl": "https://raw.githubusercontent.com/thorstenalpers/CleanMyPosts/refs/heads/update-feed/update.xml", "SecurityMode": "Strict", "IconUri": "pack://application:,,,/CleanMyPosts;component/Assets/logo.ico" }, From 04854e32dcb72c637591e4301e97f3534de339c5 Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 16:44:20 +0200 Subject: [PATCH 16/20] Fix test --- src/IntegrationTests/TestHelper.cs | 14 ++++++++++---- src/UI/Helpers/ServiceCollectionExtensions.cs | 19 ++++++++++++++++++- src/UI/Services/UpdateService.cs | 19 +++++++++++-------- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/IntegrationTests/TestHelper.cs b/src/IntegrationTests/TestHelper.cs index 709c45a..6ab724f 100644 --- a/src/IntegrationTests/TestHelper.cs +++ b/src/IntegrationTests/TestHelper.cs @@ -1,7 +1,10 @@ using CleanMyPosts.UI.Helpers; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Moq; +using NetSparkleUpdater.Interfaces; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -20,8 +23,9 @@ public static IHost SetUpHost() var cfgBuilder = new ConfigurationBuilder(); cfgBuilder.AddInMemoryCollection(new Dictionary { - ["UpdateSettings:AppCastUrl"] = "https://example.com/appcast.xml", - ["UpdateSettings:SecurityMode"] = "Unsafe" + ["Updater:AppCastUrl"] = "https://example.com/appcast.xml", + ["Updater:SecurityMode"] = "Unsafe", + ["Updater:IconUri"] = "https://raw.githubusercontent.com/thorstenalpers/CleanMyPosts/refs/heads/main/src/UI/Assets/logo.ico" }); cfgBuilder.AddUserSecrets(); cfgBuilder.AddEnvironmentVariables(); @@ -30,11 +34,13 @@ public static IHost SetUpHost() var host = Host.CreateDefaultBuilder() .ConfigureAppConfiguration((context, config) => { - config.AddConfiguration(cfg); // reuse custom config + config.AddConfiguration(cfg); }) .ConfigureServices((context, services) => { - services.AddCleanMyPosts(cfg); // your custom DI setup + services.AddCleanMyPosts(cfg); + var mockUIFactory = new Mock(); + services.AddSingleton(mockUIFactory.Object); }) .ConfigureLogging(logging => { diff --git a/src/UI/Helpers/ServiceCollectionExtensions.cs b/src/UI/Helpers/ServiceCollectionExtensions.cs index d0031f5..d9b8106 100644 --- a/src/UI/Helpers/ServiceCollectionExtensions.cs +++ b/src/UI/Helpers/ServiceCollectionExtensions.cs @@ -1,4 +1,6 @@ -using CleanMyPosts.Core.Contracts.Services; +using System.Windows; +using System.Windows.Media.Imaging; +using CleanMyPosts.Core.Contracts.Services; using CleanMyPosts.Core.Services; using CleanMyPosts.UI.Contracts.Services; using CleanMyPosts.UI.Contracts.Views; @@ -8,6 +10,9 @@ using CleanMyPosts.UI.Views; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using NetSparkleUpdater.Interfaces; +using NetSparkleUpdater.UI.WPF; namespace CleanMyPosts.UI.Helpers; @@ -19,6 +24,18 @@ public static void AddCleanMyPosts(this IServiceCollection services, IConfigurat services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(sp => + { + UIFactory factory = null; + Application.Current.Dispatcher.Invoke(() => + { + var options = sp.GetRequiredService>().Value; + var uri = new Uri(options.IconUri, UriKind.Absolute); + var imageSource = new BitmapImage(uri); + factory = new UIFactory(imageSource); + }); + return factory; + }); services.AddSingleton(); services.AddSingleton(); diff --git a/src/UI/Services/UpdateService.cs b/src/UI/Services/UpdateService.cs index 3f5bd9d..7d2b3eb 100644 --- a/src/UI/Services/UpdateService.cs +++ b/src/UI/Services/UpdateService.cs @@ -1,10 +1,10 @@ -using System.Windows.Media.Imaging; +using Ardalis.GuardClauses; using CleanMyPosts.UI.Contracts.Services; using CleanMyPosts.UI.Models; using Microsoft.Extensions.Options; using NetSparkleUpdater; +using NetSparkleUpdater.Interfaces; using NetSparkleUpdater.SignatureVerifiers; -using NetSparkleUpdater.UI.WPF; namespace CleanMyPosts.UI.Services; @@ -12,15 +12,18 @@ public class UpdateService : IUpdateService { private readonly SparkleUpdater _sparkle; - public UpdateService(IOptions options) + public UpdateService(IOptions options, IUIFactory uIFactory) { - var uri = new Uri(options.Value.IconUri, UriKind.Absolute); - var imageSource = new BitmapImage(uri); + var opts = options.Value; - var verifier = new DSAChecker(options.Value.SecurityMode); - _sparkle = new SparkleUpdater(options.Value.AppCastUrl, verifier) + Guard.Against.Null(opts, nameof(options)); + Guard.Against.NullOrWhiteSpace(opts.AppCastUrl, nameof(opts.AppCastUrl)); + Guard.Against.NullOrWhiteSpace(opts.SecurityMode.ToString(), nameof(opts.SecurityMode)); + + var verifier = new DSAChecker(opts.SecurityMode); + _sparkle = new SparkleUpdater(opts.AppCastUrl, verifier) { - UIFactory = new UIFactory(imageSource), + UIFactory = uIFactory, RelaunchAfterUpdate = true, UseNotificationToast = true }; From 4cfe8e761b7698e97ae8fabecdf09e926060b808 Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 17:33:51 +0200 Subject: [PATCH 17/20] Fix ci --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 750f0e4..3a57bda 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,10 +67,11 @@ jobs: --logger "console;verbosity=detailed" # Generate coverage reports - dotnet tool run dotnet-reportgenerator ` + dotnet tool run reportgenerator ` -reports:TestResults/**/coverage.cobertura.xml ` -targetdir:TestResults/Reports ` - -reporttypes:"Html;lcov;SonarQube;Cobertura" + -reporttypes:"SonarQube" ` + -reportfile:"SonarQube.xml" dotnet tool run dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" From 3468ae3fdef7b63657741b76eadeb12d589e333c Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 17:43:51 +0200 Subject: [PATCH 18/20] Fix sonar reports --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a57bda..3ac28b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,8 +70,7 @@ jobs: dotnet tool run reportgenerator ` -reports:TestResults/**/coverage.cobertura.xml ` -targetdir:TestResults/Reports ` - -reporttypes:"SonarQube" ` - -reportfile:"SonarQube.xml" + -reporttypes:"Html;lcov;SonarQube;Cobertura" ` dotnet tool run dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" From f166297816a8f157f1f09e4a3c8cb91233e1014c Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 18:08:28 +0200 Subject: [PATCH 19/20] Fix sonar lint issues --- src/UI/App.xaml.cs | 3 ++- src/UI/Converters/EnumToBooleanConverter.cs | 18 ++++-------------- src/UI/Helpers/Helper.cs | 2 +- src/UI/Models/AppConfig.cs | 1 + src/UI/Services/ApplicationHostService.cs | 11 +++++------ src/UI/Services/UpdateService.cs | 6 +++--- src/UI/Services/WindowManagerService.cs | 16 ++++++++-------- src/UI/Services/XWebViewScriptService.cs | 19 +++++++------------ src/UI/ViewModels/XViewModel.cs | 19 ++++++++----------- src/UI/Views/OverlayWindow.xaml.cs | 2 +- src/UI/appsettings.json | 3 ++- 11 files changed, 42 insertions(+), 58 deletions(-) diff --git a/src/UI/App.xaml.cs b/src/UI/App.xaml.cs index e08f676..fbd7f2a 100644 --- a/src/UI/App.xaml.cs +++ b/src/UI/App.xaml.cs @@ -1,5 +1,6 @@ using System.Windows; using System.Windows.Threading; +using CleanMyPosts.Core.Exception; using CleanMyPosts.UI.Helpers; using CleanMyPosts.UI.ViewModels; using Microsoft.Extensions.Configuration; @@ -61,7 +62,7 @@ private async void OnStartup(object sender, StartupEventArgs e) catch (Exception ex) { Log.Fatal(ex, "Application start-up failed."); - throw; + throw new CleanMyPostsException("Application start-up failed.", ex); } } diff --git a/src/UI/Converters/EnumToBooleanConverter.cs b/src/UI/Converters/EnumToBooleanConverter.cs index 38df352..d049659 100644 --- a/src/UI/Converters/EnumToBooleanConverter.cs +++ b/src/UI/Converters/EnumToBooleanConverter.cs @@ -9,26 +9,16 @@ public class EnumToBooleanConverter : IValueConverter public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (parameter is string enumString) + if (parameter is string enumString && Enum.IsDefined(EnumType, value)) { - if (Enum.IsDefined(EnumType, value)) - { - var enumValue = Enum.Parse(EnumType, enumString); - - return enumValue.Equals(value); - } + var enumValue = Enum.Parse(EnumType, enumString); + return enumValue.Equals(value); } - return false; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - if (parameter is string enumString) - { - return Enum.Parse(EnumType, enumString); - } - - return null; + return parameter is string enumString ? Enum.Parse(EnumType, enumString) : null; } } diff --git a/src/UI/Helpers/Helper.cs b/src/UI/Helpers/Helper.cs index 6f7d081..fccf091 100644 --- a/src/UI/Helpers/Helper.cs +++ b/src/UI/Helpers/Helper.cs @@ -3,6 +3,6 @@ public static class Helper { public static string CleanJsonResult(string json) { - return json?.Replace("\\\"", "\"")?.Trim('\"') ?? ""; + return json.Replace("\\\"", "\"")?.Trim('\"') ?? ""; } } diff --git a/src/UI/Models/AppConfig.cs b/src/UI/Models/AppConfig.cs index 7fa747f..8c8479e 100644 --- a/src/UI/Models/AppConfig.cs +++ b/src/UI/Models/AppConfig.cs @@ -4,4 +4,5 @@ public class AppConfig { public string ConfigurationsFolder { get; set; } public string AppPropertiesFileName { get; set; } + public string XBaseUrl { get; set; } } diff --git a/src/UI/Services/ApplicationHostService.cs b/src/UI/Services/ApplicationHostService.cs index f0a6f4b..d0fe5ef 100644 --- a/src/UI/Services/ApplicationHostService.cs +++ b/src/UI/Services/ApplicationHostService.cs @@ -1,8 +1,8 @@ -using Microsoft.Extensions.Hosting; -using CleanMyPosts.UI.Contracts.Activation; +using CleanMyPosts.UI.Contracts.Activation; using CleanMyPosts.UI.Contracts.Services; using CleanMyPosts.UI.Contracts.Views; using CleanMyPosts.UI.ViewModels; +using Microsoft.Extensions.Hosting; namespace CleanMyPosts.UI.Services; @@ -17,7 +17,6 @@ public class ApplicationHostService(IServiceProvider serviceProvider, private readonly IPersistAndRestoreService _persistAndRestoreService = persistAndRestoreService; private readonly IThemeSelectorService _themeSelectorService = themeSelectorService; private readonly IEnumerable _activationHandlers = activationHandlers; - private IShellWindow _shellWindow; private bool _isInitialized; public async Task StartAsync(CancellationToken cancellationToken) @@ -65,9 +64,9 @@ private async Task HandleActivationAsync() if (!System.Windows.Application.Current.Windows.OfType().Any()) { - _shellWindow = _serviceProvider.GetService(typeof(IShellWindow)) as IShellWindow; - _navigationService.Initialize(_shellWindow.GetNavigationFrame()); - _shellWindow.ShowWindow(); + var shellWindow = _serviceProvider.GetService(typeof(IShellWindow)) as IShellWindow; + _navigationService.Initialize(shellWindow.GetNavigationFrame()); + shellWindow.ShowWindow(); _navigationService.NavigateTo(typeof(XViewModel).FullName); await Task.CompletedTask; } diff --git a/src/UI/Services/UpdateService.cs b/src/UI/Services/UpdateService.cs index 7d2b3eb..f887b50 100644 --- a/src/UI/Services/UpdateService.cs +++ b/src/UI/Services/UpdateService.cs @@ -16,9 +16,9 @@ public UpdateService(IOptions options, IUIFactory uIFactory) { var opts = options.Value; - Guard.Against.Null(opts, nameof(options)); - Guard.Against.NullOrWhiteSpace(opts.AppCastUrl, nameof(opts.AppCastUrl)); - Guard.Against.NullOrWhiteSpace(opts.SecurityMode.ToString(), nameof(opts.SecurityMode)); + Guard.Against.Null(opts); + Guard.Against.NullOrWhiteSpace(opts.AppCastUrl); + Guard.Against.NullOrWhiteSpace(opts.SecurityMode.ToString()); var verifier = new DSAChecker(opts.SecurityMode); _sparkle = new SparkleUpdater(opts.AppCastUrl, verifier) diff --git a/src/UI/Services/WindowManagerService.cs b/src/UI/Services/WindowManagerService.cs index af313f8..e4ec40e 100644 --- a/src/UI/Services/WindowManagerService.cs +++ b/src/UI/Services/WindowManagerService.cs @@ -15,9 +15,9 @@ public class WindowManagerService(IServiceProvider serviceProvider, IPageService private readonly IPageService _pageService = pageService; public Window MainWindow => Application.Current.MainWindow; - public void OpenInNewWindow(string key, object parameter = null) + public void OpenInNewWindow(string pageKey, object parameter = null) { - var existingWindow = GetWindow(key); + var existingWindow = GetWindow(pageKey); if (existingWindow is not null) { existingWindow.Activate(); @@ -39,11 +39,11 @@ public void OpenInNewWindow(string key, object parameter = null) frame.Navigated += OnNavigated; newWindow.Closed += OnWindowClosed; - frame.Navigate(_pageService.GetPage(key), parameter); + frame.Navigate(_pageService.GetPage(pageKey), parameter); newWindow.Show(); } - public bool? OpenInDialog(string key, object parameter = null) + public bool? OpenInDialog(string pageKey, object parameter = null) { if (_serviceProvider.GetService(typeof(IShellDialogWindow)) is not IShellDialogWindow shellWindow) { @@ -54,17 +54,17 @@ public void OpenInNewWindow(string key, object parameter = null) frame.Navigated += OnNavigated; ((Window)shellWindow).Closed += OnWindowClosed; - frame.Navigate(_pageService.GetPage(key), parameter); + frame.Navigate(_pageService.GetPage(pageKey), parameter); return ((Window)shellWindow).ShowDialog(); } - public Window GetWindow(string key) + public Window GetWindow(string pageKey) { foreach (Window window in Application.Current.Windows) { var dataContext = window.GetDataContext(); - if (dataContext?.GetType().FullName == key) + if (dataContext?.GetType().FullName == pageKey) { return window; } @@ -72,7 +72,7 @@ public Window GetWindow(string key) return null; } - private void OnNavigated(object sender, NavigationEventArgs e) + private static void OnNavigated(object sender, NavigationEventArgs e) { if (sender is Frame frame && frame.GetDataContext() is INavigationAware navigationAware) diff --git a/src/UI/Services/XWebViewScriptService.cs b/src/UI/Services/XWebViewScriptService.cs index 02afb43..f9339ba 100644 --- a/src/UI/Services/XWebViewScriptService.cs +++ b/src/UI/Services/XWebViewScriptService.cs @@ -15,9 +15,8 @@ public class XWebViewScriptService(ILogger logger, IWebVi public async Task ShowPostsAsync() { - Guard.Against.Null(_userName, nameof(_userName)); + Guard.Against.Null(_userName); - //var searchQuery = $"from:{_userName} since:2000-01-01"; var searchQuery = $"from:{_userName}"; var encodedQuery = WebUtility.UrlEncode(searchQuery); var url = new Uri($"https://x.com/search?q={encodedQuery}&src=typed_query"); @@ -27,17 +26,15 @@ public async Task ShowPostsAsync() _webViewHostService.Source = url; if (!await WaitForFullDocumentReadyAsync()) { - _logger.LogWarning("Navigation to {url} failed.", url); - return; + _logger.LogWarning("Navigation to {Url} failed.", url); } } } public async Task DeletePostsAsync() { - Guard.Against.Null(_userName, nameof(_userName)); + Guard.Against.Null(_userName); - //var searchQuery = $"from:{_userName} since:2000-01-01"; var searchQuery = $"from:{_userName}"; var encodedQuery = WebUtility.UrlEncode(searchQuery); var url = new Uri($"https://x.com/search?q={encodedQuery}&src=typed_query"); @@ -85,7 +82,7 @@ public async Task DeletePostsAsync() public async Task ShowLikesAsync() { - Guard.Against.Null(_userName, nameof(_userName)); + Guard.Against.Null(_userName); var url = new Uri($"https://x.com/{WebUtility.UrlEncode(_userName)}/likes"); if (_webViewHostService.Source != url) @@ -93,8 +90,7 @@ public async Task ShowLikesAsync() _webViewHostService.Source = url; if (!await WaitForFullDocumentReadyAsync()) { - _logger.LogWarning("Navigation to {url} failed.", url); - return; + _logger.LogWarning("Navigation to {Url} failed.", url); } } } @@ -106,7 +102,7 @@ public Task DeleteStarredAsync() public async Task ShowFollowingAsync() { - Guard.Against.Null(_userName, nameof(_userName)); + Guard.Against.Null(_userName); var url = new Uri($"https://x.com/{WebUtility.UrlEncode(_userName)}/following"); if (_webViewHostService.Source != url) @@ -114,8 +110,7 @@ public async Task ShowFollowingAsync() _webViewHostService.Source = url; if (!await WaitForFullDocumentReadyAsync()) { - _logger.LogWarning("Navigation to {url} failed.", url); - return; + _logger.LogWarning("Navigation to {Url} failed.", url); } } } diff --git a/src/UI/ViewModels/XViewModel.cs b/src/UI/ViewModels/XViewModel.cs index 422af76..d10a3dc 100644 --- a/src/UI/ViewModels/XViewModel.cs +++ b/src/UI/ViewModels/XViewModel.cs @@ -5,6 +5,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Microsoft.Web.WebView2.Wpf; namespace CleanMyPosts.UI.ViewModels; @@ -14,10 +15,9 @@ public partial class XViewModel : ObservableObject private readonly IWebViewHostService _webViewHostService; private readonly ILogger _logger; private readonly IXWebViewScriptService _xWebViewScriptService; - //private readonly IWindowManagerService _windowManagerService; private OverlayWindow _overlayWindow; - private const string XBaseUrl = "https://x.com"; + private readonly string xBaseUrl; [ObservableProperty] private bool _areButtonsEnabled; @@ -26,16 +26,16 @@ public partial class XViewModel : ObservableObject private string _userName; public XViewModel(ILogger logger, - //IWindowManagerService windowManagerService, IWebViewHostService webViewHostService, + IOptions options, IXWebViewScriptService xWebViewScriptService) { _webViewHostService = webViewHostService ?? throw new ArgumentNullException(nameof(webViewHostService)); _xWebViewScriptService = xWebViewScriptService ?? throw new ArgumentNullException(nameof(xWebViewScriptService)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - //_windowManagerService = windowManagerService ?? throw new ArgumentNullException(nameof(windowManagerService)); _webViewHostService.NavigationCompleted += OnNavigationCompleted; _webViewHostService.WebMessageReceived += OnWebMessageReceived; + xBaseUrl = options.Value.XBaseUrl; } public async Task InitializeAsync(WebView2 webView) @@ -47,7 +47,7 @@ public async Task InitializeAsync(WebView2 webView) await _webViewHostService.InitializeAsync(webView); - _webViewHostService.Source = new Uri(XBaseUrl); + _webViewHostService.Source = new Uri(xBaseUrl); var jsScript = @" window.onerror = function(message, source, lineno, colno, error) { @@ -162,8 +162,7 @@ private async Task ShowPosts() private async Task DeletePosts() { EnableUserInteractions(false); - await Task.Delay(10000); - //await _xWebViewScriptService.DeleteAllPostsAsync(_webView); + await _xWebViewScriptService.DeletePostsAsync(); EnableUserInteractions(true); } @@ -179,8 +178,7 @@ private async Task ShowLikes() private async Task DeleteLikes() { EnableUserInteractions(false); - await Task.Delay(10000); - //await _xWebViewScriptService.DeleteAllPostsAsync(_webView); + await Task.Delay(10002); EnableUserInteractions(true); } @@ -196,8 +194,7 @@ private async Task ShowFollowing() private async Task DeleteFollowing() { EnableUserInteractions(false); - await Task.Delay(10000); - //await _xWebViewScriptService.DeleteAllPostsAsync(_webView); + await Task.Delay(10001); EnableUserInteractions(true); } diff --git a/src/UI/Views/OverlayWindow.xaml.cs b/src/UI/Views/OverlayWindow.xaml.cs index 4555872..c7879d6 100644 --- a/src/UI/Views/OverlayWindow.xaml.cs +++ b/src/UI/Views/OverlayWindow.xaml.cs @@ -22,7 +22,7 @@ private void Window_MouseDown(object sender, MouseButtonEventArgs e) try { // Try dragging the main window instead - ((Window)mainWindow).DragMove(); + mainWindow.DragMove(); } catch (InvalidOperationException) { diff --git a/src/UI/appsettings.json b/src/UI/appsettings.json index dd5e0d2..ea35664 100644 --- a/src/UI/appsettings.json +++ b/src/UI/appsettings.json @@ -1,7 +1,8 @@ { "AppConfig": { "configurationsFolder": "CleanMyPosts\\Configurations", - "appPropertiesFileName": "AppProperties.json" + "appPropertiesFileName": "AppProperties.json", + "XBaseUrl": "https://x.com" }, "Updater": { "AppCastUrl": "https://raw.githubusercontent.com/thorstenalpers/CleanMyPosts/refs/heads/update-feed/update.xml", From 049b403cbe9321b3a708b6a55d3e62b102f1bc8b Mon Sep 17 00:00:00 2001 From: thorstenalpers Date: Sun, 18 May 2025 18:13:05 +0200 Subject: [PATCH 20/20] Fix sonar lint issue --- src/UI/Helpers/Helper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UI/Helpers/Helper.cs b/src/UI/Helpers/Helper.cs index fccf091..a18137d 100644 --- a/src/UI/Helpers/Helper.cs +++ b/src/UI/Helpers/Helper.cs @@ -3,6 +3,6 @@ public static class Helper { public static string CleanJsonResult(string json) { - return json.Replace("\\\"", "\"")?.Trim('\"') ?? ""; + return json.Replace("\\\"", "\"").Trim('\"'); } }