From 2e501feab42f319151470dbc3ede985aee7d8d1a Mon Sep 17 00:00:00 2001 From: Stephen Maunder Date: Sat, 18 Nov 2017 18:18:41 +0000 Subject: [PATCH] Update build process --- .vscode/launch.json | 33 ++++ .vscode/settings.json | 15 ++ .vscode/tasks.json | 56 +++++++ CHANGELOG.md | 8 + SnipeItPS.build.ps1 | 100 +++++++++++-- SnipeitPS/Public/Assets.psm1 | 52 +++---- SnipeitPS/Public/Categories.psm1 | 6 +- SnipeitPS/Public/Components.psm1 | 30 ++-- SnipeitPS/Public/Manufacturers.psm1 | 18 +-- SnipeitPS/Public/Models.psm1 | 20 +-- SnipeitPS/Public/Set-Info.psm1 | 2 +- SnipeitPS/Public/Status.psm1 | 8 +- SnipeitPS/Public/Users.psm1 | 8 +- Tests/SnipeItPS.Tests.ps1 | 224 ++++++++++++++++++++++++---- 14 files changed, 460 insertions(+), 120 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 CHANGELOG.md diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8f51e52 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,33 @@ +{ + "version": "0.2.0", + "configurations": [{ + "type": "PowerShell", + "request": "launch", + "name": "PowerShell Pester Tests", + "script": "Invoke-Pester", + "args": [], + "cwd": "${workspaceRoot}" + }, + { + "type": "PowerShell", + "request": "launch", + "name": "PowerShell Launch (current file)", + "script": "${file}", + "args": [], + "cwd": "${file}" + }, + { + "type": "PowerShell", + "request": "attach", + "name": "PowerShell Attach to Host Process", + "processId": "${command.PickPSHostProcess}", + "runspaceId": 1 + }, + { + "type": "PowerShell", + "request": "launch", + "name": "PowerShell Interactive Session", + "cwd": "${workspaceRoot}" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0cd992e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.encoding": "utf8", + "files.eol": "\r\n", + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "editor.tabSize": 4, + "[markdown]": { + "editor.wordwrap": "on", + "editor.renderWhitespace": "all", + "editor.acceptSuggestionOnEnter": false, + "editor.rulers": [80], + "editor.trimAutoWhitespace": false + }, +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..602228b --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,56 @@ +// Available variables which can be used inside of strings. +// ${workspaceRoot}: the root folder of the team +// ${file}: the current opened file +// ${relativeFile}: the current opened file relative to workspaceRoot +// ${fileBasename}: the current opened file's basename +// ${fileDirname}: the current opened file's dirname +// ${fileExtname}: the current opened file's extension +// ${cwd}: the current working directory of the spawned process +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + // Start PowerShell + "windows": { + "command": "${env:windir}\\sysnative\\windowspowershell\\v1.0\\PowerShell.exe", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass" + ] + }, + "linux": { + "command": "/usr/bin/powershell", + "args": [ + "-NoProfile" + ] + }, + "osx": { + "command": "/usr/local/bin/powershell", + "args": [ + "-NoProfile" + ] + }, + // Show the output window always + "showOutput": "always", + // Associate with test task runner + "tasks": [{ + "taskName": "Build Help", + "suppressTaskName": true, + "args": [ + "Write-Host 'Invoking platyPS'; New-ExternalHelp -Path .\\docs\\en-US -OutputPath .\\ConfluencePS\\en-US -Force;", + "Invoke-Command { Write-Host 'Completed Build task in task runner.' }" + ] + }, + { + "taskName": "Test", + "suppressTaskName": true, + "isTestCommand": true, + "args": [ + "Write-Host 'Invoking Pester'; Invoke-Pester -PesterOption @{IncludeVSCodeMarker=$true};", + "Invoke-Command { Write-Host 'Completed Test task in task runner.' }" + ], + "problemMatcher": "$pester" + } + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5dc4d10 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Change Log + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/), +and this project adheres to [Semantic Versioning](http://semver.org/). + +## [1.0] - 2017-11-18 diff --git a/SnipeItPS.build.ps1 b/SnipeItPS.build.ps1 index b798928..ee9bb33 100644 --- a/SnipeItPS.build.ps1 +++ b/SnipeItPS.build.ps1 @@ -10,15 +10,20 @@ if ($PSBoundParameters.ContainsKey('Verbose')) { if (!($env:releasePath)) { $releasePath = "$BuildRoot\Release" } -else { +elseif ($env:releasePath) { $releasePath = $env:releasePath } +else { + $releasePath = "$($pwd.Path)\Release" +} $env:PSModulePath = "$($env:PSModulePath);$releasePath" +Import-Module BuildHelpers # Ensure Invoke-Build works in the most strict mode. Set-StrictMode -Version Latest +# region debug information task ShowDebug { Write-Build Gray Write-Build Gray ('Project name: {0}' -f $env:APPVEYOR_PROJECT_NAME) @@ -42,12 +47,80 @@ task ShowDebug { Write-Build Gray } +# Synopsis: Install pandoc to .\Tools\ +task InstallPandoc -If (-not (Test-Path Tools\pandoc.exe)) { + # Setup + if (-not (Test-Path "$BuildRoot\Tools")) { + $null = New-Item -Path "$BuildRoot\Tools" -ItemType Directory + } + + # Get latest bits + $latestRelease = "https://github.com/jgm/pandoc/releases/download/1.19.2.1/pandoc-1.19.2.1-windows.msi" + Invoke-WebRequest -Uri $latestRelease -OutFile "$($env:temp)\pandoc.msi" + + # Extract bits + $null = New-Item -Path $env:temp\pandoc -ItemType Directory -Force + Start-Process -Wait -FilePath msiexec.exe -ArgumentList " /qn /a `"$($env:temp)\pandoc.msi`" targetdir=`"$($env:temp)\pandoc\`"" + + # Move to Tools folder + Copy-Item -Path "$($env:temp)\pandoc\Pandoc\pandoc.exe" -Destination "$BuildRoot\Tools\" + Copy-Item -Path "$($env:temp)\pandoc\Pandoc\pandoc-citeproc.exe" -Destination "$BuildRoot\Tools\" + + # Clean + Remove-Item -Path "$($env:temp)\pandoc" -Recurse -Force +} +# endregion + +# region test +task Test RapidTest + +# Synopsis: Using the "Fast" Test Suit +task RapidTest PesterTests +# Synopsis: Using the complete Test Suit, which includes all supported Powershell versions +task FullTest TestVersions + +# Synopsis: Warn about not empty git status if .git exists. +task GitStatus -If (Test-Path .git) { + $status = exec { git status -s } + if ($status) { + Write-Warning "Git status: $($status -join ', ')" + } +} + +task TestVersions TestPS3, TestPS4, TestPS4, TestPS5 +task TestPS3 { + exec {powershell.exe -Version 3 -NoProfile Invoke-Build PesterTests} +} +task TestPS4 { + exec {powershell.exe -Version 4 -NoProfile Invoke-Build PesterTests} +} +task TestPS5 { + exec {powershell.exe -Version 5 -NoProfile Invoke-Build PesterTests} +} + +# Synopsis: Invoke Pester Tests +task PesterTests, { + try { + $result = Invoke-Pester -PassThru -OutputFile "$BuildRoot\TestResult.xml" -OutputFormat "NUnitXml" + if ($env:APPVEYOR_PROJECT_NAME) { + Add-TestResultToAppveyor -TestFile "$BuildRoot\TestResult.xml" + Remove-Item "$BuildRoot\TestResult.xml" -Force + } + assert ($result.FailedCount -eq 0) "$($result.FailedCount) Pester test(s) failed." + } + catch { + throw + } +} +# endregion + # region build # Synopsis: Build shippable release -task Build GenerateRelease, UpdateManifest +task Build GenerateRelease, ConvertMarkdown, UpdateManifest + # Synopsis: Generate .\Release structure -task GenerateRelease { +task GenerateRelease, { # Setup if (-not (Test-Path "$releasePath\SnipeitPS")) { $null = New-Item -Path "$releasePath\SnipeitPS" -ItemType Directory @@ -56,26 +129,20 @@ task GenerateRelease { # Copy module Copy-Item -Path "$BuildRoot\SnipeitPS\*" -Destination "$releasePath\SnipeitPS" -Recurse -Force # Copy additional files - <#$additionalFiles = @( + $additionalFiles = @( "$BuildRoot\CHANGELOG.md" "$BuildRoot\LICENSE" "$BuildRoot\README.md" ) - Copy-Item -Path $additionalFiles -Destination "$releasePath\SnipeitPS" -Force#> + Copy-Item -Path $additionalFiles -Destination "$releasePath\SnipeitPS" -Force } # Synopsis: Update the manifest of the module task UpdateManifest GetVersion, { - $ModuleAlias = (Get-Alias | Where source -eq JiraPS) - - Remove-Module JiraPS -ErrorAction SilentlyContinue - Import-Module "$releasePath\SnipeitPS\SnipeitPS.psd1" Update-Metadata -Path "$releasePath\SnipeitPS\SnipeitPS.psd1" -PropertyName ModuleVersion -Value $script:Version # Update-Metadata -Path "$releasePath\SnipeitPS\SnipeitPS.psd1" -PropertyName FileList -Value (Get-ChildItem $releasePath\SnipeitPS -Recurse).Name - if ($ModuleAlias) { - Update-Metadata -Path "$releasePath\SnipeitPS\SnipeitPS.psd1" -PropertyName AliasesToExport -Value @($ModuleAlias.Name) - } - Set-ModuleFunctions -Name "$releasePath\SnipeitPS\SnipeitPS.psd1" -FunctionsToExport ([string[]](Get-ChildItem "SnipeitPS\public\*.psm1").BaseName) + $functionsToExport = Get-ChildItem "$BuildRoot\SnipeitPS\Public" | ForEach-Object {$_.BaseName} + Set-ModuleFunctions -Name "$releasePath\SnipeitPS\SnipeitPS.psd1" -FunctionsToExport $functionsToExport } task GetVersion { @@ -96,12 +163,13 @@ task GetVersion { $newRevision } - # endregion + #region Cleaning tasks task Clean RemoveGeneratedFiles + # Synopsis: Remove generated and temp files. task RemoveGeneratedFiles { $itemsToRemove = @( @@ -112,6 +180,6 @@ task RemoveGeneratedFiles { Remove-Item $itemsToRemove -Force -Recurse -ErrorAction 0 } +# endregion - -task . Build, Clean \ No newline at end of file +task . ShowDebug, Clean, Test, Build, Deploy diff --git a/SnipeitPS/Public/Assets.psm1 b/SnipeitPS/Public/Assets.psm1 index 8b695f6..c019f71 100644 --- a/SnipeitPS/Public/Assets.psm1 +++ b/SnipeitPS/Public/Assets.psm1 @@ -9,7 +9,7 @@ URL of Snipeit system, can be set using Set-Info command Users API Key for Snipeit, can be set using Set-Info command .EXAMPLE -Get-Asset -url "https://assets.dip.co.uk" -token "token..." +Get-Asset -url "https://assets.dip.co.uk" -token "token..." .EXAMPLE Get-Asset -url "https://assets.dip.co.uk" -token "token..." | Where-Object {$_.name -eq "SUPPORT23" } @@ -18,11 +18,11 @@ Get-Asset -url "https://assets.dip.co.uk" -token "token..." | Where-Object {$_.n function Get-Asset() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey ) @@ -35,20 +35,20 @@ function Get-Asset() function New-Asset() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [string]$Name, - - [parameter(mandatory=$true)] + + [parameter(mandatory=$true)] [string]$Status_id, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$Model_id, - - [parameter(mandatory=$true)] + + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey, [hashtable] $customfields @@ -74,23 +74,23 @@ function New-Asset() function Set-Asset() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [int]$id, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$Name, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$Status_id, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$Model_id, - - [parameter(mandatory=$true)] + + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey, [hashtable] $customfields @@ -115,17 +115,17 @@ function Set-Asset() function Set-AssetOwner() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [int]$id, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [int]$user_id, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey ) @@ -138,7 +138,7 @@ function Set-AssetOwner() $result = Invoke-Method -Uri "$url/api/v1/hardware/$id/checkout" ` -Method POST ` -Token $apiKey ` - -Body $Body + -Body $Body return $result } diff --git a/SnipeitPS/Public/Categories.psm1 b/SnipeitPS/Public/Categories.psm1 index 31fc78a..c93ce35 100644 --- a/SnipeitPS/Public/Categories.psm1 +++ b/SnipeitPS/Public/Categories.psm1 @@ -1,10 +1,10 @@ function Get-Categories() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey ) diff --git a/SnipeitPS/Public/Components.psm1 b/SnipeitPS/Public/Components.psm1 index 1b2e785..64cc7cb 100644 --- a/SnipeitPS/Public/Components.psm1 +++ b/SnipeitPS/Public/Components.psm1 @@ -1,10 +1,10 @@ function Get-Component() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey ) @@ -17,20 +17,20 @@ function Get-Component() function New-Component() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [string]$name, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$category_id, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$qty, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey ) @@ -52,17 +52,17 @@ function New-Component() function Update-Component() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [string]$id, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$qty, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey ) @@ -99,4 +99,4 @@ function Checkout-Component($component_id, $asset_id = "") -UserAgent "DI Script/0.1" return $Manufacturers -}#> \ No newline at end of file +}#> diff --git a/SnipeitPS/Public/Manufacturers.psm1 b/SnipeitPS/Public/Manufacturers.psm1 index 410c5bd..def3ee3 100644 --- a/SnipeitPS/Public/Manufacturers.psm1 +++ b/SnipeitPS/Public/Manufacturers.psm1 @@ -1,10 +1,10 @@ function Get-Manufacturers() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey ) @@ -17,14 +17,14 @@ function Get-Manufacturers() function New-Manufacturer() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [string]$Name, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey ) @@ -40,6 +40,6 @@ function New-Manufacturer() -Method POST ` -Body $Body ` -Token $apiKey - + $result -} \ No newline at end of file +} diff --git a/SnipeitPS/Public/Models.psm1 b/SnipeitPS/Public/Models.psm1 index b7e39b7..7ceca54 100644 --- a/SnipeitPS/Public/Models.psm1 +++ b/SnipeitPS/Public/Models.psm1 @@ -1,11 +1,11 @@ function Get-Models() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey ) @@ -20,22 +20,22 @@ function Get-Models() function New-Model() { Param( - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$name, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [int]$category_id, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [int]$manufacturer_id, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [int]$fieldset_id, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey ) @@ -54,4 +54,4 @@ function New-Model() -Token $apiKey $result -} \ No newline at end of file +} diff --git a/SnipeitPS/Public/Set-Info.psm1 b/SnipeitPS/Public/Set-Info.psm1 index 3307a01..a0ef88e 100644 --- a/SnipeitPS/Public/Set-Info.psm1 +++ b/SnipeitPS/Public/Set-Info.psm1 @@ -47,4 +47,4 @@ function Set-Info { } } } -} \ No newline at end of file +} diff --git a/SnipeitPS/Public/Status.psm1 b/SnipeitPS/Public/Status.psm1 index 3a4887a..95141f3 100644 --- a/SnipeitPS/Public/Status.psm1 +++ b/SnipeitPS/Public/Status.psm1 @@ -1,10 +1,10 @@ function Get-Status() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey ) @@ -13,4 +13,4 @@ function Get-Status() -Token $apiKey $result -} \ No newline at end of file +} diff --git a/SnipeitPS/Public/Users.psm1 b/SnipeitPS/Public/Users.psm1 index c2b3cee..b11cc85 100644 --- a/SnipeitPS/Public/Users.psm1 +++ b/SnipeitPS/Public/Users.psm1 @@ -9,7 +9,7 @@ URL of Snipeit system, can be set using Set-Info command Users API Key for Snipeit, can be set using Set-Info command .EXAMPLE -Get-Users -url "https://assets.dip.co.uk" -token "token..." +Get-Users -url "https://assets.dip.co.uk" -token "token..." .EXAMPLE Get-Users -url "https://assets.dip.co.uk" -token "token..." | Where-Object {$_.username -eq "stephenm" } @@ -17,11 +17,11 @@ Get-Users -url "https://assets.dip.co.uk" -token "token..." | Where-Object {$_.u #> function Get-Users() { - Param( - [parameter(mandatory=$true)] + Param( + [parameter(mandatory=$true)] [string]$url, - [parameter(mandatory=$true)] + [parameter(mandatory=$true)] [string]$apiKey ) diff --git a/Tests/SnipeItPS.Tests.ps1 b/Tests/SnipeItPS.Tests.ps1 index 40d25e9..2fe046a 100644 --- a/Tests/SnipeItPS.Tests.ps1 +++ b/Tests/SnipeItPS.Tests.ps1 @@ -1,53 +1,213 @@ -$ModulePath = Split-Path -Path $PSScriptRoot -Parent -$ModuleName = Split-Path -Path $ModulePath -Leaf +#Requires -Modules PSScriptAnalyzer -# Make sure one or multiple versions of the module are not loaded -Get-Module -Name $ModuleName | Remove-Module +$here = Split-Path -Parent $MyInvocation.MyCommand.Path +$projectRoot = Split-Path -Parent $here +$moduleRoot = "$projectRoot\SnipeitPS" -# Import the module and store the information about the module -$ModuleInformation = Import-Module -Name "$ModulePath\$ModuleName.psd1" -PassThru -$ModuleInformation | Format-List +$manifestFile = "$moduleRoot\SnipeitPS.psd1" +$changelogFile = "$projectRoot\CHANGELOG.md" +$appveyorFile = "$projectRoot\appveyor.yml" +$publicFunctions = "$moduleRoot\Public" +$privateFunctions = "$moduleRoot\Private" -# Get the functions present in the Manifest -$ExportedFunctions = $ModuleInformation.ExportedFunctions.Values.Name +Describe "SnipeitPS" { + Context "All required tests are present" { + # We want to make sure that every .ps1 file in the Functions directory that isn't a Pester test has an associated Pester test. + # This helps keep me honest and makes sure I'm testing my code appropriately. + It "Includes a test for each PowerShell function in the module" { + # Get-ChildItem -Path $publicFunctions -Filter "*.ps1" -Recurse | Where-Object -FilterScript {$_.Name -notlike '*.Tests.ps1'} | % { + # $expectedTestFile = Join-Path $projectRoot "Tests\$($_.BaseName).Tests.ps1" + # $expectedTestFile | Should Exist + # } + } + } -# Get the functions present in the Public folder -$PS1Functions = Get-ChildItem -Path "$ModulePath\Public\*.ps1" + Context "Manifest, changelog, and AppVeyor" { -Describe "$ModuleName Module - Testing Manifest File (.psd1)" { - Context "Manifest" { - It "Should contain RootModule" { - $ModuleInformation.RootModule | Should Not BeNullOrEmpty + # These tests are...erm, borrowed...from the module tests from the Pester module. + # I think they are excellent for sanity checking, and all credit for the following + # tests goes to Dave Wyatt, the genius behind Pester. I've just adapted them + # slightly to match ConfluencePS. + + $script:manifest = $null + + foreach ($line in (Get-Content $changelogFile)) + { + if ($line -match "^\D*(?(\d+\.){1,3}\d+)") + { + $changelogVersion = $matches.Version + break + } } - It "Should contain ModuleVersion" { - $ModuleInformation.Version | Should Not BeNullOrEmpty + foreach ($line in (Get-Content $appveyorFile)) + { + # (?()) - non-capturing group, but named Version. This makes it + # easy to reference the inside group later. + + if ($line -match '^\D*(?(\d+\.){1,3}\d+).\{build\}') + { + $appveyorVersion = $matches.Version + break + } } - It "Should contain GUID" { - $ModuleInformation.Guid | Should Not BeNullOrEmpty + It "Includes a valid manifest file" { + { + $script:manifest = Test-ModuleManifest -Path $script:manifestFile -ErrorAction Stop -WarningAction SilentlyContinue + } | Should Not Throw } - It "Should contain Author" { - $ModuleInformation.Author | Should Not BeNullOrEmpty + # There is a bug that prevents Test-ModuleManifest from updating correctly when the manifest file changes. See here: + # https://connect.microsoft.com/PowerShell/feedback/details/1541659/test-modulemanifest-the-psmoduleinfo-is-not-updated + + # As a temp workaround, we'll just read the manifest as a raw hashtable. + # Credit to this workaround comes from here: + # https://psescape.azurewebsites.net/pester-testing-your-module-manifest/ + $script:manifest = Invoke-Expression (Get-Content $script:manifestFile -Raw) + + It "Manifest file includes the correct root module" { + $script:manifest.RootModule | Should Be 'SnipeitPS' } - It "Should contain Description" { - $ModuleInformation.Description | Should Not BeNullOrEmpty + It "Manifest file includes the correct guid" { + $script:manifest.Guid | Should Be 'f86f4db4-1cb1-45c4-b7bf-6762531bfdeb' } - It "Compare the count of Function Exported and the PS1 files found" { - $status = $ExportedFunctions.Count -eq $PS1Functions.Count - $status | Should Be $true + It "Manifest file includes a valid version" { + # $script:manifest.Version -as [Version] | Should Not BeNullOrEmpty + $script:manifest.ModuleVersion -as [Version] | Should Not BeNullOrEmpty } - It "Compare the missing function" { - If ($ExportedFunctions.count -ne $PS1Functions.count) { - $Compare = Compare-Object -ReferenceObject $ExportedFunctions -DifferenceObject $PS1Functions.Basename - $Compare.InputObject -Join ',' | Should BeNullOrEmpty + It "Includes a changelog file" { + $changelogFile | Should Exist + } + + # $changelogVersion = $null + It "Changelog includes a valid version number" { + $changelogVersion | Should Not BeNullOrEmpty + $changelogVersion -as [Version] | Should Not BeNullOrEmpty + } + + It "Changelog version matches manifest version" { + $changelogVersion -as [Version] | Should Be ( $script:manifest.ModuleVersion -as [Version] ) + } + + # Back to me! Pester doesn't use AppVeyor, as far as I know, and I do. + + It "Includes an appveyor.yml file" { + $appveyorFile | Should Exist + } + + It "Appveyor.yml file includes the module version" { + $appveyorVersion | Should Not BeNullOrEmpty + $appveyorVersion -as [Version] | Should Not BeNullOrEmpty + } + + It "Appveyor version matches manifest version" { + $appveyorVersion -as [Version] | Should Be ( $script:manifest.ModuleVersion -as [Version] ) + } + } + + # The CI changes I'm testng now will render this section obsolete, + # as it should automatically patch the module manifest file with all + # exported function names. + # Leaving the code here for the moment while I can ensure those + # features are working correctly. + + # + # Context "Function checking" { + # $functionFiles = Get-ChildItem $publicFunctions -Filter *.ps1 | + # Select-Object -ExpandProperty BaseName | + # Where-Object { $_ -notlike "*.Tests" } + + # $internalFiles = Get-ChildItem $privateFunctions -Filter *.ps1 | + # Select-Object -ExpandProperty BaseName | + # Where-Object { $_ -notlike "*.Tests" } + + # #$exportedFunctions = $script:manifest.ExportedFunctions.Values.Name + # $exportedFunctions = $script:manifest.FunctionsToExport + + # foreach ($f in $functionFiles) { + # It "Exports $f" { + # $exportedFunctions -contains $f | Should Be $true + # } + # } + + # foreach ($f in $internalFiles) { + # It "Does not export $f" { + # $exportedFunctions -contains $f | Should Be $false + # } + # } + # } + + Context "Style checking" { + + # This section is again from the mastermind, Dave Wyatt. Again, credit + # goes to him for these tests. + + $files = @( + Get-ChildItem $here -Include *.ps1, *.psm1 + Get-ChildItem $publicFunctions -Include *.ps1, *.psm1 -Recurse + ) + + It 'Source files contain no trailing whitespace' { + $badLines = @( + foreach ($file in $files) + { + $lines = [System.IO.File]::ReadAllLines($file.FullName) + $lineCount = $lines.Count + + for ($i = 0; $i -lt $lineCount; $i++) + { + if ($lines[$i] -match '\s+$') + { + 'File: {0}, Line: {1}' -f $file.FullName, ($i + 1) + } + } + } + ) + + if ($badLines.Count -gt 0) + { + throw "The following $($badLines.Count) lines contain trailing whitespace: `r`n`r`n$($badLines -join "`r`n")" + } + } + + It 'Source files all end with a newline' { + $badFiles = @( + foreach ($file in $files) + { + $string = [System.IO.File]::ReadAllText($file.FullName) + if ($string.Length -gt 0 -and $string[-1] -ne "`n") + { + $file.FullName + } + } + ) + + if ($badFiles.Count -gt 0) + { + throw "The following files do not end with a newline: `r`n`r`n$($badFiles -join "`r`n")" + } + } + } + + Context 'PSScriptAnalyzer Rules' { + $analysis = Invoke-ScriptAnalyzer -Path "$moduleRoot" -Recurse -Settings "$projectRoot\PSScriptAnalyzerSettings.psd1" + $scriptAnalyzerRules = Get-ScriptAnalyzerRule + + forEach ($rule in $scriptAnalyzerRules) + { + It "Should pass $rule" { + If (($analysis) -and ($analysis.RuleName -contains $rule)) + { + $analysis | + Where RuleName -EQ $rule -OutVariable failures | + Out-Default + $failures.Count | Should Be 0 + } } } } } - -Get-Module -Name $ModuleName | Remove-Module \ No newline at end of file