diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 00000000..526a9935
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,137 @@
+# Contributing
+
+## Project Scope
+
+The Electron.NET project ultimately tries to provide a framework for developing cross-platform client applications on the basis of .NET and Electron. Anything that is related to this goal will be considered. The project aims to be as close to Electron with .NET as a basis as possible. If your contribution does not reflect that goal, the chances of accepting it are limited.
+
+## Code License
+
+This is an open source project falling under the [MIT License](../LICENSE). By using, distributing, or contributing to this project, you accept and agree that all code within the Electron.NET project and its libraries are licensed under MIT license.
+
+## Becoming a Contributor
+
+Usually appointing someone as a contributor follows this process:
+
+1. An individual contributes actively via discussions (reporting bugs, giving feedback to existing or opening new issues) and / or pull requests
+2. The individual is either directly asked, invited or asks for contributor rights on the project
+3. The individual uses the contribution rights to sustain or increase the active contributions
+
+Every contributor might have to sign the contributor's license agreement (CLA) to establish a legal trust between the project and its contributors.
+
+## Working on Electron.NET
+
+### Issue Discussion
+
+Discussion of issues should be placed transparently in the issue tracker here on GitHub.
+
+* [General issues, bugs, new features](https://github.com/ElectronNET/Electron.NET/issues)
+* [General discussions, help, exchange of ideas](https://github.com/ElectronNET/Electron.NET/discussions)
+
+### Modifying the code
+
+Electron.NET and its libraries uses features from the latest versions of C# (e.g., C# 10). You will therefore need a C# compiler that is up for the job.
+
+1. Fork and clone the repo.
+2. First try to build the ElectronNET.Core library and see if you get the tests running.
+3. You will be required to resolve some dependencies via NuGet.
+
+The build system of Electron.NET uses NUKE.
+
+### Code Conventions
+
+Most parts in the Electron.NET project are fairly straight forward. Among these are:
+
+* Always use statement blocks for control statements, e.g., in a for-loop, if-condition, ...
+* You may use a simple (throw) statement in case of enforcing contracts on argument
+* Be explicit about modifiers (some files follow an older convention of the code base, but we settled on the explicit style)
+
+### Development Workflow
+
+1. If no issue already exists for the work you'll be doing, create one to document the problem(s) being solved and self-assign.
+2. Otherwise please let us know that you are working on the problem. Regular status updates (e.g. "still in progress", "no time anymore", "practically done", "pull request issued") are highly welcome.
+3. Create a new branch—please don't work in the `main` branch directly. It is reserved for releases. We recommend naming the branch to match the issue being addressed (`feature/#777` or `issue-777`).
+4. Add failing tests for the change you want to make. Tests are crucial and should be taken from W3C (or other specification).
+5. Fix stuff. Always go from edge case to edge case.
+6. All tests should pass now. Also your new implementation should not break existing tests.
+7. Update the documentation to reflect any changes. (or document such changes in the original issue)
+8. Push to your fork or push your issue-specific branch to the main repository, then submit a pull request against `develop`.
+
+Just to illustrate the git workflow for Electron.NET a little bit more we've added the following graphs.
+
+Initially, Electron.NET starts at the `main` branch. This branch should contain the latest stable (or released) version.
+
+Here we now created a new branch called `develop`. This is the development branch.
+
+Now active work is supposed to be done. Therefore a new branch should be created. Let's create one:
+
+```sh
+git checkout -b feature/#777
+```
+
+There may be many of these feature branches. Most of them are also pushed to the server for discussion or synchronization.
+
+```sh
+git push -u origin feature/#777
+```
+
+Now feature branches may be closed when they are done. Here we simply merge with the feature branch(es). For instance the following command takes the `feature/#777` branch from the server and merges it with the `develop` branch.
+
+```sh
+git checkout develop
+git pull
+git pull origin feature/#777
+git push
+```
+
+Finally, we may have all the features that are needed to release a new version of Electron.NET. Here we tag the release. For instance for the 1.0 release we use `v1.0`.
+
+```sh
+git checkout main
+git merge develop
+git tag v1.0
+```
+
+(The last part is automatically performed by our CI system. Don't tag manually.)
+
+### Versioning
+
+The rules of [semver](http://semver.org/) don't necessarily apply here, but we will try to stay quite close to them.
+
+Prior to version 1.0.0 we use the following scheme:
+
+1. MINOR versions for reaching a feature milestone potentially combined with dramatic API changes
+2. PATCH versions for refinements (e.g. performance improvements, bug fixes)
+
+After releasing version 1.0.0 the scheme changes to become:
+
+1. MAJOR versions at maintainers' discretion following significant changes to the codebase (e.g., API changes)
+2. MINOR versions for backwards-compatible enhancements (e.g., performance improvements)
+3. PATCH versions for backwards-compatible bug fixes (e.g., spec compliance bugs, support issues)
+
+#### Code style
+
+Regarding code style like indentation and whitespace, **follow the conventions you see used in the source already.** In general most of the [C# coding guidelines from Microsoft](https://msdn.microsoft.com/en-us/library/ff926074.aspx) are followed. This project prefers type inference with `var` to explicitly stating (redundant) information.
+
+It is also important to keep a certain `async`-flow and to always use `ConfigureAwait(false)` in conjunction with an `await` expression.
+
+## Backwards Compatibility
+
+We always try to remain backwards compatible beyond the currently supported versions of .NET.
+
+For instance, in December 2025 there have been activity to remove .NET 6 support from the codebase. We rejected this. Key points:
+
+1. We have absolutely no need to drop `.net6` support. It doesn't hurt us in any way.
+2. Many are still using `.net6`, including Electron.NET (non-Core) users. It doesn't make sense to force them to update two things at the same time (.NET + Electron.NET).
+3. We MUST NOT and NEVER update `Microsoft.Build.Utilities.Core`. This will make Electron.NET stop working on older Visual Studio and MSBuild versions. There's are also no reasons to update it in the first place.
+
+It's important to note that the Microsoft label of "Out of support" on .NET has almost no practical meaning. We've rarely (if ever) seen any bugs fixed in the same .NET version which mattered. The bugs that all new .NET versions have are much worse than mature .NET versions which are declared as "out of support". Keep in mind that the LTS matters most for active development / ongoing supported projects. If, e.g., a TV has been released a decade ago it most likely won't be patched. Still, you might want to deploy applications to it, which then naturally would involve being based on "out of support" versions of the framework.
+
+TL;DR: Unless there is a technical reason (e.g., a crucial new API not being available) we should not drop "out of support" .NET versions. At the time of writing (December 2025) the minimum supported .NET version remains at `.net6`.
+
+## Timeline
+
+**All of this information is related to ElectronNET.Core pre-v1!**
+
+We pretty much release whenever we have something new (i.e., do fixes such as a 0.1.1, or add new features, such as a 0.2.0) quite quickly.
+
+We will go for a 1.0.0 release of this as early as ~mid of January 2026 (unless we find some critical things or want to extend the beta phase for ElectronNET.Core). This should be sufficient time to get some user input and have enough experience to call it stable.
diff --git a/.github/workflows/ci.yml b/.github/workflows/Build and Publish.yml
similarity index 63%
rename from .github/workflows/ci.yml
rename to .github/workflows/Build and Publish.yml
index a9b81df7..17bd8ca0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/Build and Publish.yml
@@ -1,33 +1,25 @@
-name: CI
+name: Build and Publish
-on: [push, pull_request]
+on: [push]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
-jobs:
- # linux:
- # runs-on: ubuntu-latest
- # timeout-minutes: 10
-
- # steps:
- # - uses: actions/checkout@v4
+concurrency:
+ group: build-publish-${{ github.ref }}
+ cancel-in-progress: true
- # - name: Setup dotnet
- # uses: actions/setup-dotnet@v4
- # with:
- # dotnet-version: |
- # 6.0.x
- # 8.0.x
- # 10.0.x
-
- # - name: Build
- # run: ./build.sh
+jobs:
+ Integration-Tests:
+ uses: ./.github/workflows/integration-tests.yml
+ name: '1'
- windows:
+ Publish:
+ needs: [Integration-Tests]
runs-on: windows-latest
timeout-minutes: 10
+ name: '2 / Publish'
steps:
- uses: actions/checkout@v4
diff --git a/.github/workflows/PR Validation.yml b/.github/workflows/PR Validation.yml
new file mode 100644
index 00000000..540db15b
--- /dev/null
+++ b/.github/workflows/PR Validation.yml
@@ -0,0 +1,39 @@
+name: PR Validation
+
+on: [pull_request]
+
+concurrency:
+ group: pr-validation-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ Whitespace-Check:
+ uses: ./.github/workflows/trailing-whitespace-check.yml
+ secrets: inherit
+ name: '1'
+
+ Tests:
+ needs: Whitespace-Check
+ uses: ./.github/workflows/integration-tests.yml
+ secrets: inherit
+ name: '2'
+
+ build:
+ needs: [Whitespace-Check, Tests]
+ runs-on: windows-latest
+ timeout-minutes: 10
+ name: '3 / Build'
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup dotnet
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: |
+ 6.0.x
+ 8.0.x
+ 10.0.x
+
+ - name: Build
+ run: .\build.ps1
diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index ea520a77..89ad3c95 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -1,10 +1,7 @@
-name: Tests
+name: Tests
on:
- push:
- branches: [ develop, main ]
- pull_request:
- branches: [ develop, main ]
+ workflow_call:
concurrency:
group: integration-tests-${{ github.ref }}
@@ -12,18 +9,28 @@ concurrency:
jobs:
tests:
- name: Integration Tests (${{ matrix.os }})
+ name: ${{ matrix.os }} API-${{ matrix.electronVersion }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
+ os: [ubuntu-22.04, ubuntu-24.04, windows-2022, windows-2025, macos-14, macos-15-intel, macos-26]
+ electronVersion: ['30.4.0', '38.2.2']
include:
+ - os: ubuntu-22.04
+ rid: linux-x64
- os: ubuntu-24.04
rid: linux-x64
- os: windows-2022
rid: win-x64
+ - os: windows-2025
+ rid: win-x64
- os: macos-14
rid: osx-arm64
+ - os: macos-15-intel
+ rid: osx-x64
+ - os: macos-26
+ rid: osx-arm64
env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
@@ -35,6 +42,13 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
+ - name: Random delay (0-20 seconds)
+ shell: bash
+ run: |
+ DELAY=$((RANDOM % 21))
+ echo "Waiting for $DELAY seconds..."
+ sleep $DELAY
+
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
@@ -46,28 +60,30 @@ jobs:
node-version: '22'
- name: Restore
- run: dotnet restore -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj
+ run: dotnet restore -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj
- name: Build
- run: dotnet build --no-restore -c Release -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj
+ run: dotnet build --no-restore -c Release -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj
- name: Install Linux GUI dependencies
if: runner.os == 'Linux'
run: |
set -e
sudo apt-get update
- # Core Electron dependencies
+ . /etc/os-release
+ if [ "$VERSION_ID" = "24.04" ]; then ALSA_PKG=libasound2t64; else ALSA_PKG=libasound2; fi
+ echo "Using ALSA package: $ALSA_PKG"
sudo apt-get install -y xvfb \
- libgtk-3-0 libnss3 libgdk-pixbuf-2.0-0 libdrm2 libgbm1 libxss1 libxtst6 libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 libx11-xcb1 libasound2t64
+ libgtk-3-0 libnss3 libgdk-pixbuf-2.0-0 libdrm2 libgbm1 libxss1 libxtst6 libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 libx11-xcb1 "$ALSA_PKG"
- name: Run tests (Linux)
if: runner.os == 'Linux'
continue-on-error: true
run: |
- mkdir -p test-results/Ubuntu
+ mkdir -p test-results
xvfb-run -a dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj \
- -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} \
- --logger "trx;LogFileName=Ubuntu.trx" \
+ -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} \
+ --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" \
--logger "console;verbosity=detailed" \
--results-directory test-results
@@ -75,21 +91,21 @@ jobs:
if: runner.os == 'Windows'
continue-on-error: true
run: |
- New-Item -ItemType Directory -Force -Path test-results/Windows | Out-Null
- dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} --logger "trx;LogFileName=Windows.trx" --logger "console;verbosity=detailed" --results-directory test-results
+ New-Item -ItemType Directory -Force -Path test-results | Out-Null
+ dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" --logger "console;verbosity=detailed" --results-directory test-results
- name: Run tests (macOS)
if: runner.os == 'macOS'
continue-on-error: true
run: |
- mkdir -p test-results/macOS
- dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} --logger "trx;LogFileName=macOS.trx" --logger "console;verbosity=detailed" --results-directory test-results
+ mkdir -p test-results
+ dotnet test src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj -c Release --no-build -r ${{ matrix.rid }} -p:RuntimeIdentifier=${{ matrix.rid }} -p:ElectronVersion=${{ matrix.electronVersion }} --logger "trx;LogFileName=${{ matrix.os }}-electron-${{ matrix.electronVersion }}.trx" --logger "console;verbosity=detailed" --results-directory test-results
- name: Upload raw test results
if: always()
uses: actions/upload-artifact@v4
with:
- name: test-results-${{ matrix.os }}
+ name: test-results-${{ matrix.os }}-electron-${{ matrix.electronVersion }}
path: test-results/*.trx
retention-days: 7
@@ -121,7 +137,7 @@ jobs:
dotnet new tool-manifest
dotnet tool install DotnetCtrfJsonReporter --local
- - name: Convert TRX → CTRF and clean names (keep suites; set filePath=OS)
+ - name: Convert TRX → CTRF and clean names (filePath=OS|Electron X.Y.Z)
shell: bash
run: |
set -euo pipefail
@@ -129,28 +145,28 @@ jobs:
shopt -s globstar nullglob
conv=0
for trx in test-results/**/*.trx; do
- fname="$(basename "$trx")"
- os="${fname%.trx}"
- outdir="ctrf/${os}"
+ base="$(basename "$trx" .trx)" # e.g. ubuntu-22.04-electron-30.4.0
+ os="${base%%-electron-*}"
+ electron="${base#*-electron-}"
+ label="$os|Electron $electron"
+ outdir="ctrf/${label}"
mkdir -p "$outdir"
out="${outdir}/ctrf-report.json"
dotnet tool run DotnetCtrfJsonReporter -p "$trx" -d "$outdir" -f "ctrf-report.json"
- jq --arg os "$os" '.results.tests |= map(.filePath = $os)' "$out" > "${out}.tmp" && mv "${out}.tmp" "$out"
+ jq --arg fp "$label" '.results.tests |= map(.filePath = $fp)' "$out" > "${out}.tmp" && mv "${out}.tmp" "$out"
echo "Converted & normalized $trx -> $out"
conv=$((conv+1))
done
echo "Processed $conv TRX file(s)"
-
- name: Publish Test Report
if: always()
uses: ctrf-io/github-test-reporter@v1
with:
report-path: 'ctrf/**/*.json'
-
summary: true
pull-request: false
status-check: false
@@ -163,7 +179,6 @@ jobs:
group-by: 'suite'
upload-artifact: true
fetch-previous-results: true
-
summary-report: false
summary-delta-report: true
github-report: true
@@ -181,29 +196,25 @@ jobs:
flaky-rate-report: true
fail-rate-report: false
slowest-report: false
-
report-order: 'summary-delta-report,failed-report,skipped-report,suite-folded-report,file-report,previous-results-report,github-report'
env:
GITHUB_TOKEN: ${{ github.token }}
+ - name: Save PR Number
+ if: github.event_name == 'pull_request'
+ run: echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV
- - name: Create PR Comment
- if: always()
- uses: ctrf-io/github-test-reporter@v1
- with:
- report-path: 'ctrf/**/*.json'
-
- summary: true
- pull-request: true
- use-suite-name: true
- update-comment: true
- always-group-by: true
- overwrite-comment: true
- upload-artifact: false
+ - name: Write PR Number to File
+ if: github.event_name == 'pull_request'
+ run: echo "$PR_NUMBER" > pr_number.txt
+ shell: bash
- pull-request-report: true
- env:
- GITHUB_TOKEN: ${{ github.token }}
+ - name: Upload PR Number Artifact
+ if: github.event_name == 'pull_request'
+ uses: actions/upload-artifact@v4
+ with:
+ name: pr_number
+ path: pr_number.txt
- name: Summary
- run: echo "All matrix test jobs completed."
\ No newline at end of file
+ run: echo "All matrix test jobs completed."
diff --git a/.github/workflows/pr-comment.yml b/.github/workflows/pr-comment.yml
new file mode 100644
index 00000000..23dac1fc
--- /dev/null
+++ b/.github/workflows/pr-comment.yml
@@ -0,0 +1,81 @@
+name: Create PR Comments
+
+on:
+ workflow_run:
+ workflows: [ "PR Validation" ]
+ types: [completed]
+
+permissions:
+ contents: read
+ actions: read
+ pull-requests: write
+
+jobs:
+ pr-comment:
+ name: Post Test Result as PR comment
+ runs-on: ubuntu-24.04
+ if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion != 'cancelled'
+
+ steps:
+ - name: Download CTRF artifact
+ uses: dawidd6/action-download-artifact@v8
+ with:
+ github_token: ${{ github.token }}
+ run_id: ${{ github.event.workflow_run.id }}
+ name: ctrf-report
+ path: ctrf
+
+ - name: Download PR Number Artifact
+ uses: dawidd6/action-download-artifact@v8
+ with:
+ github_token: ${{ github.token }}
+ run_id: ${{ github.event.workflow_run.id }}
+ name: pr_number
+ path: pr_number
+
+ - name: Read PR Number
+ run: |
+ set -Eeuo pipefail
+ FILE='pr_number/pr_number.txt'
+
+ # Ensure file exists
+ if [ ! -f "$FILE" ] || [ -L "$FILE" ]; then
+ echo "Error: $FILE is missing or is not a regular file." >&2
+ exit 1
+ fi
+
+ # Chec file size
+ if [ "$(wc -c < "$FILE" | tr -d ' ')" -gt 200 ]; then
+ echo "Error: $FILE is too large." >&2
+ exit 1
+ fi
+
+ # Read first line
+ PR_NUMBER=""
+ IFS= read -r PR_NUMBER < "$FILE" || true
+
+ # Validate whether it's a number
+ if ! [[ "$PR_NUMBER" =~ ^[0-9]{1,10}$ ]]; then
+ echo "Error: PR_NUMBER is not a valid integer on the first line." >&2
+ exit 1
+ fi
+
+ printf 'PR_NUMBER=%s\n' "$PR_NUMBER" >> "$GITHUB_ENV"
+
+ - name: Post PR Comment
+ uses: ctrf-io/github-test-reporter@v1
+ with:
+ report-path: 'ctrf/**/*.json'
+ issue: ${{ env.PR_NUMBER }}
+
+ summary: true
+ pull-request: true
+ use-suite-name: true
+ update-comment: true
+ always-group-by: true
+ overwrite-comment: true
+ upload-artifact: false
+
+ pull-request-report: true
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
diff --git a/.github/workflows/retry-test-jobs.yml b/.github/workflows/retry-test-jobs.yml
new file mode 100644
index 00000000..1fd0342c
--- /dev/null
+++ b/.github/workflows/retry-test-jobs.yml
@@ -0,0 +1,50 @@
+name: Tests auto-rerun
+
+on:
+ workflow_run:
+ workflows: [ "PR Validation", "Build and Publish" ]
+ types: [ completed ]
+
+jobs:
+ rerun-failed-matrix-jobs-once:
+ if: >
+ ${{
+ github.event.workflow_run.conclusion == 'failure' &&
+ github.event.workflow_run.run_attempt == 1
+ }}
+ runs-on: ubuntu-24.04
+
+ permissions:
+ actions: write
+ contents: read
+
+ steps:
+ - name: Decide whether to rerun (only if matrix jobs failed)
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ REPO: ${{ github.repository }}
+ RUN_ID: ${{ github.event.workflow_run.id }}
+ run: |
+ echo "Inspecting jobs of workflow run $RUN_ID in $REPO"
+
+ jobs_json="$(gh api repos/$REPO/actions/runs/$RUN_ID/jobs)"
+
+ echo "Jobs and conclusions:"
+ echo "$jobs_json" | jq '.jobs[] | {name: .name, conclusion: .conclusion}'
+
+ failed_matrix_jobs=$(echo "$jobs_json" | jq '
+ [ .jobs[]
+ | select(.conclusion == "failure"
+ and (.name | contains(" API-")))
+ ]
+ | length
+ ')
+
+ echo "Failed Integration Tests matrix jobs: $failed_matrix_jobs"
+
+ if [ "$failed_matrix_jobs" -gt 0 ]; then
+ echo "Detected failing Integration Tests jobs – re-running failed jobs for this run."
+ gh run rerun "$RUN_ID" --failed
+ else
+ echo "Only non-matrix jobs (like Test Results) failed – not auto-rerunning."
+ fi
diff --git a/.github/workflows/trailing-whitespace-check.yml b/.github/workflows/trailing-whitespace-check.yml
index 299bb98f..9b8c7799 100644
--- a/.github/workflows/trailing-whitespace-check.yml
+++ b/.github/workflows/trailing-whitespace-check.yml
@@ -1,11 +1,10 @@
-name: Trailing Whitespace Check
+name: Whitespace Check
on:
- pull_request:
- types: [opened, synchronize, reopened]
+ workflow_call:
jobs:
- check-trailing-whitespace:
+ check-whitespace:
runs-on: ubuntu-latest
permissions:
contents: read
diff --git a/Changelog.md b/Changelog.md
index 42934363..2859e7b5 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,25 @@
+# 0.3.0
+
+## ElectronNET.Core
+
+- Updated infrastructure (#937, #939) @softworkz
+- Updated all model classes to Electron API 39.2 (#949) @softworkz
+- Fixed output path for `electron-builder` (#942) @softworkz
+- Fixed floating point display resolution (#944) @softworkz
+- Fixed error in case of missing electron-host-hook (#978)
+- Fixed previous API break using exposed `JsonElement` objects (#938) @softworkz
+- Fixed and improved several test cases (#962) @softworkz
+- Fixed startup of Electron.NET from VS Code Debug Adapter (#952)
+- Fixed the `BrowserWindowOptions` (#945) @softworkz
+- Fixed example for `AutoMenuHide` to reflect platform capabilities (#982) @markatosi
+- Added several migration checks for publishing (#966) @softworkz
+- Added more test runners for E2E tests (#950, #951) @agracio
+- Added dynamic updates for tray menu (#973) @davidroth
+- Added matrix tests with 6 runners and 2 electron version (#948) @softworkz
+- Added additional APIs for WebContents (#958) @agracio
+- Added documentation for MacOS package publish (#983) @markatosi
+- Added sample application for `ElectronHostHook` (#967) @adityashirsatrao007
+
# 0.2.0
## ElectronNET.Core
diff --git a/README.md b/README.md
index 3fb436ba..c264e3be 100644
--- a/README.md
+++ b/README.md
@@ -14,9 +14,9 @@ Build cross platform desktop applications with .NET 6/8/10 - from console apps t
## Wait - how does that work exactly?
-Well... there are lots of different approaches how to get a X-plat desktop app running. Electron.NET provides a range of ways to build .NET based solutions using Electron at the side of presentation.
+Well... there are lots of different approaches how to get a X-plat desktop app running. Electron.NET provides a range of ways to build .NET-based solutions using Electron at the side of presentation.
-While the classic Electron.NET setup, using an ASP.NET host ran by the Electron side is still the primary way, there's more flexibility now: both, dotnet and Electron are now able to launch the other for better lifetime management, and when you don't need a local web server - like when running content from files or remote servers, you can drop the ASP.NET stack altogether and got with a lightweight console app instead.
+While the classic Electron.NET setup (using an ASP.NET host run by the Electron side) is still the primary way, there's more flexibility now. Both .NET and Electron are now able to launch the other for better lifetime management, and when you don't need a local web server (like when running content from files or remote servers), you can drop the ASP.NET stack altogether and go with a lightweight console app instead.
## 📦 NuGet
@@ -133,9 +133,10 @@ builder.UseElectron(args, async () =>
{
var options = new BrowserWindowOptions {
Show = false,
- AutoHideMenuBar = true,
IsRunningBlazor = true, // <-- crucial
};
+ if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux())
+ options.AutoHideMenuBar = true;
var browserWindow = await Electron.WindowManager.CreateWindowAsync(options);
browserWindow.OnReadyToShow += () => browserWindow.Show();
});
@@ -169,7 +170,7 @@ Just press `F5` in Visual Studio or use dotnet for debugging.
## 📔 Usage of the Electron API
-A complete documentation is available on the Wiki.
+Complete documentation is available on the Wiki.
In this YouTube video, we show you how you can create a new project, use the Electron.NET API, debug a application and build an executable desktop app for Windows: [Electron.NET - Getting Started](https://www.youtube.com/watch?v=nuM6AojRFHk)
diff --git a/docs/Core/Migration-Checks.md b/docs/Core/Migration-Checks.md
new file mode 100644
index 00000000..4464e6b9
--- /dev/null
+++ b/docs/Core/Migration-Checks.md
@@ -0,0 +1,235 @@
+# Migration Checks
+
+Electron.NET includes automatic build-time validation checks that help users migrating from previous versions avoid common configuration issues. These checks run automatically during the build process and provide helpful guidance when problems are detected.
+
+## Overview
+
+When you build an Electron.NET project, the following validation checks are performed:
+
+| Code | Check | Description |
+|------|-------|-------------|
+| [ELECTRON001](#1-packagejson-not-allowed) | package.json not allowed | Ensures no package.json exists outside ElectronHostHook |
+| [ELECTRON002](#2-electron-manifestjson-not-allowed) | electron-manifest.json not allowed | Detects deprecated manifest files |
+| [ELECTRON003](#3-electron-builderjson-location) | electron-builder.json location | Verifies electron-builder.json exists in Properties folder |
+| [ELECTRON004](#3-electron-builderjson-location) | electron-builder.json wrong location | Warns if electron-builder.json is found in incorrect locations |
+| [ELECTRON005](#4-parent-paths-not-allowed-in-electron-builderjson) | Parent paths not allowed | Checks for `..` references in config |
+| [ELECTRON006](#5-publish-profile-validation) | ASP.NET publish profile mismatch | Warns when ASP.NET projects have console-style profiles |
+| [ELECTRON007](#5-publish-profile-validation) | Console publish profile mismatch | Warns when console projects have ASP.NET-style profiles |
+
+---
+
+## 1. package.json not allowed
+
+**Warning Code:** `ELECTRON001`
+
+### What is checked
+
+The build system scans for `package.json` and `package-lock.json` files in your project directory. These files should not exist in the project root or subdirectories (with one exception).
+
+### Why this matters
+
+In previous versions of Electron.NET, a `package.json` file was required in the project. The new version generates this file automatically from MSBuild properties defined in your `.csproj` file.
+
+### Exception
+
+A `package.json` file **is allowed** in the `ElectronHostHook` folder if you're using custom host hooks. This is the only valid location for a manually maintained package.json.
+
+### How to fix
+
+1. **Open your project's `.csproj` file**
+2. **Add the required properties** to a PropertyGroup with the label `ElectronNetCommon`:
+
+```xml
+
+ my-electron-app
+ My Electron App
+ 1.0.0
+ My awesome Electron.NET application
+ My Company
+ Copyright © 2025
+ 30.0.9
+
+```
+
+3. **Delete the old `package.json`** file from your project root
+
+> **See also:** [Migration Guide](Migration-Guide.md) for complete migration instructions.
+
+---
+
+## 2. electron-manifest.json not allowed
+
+**Warning Code:** `ELECTRON002`
+
+### What is checked
+
+The build system checks for the presence of `electron.manifest.json` or `electron-manifest.json` files in your project.
+
+### Why this matters
+
+The `electron.manifest.json` file format is deprecated. All configuration should now be specified using:
+- MSBuild properties in your `.csproj` file (for application metadata)
+- The `electron-builder.json` file in the `Properties` folder (for build configuration)
+
+### How to fix
+
+1. **Migrate application properties** to your `.csproj` file (see [Migration Guide](Migration-Guide.md))
+2. **Move the `build` section** from `electron.manifest.json` to `Properties/electron-builder.json`
+3. **Delete the old `electron.manifest.json`** file
+
+**Example electron-builder.json:**
+```json
+{
+ "compression": "maximum",
+ "win": {
+ "icon": "Assets/app.ico",
+ "target": ["nsis", "portable"]
+ },
+ "linux": {
+ "icon": "Assets/app.png",
+ "target": ["AppImage", "deb"]
+ },
+ "mac": {
+ "icon": "Assets/app.icns",
+ "target": ["dmg", "zip"]
+ }
+}
+```
+
+---
+
+## 3. electron-builder.json Location
+
+**Warning Codes:** `ELECTRON003`, `ELECTRON004`
+
+### What is checked
+
+- `ELECTRON003`: Verifies that an `electron-builder.json` file exists in the `Properties` folder
+- `ELECTRON004`: Warns if `electron-builder.json` is found in incorrect locations
+
+### Why this matters
+
+The `electron-builder.json` file must be located in the `Properties` folder so it can be properly copied to the output directory during publishing.
+
+### How to fix
+
+1. **Create the Properties folder** if it doesn't exist
+2. **Move or create** `electron-builder.json` in `Properties/electron-builder.json`
+3. **Remove** any `electron-builder.json` files from other locations
+
+**Expected structure:**
+```
+MyProject/
+├── Properties/
+│ ├── electron-builder.json ✅ Correct location
+│ ├── launchSettings.json
+│ └── PublishProfiles/
+├── MyProject.csproj
+└── Program.cs
+```
+
+---
+
+## 4. Parent paths not allowed in electron-builder.json
+
+**Warning Code:** `ELECTRON005`
+
+### What is checked
+
+The build system scans the `electron-builder.json` file for parent-path references (`..`).
+
+### Why this matters
+
+During the publish process, the `electron-builder.json` file is copied to the build output directory. Any relative paths in this file are resolved from that location, not from your project directory. Parent-path references (`../`) will not work correctly because they would point outside the published application.
+
+### How to fix
+
+1. **Move resource files** (icons, installers, etc.) inside your project folder structure
+2. **Configure the files** to be copied to the output directory in your `.csproj`:
+
+```xml
+
+
+ PreserveNewest
+
+
+```
+
+3. **Update paths** in `electron-builder.json` to use relative paths without `..`:
+
+**Before (incorrect):**
+```json
+{
+ "win": {
+ "icon": "../SharedAssets/app.ico"
+ }
+}
+```
+
+**After (correct):**
+```json
+{
+ "win": {
+ "icon": "Assets/app.ico"
+ }
+}
+```
+
+---
+
+## 5. Publish Profile Validation
+
+**Warning Codes:** `ELECTRON006`, `ELECTRON007`
+
+### What is checked
+
+The build system examines `.pubxml` files in the `Properties/PublishProfiles` folder and validates that they match the project type:
+
+- **ELECTRON006**: For **ASP.NET projects** (using `Microsoft.NET.Sdk.Web`), checks that publish profiles include `WebPublishMethod`. This property is required for proper ASP.NET publishing.
+
+- **ELECTRON007**: For **console/other projects** (not using the Web SDK), checks that publish profiles do NOT include the `WebPublishMethod` property. These ASP.NET-specific properties are incorrect for non-web applications.
+
+### Why this matters
+
+Electron.NET supports both ASP.NET and console application project types, each requiring different publish profile configurations:
+
+| Project Type | SDK | Expected Properties |
+|--------------|-----|---------------------|
+| ASP.NET (Razor Pages, MVC, Blazor) | `Microsoft.NET.Sdk.Web` | `WebPublishMethod`, no `PublishProtocol` |
+| Console Application | `Microsoft.NET.Sdk` | `PublishProtocol`, no `WebPublishMethod` |
+
+Using the wrong publish profile type can lead to incomplete or broken builds.
+
+### How to fix
+
+1. **Delete existing publish profiles** from `Properties/PublishProfiles/`
+2. **Create new publish profiles** using the Visual Studio Publishing Wizard:
+ - Right-click on the project in Solution Explorer
+ - Select **Publish...**
+ - Follow the wizard to create a **Folder** publish profile
+
+For correct publish profile examples for both ASP.NET and Console applications, see **[Package Building](../Using/Package-Building.md#step-1-create-publish-profiles)**.
+
+---
+
+## Disabling Migration Checks
+
+If you need to disable specific migration checks (not recommended), you can set the following properties in your `.csproj` file:
+
+```xml
+
+
+ true
+
+```
+
+> ⚠️ **Warning:** Disabling migration checks may result in build or runtime errors. Only disable checks if you fully understand the implications.
+
+---
+
+## See Also
+
+- [Migration Guide](Migration-Guide.md) - Complete step-by-step migration instructions
+- [Advanced Migration Topics](Advanced-Migration-Topics.md) - Complex migration scenarios
+- [Configuration](../Using/Configuration.md) - Project configuration options
+- [Package Building](../Using/Package-Building.md) - Building distributable packages
diff --git a/docs/GettingStarted/Console-App.md b/docs/GettingStarted/Console-App.md
index 4a006713..1659e392 100644
--- a/docs/GettingStarted/Console-App.md
+++ b/docs/GettingStarted/Console-App.md
@@ -54,7 +54,7 @@ Add the Electron.NET configuration to your `.csproj` file:
-
+
```
diff --git a/docs/Using/Package-Building.md b/docs/Using/Package-Building.md
index 28385959..6b6325e5 100644
--- a/docs/Using/Package-Building.md
+++ b/docs/Using/Package-Building.md
@@ -26,12 +26,15 @@ Add publish profiles to `Properties/PublishProfiles/`:
Release
Any CPU
- publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\
- FileSystem
+ true
+ FileSystem
+ publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\
+ FileSystem
+ <_TargetId>Folder
net10.0
win-x64
+ 48eff821-2f4d-60cc-aa44-be0f1d6e5f35
true
- false
```
@@ -46,12 +49,61 @@ Add publish profiles to `Properties/PublishProfiles/`:
Release
Any CPU
- publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\
- FileSystem
+ true
+ FileSystem
+ publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\
+ FileSystem
+ <_TargetId>Folder
net10.0
linux-x64
+ 48eff821-2f4d-60cc-aa44-be0f1d6e5f35
+ true
+
+
+```
+
+#### ASP.NET Application Profile (macOS Apple Silicon ARM64)
+
+**osx-arm64.pubxml:**
+
+```xml
+
+
+
+ Release
+ Any CPU
+ true
+ FileSystem
+ publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\
+ FileSystem
+ <_TargetId>Folder
+ net10.0
+ osx-arm64
+ 48eff821-2f4d-60cc-aa44-be0f1d6e5f35
+ true
+
+
+```
+
+#### ASP.NET Application Profile (macOS Intel x64)
+
+**osx-x64.pubxml:**
+
+```xml
+
+
+
+ Release
+ Any CPU
+ true
+ FileSystem
+ publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\
+ FileSystem
+ <_TargetId>Folder
+ net10.0
+ osx-x64
+ 48eff821-2f4d-60cc-aa44-be0f1d6e5f35
true
- false
```
@@ -97,6 +149,46 @@ Add publish profiles to `Properties/PublishProfiles/`:
```
+#### Console Application Profile (macOS Apple Silicon ARM64)
+
+**osx-arm64.pubxml:**
+
+```xml
+
+
+
+ Release
+ Any CPU
+ publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\
+ FileSystem
+ net10.0
+ osx-arm64
+ false
+ false
+
+
+```
+
+#### Console Application Profile (macOS Intel x64)
+
+**osx-x64.pubxml:**
+
+```xml
+
+
+
+ Release
+ Any CPU
+ publish\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\
+ FileSystem
+ net10.0
+ osx-x64
+ false
+ false
+
+
+```
+
### Step 2: Configure Electron Builder
ElectronNET.Core automatically adds a default `electron-builder.json` file under `Properties\electron-builder.json`.
diff --git a/docs/_Sidebar.md b/docs/_Sidebar.md
index b76d5afd..487f547f 100644
--- a/docs/_Sidebar.md
+++ b/docs/_Sidebar.md
@@ -9,6 +9,7 @@
- [What's new?](Core/What's-New.md)
- [Migration Guide](Core/Migration-Guide.md)
+- [Migration Checks](Core/Migration-Checks.md)
- [Advanced Migration](Core/Advanced-Migration-Topics.md)
# Getting Started
diff --git a/nuke/_build.csproj b/nuke/_build.csproj
index 78e4653a..47ebb0a1 100644
--- a/nuke/_build.csproj
+++ b/nuke/_build.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net10.0
CS0649;CS0169
..
@@ -11,9 +11,9 @@
-
-
-
+
+
+
diff --git a/src/ElectronNET.API/API/ApiBase.cs b/src/ElectronNET.API/API/ApiBase.cs
index 95231fe1..96f68313 100644
--- a/src/ElectronNET.API/API/ApiBase.cs
+++ b/src/ElectronNET.API/API/ApiBase.cs
@@ -31,7 +31,7 @@ protected enum SocketEventNameTypes
CamelCase,
}
- private const int InvocationTimeout = 1000;
+ private static readonly TimeSpan InvocationTimeout = 1000.ms();
private readonly string objectName;
private readonly ConcurrentDictionary invocators;
@@ -116,6 +116,11 @@ protected void CallMethod3(object val1, object val2, object val3, [CallerMemberN
}
protected Task InvokeAsync(object arg = null, [CallerMemberName] string callerName = null)
+ {
+ return this.InvokeAsyncWithTimeout(InvocationTimeout, arg, callerName);
+ }
+
+ protected Task InvokeAsyncWithTimeout(TimeSpan invocationTimeout, object arg = null, [CallerMemberName] string callerName = null)
{
Debug.Assert(callerName != null, nameof(callerName) + " != null");
@@ -123,7 +128,7 @@ protected Task InvokeAsync(object arg = null, [CallerMemberName] string ca
{
return this.invocators.GetOrAdd(callerName, _ =>
{
- var getter = new Invocator(this, callerName, InvocationTimeout, arg);
+ var getter = new Invocator(this, callerName, invocationTimeout, arg);
getter.Task().ContinueWith(_ =>
{
@@ -240,7 +245,7 @@ internal class Invocator : Invocator
private readonly Task tcsTask;
private TaskCompletionSource tcs;
- public Invocator(ApiBase apiBase, string callerName, int timeoutMs, object arg = null)
+ public Invocator(ApiBase apiBase, string callerName, TimeSpan timeout, object arg = null)
{
this.tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
this.tcsTask = this.tcs.Task;
@@ -301,7 +306,7 @@ public Invocator(ApiBase apiBase, string callerName, int timeoutMs, object arg =
_ = apiBase.Id >= 0 ? BridgeConnector.Socket.Emit(messageName, apiBase.Id) : BridgeConnector.Socket.Emit(messageName);
}
- System.Threading.Tasks.Task.Delay(InvocationTimeout).ContinueWith(_ =>
+ System.Threading.Tasks.Task.Delay(timeout).ContinueWith(_ =>
{
if (this.tcs != null)
{
@@ -309,7 +314,7 @@ public Invocator(ApiBase apiBase, string callerName, int timeoutMs, object arg =
{
if (this.tcs != null)
{
- var ex = new TimeoutException($"No response after {timeoutMs:D}ms trying to retrieve value {apiBase.objectName}.{callerName}()");
+ var ex = new TimeoutException($"No response after {timeout:D}ms trying to retrieve value {apiBase.objectName}.{callerName}()");
this.tcs.TrySetException(ex);
this.tcs = null;
}
diff --git a/src/ElectronNET.API/API/App.cs b/src/ElectronNET.API/API/App.cs
index c486f90a..a1843031 100644
--- a/src/ElectronNET.API/API/App.cs
+++ b/src/ElectronNET.API/API/App.cs
@@ -539,7 +539,7 @@ public async Task GetPathAsync(PathName pathName, CancellationToken canc
using (cancellationToken.Register(() => taskCompletionSource.TrySetCanceled()))
{
BridgeConnector.Socket.Once("appGetPathCompleted", taskCompletionSource.SetResult);
- BridgeConnector.Socket.Emit("appGetPath", pathName.GetDescription());
+ BridgeConnector.Socket.Emit("appGetPath", pathName);
return await taskCompletionSource.Task
.ConfigureAwait(false);
@@ -560,7 +560,7 @@ public async Task GetPathAsync(PathName pathName, CancellationToken canc
///
public void SetPath(PathName name, string path)
{
- this.CallMethod2(name.GetDescription(), path);
+ this.CallMethod2(name, path);
}
///
diff --git a/src/ElectronNET.API/API/BrowserWindow.cs b/src/ElectronNET.API/API/BrowserWindow.cs
index bd60b77d..bc863988 100644
--- a/src/ElectronNET.API/API/BrowserWindow.cs
+++ b/src/ElectronNET.API/API/BrowserWindow.cs
@@ -681,7 +681,7 @@ public void SetAspectRatio(int aspectRatio, Size extraSize) =>
/// Values include normal, floating, torn-off-menu, modal-panel, main-menu,
/// status, pop-up-menu and screen-saver. The default is floating.
/// See the macOS docs
- public void SetAlwaysOnTop(bool flag, OnTopLevel level) => this.CallMethod2(flag, level.GetDescription());
+ public void SetAlwaysOnTop(bool flag, OnTopLevel level) => this.CallMethod2(flag, level);
///
/// Sets whether the window should show always on top of other windows.
@@ -694,7 +694,7 @@ public void SetAspectRatio(int aspectRatio, Size extraSize) =>
/// See the macOS docs
/// The number of layers higher to set this window relative to the given level.
/// The default is 0. Note that Apple discourages setting levels higher than 1 above screen-saver.
- public void SetAlwaysOnTop(bool flag, OnTopLevel level, int relativeLevel) => this.CallMethod3(flag, level.GetDescription(), relativeLevel);
+ public void SetAlwaysOnTop(bool flag, OnTopLevel level, int relativeLevel) => this.CallMethod3(flag, level, relativeLevel);
///
/// Whether the window is always on top of other windows.
@@ -1190,7 +1190,7 @@ public async Task> GetChildWindowsAsync()
/// menu, popover, sidebar, medium-light or ultra-dark.
/// See the macOS documentation for more details.
[SupportedOSPlatform("macOS")]
- public void SetVibrancy(Vibrancy type) => this.CallMethod1(type.GetDescription());
+ public void SetVibrancy(Vibrancy type) => this.CallMethod1(type);
///
/// Render and control web pages.
diff --git a/src/ElectronNET.API/API/Dialog.cs b/src/ElectronNET.API/API/Dialog.cs
index 21816463..3b9739d5 100644
--- a/src/ElectronNET.API/API/Dialog.cs
+++ b/src/ElectronNET.API/API/Dialog.cs
@@ -206,10 +206,10 @@ public Task ShowCertificateTrustDialogAsync(CertificateTrustDialogOptions option
[SupportedOSPlatform("Windows")]
public Task ShowCertificateTrustDialogAsync(BrowserWindow browserWindow, CertificateTrustDialogOptions options)
{
- var tcs = new TaskCompletionSource
+
diff --git a/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs b/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs
index 0cf21c54..61cae062 100644
--- a/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs
+++ b/src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs
@@ -76,7 +76,7 @@ private async Task StartInternal(string startCmd, string args, string directoriy
{
try
{
- await Task.Delay(10).ConfigureAwait(false);
+ await Task.Delay(10.ms()).ConfigureAwait(false);
Console.Error.WriteLine("[StartInternal]: startCmd: {0}", startCmd);
Console.Error.WriteLine("[StartInternal]: args: {0}", args);
@@ -85,7 +85,7 @@ private async Task StartInternal(string startCmd, string args, string directoriy
this.process.ProcessExited += this.Process_Exited;
this.process.Run(startCmd, args, directoriy);
- await Task.Delay(500).ConfigureAwait(false);
+ await Task.Delay(500.ms()).ConfigureAwait(false);
Console.Error.WriteLine("[StartInternal]: after run:");
diff --git a/src/ElectronNET.API/Serialization/JsonToBoxedPrimitivesConverter.cs b/src/ElectronNET.API/Serialization/JsonToBoxedPrimitivesConverter.cs
new file mode 100644
index 00000000..5db53047
--- /dev/null
+++ b/src/ElectronNET.API/Serialization/JsonToBoxedPrimitivesConverter.cs
@@ -0,0 +1,126 @@
+namespace ElectronNET.Serialization
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
+
+ public sealed class JsonToBoxedPrimitivesConverter : JsonConverter
+ {
+ public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return ReadValue(ref reader);
+ }
+
+ private static object ReadValue(ref Utf8JsonReader r)
+ {
+ switch (r.TokenType)
+ {
+ case JsonTokenType.StartObject:
+
+ var obj = new Dictionary();
+ while (r.Read())
+ {
+ if (r.TokenType == JsonTokenType.EndObject)
+ {
+ return obj;
+ }
+
+ if (r.TokenType != JsonTokenType.PropertyName)
+ {
+ throw new JsonException("Expected property name.");
+ }
+
+ string name = r.GetString()!;
+ if (!r.Read())
+ {
+ throw new JsonException("Unexpected end while reading property value.");
+ }
+
+ obj[name] = ReadValue(ref r);
+ }
+
+ throw new JsonException("Unexpected end while reading object.");
+
+ case JsonTokenType.StartArray:
+
+ var list = new List();
+ while (r.Read())
+ {
+ if (r.TokenType == JsonTokenType.EndArray)
+ {
+ return list;
+ }
+
+ list.Add(ReadValue(ref r));
+ }
+
+ throw new JsonException("Unexpected end while reading array.");
+
+ case JsonTokenType.True: return true;
+ case JsonTokenType.False: return false;
+ case JsonTokenType.Null: return null;
+
+ case JsonTokenType.Number:
+
+ if (r.TryGetInt32(out int i))
+ {
+ return i;
+ }
+
+ if (r.TryGetInt64(out long l))
+ {
+ return l;
+ }
+
+ if (r.TryGetDouble(out double d))
+ {
+ return d;
+ }
+
+ return r.GetDecimal();
+
+ case JsonTokenType.String:
+
+ string s = r.GetString()!;
+
+ if (DateTimeOffset.TryParse(s, out var dto))
+ {
+ return dto;
+ }
+
+ if (DateTime.TryParse(s, out var dt))
+ {
+ return dt;
+ }
+
+ if (TimeSpan.TryParse(s, out var ts))
+ {
+ return ts;
+ }
+
+ if (Guid.TryParse(s, out var g))
+ {
+ return g;
+ }
+
+ return s;
+
+ default:
+ throw new JsonException($"Unsupported token {r.TokenType}");
+ }
+ }
+
+ public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
+ {
+ if (value is null)
+ {
+ writer.WriteNullValue();
+ return;
+ }
+
+ writer.WriteStartObject();
+ writer.WriteEndObject();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs b/src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs
index c6078365..26d524f7 100644
--- a/src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs
+++ b/src/ElectronNET.AspNet/API/WebHostBuilderExtensions.cs
@@ -61,6 +61,11 @@ public static IWebHostBuilder UseElectron(this IWebHostBuilder builder, string[]
{
ElectronNetRuntime.OnAppReadyCallback = onAppReadyCallback;
+ // no matter how this is set - let's unset to prevent Electron not starting as expected
+ // e.g., VS Code sets this env variable, but this will cause `require("electron")` to not
+ // work as expected, see issue #952
+ Environment.SetEnvironmentVariable("ELECTRON_RUN_AS_NODE", null);
+
var webPort = PortHelper.GetFreePort(ElectronNetRuntime.AspNetWebPort ?? ElectronNetRuntime.DefaultWebPort);
ElectronNetRuntime.AspNetWebPort = webPort;
diff --git a/src/ElectronNET.ConsoleApp/ElectronNET.ConsoleApp.csproj b/src/ElectronNET.ConsoleApp/ElectronNET.ConsoleApp.csproj
index 11ad090d..caf726c6 100644
--- a/src/ElectronNET.ConsoleApp/ElectronNET.ConsoleApp.csproj
+++ b/src/ElectronNET.ConsoleApp/ElectronNET.ConsoleApp.csproj
@@ -8,7 +8,7 @@
- net8.0
+ net10.0
exe
@@ -16,6 +16,7 @@
false
False
false
+ disable
128.png
@@ -69,7 +70,7 @@
-
+
diff --git a/src/ElectronNET.ConsoleApp/Properties/PublishProfiles/linux-x64.pubxml b/src/ElectronNET.ConsoleApp/Properties/PublishProfiles/linux-x64.pubxml
index c92d1b50..2c9cd897 100644
--- a/src/ElectronNET.ConsoleApp/Properties/PublishProfiles/linux-x64.pubxml
+++ b/src/ElectronNET.ConsoleApp/Properties/PublishProfiles/linux-x64.pubxml
@@ -7,7 +7,7 @@
publish\Release\net8.0\linux-x64
FileSystem
<_TargetId>Folder
- net8.0
+ net10.0
linux-x64
false
false
diff --git a/src/ElectronNET.ConsoleApp/Properties/PublishProfiles/publish-win-x64.pubxml b/src/ElectronNET.ConsoleApp/Properties/PublishProfiles/publish-win-x64.pubxml
index 01940b38..cf6e6e09 100644
--- a/src/ElectronNET.ConsoleApp/Properties/PublishProfiles/publish-win-x64.pubxml
+++ b/src/ElectronNET.ConsoleApp/Properties/PublishProfiles/publish-win-x64.pubxml
@@ -12,7 +12,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
publish\Release\net8.0\win-x64\
FileSystem
<_TargetId>Folder
- net8.0
+ net10.0
win-x64
true
diff --git a/src/ElectronNET.ConsoleApp/Properties/PublishProfiles/win-x64.pubxml b/src/ElectronNET.ConsoleApp/Properties/PublishProfiles/win-x64.pubxml
index fbd20075..bda8dcc7 100644
--- a/src/ElectronNET.ConsoleApp/Properties/PublishProfiles/win-x64.pubxml
+++ b/src/ElectronNET.ConsoleApp/Properties/PublishProfiles/win-x64.pubxml
@@ -7,7 +7,7 @@
publish\Release\net8.0\win-x64
FileSystem
<_TargetId>Folder
- net8.0
+ net10.0
win-x64
false
false
diff --git a/src/ElectronNET.Host/ElectronNET.Host.esproj b/src/ElectronNET.Host/ElectronNET.Host.esproj
index 0f56fbfb..25d35af7 100644
--- a/src/ElectronNET.Host/ElectronNET.Host.esproj
+++ b/src/ElectronNET.Host/ElectronNET.Host.esproj
@@ -1,4 +1,4 @@
-
+
diff --git a/src/ElectronNET.Host/api/tray.js b/src/ElectronNET.Host/api/tray.js
index 103acfbf..ae20b505 100644
--- a/src/ElectronNET.Host/api/tray.js
+++ b/src/ElectronNET.Host/api/tray.js
@@ -50,11 +50,7 @@ module.exports = (socket) => {
const trayIcon = electron_1.nativeImage.createFromPath(image);
tray.value = new electron_1.Tray(trayIcon);
if (menuItems) {
- const menu = electron_1.Menu.buildFromTemplate(menuItems);
- addMenuItemClickConnector(menu.items, (id) => {
- electronSocket.emit('trayMenuItemClicked', id);
- });
- tray.value.setContextMenu(menu);
+ applyContextMenu(menuItems);
}
});
socket.on('tray-destroy', () => {
@@ -62,6 +58,11 @@ module.exports = (socket) => {
tray.value.destroy();
}
});
+ socket.on('set-contextMenu', (menuItems) => {
+ if (menuItems && tray.value) {
+ applyContextMenu(menuItems);
+ }
+ });
socket.on('tray-setImage', (image) => {
if (tray.value) {
tray.value.setImage(image);
@@ -118,6 +119,13 @@ module.exports = (socket) => {
});
}
});
+ function applyContextMenu(menuItems) {
+ const menu = electron_1.Menu.buildFromTemplate(menuItems);
+ addMenuItemClickConnector(menu.items, (id) => {
+ electronSocket.emit('trayMenuItemClicked', id);
+ });
+ tray.value.setContextMenu(menu);
+ }
function addMenuItemClickConnector(menuItems, callback) {
menuItems.forEach((item) => {
if (item.submenu && item.submenu.items.length > 0) {
diff --git a/src/ElectronNET.Host/api/tray.js.map b/src/ElectronNET.Host/api/tray.js.map
index 8d6584ba..3634cb9f 100644
--- a/src/ElectronNET.Host/api/tray.js.map
+++ b/src/ElectronNET.Host/api/tray.js.map
@@ -1 +1 @@
-{"version":3,"file":"tray.js","sourceRoot":"","sources":["tray.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"tray.js","sourceRoot":"","sources":["tray.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/src/ElectronNET.Host/api/tray.ts b/src/ElectronNET.Host/api/tray.ts
index 1481f9a6..9c101022 100644
--- a/src/ElectronNET.Host/api/tray.ts
+++ b/src/ElectronNET.Host/api/tray.ts
@@ -59,12 +59,7 @@ export = (socket: Socket) => {
tray.value = new Tray(trayIcon);
if (menuItems) {
- const menu = Menu.buildFromTemplate(menuItems);
-
- addMenuItemClickConnector(menu.items, (id) => {
- electronSocket.emit('trayMenuItemClicked', id);
- });
- tray.value.setContextMenu(menu);
+ applyContextMenu(menuItems);
}
});
@@ -74,6 +69,12 @@ export = (socket: Socket) => {
}
});
+ socket.on('set-contextMenu', (menuItems) => {
+ if (menuItems && tray.value) {
+ applyContextMenu(menuItems);
+ }
+ });
+
socket.on('tray-setImage', (image) => {
if (tray.value) {
tray.value.setImage(image);
@@ -136,6 +137,14 @@ export = (socket: Socket) => {
}
});
+ function applyContextMenu(menuItems) {
+ const menu = Menu.buildFromTemplate(menuItems);
+ addMenuItemClickConnector(menu.items, (id) => {
+ electronSocket.emit('trayMenuItemClicked', id);
+ });
+ tray.value.setContextMenu(menu);
+ }
+
function addMenuItemClickConnector(menuItems, callback) {
menuItems.forEach((item) => {
if (item.submenu && item.submenu.items.length > 0) {
diff --git a/src/ElectronNET.Host/api/webContents.js b/src/ElectronNET.Host/api/webContents.js
index 3d0c8b0e..add58233 100644
--- a/src/ElectronNET.Host/api/webContents.js
+++ b/src/ElectronNET.Host/api/webContents.js
@@ -78,7 +78,7 @@ module.exports = (socket) => {
electronSocket.emit("webContents-domReady" + id);
});
});
- socket.on("webContentsOpenDevTools", (id, options) => {
+ socket.on("webContents-openDevTools", (id, options) => {
if (options) {
getWindowById(id).webContents.openDevTools(options);
}
@@ -315,6 +315,61 @@ module.exports = (socket) => {
const extension = await browserWindow.webContents.session.loadExtension(path, { allowFileAccess: allowFileAccess });
electronSocket.emit("webContents-session-loadExtension-completed", extension);
});
+ socket.on('webContents-getZoomFactor', (id) => {
+ const browserWindow = getWindowById(id);
+ const text = browserWindow.webContents.getZoomFactor();
+ electronSocket.emit('webContents-getZoomFactor-completed', text);
+ });
+ socket.on('webContents-setZoomFactor', (id, factor) => {
+ const browserWindow = getWindowById(id);
+ browserWindow.webContents.setZoomFactor(factor);
+ });
+ socket.on('webContents-getZoomLevel', (id) => {
+ const browserWindow = getWindowById(id);
+ const content = browserWindow.webContents.getZoomLevel();
+ electronSocket.emit('webContents-getZoomLevel-completed', content);
+ });
+ socket.on('webContents-setZoomLevel', (id, level) => {
+ const browserWindow = getWindowById(id);
+ browserWindow.webContents.setZoomLevel(level);
+ });
+ socket.on('webContents-setVisualZoomLevelLimits', async (id, minimumLevel, maximumLevel) => {
+ const browserWindow = getWindowById(id);
+ await browserWindow.webContents.setVisualZoomLevelLimits(minimumLevel, maximumLevel);
+ electronSocket.emit('webContents-setVisualZoomLevelLimits-completed');
+ });
+ socket.on("webContents-toggleDevTools", (id) => {
+ getWindowById(id).webContents.toggleDevTools();
+ });
+ socket.on("webContents-closeDevTools", (id) => {
+ getWindowById(id).webContents.closeDevTools();
+ });
+ socket.on("webContents-isDevToolsOpened", function (id) {
+ const browserWindow = getWindowById(id);
+ electronSocket.emit('webContents-isDevToolsOpened-completed', browserWindow.webContents.isDevToolsOpened());
+ });
+ socket.on("webContents-isDevToolsFocused", function (id) {
+ const browserWindow = getWindowById(id);
+ electronSocket.emit('webContents-isDevToolsFocused-completed', browserWindow.webContents.isDevToolsFocused());
+ });
+ socket.on("webContents-setAudioMuted", (id, muted) => {
+ getWindowById(id).webContents.setAudioMuted(muted);
+ });
+ socket.on("webContents-isAudioMuted", function (id) {
+ const browserWindow = getWindowById(id);
+ electronSocket.emit('webContents-isAudioMuted-completed', browserWindow.webContents.isAudioMuted());
+ });
+ socket.on("webContents-isCurrentlyAudible", function (id) {
+ const browserWindow = getWindowById(id);
+ electronSocket.emit('webContents-isCurrentlyAudible-completed', browserWindow.webContents.isCurrentlyAudible());
+ });
+ socket.on("webContents-getUserAgent", function (id) {
+ const browserWindow = getWindowById(id);
+ electronSocket.emit('webContents-getUserAgent-completed', browserWindow.webContents.getUserAgent());
+ });
+ socket.on("webContents-setUserAgent", (id, userAgent) => {
+ getWindowById(id).webContents.setUserAgent(userAgent);
+ });
function getWindowById(id) {
if (id >= 1000) {
return (0, browserView_1.browserViewMediateService)(id - 1000);
diff --git a/src/ElectronNET.Host/api/webContents.js.map b/src/ElectronNET.Host/api/webContents.js.map
index c3f3e451..f914af82 100644
--- a/src/ElectronNET.Host/api/webContents.js.map
+++ b/src/ElectronNET.Host/api/webContents.js.map
@@ -1 +1 @@
-{"version":3,"file":"webContents.js","sourceRoot":"","sources":["webContents.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"webContents.js","sourceRoot":"","sources":["webContents.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/src/ElectronNET.Host/api/webContents.ts b/src/ElectronNET.Host/api/webContents.ts
index 2d156619..fe54a521 100644
--- a/src/ElectronNET.Host/api/webContents.ts
+++ b/src/ElectronNET.Host/api/webContents.ts
@@ -1,5 +1,5 @@
import { Socket } from "net";
-import { BrowserWindow, BrowserView } from "electron";
+import {BrowserWindow, BrowserView} from "electron";
import { browserViewMediateService } from "./browserView";
const fs = require("fs");
let electronSocket;
@@ -101,7 +101,7 @@ export = (socket: Socket) => {
});
});
- socket.on("webContentsOpenDevTools", (id, options) => {
+ socket.on("webContents-openDevTools", (id, options) => {
if (options) {
getWindowById(id).webContents.openDevTools(options);
} else {
@@ -466,7 +466,76 @@ export = (socket: Socket) => {
}
);
- function getWindowById(
+ socket.on('webContents-getZoomFactor', (id) => {
+ const browserWindow = getWindowById(id);
+ const text = browserWindow.webContents.getZoomFactor();
+ electronSocket.emit('webContents-getZoomFactor-completed', text);
+ });
+
+ socket.on('webContents-setZoomFactor', (id, factor) => {
+ const browserWindow = getWindowById(id);
+ browserWindow.webContents.setZoomFactor(factor);
+ });
+
+ socket.on('webContents-getZoomLevel', (id) => {
+ const browserWindow = getWindowById(id);
+ const content = browserWindow.webContents.getZoomLevel();
+ electronSocket.emit('webContents-getZoomLevel-completed', content);
+ });
+
+ socket.on('webContents-setZoomLevel', (id, level) => {
+ const browserWindow = getWindowById(id);
+ browserWindow.webContents.setZoomLevel(level);
+ });
+
+ socket.on('webContents-setVisualZoomLevelLimits', async (id, minimumLevel, maximumLevel) => {
+ const browserWindow = getWindowById(id);
+ await browserWindow.webContents.setVisualZoomLevelLimits(minimumLevel, maximumLevel);
+ electronSocket.emit('webContents-setVisualZoomLevelLimits-completed');
+ });
+
+ socket.on("webContents-toggleDevTools", (id) => {
+ getWindowById(id).webContents.toggleDevTools();
+ });
+
+ socket.on("webContents-closeDevTools", (id) => {
+ getWindowById(id).webContents.closeDevTools();
+ });
+
+ socket.on("webContents-isDevToolsOpened", function (id) {
+ const browserWindow = getWindowById(id);
+ electronSocket.emit('webContents-isDevToolsOpened-completed', browserWindow.webContents.isDevToolsOpened());
+ });
+
+ socket.on("webContents-isDevToolsFocused", function (id) {
+ const browserWindow = getWindowById(id);
+ electronSocket.emit('webContents-isDevToolsFocused-completed', browserWindow.webContents.isDevToolsFocused());
+ });
+
+ socket.on("webContents-setAudioMuted", (id, muted) => {
+ getWindowById(id).webContents.setAudioMuted(muted);
+ });
+
+ socket.on("webContents-isAudioMuted", function (id) {
+ const browserWindow = getWindowById(id);
+ electronSocket.emit('webContents-isAudioMuted-completed', browserWindow.webContents.isAudioMuted());
+ });
+
+ socket.on("webContents-isCurrentlyAudible", function (id) {
+ const browserWindow = getWindowById(id);
+ electronSocket.emit('webContents-isCurrentlyAudible-completed', browserWindow.webContents.isCurrentlyAudible());
+ });
+
+ socket.on("webContents-getUserAgent", function (id) {
+ const browserWindow = getWindowById(id);
+ electronSocket.emit('webContents-getUserAgent-completed', browserWindow.webContents.getUserAgent());
+ });
+
+ socket.on("webContents-setUserAgent", (id, userAgent) => {
+ getWindowById(id).webContents.setUserAgent(userAgent);
+ });
+
+ function getWindowById(
id: number
): Electron.BrowserWindow | Electron.BrowserView {
if (id >= 1000) {
diff --git a/src/ElectronNET.Host/main.js b/src/ElectronNET.Host/main.js
index 1cddaa5a..2562ca7b 100644
--- a/src/ElectronNET.Host/main.js
+++ b/src/ElectronNET.Host/main.js
@@ -5,14 +5,13 @@ const path = require('path');
const cProcess = require('child_process').spawn;
const portscanner = require('portscanner');
const { imageSize } = require('image-size');
-const { HookService } = require('electron-host-hook');
let io, server, browserWindows, ipc, apiProcess, loadURL;
let appApi, menu, dialogApi, notification, tray, webContents;
let globalShortcut, shellApi, screen, clipboard, autoUpdater;
let commandLine, browserView;
let powerMonitor;
let processInfo;
-let splashScreen, hostHook;
+let splashScreen;
let nativeTheme;
let dock;
let launchFile;
@@ -263,6 +262,7 @@ function startSocketApiBridge(port) {
console.log('Electron Socket: starting...');
server = require('http').createServer();
const { Server } = require('socket.io');
+ let hostHook;
io = new Server({
pingTimeout: 60000, // in ms, default is 5000
pingInterval: 10000, // in ms, default is 25000
@@ -359,6 +359,8 @@ function startSocketApiBridge(port) {
});
try {
+ const { HookService } = require('electron-host-hook');
+
if (hostHook === undefined) {
hostHook = new HookService(socket, app);
hostHook.onHostReady();
@@ -366,6 +368,7 @@ function startSocketApiBridge(port) {
} catch (error) {
console.error(error.message);
}
+
console.log('Electron Socket: startup complete.');
});
}
@@ -373,7 +376,7 @@ function startSocketApiBridge(port) {
function startAspCoreBackend(electronPort) {
startBackend();
- function startBackend() {
+ function startBackend() {
loadURL = `about:blank`;
const envParam = getEnvironmentParameter();
const parameters = [
diff --git a/src/ElectronNET.IntegrationTests/Common/IntegrationFactAttribute.cs b/src/ElectronNET.IntegrationTests/Common/IntegrationFactAttribute.cs
new file mode 100644
index 00000000..6142bdfd
--- /dev/null
+++ b/src/ElectronNET.IntegrationTests/Common/IntegrationFactAttribute.cs
@@ -0,0 +1,101 @@
+namespace ElectronNET.IntegrationTests.Common
+{
+ using System.Runtime.InteropServices;
+ using Xunit.Sdk;
+
+ ///
+ /// Custom fact attribute with a default timeout of 20 seconds, allowing tests to be skipped on specific environments.
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Method)]
+ [XunitTestCaseDiscoverer("Xunit.Sdk.SkippableFactDiscoverer", "Xunit.SkippableFact")]
+ internal sealed class IntegrationFactAttribute : FactAttribute
+ {
+ private static readonly bool IsOnWsl;
+
+ private static readonly bool IsOnCI;
+
+ static IntegrationFactAttribute()
+ {
+ IsOnWsl = DetectWsl();
+ IsOnCI = DetectCI();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public IntegrationFactAttribute()
+ {
+ this.Timeout = 20_000;
+ }
+
+ public bool SkipOnWsl { get; set; }
+
+ public bool SkipOnCI { get; set; }
+
+ ///
+ /// Marks the test so that it will not be run, and gets or sets the skip reason
+ ///
+ public override string Skip {
+ get
+ {
+ if (IsOnWsl && this.SkipOnWsl)
+ {
+ return "Skipping test on WSL environment.";
+ }
+
+ if (IsOnCI && this.SkipOnCI)
+ {
+ return "Skipping test on CI environment.";
+ }
+
+ return base.Skip;
+ }
+ set
+ {
+ base.Skip = value;
+ }
+ }
+
+ private static bool DetectWsl()
+ {
+ try
+ {
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return false;
+ }
+
+ if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WSL_DISTRO_NAME")) ||
+ !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WSL_INTEROP")))
+ {
+ return true;
+ }
+
+ return false;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ private static bool DetectCI()
+ {
+ try
+ {
+ if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TF_BUILD")) ||
+ !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GITHUB_ACTIONS")))
+ {
+ return true;
+ }
+
+ return false;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/ElectronNET.IntegrationTests/Common/IntegrationTestBase.cs b/src/ElectronNET.IntegrationTests/Common/IntegrationTestBase.cs
new file mode 100644
index 00000000..5a6407fa
--- /dev/null
+++ b/src/ElectronNET.IntegrationTests/Common/IntegrationTestBase.cs
@@ -0,0 +1,23 @@
+namespace ElectronNET.IntegrationTests.Common
+{
+ using ElectronNET.API;
+ using ElectronNET.API.Entities;
+
+ // Base class for integration tests providing shared access to MainWindow and OS platform constants
+ public abstract class IntegrationTestBase
+ {
+ protected IntegrationTestBase(ElectronFixture fixture)
+ {
+ Fixture = fixture;
+ MainWindow = fixture.MainWindow;
+ }
+
+ protected ElectronFixture Fixture { get; }
+ protected BrowserWindow MainWindow { get; }
+
+ // Constants for SupportedOSPlatform attributes
+ public const string Windows = "Windows";
+ public const string MacOS = "macOS";
+ public const string Linux = "Linux";
+ }
+}
diff --git a/src/ElectronNET.IntegrationTests/Common/SkipOnWslFactAttribute.cs b/src/ElectronNET.IntegrationTests/Common/SkipOnWslFactAttribute.cs
deleted file mode 100644
index 8c1a3d02..00000000
--- a/src/ElectronNET.IntegrationTests/Common/SkipOnWslFactAttribute.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-namespace ElectronNET.IntegrationTests.Common
-{
- using System.Runtime.InteropServices;
-
- [AttributeUsage(AttributeTargets.Method)]
- internal sealed class SkipOnWslFactAttribute : FactAttribute
- {
- private static readonly bool IsOnWsl;
-
- static SkipOnWslFactAttribute()
- {
- IsOnWsl = DetectWsl();
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- public SkipOnWslFactAttribute()
- {
- if (IsOnWsl)
- {
- this.Skip = "Skipping test on WSL environment.";
- }
- }
-
- private static bool DetectWsl()
- {
- try
- {
- if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
- {
- return false;
- }
-
- if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WSL_DISTRO_NAME")) ||
- !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WSL_INTEROP")))
- {
- return true;
- }
-
- return false;
- }
- catch
- {
- return false;
- }
- }
- }
-}
diff --git a/src/ElectronNET.IntegrationTests/ElectronFixture.cs b/src/ElectronNET.IntegrationTests/ElectronFixture.cs
index cfaf8df2..59be6c95 100644
--- a/src/ElectronNET.IntegrationTests/ElectronFixture.cs
+++ b/src/ElectronNET.IntegrationTests/ElectronFixture.cs
@@ -4,6 +4,7 @@ namespace ElectronNET.IntegrationTests
using System.Reflection;
using ElectronNET.API;
using ElectronNET.API.Entities;
+ using ElectronNET.Common;
// Shared fixture that starts Electron runtime once
[SuppressMessage("ReSharper", "MethodHasAsyncOverload")]
@@ -26,7 +27,7 @@ public async Task InitializeAsync()
await runtimeController.Start();
Console.Error.WriteLine("[ElectronFixture] Waiting for Ready...");
- await Task.WhenAny(runtimeController.WaitReadyTask, Task.Delay(TimeSpan.FromSeconds(10)));
+ await Task.WhenAny(runtimeController.WaitReadyTask, Task.Delay(10.seconds()));
if (!runtimeController.WaitReadyTask.IsCompleted)
{
diff --git a/src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj b/src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj
index c12d1e57..ad9d60c3 100644
--- a/src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj
+++ b/src/ElectronNET.IntegrationTests/ElectronNET.IntegrationTests.csproj
@@ -10,7 +10,7 @@
net10.0
enable
- enable
+ disable
false
true
diff --git a/src/ElectronNET.IntegrationTests/Tests/AppTests.cs b/src/ElectronNET.IntegrationTests/Tests/AppTests.cs
index 1a15efdb..6d962f25 100644
--- a/src/ElectronNET.IntegrationTests/Tests/AppTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/AppTests.cs
@@ -6,19 +6,16 @@ namespace ElectronNET.IntegrationTests.Tests
using System.IO;
using System.Runtime.Versioning;
using System.Threading.Tasks;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class AppTests
+ public class AppTests : IntegrationTestBase
{
- // ReSharper disable once NotAccessedField.Local
- private readonly ElectronFixture fx;
-
- public AppTests(ElectronFixture fx)
+ public AppTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_get_app_path()
{
var path = await Electron.App.GetAppPathAsync();
@@ -26,7 +23,7 @@ public async Task Can_get_app_path()
Directory.Exists(path).Should().BeTrue();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_get_version_and_locale()
{
var version = await Electron.App.GetVersionAsync();
@@ -35,7 +32,7 @@ public async Task Can_get_version_and_locale()
locale.Should().NotBeNullOrWhiteSpace();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_get_special_paths()
{
var userData = await Electron.App.GetPathAsync(PathName.UserData);
@@ -47,7 +44,7 @@ public async Task Can_get_special_paths()
Directory.Exists(temp).Should().BeTrue();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_get_app_metrics()
{
var metrics = await Electron.App.GetAppMetricsAsync();
@@ -55,23 +52,23 @@ public async Task Can_get_app_metrics()
metrics.Length.Should().BeGreaterThan(0);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_get_gpu_feature_status()
{
var status = await Electron.App.GetGpuFeatureStatusAsync();
status.Should().NotBeNull();
}
- [SkippableFact(Timeout = 20000)]
- [SupportedOSPlatform("macOS")]
- [SupportedOSPlatform("Windows")]
+ [IntegrationFact]
+ [SupportedOSPlatform(MacOS)]
+ [SupportedOSPlatform(Windows)]
public async Task Can_get_login_item_settings()
{
var settings = await Electron.App.GetLoginItemSettingsAsync();
settings.Should().NotBeNull();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task CommandLine_append_and_query_switch()
{
var switchName = "integration-switch";
@@ -80,9 +77,9 @@ public async Task CommandLine_append_and_query_switch()
(await Electron.App.CommandLine.GetSwitchValueAsync(switchName)).Should().Be("value123");
}
- [SkippableFact(Timeout = 20000)]
- [SupportedOSPlatform("macOS")]
- [SupportedOSPlatform("Windows")]
+ [IntegrationFact]
+ [SupportedOSPlatform(MacOS)]
+ [SupportedOSPlatform(Windows)]
public async Task Accessibility_support_toggle()
{
Electron.App.SetAccessibilitySupportEnabled(true);
@@ -91,7 +88,7 @@ public async Task Accessibility_support_toggle()
Electron.App.SetAccessibilitySupportEnabled(false);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task UserAgentFallback_roundtrip()
{
var original = await Electron.App.UserAgentFallbackAsync;
@@ -101,9 +98,9 @@ public async Task UserAgentFallback_roundtrip()
Electron.App.UserAgentFallback = original; // restore
}
- [SkippableFact(Timeout = 20000)]
- [SupportedOSPlatform("Linux")]
- [SupportedOSPlatform("macOS")]
+ [IntegrationFact]
+ [SupportedOSPlatform(Linux)]
+ [SupportedOSPlatform(MacOS)]
public async Task BadgeCount_set_and_reset_where_supported()
{
await Electron.App.SetBadgeCountAsync(2);
@@ -113,14 +110,14 @@ public async Task BadgeCount_set_and_reset_where_supported()
await Electron.App.SetBadgeCountAsync(0);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task App_metrics_have_cpu_info()
{
var metrics = await Electron.App.GetAppMetricsAsync();
metrics[0].Cpu.Should().NotBeNull();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task App_gpu_feature_status_has_some_fields()
{
var status = await Electron.App.GetGpuFeatureStatusAsync();
diff --git a/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs b/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs
index 1bcb3c04..65279a89 100644
--- a/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/AutoUpdaterTests.cs
@@ -2,18 +2,17 @@
{
using API;
using System.Threading.Tasks;
+ using ElectronNET.Common;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class AutoUpdaterTests
+ public class AutoUpdaterTests : IntegrationTestBase
{
- private readonly ElectronFixture fx;
-
- public AutoUpdaterTests(ElectronFixture fx)
+ public AutoUpdaterTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task AutoDownload_check()
{
Electron.AutoUpdater.AutoDownload = false;
@@ -24,7 +23,7 @@ public async Task AutoDownload_check()
test2.Should().BeTrue();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task AutoInstallOnAppQuit_check()
{
Electron.AutoUpdater.AutoInstallOnAppQuit = false;
@@ -35,7 +34,7 @@ public async Task AutoInstallOnAppQuit_check()
test2.Should().BeTrue();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task AllowPrerelease_check()
{
Electron.AutoUpdater.AllowPrerelease = false;
@@ -46,7 +45,7 @@ public async Task AllowPrerelease_check()
test2.Should().BeTrue();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task FullChangelog_check()
{
Electron.AutoUpdater.FullChangelog = false;
@@ -57,7 +56,7 @@ public async Task FullChangelog_check()
test2.Should().BeTrue();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task AllowDowngrade_check()
{
Electron.AutoUpdater.AllowDowngrade = false;
@@ -68,14 +67,14 @@ public async Task AllowDowngrade_check()
test2.Should().BeTrue();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task UpdateConfigPath_check()
{
var test1 = Electron.AutoUpdater.UpdateConfigPath;
test1.Should().Be(string.Empty);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task CurrentVersionAsync_check()
{
var semver = await Electron.AutoUpdater.CurrentVersionAsync;
@@ -83,18 +82,18 @@ public async Task CurrentVersionAsync_check()
semver.Major.Should().BeGreaterThan(0);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task ChannelAsync_check()
{
var test = await Electron.AutoUpdater.ChannelAsync;
test.Should().Be(string.Empty);
Electron.AutoUpdater.SetChannel = "beta";
- await Task.Delay(500);
+ await Task.Delay(500.ms());
test = await Electron.AutoUpdater.ChannelAsync;
test.Should().Be("beta");
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task RequestHeadersAsync_check()
{
var headers = new Dictionary
@@ -104,27 +103,28 @@ public async Task RequestHeadersAsync_check()
var test = await Electron.AutoUpdater.RequestHeadersAsync;
test.Should().BeNull();
Electron.AutoUpdater.RequestHeaders = headers;
+ await Task.Delay(500.ms());
test = await Electron.AutoUpdater.RequestHeadersAsync;
test.Should().NotBeNull();
test.Count.Should().Be(1);
test["key1"].Should().Be("value1");
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task CheckForUpdatesAsync_check()
{
var test = await Electron.AutoUpdater.CheckForUpdatesAsync();
test.Should().BeNull();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task CheckForUpdatesAndNotifyAsync_check()
{
var test = await Electron.AutoUpdater.CheckForUpdatesAsync();
test.Should().BeNull();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task GetFeedURLAsync_check()
{
var test = await Electron.AutoUpdater.GetFeedURLAsync();
diff --git a/src/ElectronNET.IntegrationTests/Tests/BrowserViewTests.cs b/src/ElectronNET.IntegrationTests/Tests/BrowserViewTests.cs
index 8405da75..6fa25c0b 100644
--- a/src/ElectronNET.IntegrationTests/Tests/BrowserViewTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/BrowserViewTests.cs
@@ -2,22 +2,20 @@ namespace ElectronNET.IntegrationTests.Tests
{
using ElectronNET.API;
using ElectronNET.API.Entities;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class BrowserViewTests
+ public class BrowserViewTests : IntegrationTestBase
{
- private readonly ElectronFixture fx;
-
- public BrowserViewTests(ElectronFixture fx)
+ public BrowserViewTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Create_browser_view_and_adjust_bounds()
{
var view = await Electron.WindowManager.CreateBrowserViewAsync(new BrowserViewConstructorOptions());
- this.fx.MainWindow.SetBrowserView(view);
+ this.MainWindow.SetBrowserView(view);
view.Bounds = new Rectangle { X = 0, Y = 0, Width = 300, Height = 200 };
// Access bounds again (synchronous property fetch)
var current = view.Bounds;
diff --git a/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs b/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs
index 6c0bd7e2..c22b8908 100644
--- a/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/BrowserWindowTests.cs
@@ -1,144 +1,163 @@
namespace ElectronNET.IntegrationTests.Tests
{
- using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using ElectronNET.API;
using ElectronNET.API.Entities;
+ using ElectronNET.Common;
using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class BrowserWindowTests
+ public class BrowserWindowTests : IntegrationTestBase
{
- private readonly ElectronFixture fx;
-
- public BrowserWindowTests(ElectronFixture fx)
+ public BrowserWindowTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_set_and_get_title()
{
const string title = "Integration Test Title";
- this.fx.MainWindow.SetTitle(title);
- await Task.Delay(500);
- var roundTrip = await this.fx.MainWindow.GetTitleAsync();
+ this.MainWindow.SetTitle(title);
+ await Task.Delay(500.ms());
+ var roundTrip = await this.MainWindow.GetTitleAsync();
roundTrip.Should().Be(title);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_resize_and_get_size()
{
- this.fx.MainWindow.SetSize(643, 482);
- await Task.Delay(500);
- var size = await this.fx.MainWindow.GetSizeAsync();
+ this.MainWindow.SetSize(643, 482);
+ await Task.Delay(500.ms());
+ var size = await this.MainWindow.GetSizeAsync();
size.Should().HaveCount(2);
size[0].Should().Be(643);
size[1].Should().Be(482);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_set_progress_bar_and_clear()
{
- this.fx.MainWindow.SetProgressBar(0.5);
+ this.MainWindow.SetProgressBar(0.5);
// No direct getter; rely on absence of error. Try changing again.
- this.fx.MainWindow.SetProgressBar(-1); // clears
- await Task.Delay(50);
+ this.MainWindow.SetProgressBar(-1); // clears
+ await Task.Delay(50.ms());
}
- [SkipOnWslFact(Timeout = 20000)]
+ [IntegrationFact(SkipOnWsl = true)]
public async Task Can_set_and_get_position()
{
- this.fx.MainWindow.SetPosition(134, 246);
- await Task.Delay(500);
- var pos = await this.fx.MainWindow.GetPositionAsync();
+ this.MainWindow.SetPosition(134, 246);
+ await Task.Delay(500.ms());
+ var pos = await this.MainWindow.GetPositionAsync();
pos.Should().BeEquivalentTo([134, 246]);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_set_and_get_bounds()
{
var bounds = new Rectangle { X = 10, Y = 20, Width = 400, Height = 300 };
- this.fx.MainWindow.SetBounds(bounds);
- await Task.Delay(500);
- var round = await this.fx.MainWindow.GetBoundsAsync();
+ this.MainWindow.SetBounds(bounds);
+ await Task.Delay(500.ms());
+ var round = await this.MainWindow.GetBoundsAsync();
round.Should().BeEquivalentTo(bounds);
round.Width.Should().Be(400);
round.Height.Should().Be(300);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_set_and_get_content_bounds()
{
var bounds = new Rectangle { X = 0, Y = 0, Width = 300, Height = 200 };
- this.fx.MainWindow.SetContentBounds(bounds);
- await Task.Delay(500);
- var round = await this.fx.MainWindow.GetContentBoundsAsync();
+ this.MainWindow.SetContentBounds(bounds);
+ await Task.Delay(500.ms());
+ var round = await this.MainWindow.GetContentBoundsAsync();
round.Width.Should().BeGreaterThan(0);
round.Height.Should().BeGreaterThan(0);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Show_hide_visibility_roundtrip()
{
- this.fx.MainWindow.Show();
- await Task.Delay(500);
- (await this.fx.MainWindow.IsVisibleAsync()).Should().BeTrue();
- this.fx.MainWindow.Hide();
- await Task.Delay(500);
- (await this.fx.MainWindow.IsVisibleAsync()).Should().BeFalse();
+ BrowserWindow window = null;
+
+ try
+ {
+ window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank");
+
+ await Task.Delay(100.ms());
+
+ window.Show();
+
+ await Task.Delay(500.ms());
+ (await window.IsVisibleAsync()).Should().BeTrue();
+
+ window.Hide();
+ await Task.Delay(500.ms());
+
+ (await window.IsVisibleAsync()).Should().BeFalse();
+ }
+ finally
+ {
+ window?.Destroy();
+ }
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task AlwaysOnTop_toggle_and_query()
{
- this.fx.MainWindow.SetAlwaysOnTop(true);
- await Task.Delay(500);
- (await this.fx.MainWindow.IsAlwaysOnTopAsync()).Should().BeTrue();
- this.fx.MainWindow.SetAlwaysOnTop(false);
- await Task.Delay(500);
- (await this.fx.MainWindow.IsAlwaysOnTopAsync()).Should().BeFalse();
+ this.MainWindow.SetAlwaysOnTop(true);
+ await Task.Delay(500.ms());
+ (await this.MainWindow.IsAlwaysOnTopAsync()).Should().BeTrue();
+ this.MainWindow.SetAlwaysOnTop(false);
+ await Task.Delay(500.ms());
+ (await this.MainWindow.IsAlwaysOnTopAsync()).Should().BeFalse();
}
- [SkippableFact(Timeout = 20000)]
- [SupportedOSPlatform("Linux")]
- [SupportedOSPlatform("Windows")]
+ [IntegrationFact]
+ [SupportedOSPlatform(Linux)]
+ [SupportedOSPlatform(Windows)]
public async Task MenuBar_auto_hide_and_visibility()
{
- this.fx.MainWindow.SetAutoHideMenuBar(true);
- await Task.Delay(500);
- (await this.fx.MainWindow.IsMenuBarAutoHideAsync()).Should().BeTrue();
- this.fx.MainWindow.SetMenuBarVisibility(false);
- await Task.Delay(500);
- (await this.fx.MainWindow.IsMenuBarVisibleAsync()).Should().BeFalse();
- this.fx.MainWindow.SetMenuBarVisibility(true);
- await Task.Delay(500);
- (await this.fx.MainWindow.IsMenuBarVisibleAsync()).Should().BeTrue();
+ this.MainWindow.SetAutoHideMenuBar(true);
+ await Task.Delay(500.ms());
+ (await this.MainWindow.IsMenuBarAutoHideAsync()).Should().BeTrue();
+ this.MainWindow.SetMenuBarVisibility(false);
+ await Task.Delay(500.ms());
+ (await this.MainWindow.IsMenuBarVisibleAsync()).Should().BeFalse();
+ this.MainWindow.SetMenuBarVisibility(true);
+ await Task.Delay(500.ms());
+ (await this.MainWindow.IsMenuBarVisibleAsync()).Should().BeTrue();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task ReadyToShow_event_fires_after_content_ready()
{
- var window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = false }, "about:blank");
- var tcs = new TaskCompletionSource();
- window.OnReadyToShow += () => tcs.TrySetResult();
+ BrowserWindow window = null;
- // Trigger a navigation and wait for DOM ready so the renderer paints, which emits ready-to-show
- var domReadyTcs = new TaskCompletionSource();
- window.WebContents.OnDomReady += () => domReadyTcs.TrySetResult();
- await Task.Delay(500);
- await window.WebContents.LoadURLAsync("about:blank");
- await domReadyTcs.Task;
+ try
+ {
+ window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = false }, "about:blank");
+ var tcs = new TaskCompletionSource();
+ window.OnReadyToShow += () => tcs.TrySetResult();
- var completed = await Task.WhenAny(tcs.Task, Task.Delay(3000));
- completed.Should().Be(tcs.Task);
+ // Trigger a navigation and wait for DOM ready so the renderer paints, which emits ready-to-show
+ var domReadyTcs = new TaskCompletionSource();
+ window.WebContents.OnDomReady += () => domReadyTcs.TrySetResult();
+ await Task.Delay(500.ms());
+ await window.WebContents.LoadURLAsync("about:blank");
+ await domReadyTcs.Task;
- // Typical usage is to show once ready
- window.Show();
+ var completed = await Task.WhenAny(tcs.Task, Task.Delay(3.seconds()));
+ completed.Should().Be(tcs.Task);
+ }
+ finally
+ {
+ window?.Destroy();
+ }
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task PageTitleUpdated_event_fires_on_title_change()
{
var window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank");
@@ -148,90 +167,90 @@ public async Task PageTitleUpdated_event_fires_on_title_change()
// Navigate and wait for DOM ready, then change the document.title to trigger the event
var domReadyTcs = new TaskCompletionSource();
window.WebContents.OnDomReady += () => domReadyTcs.TrySetResult();
- await Task.Delay(500);
+ await Task.Delay(500.ms());
await window.WebContents.LoadURLAsync("about:blank");
await domReadyTcs.Task;
await window.WebContents.ExecuteJavaScriptAsync("document.title='NewTitle';");
// Wait for event up to a short timeout
- var completed2 = await Task.WhenAny(tcs.Task, Task.Delay(3000));
+ var completed2 = await Task.WhenAny(tcs.Task, Task.Delay(3.seconds()));
completed2.Should().Be(tcs.Task);
(await tcs.Task).Should().Be("NewTitle");
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Resize_event_fires_on_size_change()
{
var window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = false }, "about:blank");
var resized = false;
window.OnResize += () => resized = true;
- await Task.Delay(500);
+ await Task.Delay(500.ms());
window.SetSize(500, 400);
- await Task.Delay(300);
+ await Task.Delay(300.ms());
resized.Should().BeTrue();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Progress_bar_and_always_on_top_toggle()
{
- var win = this.fx.MainWindow;
+ var win = this.MainWindow;
win.SetProgressBar(0.5);
- await Task.Delay(50);
- win.SetProgressBar(0.8, new ProgressBarOptions { Mode = ProgressBarMode.normal });
- await Task.Delay(50);
+ await Task.Delay(50.ms());
+ win.SetProgressBar(0.8, new ProgressBarOptions());
+ await Task.Delay(50.ms());
win.SetAlwaysOnTop(true);
- await Task.Delay(500);
+ await Task.Delay(500.ms());
(await win.IsAlwaysOnTopAsync()).Should().BeTrue();
win.SetAlwaysOnTop(false);
- await Task.Delay(500);
+ await Task.Delay(500.ms());
(await win.IsAlwaysOnTopAsync()).Should().BeFalse();
}
- [SkippableFact(Timeout = 20000)]
- [SupportedOSPlatform("Linux")]
- [SupportedOSPlatform("Windows")]
+ [IntegrationFact]
+ [SupportedOSPlatform(Linux)]
+ [SupportedOSPlatform(Windows)]
public async Task Menu_bar_visibility_and_auto_hide()
{
- var win = this.fx.MainWindow;
+ var win = this.MainWindow;
win.SetAutoHideMenuBar(true);
- await Task.Delay(500);
+ await Task.Delay(500.ms());
(await win.IsMenuBarAutoHideAsync()).Should().BeTrue();
win.SetMenuBarVisibility(true);
- await Task.Delay(500);
+ await Task.Delay(500.ms());
(await win.IsMenuBarVisibleAsync()).Should().BeTrue();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Parent_child_relationship_roundtrip()
{
var child = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = false, Width = 300, Height = 200 }, "about:blank");
- this.fx.MainWindow.SetParentWindow(null); // ensure top-level
- child.SetParentWindow(this.fx.MainWindow);
- await Task.Delay(500);
+ this.MainWindow.SetParentWindow(null); // ensure top-level
+ child.SetParentWindow(this.MainWindow);
+ await Task.Delay(500.ms());
var parent = await child.GetParentWindowAsync();
- parent.Id.Should().Be(this.fx.MainWindow.Id);
- var kids = await this.fx.MainWindow.GetChildWindowsAsync();
+ parent.Id.Should().Be(this.MainWindow.Id);
+ var kids = await this.MainWindow.GetChildWindowsAsync();
kids.Select(k => k.Id).Should().Contain(child.Id);
child.Destroy();
}
- [SkippableFact(Timeout = 20000)]
- [SupportedOSPlatform("macOS")]
+ [IntegrationFact]
+ [SupportedOSPlatform(MacOS)]
public async Task Represented_filename_and_edited_flags()
{
- var win = this.fx.MainWindow;
+ var win = this.MainWindow;
var temp = Path.Combine(Path.GetTempPath(), "electronnet_test.txt");
File.WriteAllText(temp, "test");
win.SetRepresentedFilename(temp);
- await Task.Delay(500);
+ await Task.Delay(500.ms());
var represented = await win.GetRepresentedFilenameAsync();
represented.Should().Be(temp);
win.SetDocumentEdited(true);
- await Task.Delay(500);
+ await Task.Delay(500.ms());
var edited = await win.IsDocumentEditedAsync();
edited.Should().BeTrue();
diff --git a/src/ElectronNET.IntegrationTests/Tests/ClipboardTests.cs b/src/ElectronNET.IntegrationTests/Tests/ClipboardTests.cs
index 17ff0dd6..4ef2a991 100644
--- a/src/ElectronNET.IntegrationTests/Tests/ClipboardTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/ClipboardTests.cs
@@ -2,19 +2,16 @@ namespace ElectronNET.IntegrationTests.Tests
{
using System.Runtime.Versioning;
using ElectronNET.API;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class ClipboardTests
+ public class ClipboardTests : IntegrationTestBase
{
- // ReSharper disable once NotAccessedField.Local
- private readonly ElectronFixture fx;
-
- public ClipboardTests(ElectronFixture fx)
+ public ClipboardTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Clipboard_text_roundtrip()
{
var text = $"Hello Electron {Guid.NewGuid()}";
@@ -23,7 +20,7 @@ public async Task Clipboard_text_roundtrip()
read.Should().Be(text);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Available_formats_contains_text_after_write()
{
var text = "FormatsTest";
@@ -32,9 +29,9 @@ public async Task Available_formats_contains_text_after_write()
formats.Should().Contain(f => f.Contains("text") || f.Contains("TEXT") || f.Contains("plain"));
}
- [SkippableFact(Timeout = 20000)]
- [SupportedOSPlatform("macOS")]
- [SupportedOSPlatform("Windows")]
+ [IntegrationFact]
+ [SupportedOSPlatform(MacOS)]
+ [SupportedOSPlatform(Windows)]
public async Task Bookmark_write_and_read()
{
var url = "https://electron-test.com";
diff --git a/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs b/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs
index 1cba009b..1bb6cc92 100644
--- a/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/CookiesTests.cs
@@ -1,26 +1,26 @@
namespace ElectronNET.IntegrationTests.Tests
{
+ using ElectronNET.Common;
+ using ElectronNET.IntegrationTests.Common;
+
[Collection("ElectronCollection")]
- public class CookiesTests
+ public class CookiesTests : IntegrationTestBase
{
- private readonly ElectronFixture fx;
-
- public CookiesTests(ElectronFixture fx)
+ public CookiesTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [Fact(Skip = "Cookie set/get requires navigation to domain; skipping until test harness serves page")]
+ [IntegrationFact(Skip = "Cookie set/get requires navigation to domain; skipping until test harness serves page")]
public async Task Cookie_set_get_remove_sequence()
{
- var session = this.fx.MainWindow.WebContents.Session;
+ var session = this.MainWindow.WebContents.Session;
var changed = false;
session.Cookies.OnChanged += (cookie, cause, removed) => changed = true;
// Navigate to example.com so cookie domain matches
- await this.fx.MainWindow.WebContents.LoadURLAsync("https://example.com");
+ await this.MainWindow.WebContents.LoadURLAsync("https://example.com");
// Set via renderer for now
- await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("document.cookie='integration_cookie=1;path=/';");
- await Task.Delay(500);
+ await this.MainWindow.WebContents.ExecuteJavaScriptAsync("document.cookie='integration_cookie=1;path=/';");
+ await Task.Delay(500.ms());
changed.Should().BeTrue();
}
}
diff --git a/src/ElectronNET.IntegrationTests/Tests/GlobalShortcutTests.cs b/src/ElectronNET.IntegrationTests/Tests/GlobalShortcutTests.cs
index 625618f3..942e6311 100644
--- a/src/ElectronNET.IntegrationTests/Tests/GlobalShortcutTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/GlobalShortcutTests.cs
@@ -2,11 +2,16 @@ namespace ElectronNET.IntegrationTests.Tests
{
using System.Runtime.InteropServices;
using ElectronNET.API;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class GlobalShortcutTests
+ public class GlobalShortcutTests : IntegrationTestBase
{
- [Fact(Timeout = 20000)]
+ public GlobalShortcutTests(ElectronFixture fx) : base(fx)
+ {
+ }
+
+ [IntegrationFact]
public async Task Can_register_and_unregister()
{
var accel = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "Cmd+Alt+G" : "Ctrl+Alt+G";
diff --git a/src/ElectronNET.IntegrationTests/Tests/HostHookTests.cs b/src/ElectronNET.IntegrationTests/Tests/HostHookTests.cs
index 254f7aa4..05b15495 100644
--- a/src/ElectronNET.IntegrationTests/Tests/HostHookTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/HostHookTests.cs
@@ -1,11 +1,16 @@
namespace ElectronNET.IntegrationTests.Tests
{
using ElectronNET.API;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class HostHookTests
+ public class HostHookTests : IntegrationTestBase
{
- [Fact(Skip = "Requires HostHook setup; skipping")]
+ public HostHookTests(ElectronFixture fx) : base(fx)
+ {
+ }
+
+ [IntegrationFact(Skip = "Requires HostHook setup; skipping")]
public async Task HostHook_call_returns_value()
{
var result = await Electron.HostHook.CallAsync("create-excel-file", ".");
diff --git a/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs b/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs
index 2596e9bd..6136fa0a 100644
--- a/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/IpcMainTests.cs
@@ -1,77 +1,93 @@
namespace ElectronNET.IntegrationTests.Tests
{
using ElectronNET.API;
+ using ElectronNET.Common;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class IpcMainTests
+ public class IpcMainTests : IntegrationTestBase
{
- private readonly ElectronFixture fx;
-
- public IpcMainTests(ElectronFixture fx)
+ public IpcMainTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Ipc_On_receives_message_from_renderer()
{
+ object received = null;
+
var tcs = new TaskCompletionSource();
- await Electron.IpcMain.On("ipc-on-test", obj => tcs.TrySetResult(obj?.ToString() ?? string.Empty));
- await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.send('ipc-on-test','payload123')");
+ await Electron.IpcMain.On("ipc-on-test", obj =>
+ {
+ received = obj;
+ tcs.TrySetResult(obj as string);
+ });
+
+ await this.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.send('ipc-on-test','payload123')");
+
var result = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5));
+
+ received.Should().BeOfType();
+ received.Should().Be("payload123");
result.Should().Be("payload123");
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Ipc_Once_only_fires_once()
{
var count = 0;
Electron.IpcMain.Once("ipc-once-test", _ => count++);
- await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("const {ipcRenderer}=require('electron'); ipcRenderer.send('ipc-once-test','a'); ipcRenderer.send('ipc-once-test','b');");
- await Task.Delay(500);
+ await this.MainWindow.WebContents.ExecuteJavaScriptAsync("const {ipcRenderer}=require('electron'); ipcRenderer.send('ipc-once-test','a'); ipcRenderer.send('ipc-once-test','b');");
+ await Task.Delay(500.ms());
count.Should().Be(1);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Ipc_RemoveAllListeners_stops_receiving()
{
var fired = false;
await Electron.IpcMain.On("ipc-remove-test", _ => fired = true);
Electron.IpcMain.RemoveAllListeners("ipc-remove-test");
- await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.send('ipc-remove-test','x')");
- await Task.Delay(400);
+ await this.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.send('ipc-remove-test','x')");
+ await Task.Delay(400.ms());
fired.Should().BeFalse();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Ipc_OnSync_returns_value()
{
+ object received = null;
+
Electron.IpcMain.OnSync("ipc-sync-test", (obj) =>
{
- obj.Should().NotBeNull();
+ received = obj;
return "pong";
});
- var ret = await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.sendSync('ipc-sync-test','ping')");
+ var ret = await this.MainWindow.WebContents.ExecuteJavaScriptAsync("require('electron').ipcRenderer.sendSync('ipc-sync-test','ping')");
+
+ received.Should().BeOfType();
+ received.Should().Be("ping");
+
ret.Should().Be("pong");
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Ipc_Send_from_main_reaches_renderer()
{
// Listener: store raw arg; if Electron packs differently we will normalize later
- await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync(@"(function(){ const {ipcRenderer}=require('electron'); ipcRenderer.once('main-to-render',(e,arg)=>{ globalThis.__mainToRender = arg;}); return 'ready'; })();");
- Electron.IpcMain.Send(this.fx.MainWindow, "main-to-render", "hello-msg");
+ await this.MainWindow.WebContents.ExecuteJavaScriptAsync(@"(function(){ const {ipcRenderer}=require('electron'); ipcRenderer.once('main-to-render',(e,arg)=>{ globalThis.__mainToRender = arg;}); return 'ready'; })();");
+ Electron.IpcMain.Send(this.MainWindow, "main-to-render", "hello-msg");
string value = "";
for (int i = 0; i < 20; i++)
{
- var jsVal = await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync("globalThis.__mainToRender === undefined ? '' : (typeof globalThis.__mainToRender === 'string' ? globalThis.__mainToRender : JSON.stringify(globalThis.__mainToRender))");
+ var jsVal = await this.MainWindow.WebContents.ExecuteJavaScriptAsync("globalThis.__mainToRender === undefined ? '' : (typeof globalThis.__mainToRender === 'string' ? globalThis.__mainToRender : JSON.stringify(globalThis.__mainToRender))");
value = jsVal?.ToString() ?? "";
if (!string.IsNullOrEmpty(value))
{
break;
}
- await Task.Delay(100);
+ await Task.Delay(100.ms());
}
// Normalize possible JSON array ["hello-msg"] case
diff --git a/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs b/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs
index 85a9d847..7c9a900d 100644
--- a/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/MenuTests.cs
@@ -2,18 +2,17 @@ namespace ElectronNET.IntegrationTests.Tests
{
using ElectronNET.API;
using ElectronNET.API.Entities;
+ using ElectronNET.Common;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class MenuTests
+ public class MenuTests : IntegrationTestBase
{
- private readonly ElectronFixture fx;
-
- public MenuTests(ElectronFixture fx)
+ public MenuTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task ApplicationMenu_click_invokes_handler()
{
var clicked = false;
@@ -30,29 +29,29 @@ public async Task ApplicationMenu_click_invokes_handler()
};
Electron.Menu.SetApplicationMenu(items);
var targetId = items[0].Submenu[0].Id;
- await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync($"require('electron').ipcRenderer.send('integration-click-application-menu','{targetId}')");
+ await this.MainWindow.WebContents.ExecuteJavaScriptAsync($"require('electron').ipcRenderer.send('integration-click-application-menu','{targetId}')");
for (int i = 0; i < 20 && !clicked; i++)
{
- await Task.Delay(100);
+ await Task.Delay(100.ms());
}
clicked.Should().BeTrue();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task ContextMenu_popup_registers_items()
{
- var win = this.fx.MainWindow;
+ var win = this.MainWindow;
var ctxClicked = false;
var ctxItems = new[] { new MenuItem { Label = "Ctx", Click = () => ctxClicked = true } };
Electron.Menu.SetContextMenu(win, ctxItems);
var ctxId = ctxItems[0].Id;
// simulate popup then click
Electron.Menu.ContextMenuPopup(win);
- await this.fx.MainWindow.WebContents.ExecuteJavaScriptAsync($"require('electron').ipcRenderer.send('integration-click-context-menu',{win.Id},'{ctxId}')");
+ await this.MainWindow.WebContents.ExecuteJavaScriptAsync($"require('electron').ipcRenderer.send('integration-click-context-menu',{win.Id},'{ctxId}')");
for (int i = 0; i < 20 && !ctxClicked; i++)
{
- await Task.Delay(100);
+ await Task.Delay(100.ms());
}
ctxClicked.Should().BeTrue();
diff --git a/src/ElectronNET.IntegrationTests/Tests/MultiEventRegistrationTests.cs b/src/ElectronNET.IntegrationTests/Tests/MultiEventRegistrationTests.cs
index e00c8509..3d4c421b 100644
--- a/src/ElectronNET.IntegrationTests/Tests/MultiEventRegistrationTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/MultiEventRegistrationTests.cs
@@ -1,13 +1,12 @@
namespace ElectronNET.IntegrationTests.Tests
{
+ using ElectronNET.IntegrationTests.Common;
+
[Collection("ElectronCollection")]
- public class MultiEventRegistrationTests
+ public class MultiEventRegistrationTests : IntegrationTestBase
{
- private readonly ElectronFixture fx;
-
- public MultiEventRegistrationTests(ElectronFixture fx)
+ public MultiEventRegistrationTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
private static async Task WaitAllOrTimeout(TimeSpan timeout, params Task[] tasks)
@@ -17,10 +16,10 @@ private static async Task WaitAllOrTimeout(TimeSpan timeout, params Task[]
return ReferenceEquals(completed, all) && all.IsCompletedSuccessfully;
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task BrowserWindow_OnResize_multiple_handlers_called()
{
- var win = this.fx.MainWindow;
+ var win = this.MainWindow;
var h1 = new TaskCompletionSource();
var h2 = new TaskCompletionSource();
var h3 = new TaskCompletionSource();
@@ -41,10 +40,10 @@ public async Task BrowserWindow_OnResize_multiple_handlers_called()
}
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task WebContents_OnDomReady_multiple_handlers_called()
{
- var wc = this.fx.MainWindow.WebContents;
+ var wc = this.MainWindow.WebContents;
var r1 = new TaskCompletionSource();
var r2 = new TaskCompletionSource();
diff --git a/src/ElectronNET.IntegrationTests/Tests/NativeImageTests.cs b/src/ElectronNET.IntegrationTests/Tests/NativeImageTests.cs
index fd8cac8a..e7a09b0c 100644
--- a/src/ElectronNET.IntegrationTests/Tests/NativeImageTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/NativeImageTests.cs
@@ -1,15 +1,20 @@
+using System.Drawing;
+using ElectronNET.API.Entities;
+using ElectronNET.IntegrationTests.Common;
using System.Runtime.Versioning;
using RectangleEntity = ElectronNET.API.Entities.Rectangle;
namespace ElectronNET.IntegrationTests.Tests
{
- using System.Drawing;
- using ElectronNET.API.Entities;
-
- [SupportedOSPlatform("Windows")]
- public class NativeImageTests
+ [Collection("ElectronCollection")]
+ [SupportedOSPlatform(Windows)]
+ public class NativeImageTests : IntegrationTestBase
{
- [SkippableFact(Timeout = 20000)]
+ public NativeImageTests(ElectronFixture fx) : base(fx)
+ {
+ }
+
+ [IntegrationFact]
public async Task Create_from_bitmap_and_to_png()
{
using var bmp = new Bitmap(10, 10);
@@ -27,7 +32,7 @@ public async Task Create_from_bitmap_and_to_png()
png!.Length.Should().BeGreaterThan(0);
}
- [SkippableFact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Create_from_buffer_and_to_data_url()
{
// Prepare PNG bytes
@@ -46,7 +51,7 @@ public async Task Create_from_buffer_and_to_data_url()
dataUrl!.StartsWith("data:image/", StringComparison.Ordinal).Should().BeTrue();
}
- [SkippableFact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Resize_and_crop_produce_expected_sizes()
{
using var bmp = new Bitmap(12, 10);
@@ -66,7 +71,7 @@ public async Task Resize_and_crop_produce_expected_sizes()
csize.Height.Should().Be(3);
}
- [SkippableFact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Add_representation_for_scale_factor()
{
using var bmp = new Bitmap(5, 5);
diff --git a/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs b/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs
index 4128a223..9e9bb615 100644
--- a/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/NativeThemeTests.cs
@@ -3,29 +3,35 @@ namespace ElectronNET.IntegrationTests.Tests
using System.Runtime.Versioning;
using ElectronNET.API;
using ElectronNET.API.Entities;
+ using ElectronNET.Common;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class NativeThemeTests
+ public class NativeThemeTests : IntegrationTestBase
{
- [Fact(Timeout = 20000)]
+ public NativeThemeTests(ElectronFixture fx) : base(fx)
+ {
+ }
+
+ [IntegrationFact]
public async Task ThemeSource_roundtrip()
{
// Capture initial
_ = await Electron.NativeTheme.ShouldUseDarkColorsAsync();
// Force light
- await Task.Delay(50);
+ await Task.Delay(50.ms());
Electron.NativeTheme.SetThemeSource(ThemeSourceMode.Light);
- await Task.Delay(500);
+ await Task.Delay(500.ms());
var useDarkAfterLight = await Electron.NativeTheme.ShouldUseDarkColorsAsync();
var themeSourceLight = await Electron.NativeTheme.GetThemeSourceAsync();
// Force dark
Electron.NativeTheme.SetThemeSource(ThemeSourceMode.Dark);
- await Task.Delay(500);
+ await Task.Delay(500.ms());
var useDarkAfterDark = await Electron.NativeTheme.ShouldUseDarkColorsAsync();
var themeSourceDark = await Electron.NativeTheme.GetThemeSourceAsync();
// Restore system
Electron.NativeTheme.SetThemeSource(ThemeSourceMode.System);
- await Task.Delay(500);
+ await Task.Delay(500.ms());
var themeSourceSystem = await Electron.NativeTheme.GetThemeSourceAsync();
// Assertions are tolerant (platform dependent)
useDarkAfterLight.Should().BeFalse("forcing Light should result in light colors");
@@ -35,34 +41,34 @@ public async Task ThemeSource_roundtrip()
themeSourceSystem.Should().Be(ThemeSourceMode.System);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Updated_event_fires_on_change()
{
var fired = false;
Electron.NativeTheme.Updated += () => fired = true;
Electron.NativeTheme.SetThemeSource(ThemeSourceMode.Dark);
- await Task.Delay(400);
+ await Task.Delay(400.ms());
Electron.NativeTheme.SetThemeSource(ThemeSourceMode.Light);
for (int i = 0; i < 10 && !fired; i++)
{
- await Task.Delay(100);
+ await Task.Delay(100.ms());
}
fired.Should().BeTrue();
}
- [SkippableFact(Timeout = 20000)]
- [SupportedOSPlatform("macOS")]
- [SupportedOSPlatform("Windows")]
+ [IntegrationFact]
+ [SupportedOSPlatform(MacOS)]
+ [SupportedOSPlatform(Windows)]
public async Task Should_use_high_contrast_colors_check()
{
var metrics = await Electron.NativeTheme.ShouldUseHighContrastColorsAsync();
metrics.Should().Be(false);
}
- [SkippableFact(Timeout = 20000)]
- [SupportedOSPlatform("macOS")]
- [SupportedOSPlatform("Windows")]
+ [IntegrationFact]
+ [SupportedOSPlatform(MacOS)]
+ [SupportedOSPlatform(Windows)]
public async Task Should_use_inverted_colors_check()
{
var metrics = await Electron.NativeTheme.ShouldUseInvertedColorSchemeAsync();
diff --git a/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs b/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs
index a974bdc2..9f8772f4 100644
--- a/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/NotificationTests.cs
@@ -3,11 +3,17 @@ namespace ElectronNET.IntegrationTests.Tests
using System.Runtime.InteropServices;
using ElectronNET.API;
using ElectronNET.API.Entities;
+ using ElectronNET.Common;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class NotificationTests
+ public class NotificationTests : IntegrationTestBase
{
- [SkippableFact(Timeout = 20000)]
+ public NotificationTests(ElectronFixture fx) : base(fx)
+ {
+ }
+
+ [IntegrationFact]
public async Task Notification_create_check()
{
Skip.If(RuntimeInformation.IsOSPlatform(OSPlatform.Linux), "Always returns false. Might need full-blown desktop environment");
@@ -17,16 +23,16 @@ public async Task Notification_create_check()
var options = new NotificationOptions("Notification Title", "Notification test 123");
options.OnShow = () => tcs.SetResult();
- await Task.Delay(500);
+ await Task.Delay(500.ms());
Electron.Notification.Show(options);
- await Task.WhenAny(tcs.Task, Task.Delay(5_000));
+ await Task.WhenAny(tcs.Task, Task.Delay(5.seconds()));
tcs.Task.IsCompletedSuccessfully.Should().BeTrue();
}
- [SkippableFact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Notification_is_supported_check()
{
Skip.If(RuntimeInformation.IsOSPlatform(OSPlatform.Linux), "Always returns false. Might need full-blown desktop environment");
diff --git a/src/ElectronNET.IntegrationTests/Tests/ProcessTests.cs b/src/ElectronNET.IntegrationTests/Tests/ProcessTests.cs
index 3fe27248..f5a9f485 100644
--- a/src/ElectronNET.IntegrationTests/Tests/ProcessTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/ProcessTests.cs
@@ -1,11 +1,16 @@
namespace ElectronNET.IntegrationTests.Tests
{
using ElectronNET.API;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class ProcessTests
+ public class ProcessTests : IntegrationTestBase
{
- [Fact(Timeout = 20000)]
+ public ProcessTests(ElectronFixture fx) : base(fx)
+ {
+ }
+
+ [IntegrationFact]
public async Task Process_info_is_accessible()
{
// Use renderer to fetch process info and round-trip
@@ -14,7 +19,7 @@ public async Task Process_info_is_accessible()
result.Should().Be("ok");
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Process_properties_are_populated()
{
var execPath = await Electron.Process.ExecPathAsync;
diff --git a/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs b/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs
index 2ecf9ecb..1ccf1f8b 100644
--- a/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/ScreenTests.cs
@@ -6,17 +6,13 @@ namespace ElectronNET.IntegrationTests.Tests
using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class ScreenTests
+ public class ScreenTests : IntegrationTestBase
{
- // ReSharper disable once NotAccessedField.Local
- private readonly ElectronFixture fx;
-
- public ScreenTests(ElectronFixture fx)
+ public ScreenTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [SkipOnWslFact(Timeout = 20000)]
+ [IntegrationFact(SkipOnWsl = true)]
public async Task Primary_display_has_positive_dimensions()
{
var display = await Electron.Screen.GetPrimaryDisplayAsync();
@@ -24,7 +20,7 @@ public async Task Primary_display_has_positive_dimensions()
display.Size.Height.Should().BeGreaterThan(0);
}
- [SkipOnWslFact(Timeout = 20000)]
+ [IntegrationFact(SkipOnWsl = true)]
public async Task GetAllDisplays_returns_at_least_one()
{
var displays = await Electron.Screen.GetAllDisplaysAsync();
@@ -32,17 +28,15 @@ public async Task GetAllDisplays_returns_at_least_one()
displays.Length.Should().BeGreaterThan(0);
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task GetCursorScreenPoint_check()
{
var point = await Electron.Screen.GetCursorScreenPointAsync();
point.Should().NotBeNull();
- point.X.Should().BeGreaterThanOrEqualTo(0);
- point.Y.Should().BeGreaterThanOrEqualTo(0);
}
- [SkippableFact(Timeout = 20000)]
- [SupportedOSPlatform("macOS")]
+ [IntegrationFact]
+ [SupportedOSPlatform(MacOS)]
public async Task GetMenuBarWorkArea_check()
{
var area = await Electron.Screen.GetMenuBarWorkAreaAsync();
@@ -53,7 +47,7 @@ public async Task GetMenuBarWorkArea_check()
area.Width.Should().BeGreaterThan(0);
}
- [SkipOnWslFact(Timeout = 20000)]
+ [IntegrationFact(SkipOnWsl = true)]
public async Task GetDisplayNearestPoint_check()
{
var point = new Point
@@ -67,7 +61,7 @@ public async Task GetDisplayNearestPoint_check()
display.Size.Height.Should().BeGreaterThan(0);
}
- [SkipOnWslFact(Timeout = 20000)]
+ [IntegrationFact(SkipOnWsl = true)]
public async Task GetDisplayMatching_check()
{
var rectangle = new Rectangle
diff --git a/src/ElectronNET.IntegrationTests/Tests/SessionTests.cs b/src/ElectronNET.IntegrationTests/Tests/SessionTests.cs
index 50243a97..53dad7bb 100644
--- a/src/ElectronNET.IntegrationTests/Tests/SessionTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/SessionTests.cs
@@ -1,21 +1,19 @@
namespace ElectronNET.IntegrationTests.Tests
{
using ElectronNET.API.Entities;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class SessionTests
+ public class SessionTests : IntegrationTestBase
{
- private readonly ElectronFixture fx;
-
- public SessionTests(ElectronFixture fx)
+ public SessionTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Session_preloads_roundtrip()
{
- var session = this.fx.MainWindow.WebContents.Session;
+ var session = this.MainWindow.WebContents.Session;
_ = await session.GetPreloadsAsync();
// Use a dummy path; API should store value
session.SetPreloads(new[] { "/tmp/preload_dummy.js" });
@@ -23,10 +21,10 @@ public async Task Session_preloads_roundtrip()
preloadsAfter.Should().Contain("/tmp/preload_dummy.js");
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Session_proxy_set_and_resolve()
{
- var session = this.fx.MainWindow.WebContents.Session;
+ var session = this.MainWindow.WebContents.Session;
// Provide all ctor args (pacScript empty to ignore, proxyRules direct, bypass empty)
await session.SetProxyAsync(new ProxyConfig("", "direct://", ""));
var proxy = await session.ResolveProxyAsync("https://example.com");
@@ -34,10 +32,10 @@ public async Task Session_proxy_set_and_resolve()
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Session_clear_cache_and_storage_completes()
{
- var session = this.fx.MainWindow.WebContents.Session;
+ var session = this.MainWindow.WebContents.Session;
await session.ClearCacheAsync();
await session.ClearStorageDataAsync();
await session.ClearHostResolverCacheAsync();
@@ -46,10 +44,10 @@ public async Task Session_clear_cache_and_storage_completes()
ua.Should().NotBeNullOrWhiteSpace();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Session_preloads_set_multiple_and_clear()
{
- var session = this.fx.MainWindow.WebContents.Session;
+ var session = this.MainWindow.WebContents.Session;
session.SetPreloads(new[] { "/tmp/a.js", "/tmp/b.js" });
var after = await session.GetPreloadsAsync();
after.Should().Contain("/tmp/a.js").And.Contain("/tmp/b.js");
@@ -59,40 +57,40 @@ public async Task Session_preloads_set_multiple_and_clear()
empty.Should().NotContain("/tmp/a.js");
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Clear_auth_cache_overloads()
{
- var session = this.fx.MainWindow.WebContents.Session;
+ var session = this.MainWindow.WebContents.Session;
await session.ClearAuthCacheAsync();
await session.ClearAuthCacheAsync(new RemovePassword("password") { Origin = "https://example.com", Username = "user", Password = "pw", Realm = "realm", Scheme = Scheme.basic });
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Clear_storage_with_options()
{
- var session = this.fx.MainWindow.WebContents.Session;
+ var session = this.MainWindow.WebContents.Session;
await session.ClearStorageDataAsync(new ClearStorageDataOptions { Storages = new[] { "cookies" }, Quotas = new[] { "temporary" } });
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Enable_disable_network_emulation()
{
- var session = this.fx.MainWindow.WebContents.Session;
+ var session = this.MainWindow.WebContents.Session;
session.EnableNetworkEmulation(new EnableNetworkEmulationOptions { Offline = false, Latency = 10, DownloadThroughput = 50000, UploadThroughput = 20000 });
session.DisableNetworkEmulation();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Flush_storage_data_does_not_throw()
{
- var session = this.fx.MainWindow.WebContents.Session;
+ var session = this.MainWindow.WebContents.Session;
session.FlushStorageData();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Set_user_agent_affects_new_navigation()
{
- var session = this.fx.MainWindow.WebContents.Session;
+ var session = this.MainWindow.WebContents.Session;
// Set UA and verify via session API (navigator.userAgent on existing WebContents may not reflect the override)
session.SetUserAgent("IntegrationAgent/1.0");
var ua = await session.GetUserAgent();
diff --git a/src/ElectronNET.IntegrationTests/Tests/ShellTests.cs b/src/ElectronNET.IntegrationTests/Tests/ShellTests.cs
index ff185db1..3a71ad8e 100644
--- a/src/ElectronNET.IntegrationTests/Tests/ShellTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/ShellTests.cs
@@ -1,11 +1,16 @@
namespace ElectronNET.IntegrationTests.Tests
{
using ElectronNET.API;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class ShellTests
+ public class ShellTests : IntegrationTestBase
{
- [Fact(Skip = "This can keep the test process hanging until the e-mail window is closed")]
+ public ShellTests(ElectronFixture fx) : base(fx)
+ {
+ }
+
+ [IntegrationFact(Skip = "This can keep the test process hanging until the e-mail window is closed")]
public async Task OpenExternal_invalid_scheme_returns_error_or_empty()
{
var error = await Electron.Shell.OpenExternalAsync("mailto:test@example.com");
diff --git a/src/ElectronNET.IntegrationTests/Tests/ThumbarButtonTests.cs b/src/ElectronNET.IntegrationTests/Tests/ThumbarButtonTests.cs
index 15253bd5..fbbd6981 100644
--- a/src/ElectronNET.IntegrationTests/Tests/ThumbarButtonTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/ThumbarButtonTests.cs
@@ -2,28 +2,26 @@ namespace ElectronNET.IntegrationTests.Tests
{
using System.Runtime.Versioning;
using ElectronNET.API.Entities;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class ThumbarButtonTests
+ public class ThumbarButtonTests : IntegrationTestBase
{
- private readonly ElectronFixture fx;
-
- public ThumbarButtonTests(ElectronFixture fx)
+ public ThumbarButtonTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [SkippableFact(Timeout = 20000)]
- [SupportedOSPlatform("Windows")]
+ [IntegrationFact]
+ [SupportedOSPlatform(Windows)]
public async Task SetThumbarButtons_returns_success()
{
var btn = new ThumbarButton("icon.png") { Tooltip = "Test" };
- var success = await this.fx.MainWindow.SetThumbarButtonsAsync(new[] { btn });
+ var success = await this.MainWindow.SetThumbarButtonsAsync(new[] { btn });
success.Should().BeTrue();
}
- [SkippableFact(Timeout = 20000)]
- [SupportedOSPlatform("Windows")]
+ [IntegrationFact]
+ [SupportedOSPlatform(Windows)]
public async Task Thumbar_button_click_invokes_callback()
{
var icon = Path.Combine(Directory.GetCurrentDirectory(), "ElectronNET.WebApp", "wwwroot", "icon.png");
@@ -34,7 +32,7 @@ public async Task Thumbar_button_click_invokes_callback()
var tcs = new TaskCompletionSource();
var btn = new ThumbarButton(icon) { Tooltip = "Test", Flags = new[] { ThumbarButtonFlag.enabled }, Click = () => tcs.TrySetResult(true) };
- var ok = await this.fx.MainWindow.SetThumbarButtonsAsync(new[] { btn });
+ var ok = await this.MainWindow.SetThumbarButtonsAsync(new[] { btn });
ok.Should().BeTrue();
}
}
diff --git a/src/ElectronNET.IntegrationTests/Tests/TrayTests.cs b/src/ElectronNET.IntegrationTests/Tests/TrayTests.cs
index f5b07ff1..324534e2 100644
--- a/src/ElectronNET.IntegrationTests/Tests/TrayTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/TrayTests.cs
@@ -1,19 +1,16 @@
namespace ElectronNET.IntegrationTests.Tests
{
using ElectronNET.API;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class TrayTests
+ public class TrayTests : IntegrationTestBase
{
- // ReSharper disable once NotAccessedField.Local
- private readonly ElectronFixture fx;
-
- public TrayTests(ElectronFixture fx)
+ public TrayTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_create_tray_and_destroy()
{
//await Electron.Tray.Show("assets/icon.png");
diff --git a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs
index a3d62b29..7afae016 100644
--- a/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs
+++ b/src/ElectronNET.IntegrationTests/Tests/WebContentsTests.cs
@@ -1,55 +1,57 @@
+using System.Runtime.InteropServices;
+
namespace ElectronNET.IntegrationTests.Tests
{
+ using ElectronNET.API;
using ElectronNET.API.Entities;
+ using ElectronNET.Common;
+ using ElectronNET.IntegrationTests.Common;
[Collection("ElectronCollection")]
- public class WebContentsTests
+ public class WebContentsTests : IntegrationTestBase
{
- private readonly ElectronFixture fx;
-
- public WebContentsTests(ElectronFixture fx)
+ public WebContentsTests(ElectronFixture fx) : base(fx)
{
- this.fx = fx;
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_get_url_after_navigation()
{
- var wc = this.fx.MainWindow.WebContents;
+ var wc = this.MainWindow.WebContents;
await wc.LoadURLAsync("https://example.com");
var url = await wc.GetUrl();
url.Should().Contain("example.com");
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task ExecuteJavaScript_returns_title()
{
- var wc = this.fx.MainWindow.WebContents;
+ var wc = this.MainWindow.WebContents;
await wc.LoadURLAsync("https://example.com");
var title = await wc.ExecuteJavaScriptAsync("document.title");
title.Should().NotBeNull();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task DomReady_event_fires()
{
- var wc = this.fx.MainWindow.WebContents;
+ var wc = this.MainWindow.WebContents;
var fired = false;
wc.OnDomReady += () => fired = true;
await wc.LoadURLAsync("https://example.com");
- await Task.Delay(500);
+ await Task.Delay(500.ms());
fired.Should().BeTrue();
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_print_to_pdf()
{
var html = "data:text/html,PDF Test
Electron.NET
";
- await this.fx.MainWindow.WebContents.LoadURLAsync(html);
+ await this.MainWindow.WebContents.LoadURLAsync(html);
var tmp = Path.Combine(Path.GetTempPath(), $"electronnet_pdf_{Guid.NewGuid():N}.pdf");
try
{
- var ok = await this.fx.MainWindow.WebContents.PrintToPDFAsync(tmp);
+ var ok = await this.MainWindow.WebContents.PrintToPDFAsync(tmp);
ok.Should().BeTrue();
File.Exists(tmp).Should().BeTrue();
new FileInfo(tmp).Length.Should().BeGreaterThan(0);
@@ -63,21 +65,131 @@ public async Task Can_print_to_pdf()
}
}
- [Fact(Timeout = 20000)]
+ [IntegrationFact]
public async Task Can_basic_print()
{
var html = "data:text/html,Print Test
";
- await this.fx.MainWindow.WebContents.LoadURLAsync(html);
- var ok = await this.fx.MainWindow.WebContents.PrintAsync(new PrintOptions { Silent = true, PrintBackground = true });
+ await this.MainWindow.WebContents.LoadURLAsync(html);
+ var ok = await this.MainWindow.WebContents.PrintAsync(new PrintOptions { Silent = true, PrintBackground = true });
ok.Should().BeTrue();
}
- [SkippableFact(Timeout = 20000)]
+ [IntegrationFact]
public async Task GetPrintersAsync_check()
{
- Skip.If(Environment.GetEnvironmentVariable("GITHUB_TOKEN") != null, "Skipping printer test in CI environment.");
- var info = await fx.MainWindow.WebContents.GetPrintersAsync();
+ var info = await this.MainWindow.WebContents.GetPrintersAsync();
info.Should().NotBeNull();
}
+
+ [IntegrationFact]
+ public async Task GetSetZoomFactor_check()
+ {
+ await this.MainWindow.WebContents.GetZoomFactorAsync();
+ var ok = await this.MainWindow.WebContents.GetZoomFactorAsync();
+ ok.Should().BeGreaterThan(0.0);
+ this.MainWindow.WebContents.SetZoomFactor(2.0);
+ await Task.Delay(500.ms());
+ ok = await this.MainWindow.WebContents.GetZoomFactorAsync();
+ ok.Should().Be(2.0);
+ }
+
+ [IntegrationFact]
+ public async Task GetSetZoomLevel_check()
+ {
+ BrowserWindow window = null;
+
+ try
+ {
+ window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank");
+
+ await Task.Delay(100.ms());
+
+ window.WebContents.SetZoomLevel(0);
+ await Task.Delay(500.ms());
+
+ var ok = await window.WebContents.GetZoomLevelAsync();
+ ok.Should().Be(0);
+
+ window.WebContents.SetZoomLevel(2);
+ await Task.Delay(500.ms());
+
+ ok = await window.WebContents.GetZoomLevelAsync();
+ ok.Should().Be(2);
+ }
+ finally
+ {
+ window?.Destroy();
+ }
+ }
+
+ [IntegrationFact]
+ public async Task DevTools_check()
+ {
+ BrowserWindow window = null;
+
+ try
+ {
+ window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank");
+
+ await Task.Delay(3.seconds());
+
+ window.WebContents.IsDevToolsOpened().Should().BeFalse();
+ window.WebContents.OpenDevTools();
+ await Task.Delay(5.seconds());
+
+ window.WebContents.IsDevToolsOpened().Should().BeTrue();
+ window.WebContents.CloseDevTools();
+ await Task.Delay(2.seconds());
+
+ window.WebContents.IsDevToolsOpened().Should().BeFalse();
+ }
+ finally
+ {
+ window?.Destroy();
+ }
+ }
+
+ [IntegrationFact]
+ public async Task GetSetAudioMuted_check()
+ {
+ this.MainWindow.WebContents.SetAudioMuted(true);
+ await Task.Delay(500.ms());
+ var ok = await this.MainWindow.WebContents.IsAudioMutedAsync();
+ ok.Should().BeTrue();
+ this.MainWindow.WebContents.SetAudioMuted(false);
+ await Task.Delay(500.ms());
+ ok = await this.MainWindow.WebContents.IsAudioMutedAsync();
+ ok.Should().BeFalse();
+
+ // Assuming no audio is playing, IsCurrentlyAudibleAsync should return false
+ // there is no way to play audio in this test
+ ok = await this.MainWindow.WebContents.IsCurrentlyAudibleAsync();
+ ok.Should().BeFalse();
+ }
+
+ [IntegrationFact]
+ public async Task GetSetUserAgent_check()
+ {
+ BrowserWindow window = null;
+
+ try
+ {
+ window = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions { Show = true }, "about:blank");
+
+ await Task.Delay(3.seconds());
+
+ window.WebContents.SetUserAgent("MyUserAgent/1.0");
+
+ await Task.Delay(1.seconds());
+
+ var ok = await window.WebContents.GetUserAgentAsync();
+ ok.Should().Be("MyUserAgent/1.0");
+ }
+ finally
+ {
+ window?.Destroy();
+ }
+ }
+
}
}
\ No newline at end of file
diff --git a/src/ElectronNET.Samples.ElectronHostHook/Controllers/HomeController.cs b/src/ElectronNET.Samples.ElectronHostHook/Controllers/HomeController.cs
index 10c10596..f71aadb0 100644
--- a/src/ElectronNET.Samples.ElectronHostHook/Controllers/HomeController.cs
+++ b/src/ElectronNET.Samples.ElectronHostHook/Controllers/HomeController.cs
@@ -1,5 +1,6 @@
using ElectronNET.API;
using Microsoft.AspNetCore.Mvc;
+using System.Threading.Tasks;
namespace ElectronNET.Samples.ElectronHostHook.Controllers
{
diff --git a/src/ElectronNET.Samples.ElectronHostHook/ElectronNET.Samples.ElectronHostHook.csproj b/src/ElectronNET.Samples.ElectronHostHook/ElectronNET.Samples.ElectronHostHook.csproj
index 70eb4366..802cdab6 100644
--- a/src/ElectronNET.Samples.ElectronHostHook/ElectronNET.Samples.ElectronHostHook.csproj
+++ b/src/ElectronNET.Samples.ElectronHostHook/ElectronNET.Samples.ElectronHostHook.csproj
@@ -6,7 +6,7 @@
- net8.0
+ net10.0
OutOfProcess
AspNetCoreModule
false
diff --git a/src/ElectronNET.Samples.ElectronHostHook/Program.cs b/src/ElectronNET.Samples.ElectronHostHook/Program.cs
index ee8fa812..c3825ed5 100644
--- a/src/ElectronNET.Samples.ElectronHostHook/Program.cs
+++ b/src/ElectronNET.Samples.ElectronHostHook/Program.cs
@@ -1,4 +1,6 @@
using ElectronNET.API;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
namespace ElectronNET.Samples.ElectronHostHook
{
diff --git a/src/ElectronNET.WebApp/ElectronNET.WebApp.csproj b/src/ElectronNET.WebApp/ElectronNET.WebApp.csproj
index 9b849f89..65a7cbb5 100644
--- a/src/ElectronNET.WebApp/ElectronNET.WebApp.csproj
+++ b/src/ElectronNET.WebApp/ElectronNET.WebApp.csproj
@@ -8,7 +8,7 @@
- net8.0
+ net10.0
OutOfProcess
AspNetCoreModule
@@ -45,6 +45,7 @@
true
true
+ disable
@@ -75,8 +76,8 @@
-
-
+
+
diff --git a/src/ElectronNET.WebApp/Properties/PublishProfiles/linux-x64.pubxml b/src/ElectronNET.WebApp/Properties/PublishProfiles/linux-x64.pubxml
index 3c9c8310..770648d1 100644
--- a/src/ElectronNET.WebApp/Properties/PublishProfiles/linux-x64.pubxml
+++ b/src/ElectronNET.WebApp/Properties/PublishProfiles/linux-x64.pubxml
@@ -11,7 +11,7 @@
FileSystem
<_TargetId>Folder
- net8.0
+ net10.0
linux-x64
6ea447d9-343f-46b8-b456-66557bddbb9f
true
diff --git a/src/ElectronNET.WebApp/Properties/PublishProfiles/win-x64.pubxml b/src/ElectronNET.WebApp/Properties/PublishProfiles/win-x64.pubxml
index 04d7a390..874b3803 100644
--- a/src/ElectronNET.WebApp/Properties/PublishProfiles/win-x64.pubxml
+++ b/src/ElectronNET.WebApp/Properties/PublishProfiles/win-x64.pubxml
@@ -11,7 +11,7 @@
FileSystem
<_TargetId>Folder
- net8.0
+ net10.0
win-x64
6ea447d9-343f-46b8-b456-66557bddbb9f
true
diff --git a/src/ElectronNET.sln b/src/ElectronNET.sln
index ef64ab18..eb20f10e 100644
--- a/src/ElectronNET.sln
+++ b/src/ElectronNET.sln
@@ -41,7 +41,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test Apps", "Test Apps", "{
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{985D39A7-5216-4945-8167-2FD0CB387BD8}"
ProjectSection(SolutionItems) = preProject
- ..\.github\workflows\ci.yml = ..\.github\workflows\ci.yml
+ ..\.github\workflows\Build and Publish.yml = ..\.github\workflows\Build and Publish.yml
+ ..\.github\workflows\integration-tests.yml = ..\.github\workflows\integration-tests.yml
+ ..\.github\workflows\PR Validation.yml = ..\.github\workflows\PR Validation.yml
+ ..\.github\workflows\pr-comment.yml = ..\.github\workflows\pr-comment.yml
+ ..\.github\workflows\publish-wiki.yml = ..\.github\workflows\publish-wiki.yml
+ ..\.github\workflows\retry-test-jobs.yml = ..\.github\workflows\retry-test-jobs.yml
+ ..\.github\workflows\trailing-whitespace-check.yml = ..\.github\workflows\trailing-whitespace-check.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "_build", "..\nuke\_build.csproj", "{015CB06B-6CAE-209F-E050-21C3ACA5FE9F}"
diff --git a/src/ElectronNET.sln.DotSettings b/src/ElectronNET.sln.DotSettings
index 1eab21f2..fd98f554 100644
--- a/src/ElectronNET.sln.DotSettings
+++ b/src/ElectronNET.sln.DotSettings
@@ -1,6 +1,8 @@
+ DO_NOT_SHOW
False
False
+ CI
True
True
True
diff --git a/src/ElectronNET/ElectronNET.csproj b/src/ElectronNET/ElectronNET.csproj
index 48abf453..2298cd7e 100644
--- a/src/ElectronNET/ElectronNET.csproj
+++ b/src/ElectronNET/ElectronNET.csproj
@@ -10,6 +10,7 @@
$(DescriptionFirstPart) This package contains the ElectronNET project system.
false
false
+ disable
diff --git a/src/ElectronNET/build/ElectronNET.Core.targets b/src/ElectronNET/build/ElectronNET.Core.targets
index 04022f71..39da417a 100644
--- a/src/ElectronNET/build/ElectronNET.Core.targets
+++ b/src/ElectronNET/build/ElectronNET.Core.targets
@@ -23,4 +23,7 @@
+
+
+
\ No newline at end of file
diff --git a/src/ElectronNET/build/ElectronNET.LateImport.targets b/src/ElectronNET/build/ElectronNET.LateImport.targets
index f25493e9..d82dcbd8 100644
--- a/src/ElectronNET/build/ElectronNET.LateImport.targets
+++ b/src/ElectronNET/build/ElectronNET.LateImport.targets
@@ -344,7 +344,6 @@
<_OriginalPublishDir>$(PublishDir)
$(_OriginalPublishDir)bin\
- $(PublishDir)
true
@@ -460,7 +459,7 @@
- <_NpxCmd>npx electron-builder --config=./$(ElectronBuilderJson) --$(ElectronPlatform) --$(ElectronArch) -c.electronVersion=$(ElectronVersion) $(ElectronPaParams)
+ <_NpxCmd>npx electron-builder --config=./$(ElectronBuilderJson) --$(ElectronPlatform) --$(ElectronArch) -c.electronVersion=$(ElectronVersion) -c.directories.output "$(ElectronPublishUrlFullPath)" $(ElectronPaParams)
<_NpxCmd Condition="'$(IsLinuxWsl)' == 'true'">wsl bash -ic '$(_NpxCmd)'
diff --git a/src/ElectronNET/build/ElectronNET.MigrationChecks.targets b/src/ElectronNET/build/ElectronNET.MigrationChecks.targets
new file mode 100644
index 00000000..df2336fe
--- /dev/null
+++ b/src/ElectronNET/build/ElectronNET.MigrationChecks.targets
@@ -0,0 +1,280 @@
+
+
+
+
+
+ ElectronCheckNoPackageJson;
+ ElectronCheckNoManifestJson;
+ ElectronCheckElectronBuilderJson;
+ ElectronCheckNoParentPaths;
+ ElectronCheckPubxmlFiles
+
+
+
+
+
+
+
+
+
+
+
+
+ <_InvalidPackageJson Include="$(MSBuildProjectDirectory)\**\package.json"
+ Exclude="$(MSBuildProjectDirectory)\ElectronHostHook\**\package.json;
+ $(MSBuildProjectDirectory)\bin\**\package.json;
+ $(MSBuildProjectDirectory)\obj\**\package.json;
+ $(MSBuildProjectDirectory)\publish\**\package.json;
+ $(MSBuildProjectDirectory)\node_modules\**\package.json" />
+ <_InvalidPackageLockJson Include="$(MSBuildProjectDirectory)\**\package-lock.json"
+ Exclude="$(MSBuildProjectDirectory)\ElectronHostHook\**\package-lock.json;
+ $(MSBuildProjectDirectory)\bin\**\package-lock.json;
+ $(MSBuildProjectDirectory)\obj\**\package-lock.json;
+ $(MSBuildProjectDirectory)\publish\**\package-lock.json;
+ $(MSBuildProjectDirectory)\node_modules\**\package-lock.json" />
+
+
+
+ <_HasInvalidPackageJson>false
+ <_HasInvalidPackageJson Condition="@(_InvalidPackageJson->Count()) > 0 OR @(_InvalidPackageLockJson->Count()) > 0">true
+
+
+
+
+
+
+
+
+
+
+ <_InvalidManifestJson Include="$(MSBuildProjectDirectory)\**\electron.manifest.json;$(MSBuildProjectDirectory)\**\electron-manifest.json"
+ Exclude="$(MSBuildProjectDirectory)\bin\**\*;
+ $(MSBuildProjectDirectory)\obj\**\*;
+ $(MSBuildProjectDirectory)\publish\**\*;
+ $(MSBuildProjectDirectory)\node_modules\**\*" />
+
+
+
+ <_HasInvalidManifestJson>false
+ <_HasInvalidManifestJson Condition="@(_InvalidManifestJson->Count()) > 0">true
+
+
+
+
+
+
+
+
+
+
+
+ <_ElectronBuilderJsonInProperties Include="$(MSBuildProjectDirectory)\Properties\electron-builder.json" />
+
+
+
+ <_HasElectronBuilderJsonInProperties>false
+ <_HasElectronBuilderJsonInProperties Condition="Exists('$(MSBuildProjectDirectory)\Properties\electron-builder.json')">true
+
+
+
+
+ <_ElectronBuilderJsonWrongLocation Include="$(MSBuildProjectDirectory)\**\electron-builder.json"
+ Exclude="$(MSBuildProjectDirectory)\Properties\electron-builder.json;
+ $(MSBuildProjectDirectory)\bin\**\*;
+ $(MSBuildProjectDirectory)\obj\**\*;
+ $(MSBuildProjectDirectory)\publish\**\*;
+ $(MSBuildProjectDirectory)\node_modules\**\*" />
+
+
+
+ <_HasElectronBuilderJsonWrongLocation>false
+ <_HasElectronBuilderJsonWrongLocation Condition="@(_ElectronBuilderJsonWrongLocation->Count()) > 0">true
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ElectronBuilderJsonPath>$(MSBuildProjectDirectory)\Properties\electron-builder.json
+
+
+
+
+
+
+
+
+
+ <_ElectronBuilderJsonContent>@(_ElectronBuilderJsonLines, ' ')
+ <_HasParentPathReference>false
+ <_HasParentPathReference Condition="$(_ElectronBuilderJsonContent.Contains('../')) OR $(_ElectronBuilderJsonContent.Contains('..\\'))" >true
+
+
+
+
+
+
+
+
+
+
+
+ <_PubxmlFiles Include="$(MSBuildProjectDirectory)\Properties\PublishProfiles\*.pubxml" />
+
+
+
+
+ <_IsAspNetProject>false
+ <_IsAspNetProject Condition="'$(UsingMicrosoftNETSdkWeb)' == 'true'">true
+ <_HasPubxmlFiles>false
+ <_HasPubxmlFiles Condition="@(_PubxmlFiles->Count()) > 0">true
+
+
+
+
+ <_PubxmlFileInfo Include="@(_PubxmlFiles)" Condition="'%(Identity)' != ''">
+ $([System.IO.File]::ReadAllText('%(Identity)'))
+
+
+
+
+
+ <_PubxmlFileInfoWithFlags Include="@(_PubxmlFileInfo)" Condition="'%(Identity)' != ''">
+ $([System.Text.RegularExpressions.Regex]::IsMatch('%(FileContent)', '<WebPublishMethod>'))
+
+
+
+
+
+ <_AspNetMissingWebPublishMethod Include="@(_PubxmlFileInfoWithFlags)"
+ Condition="'$(_IsAspNetProject)' == 'true' AND '%(HasWebPublishMethod)' == 'False'" />
+
+
+
+
+ <_ConsolePubxmlWithAspNetProperties Include="@(_PubxmlFileInfoWithFlags)"
+ Condition="'$(_IsAspNetProject)' != 'true' AND '%(HasWebPublishMethod)' == 'True'" />
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/common.props b/src/common.props
index 554f4bf8..9026afa7 100644
--- a/src/common.props
+++ b/src/common.props
@@ -1,6 +1,6 @@
- 0.2.0
+ 0.3.0
ElectronNET.Core
Gregor Biswanger, Florian Rappl, softworkz
Electron.NET