diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 3e376e1..87d6f6c 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -91,6 +91,7 @@ jobs:
runs-on: ubuntu-latest
if: always()
steps:
+ - uses: actions/checkout@v3
- name: Download Artifacts
uses: actions/download-artifact@v3
with:
@@ -100,8 +101,8 @@ jobs:
uses: EnricoMi/publish-unit-test-result-action@v2
with:
files: artifacts/**/TestResults.xml
- - name: Publish Code Coverage Results
- uses: codacy/codacy-coverage-reporter-action@v1
+ - name: Publish Code Coverage Results to Codecov
+ uses: codecov/codecov-action@v3
with:
- project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
- coverage-reports: artifacts/CoverageResults.xml
+ token: ${{ secrets.CODECOV_TOKEN }}
+ files: artifacts/CoverageResults.xml
diff --git a/.gitignore b/.gitignore
index 17beeb5..55b4879 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ coverage.xml
codecov.sh
coverage.json
codacy-coverage-reporter-assembly.jar
+codecov.exe
diff --git a/Tasks/BurntToast-Template.psm1 b/Tasks/BurntToast-Template.psm1
index 5dca9de..4f3d261 100644
--- a/Tasks/BurntToast-Template.psm1
+++ b/Tasks/BurntToast-Template.psm1
@@ -1,9 +1,7 @@
-$WinMajorVersion = (Get-CimInstance -ClassName Win32_OperatingSystem -Property Version).Version.Split('.')[0]
+$OSVersion = [System.Environment]::OSVersion.Version
-if ($WinMajorVersion -ge 10) {
- [int] $WinBuild = (Get-CimInstance -ClassName Win32_OperatingSystem -Property BuildNumber).BuildNumber
-
- if ($WinBuild -ge 15063) {
+if ($OSVersion.Major -ge 10 -and $null -eq $env:BurntToastPesterNotWindows10) {
+ if ($OSVersion.Build -ge 15063 -and $null -eq $env:BurntToastPesterNotAnniversaryUpdate) {
$Paths = if ($IsWindows) {
"$PSScriptRoot\lib\Microsoft.Toolkit.Uwp.Notifications\net5.0-windows10.0.17763\*.dll",
"$PSScriptRoot\lib\Microsoft.Windows.SDK.NET\*.dll"
diff --git a/src/BurntToast.psm1 b/src/BurntToast.psm1
index 3f44d64..a345331 100644
--- a/src/BurntToast.psm1
+++ b/src/BurntToast.psm1
@@ -1,9 +1,7 @@
-[int] $WinMajorVersion = (Get-CimInstance -ClassName Win32_OperatingSystem -Property Version).Version.Split('.')[0]
+$OSVersion = [System.Environment]::OSVersion.Version
-if ($WinMajorVersion -ge 10) {
- [int] $WinBuild = (Get-CimInstance -ClassName Win32_OperatingSystem -Property BuildNumber).BuildNumber
-
- if ($WinBuild -ge 15063) {
+if ($OSVersion.Major -ge 10 -and $null -eq $env:BurntToastPesterNotWindows10) {
+ if ($OSVersion.Build -ge 15063 -and $null -eq $env:BurntToastPesterNotAnniversaryUpdate) {
$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )
$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue )
diff --git a/src/Public/Add-BTText.ps1 b/src/Public/Add-BTText.ps1
index 23e67bc..7ed5d25 100644
--- a/src/Public/Add-BTText.ps1
+++ b/src/Public/Add-BTText.ps1
@@ -189,17 +189,9 @@ function Add-BTText {
foreach ($ExistingLine in $ContentBuilder.Content.Visual.BindingGeneric.Children) {
if ($ExistingLine.GetType().Name -eq 'AdaptiveText') {
if ($ExistingLineCount -eq 0 -and $null -eq $ExistingLine.HintMaxLines) {
- $ExistingLineCount += if ($ExistingLine.HintMinLines) {
- $ExistingLine.HintMinLines
- } else {
- 2
- }
+ $ExistingLineCount += 2
} else {
- $ExistingLineCount += if ($ExistingLine.HintMinLines) {
- $ExistingLine.HintMinLines
- } else {
- 1
- }
+ $ExistingLineCount += 1
}
}
}
diff --git a/src/Public/Get-BTEvent.ps1 b/src/Public/Get-BTEvent.ps1
new file mode 100644
index 0000000..dbae033
--- /dev/null
+++ b/src/Public/Get-BTEvent.ps1
@@ -0,0 +1,17 @@
+function Get-BTEvent {
+ [CmdletBinding()]
+ param(
+ [string] $Argument,
+
+ [string] $SourceIdentifier = 'BurntToast_*'
+ )
+
+ $EventSubscribers = Get-EventSubscriber -SourceIdentifier $SourceIdentifier
+
+ if ($Argument) {
+ $EventSubscribers | Where-Object { $_.Action.Command -like "*Arguments -eq '$($Argument)'*" }
+ }
+ else {
+ $EventSubscribers
+ }
+}
\ No newline at end of file
diff --git a/src/Public/Register-BTEvent.ps1 b/src/Public/Register-BTEvent.ps1
new file mode 100644
index 0000000..949d30d
--- /dev/null
+++ b/src/Public/Register-BTEvent.ps1
@@ -0,0 +1,88 @@
+function Register-BTEvent {
+ <#
+ .SYNOPSIS
+ Registers an event that executes a specified action when a toast notification is activated.
+
+ .DESCRIPTION
+ The Register-BTEvent function registers an event that executes a specified action when a toast notification is activated. The function can optionally save the event data for inspection to assist with developing followup actions.
+
+ .INPUTS
+ System.Management.Automation.ScriptBlock. You cannot pipe objects to Register-BTEvent.
+
+ .OUTPUTS
+ System.Management.Automation.PSEventJob
+
+ .EXAMPLE
+ PS C:\> Register-BTEvent { Write-Host 'Toast notification activated.' }
+
+ This example registers an event that writes a message to the console when a toast notification is activated.
+
+ .EXAMPLE
+ PS C:\> Register-BTEvent { Write-Host 'Toast notification activated.' } -SaveEventData -EventDataVariableName 'ToastEvent'
+
+ This example registers an event that saves the event data in a global variable named 'ToastEvent' when a toast notification is activated, accessed via `$Global:ToastEvent`
+
+ .EXAMPLE
+ PS C:\> Register-BTEvent { Write-Host 'Toast notification activated.' } -SourceIdentifier 'MySourceIdentifier'
+
+ This example registers an event with the specified source identifier that writes a message to the console when a toast notification is activated.
+
+ .EXAMPLE
+ PS C:\> $EventRegistration = Register-BTEvent { Write-Host 'Toast notification activated.' } -PassThru
+
+ This example registers an event that writes a message to the console when a toast notification is activated and returns the event registration object.
+
+ .LINK
+ https://docs.toastit.dev/commands/register-btevent
+ #>
+
+ [CmdletBinding()]
+ param(
+ # Specifies the action to execute when the event is triggered.
+ [Parameter(Mandatory)]
+ [scriptblock] $Action,
+
+ # Indicates whether to save the event data. If this switch is specified,
+ # the function saves the event data in a global variable with the specified variable name.
+ [switch] $SaveEventData,
+
+ # Specifies the name of the variable to use when saving the event data.
+ # This parameter is only used if the SaveEventData switch is specified.
+ [string] $EventDataVariableName = 'ToastEvent',
+
+ # Specifies the source identifier to use when registering the event.
+ # If this parameter is not specified, the function generates a unique source identifier.
+ [string] $SourceIdentifier,
+
+ # Indicates whether to pass through the event registration object.
+ # If this switch is specified, the function returns the event registration object.
+ [switch] $PassThru
+ )
+
+ $SourceID = if ($SourceIdentifier) {
+ $SourceIdentifier
+ } else {
+ 'BurntToast_{0}' -f [guid]::NewGuid()
+ }
+
+ $MessageData = [PSObject] @{
+ SaveEventData = $SaveEventData
+ EventDataVariableName = $EventDataVariableName
+ }
+
+ $EventScriptString =
+@"
+if ($Event.MessageData.SaveEventData) {
+ New-Variable -Name $Event.MessageData.EventDataVariableName -Value $Event -Scope Global -Force
+}
+
+$($Action.ToString())
+"@
+
+ $CompatToastMgr = [Microsoft.Toolkit.Uwp.Notifications.ToastNotificationManagerCompat]
+ $EventRegistration = Register-ObjectEvent -InputObject $CompatToastMgr -EventName OnActivated -SourceIdentifier $SourceID -MessageData $MessageData -Action ([scriptblock]::Create($EventScriptString))
+
+ if ($PassThru) {
+ $EventRegistration
+ }
+}
\ No newline at end of file
diff --git a/src/Public/Show-BTNotification.ps1 b/src/Public/Show-BTNotification.ps1
index 553bf56..0955963 100644
--- a/src/Public/Show-BTNotification.ps1
+++ b/src/Public/Show-BTNotification.ps1
@@ -62,7 +62,7 @@ function Show-BTNotification {
$Toast.Group = $Builder.Group
$Toast.Tag = $Builder.Tag
- [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($Script:Config.AppId).Show($Toast)
+ [Microsoft.Toolkit.Uwp.Notifications.ToastNotificationManagerCompat]::CreateToastNotifier().Show($Toast)
}
}
}
diff --git a/tests/Add-BTDataBinding.Tests.ps1 b/tests/Add-BTDataBinding.Tests.ps1
index 9bd5c5a..93da89d 100644
--- a/tests/Add-BTDataBinding.Tests.ps1
+++ b/tests/Add-BTDataBinding.Tests.ps1
@@ -45,4 +45,9 @@ Describe 'Add-BTDataBinding' {
$Builder.Content.Visual.BindingGeneric.Children.Text[0].BindingName | Should -BeIn $Builder.DataBinding.Keys
$Builder.Content.Visual.BindingGeneric.Children.Text[1].BindingName | Should -BeIn $Builder.DataBinding.Keys
}
+
+ It 'returns a toast content builder to the pipeline when the PassThru switch is supplied' {
+ $Builder = New-BTContentBuilder
+ Add-BTDataBinding -ContentBuilder $Builder -Key 'Test' -Value 'Example' -PassThru | Should -BeOfType [Microsoft.Toolkit.Uwp.Notifications.ToastContentBuilder]
+ }
}
diff --git a/tests/Add-BTInputTextBox.Tests.ps1 b/tests/Add-BTInputTextBox.Tests.ps1
index 1a66f21..7a271c1 100644
--- a/tests/Add-BTInputTextBox.Tests.ps1
+++ b/tests/Add-BTInputTextBox.Tests.ps1
@@ -120,35 +120,63 @@ Describe 'Add-BTInputTextBox' {
}
It 'adds a text box with a placeholder and default text' {
- InModuleScope BurntToast {
- $ImagePath = 'C:\Fake\Images\BurntToast.png'
- Mock Optimize-BTImageSource {return $ImagePath}
+ $ImagePath = 'C:\Fake\Images\BurntToast.png'
+ Mock Optimize-BTImageSource -ModuleName BurntToast {return $ImagePath}
- $Builder = New-BTContentBuilder
- $Builder | Add-BTImage -Source $ImagePath -AppLogo
- $Builder | Add-BTInputTextBox -Id 'Example 05 - First Name' -Title 'Set your name' -PlaceholderContent "Enter your first name" -DefaultContent "John"
- $Builder | Add-BTInputTextBox -Id 'Example 05 - Last Name' -PlaceholderContent "Enter your last name" -DefaultContent "Doe"
+ $Builder = New-BTContentBuilder
+ $Builder | Add-BTImage -Source $ImagePath -AppLogo
+ $Builder | Add-BTInputTextBox -Id 'Example 05 - First Name' -Title 'Set your name' -PlaceholderContent "Enter your first name" -DefaultContent "John"
+ $Builder | Add-BTInputTextBox -Id 'Example 05 - Last Name' -PlaceholderContent "Enter your last name" -DefaultContent "Doe"
- # The resulting XML is as expected
- $ExpectedXML = ''
- $Builder.GetXml().GetXml() | Should -BeExactly $ExpectedXML
+ # The resulting XML is as expected
+ $ExpectedXML = ''
+ $Builder.GetXml().GetXml() | Should -BeExactly $ExpectedXML
- # There are no child elements
- $Builder.GetToastContent().Visual.BindingGeneric.Children | Should -HaveCount 0
+ # There are no child elements
+ $Builder.GetToastContent().Visual.BindingGeneric.Children | Should -HaveCount 0
+
+ # There are two inputs
+ $Builder.GetToastContent().Actions.Inputs | Should -HaveCount 2
- # There are two inputs
- $Builder.GetToastContent().Actions.Inputs | Should -HaveCount 2
+ $Builder.GetToastContent().Actions.Inputs[0].Id | Should -BeExactly 'Example 05 - First Name'
+ $Builder.GetToastContent().Actions.Inputs[0].Title | Should -BeExactly 'Set your name'
+ $Builder.GetToastContent().Actions.Inputs[0].PlaceholderContent | Should -BeExactly 'Enter your first name'
+ $Builder.GetToastContent().Actions.Inputs[0].DefaultInput | Should -BeExactly 'John'
- $Builder.GetToastContent().Actions.Inputs[0].Id | Should -BeExactly 'Example 05 - First Name'
- $Builder.GetToastContent().Actions.Inputs[0].Title | Should -BeExactly 'Set your name'
- $Builder.GetToastContent().Actions.Inputs[0].PlaceholderContent | Should -BeExactly 'Enter your first name'
- $Builder.GetToastContent().Actions.Inputs[0].DefaultInput | Should -BeExactly 'John'
+ $Builder.GetToastContent().Actions.Inputs[1].Id | Should -BeExactly 'Example 05 - Last Name'
+ $Builder.GetToastContent().Actions.Inputs[1].Title | Should -BeNullOrEmpty
+ $Builder.GetToastContent().Actions.Inputs[1].PlaceholderContent | Should -BeExactly 'Enter your last name'
+ $Builder.GetToastContent().Actions.Inputs[1].DefaultInput | Should -BeExactly 'Doe'
+ }
- $Builder.GetToastContent().Actions.Inputs[1].Id | Should -BeExactly 'Example 05 - Last Name'
- $Builder.GetToastContent().Actions.Inputs[1].Title | Should -BeNullOrEmpty
- $Builder.GetToastContent().Actions.Inputs[1].PlaceholderContent | Should -BeExactly 'Enter your last name'
- $Builder.GetToastContent().Actions.Inputs[1].DefaultInput | Should -BeExactly 'Doe'
+ It 'writes a warning when too many text input boxes are added to a toast notification' {
+ $ImagePath = 'C:\Fake\Images\BurntToast.png'
+ Mock Optimize-BTImageSource -ModuleName BurntToast {return $ImagePath}
+ Mock Write-Warning -ModuleName BurntToast {}
+
+ $Builder = New-BTContentBuilder
+ $Builder | Add-BTImage -Source $ImagePath -AppLogo
+ $Builder | Add-BTInputTextBox -Id 'Box 1'
+ $Builder | Add-BTInputTextBox -Id 'Box 2'
+ $Builder | Add-BTInputTextBox -Id 'Box 3'
+ $Builder | Add-BTInputTextBox -Id 'Box 4'
+ $Builder | Add-BTInputTextBox -Id 'Box 5'
+ $Builder | Add-BTInputTextBox -Id 'Box 6'
+
+ Assert-MockCalled Write-Warning -ModuleName BurntToast -Times 1 -ParameterFilter {
+ $Message -eq 'The max number of text and selection boxes (5) on the toast notification has been reached. You cannot add any more input boxes.'
}
+
+ # There are no child elements
+ $Builder.GetToastContent().Visual.BindingGeneric.Children | Should -HaveCount 0
+
+ # There are five inputs (the sixth was not added)
+ $Builder.GetToastContent().Actions.Inputs | Should -HaveCount 5
+ }
+
+ It 'returns a toast content builder to the pipeline when the PassThru switch is supplied' {
+ $Builder = New-BTContentBuilder
+ $Builder | Add-BTInputTextBox -Id 'Box 1' -PassThru | Should -BeOfType [Microsoft.Toolkit.Uwp.Notifications.ToastContentBuilder]
}
}
diff --git a/tests/Add-BTText.Tests.ps1 b/tests/Add-BTText.Tests.ps1
index 5bbb4f8..88c0dfa 100644
--- a/tests/Add-BTText.Tests.ps1
+++ b/tests/Add-BTText.Tests.ps1
@@ -197,6 +197,11 @@ Describe 'Add-BTText' {
Remove-Item $CaptureFile -Force
}
+ It 'generates the expected XML when adding a language hint' {
+ $Builder | Add-BTText -Text 'Example Toast Source' -Attribution -Language 'en-NZ' 3> $null
+ $ExpectedXML = 'Example Toast Source'
+ $Builder.GetXml().GetXml() | Should -BeExactly $ExpectedXML
+ }
}
Context 'bindable string' {
diff --git a/tests/Register-BTEvent.Tests.ps1 b/tests/Register-BTEvent.Tests.ps1
new file mode 100644
index 0000000..df83f52
--- /dev/null
+++ b/tests/Register-BTEvent.Tests.ps1
@@ -0,0 +1,38 @@
+Describe 'Register-BTEvent' {
+ BeforeAll {
+ if (Get-Module -Name 'BurntToast') {
+ Remove-Module -Name 'BurntToast'
+ }
+
+ if ($ENV:BURNTTOAST_MODULE_ROOT) {
+ Import-Module $ENV:BURNTTOAST_MODULE_ROOT -Force
+ } else {
+ Import-Module "$PSScriptRoot/../src/BurntToast.psd1" -Force
+ }
+ }
+
+ Context 'When called with default parameters' {
+ It 'should not pass through the event registration object' {
+ $EventRegistration = Register-BTEvent -Action { Write-Host 'Test' }
+
+ $EventRegistration | Should -BeNullOrEmpty
+ }
+ }
+
+ Context 'When called with custom parameters' {
+ It 'should register an event with a unique source identifier' {
+ $EventRegistration = Register-BTEvent -Action { Write-Host 'Test' } -PassThru
+
+ $EventRegistration | Should -Not -BeNullOrEmpty
+ $EventRegistration.Name | Should -Match 'BurntToast_\w{8}-\w{4}-\w{4}-\w{4}-\w{12}'
+ $EventRegistration.Command | Should -Match 'Write-Host ''Test'''
+ }
+
+ It 'should register an event with the specified source identifier' {
+ $EventRegistration = Register-BTEvent -Action { Write-Host 'Test' } -SourceIdentifier 'Pester' -PassThru
+
+ $EventRegistration | Should -Not -BeNullOrEmpty
+ $EventRegistration.Name | Should -Match 'Pester'
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Show-BTNotification.Tests.ps1 b/tests/Show-BTNotification.Tests.ps1
index b918058..dbcfa74 100644
--- a/tests/Show-BTNotification.Tests.ps1
+++ b/tests/Show-BTNotification.Tests.ps1
@@ -27,9 +27,14 @@ Describe 'New-BTContentBuilder' {
}
}
- It 'Has the expected XML content' {
+ It 'has the expected XML content' {
$ExpectedLog = 'What if: Performing the operation "Show-BTNotification" on target "submitting: First Line of TextSecond Line of Text".'
$Log | Should -Be $ExpectedLog
}
+
+ It 'does not throw an exception with a data binding' {
+ $Builder | Add-BTDataBinding -Key 'Test' -Value 'Example'
+ { Show-BTNotification -ContentBuilder $Builder } | Should -Not -Throw
+ }
}
}
diff --git a/tests/_BurntToast.Tests.ps1 b/tests/_BurntToast.Tests.ps1
index fdc7d6b..9aeeee9 100644
--- a/tests/_BurntToast.Tests.ps1
+++ b/tests/_BurntToast.Tests.ps1
@@ -1,5 +1,5 @@
Describe 'BurntToast Module' {
- Context 'meta validation' {
+ Context 'Importing on Supported Operating System' {
It 'doesn''t throw' {
if (Get-Module -Name 'BurntToast') {
Remove-Module -Name 'BurntToast'
@@ -20,4 +20,39 @@ Describe 'BurntToast Module' {
(Get-Module BurntToast).ExportedAliases.Count | Should -Be 1
}
}
+
+ Context 'Importing on Unsupported Operating Systems' {
+ It 'throws when importing on operating systems older than Windows 10' {
+ $env:BurntToastPesterNotWindows10 = $true
+
+ if (Get-Module -Name 'BurntToast') {
+ Remove-Module -Name 'BurntToast'
+ }
+
+ if ($ENV:BURNTTOAST_MODULE_ROOT) {
+ {Import-Module $ENV:BURNTTOAST_MODULE_ROOT -Force} | Should -Throw -ExpectedMessage 'This version of BurntToast will only work on Windows 10.*'
+ } else {
+ {Import-Module "$PSScriptRoot/../src/BurntToast.psd1" -Force} | Should -Throw -ExpectedMessage 'This version of BurntToast will only work on Windows 10.*'
+ }
+ }
+
+ It 'throws when importing on Windows 10 builds older than the Anniversary Update' {
+ $env:BurntToastPesterNotAnniversaryUpdate = $true
+
+ if (Get-Module -Name 'BurntToast') {
+ Remove-Module -Name 'BurntToast'
+ }
+
+ if ($ENV:BURNTTOAST_MODULE_ROOT) {
+ {Import-Module $ENV:BURNTTOAST_MODULE_ROOT -Force} | Should -Throw -ExpectedMessage 'This version of BurntToast will only work on Windows 10 Creators Update (15063) and above.*'
+ } else {
+ {Import-Module "$PSScriptRoot/../src/BurntToast.psd1" -Force} | Should -Throw -ExpectedMessage 'This version of BurntToast will only work on Windows 10 Creators Update (15063) and above.*'
+ }
+ }
+
+ AfterEach {
+ $env:BurntToastPesterNotWindows10 = $null
+ $env:BurntToastPesterNotAnniversaryUpdate = $null
+ }
+ }
}