diff --git a/.github/workflows/Action-Test-Src-Default.yml b/.github/workflows/Action-Test-Src-Default.yml new file mode 100644 index 0000000..7896cc0 --- /dev/null +++ b/.github/workflows/Action-Test-Src-Default.yml @@ -0,0 +1,23 @@ +name: Action-Test [Src-Default] + +run-name: "Action-Test [Src-Default] - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" + +on: + workflow_dispatch: + pull_request: + schedule: + - cron: '0 0 * * *' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + ActionTest: + uses: ./.github/workflows/ActionTestWorkflow.yml + with: + TestType: Src-Default + Path: tests/srcTestRepo/src + Settings: SourceCode diff --git a/.github/workflows/Action-Test-Src-WithManifest.yml b/.github/workflows/Action-Test-Src-WithManifest.yml new file mode 100644 index 0000000..f2fb1ff --- /dev/null +++ b/.github/workflows/Action-Test-Src-WithManifest.yml @@ -0,0 +1,23 @@ +name: Action-Test [Src-WithManifest] + +run-name: "Action-Test [Src-WithManifest] - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" + +on: + workflow_dispatch: + pull_request: + schedule: + - cron: '0 0 * * *' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + ActionTest: + uses: ./.github/workflows/ActionTestWorkflow.yml + with: + TestType: Src-WithManifest + Path: tests/srcWithManifestTestRepo/src + Settings: SourceCode diff --git a/.github/workflows/Action-Test-outputs.yml b/.github/workflows/Action-Test-outputs.yml new file mode 100644 index 0000000..c318e4c --- /dev/null +++ b/.github/workflows/Action-Test-outputs.yml @@ -0,0 +1,24 @@ +name: Action-Test [outputs] + +run-name: "Action-Test [outputs] - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" + +on: + workflow_dispatch: + pull_request: + schedule: + - cron: '0 0 * * *' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + ActionTest: + uses: ./.github/workflows/ActionTestWorkflow.yml + with: + TestType: outputs + Path: tests/outputTestRepo/outputs/modules/PSModuleTest + Settings: Module + SettingsFilePath: diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml deleted file mode 100644 index 6ff2f13..0000000 --- a/.github/workflows/Action-Test.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Action-Test - -run-name: "Action-Test - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" - -on: - workflow_dispatch: - pull_request: - schedule: - - cron: '0 0 * * *' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - pull-requests: read - -jobs: - ActionTestBasic: - name: Action-Test - [Basic] - runs-on: ubuntu-latest - steps: - # Need to check out as part of the test, as its a local action - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Action-Test - uses: ./ - with: - working-directory: ./tests - subject: PSModule diff --git a/.github/workflows/ActionTestWorkflow.yml b/.github/workflows/ActionTestWorkflow.yml new file mode 100644 index 0000000..b8f12df --- /dev/null +++ b/.github/workflows/ActionTestWorkflow.yml @@ -0,0 +1,50 @@ +on: + workflow_call: + inputs: + TestType: + type: string + required: true + Path: + type: string + required: true + Settings: + type: string + required: false + SettingsFilePath: + type: string + required: false + +permissions: {} + +jobs: + ActionTest: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + name: Action-Test [outputs] - [${{ matrix.os }}] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Initialize environment + uses: PSModule/Initialize-PSModule@main + + - name: Action-Test + uses: ./ + id: action-test + with: + Path: ${{ inputs.Path }} + Settings: ${{ inputs.Settings }} + SettingsFilePath: ${{ inputs.SettingsFilePath }} + + - name: Status + shell: pwsh + env: + PASSED: ${{ steps.action-test.outputs.passed }} + run: | + Write-Host "Passed: [$env:PASSED]" + if ($env:PASSED -ne 'true') { + exit 1 + } diff --git a/.github/workflows/Auto-Configure.yml b/.github/workflows/Auto-Configure.yml deleted file mode 100644 index e2321e4..0000000 --- a/.github/workflows/Auto-Configure.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Auto-Configure - -run-name: "Auto-Configure - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" - -on: - pull_request_target: - branches: - - main - types: - - closed - - opened - - reopened - - synchronize - - labeled - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: write # Required to create releases - pull-requests: write # Required to create comments on the PRs - -jobs: - Auto-Configure: - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Auto-Configure - uses: PSModule/Auto-Configure@v1 - env: - GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/Auto-Document.yml b/.github/workflows/Auto-Document.yml deleted file mode 100644 index 6a62053..0000000 --- a/.github/workflows/Auto-Document.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Auto-Document - -run-name: "Auto-Document - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" - -on: - pull_request_target: - branches: - - main - types: - - opened - - reopened - - synchronize - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: write # Required to push to the branch - -jobs: - Auto-Document: - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Auto-Document - uses: PSModule/Auto-Document@v1 - env: - GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/Auto-Release.yml b/.github/workflows/Auto-Release.yml index d6c477b..ec157c9 100644 --- a/.github/workflows/Auto-Release.yml +++ b/.github/workflows/Auto-Release.yml @@ -1,34 +1,36 @@ -name: Auto-Release - -run-name: "Auto-Release - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" - -on: - pull_request_target: - branches: - - main - types: - - closed - - opened - - reopened - - synchronize - - labeled - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: write # Required to create releases - pull-requests: write # Required to create comments on the PRs - -jobs: - Auto-Release: - runs-on: ubuntu-latest - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Auto-Release - uses: PSModule/Auto-Release@v1 - env: - GITHUB_TOKEN: ${{ github.token }} +name: Auto-Release + +run-name: "Auto-Release - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" + +on: + pull_request_target: + branches: + - main + types: + - closed + - opened + - reopened + - synchronize + - labeled + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: write + pull-requests: write + +jobs: + Auto-Release: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Auto-Release + uses: PSModule/Auto-Release@v1 + env: + GITHUB_TOKEN: ${{ github.token }} # Used for GitHub CLI authentication + with: + IncrementalPrerelease: false diff --git a/LICENSE b/LICENSE index 9a9cbf4..75789b6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2025 PSModule - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2025 PSModule + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index d560186..2bcc8a5 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,103 @@ -# Template-Action +# Invoke-ScriptAnalyzer (by PSModule) -A template repository for GitHub Actions +This repository contains a GitHub Action that runs PSScriptAnalyzer on your code. +The action analyzes PowerShell scripts using a hashtable-based settings file to +customize rule selection, severity filtering, and custom rule inclusion. -## Usage +> **Note:** This repository includes automated tests that run via Pester to ensure +> your settings file is working as expected. -### Inputs +## Action Details -### Secrets +- **Name:** Invoke-ScriptAnalyzer (by PSModule) +- **Description:** Runs PSScriptAnalyzer on the code. +- **Author:** PSModule +- **Branding:** + Icon: `check-square` + Color: `gray-dark` -### Outputs +## Inputs -### Example +| Input | Description | Required | Default | +|---------------------|-------------------------------------------------------------------|----------|-----------------------------------------------------------------------------| +| **Path** | The path to the code to test. | Yes | `${{ github.workspace }}` | +| **Settings** | The type of tests to run: `Module`, `SourceCode`, or `Custom`. | No | `Custom` | +| **SettingsFilePath**| If `Custom` is selected, the path to the settings file. | No | `${{ github.workspace }}/.github/linters/.powershell-psscriptanalyzer.psd1` | + +## Outputs + +| Output | Description | Value | +|---------|---------------------------------------|--------------------------------------------| +| passed | Indicates if the tests passed. | `${{ steps.test.outputs.Passed }}` | + +## Files Overview + +- **action.yml** + Describes the action inputs, outputs, and run steps. The action uses a + composite run steps approach with two main steps: + 1. **Get test paths:** Uses a script to resolve paths and settings. + 2. **Invoke-Pester:** Runs Pester tests against PSScriptAnalyzer. + +- **scripts/main.ps1** + Determines the correct settings file path based on the test type. It + supports testing a module, source code, or using a custom settings file. + +- **scripts/tests/PSScriptAnalyzer/** + Contains Pester tests that run PSScriptAnalyzer using the provided settings + file. The tests check for issues reported by PSScriptAnalyzer based on rule + configuration. + +## How It Works + +1. **Path Resolution:** + The action reads inputs and determines the code path, test path, and the + settings file path. For custom settings, it uses the file at: + ```powershell + .github/linters/.powershell-psscriptanalyzer.psd1 + ``` + Otherwise, it uses a default settings file from the test folder. + +2. **Pester Testing:** + The tests import the settings file and use `Invoke-ScriptAnalyzer` to scan + the code. Each rule is evaluated, and if a rule violation is found, the test + will fail for that rule. Rules that are marked to be skipped (via exclusions + in the settings file) are automatically skipped in the test. + +3. **Automation:** + Designed for CI/CD, this action integrates with GitHub Actions, Azure Pipelines, + and other systems. The settings file customizes analysis, letting you control + rule inclusion, severity filtering, and custom rule paths. + +## Example Workflow + +Below is an example workflow configuration using this action: ```yaml -Example here +name: Analyze PowerShell Code + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Invoke PSScriptAnalyzer + uses: PSModule/Invoke-ScriptAnalyzer@v1 + with: + Path: ${{ github.workspace }} + Settings: Custom + SettingsFilePath: ${{ github.workspace }}/.github/linters/.powershell-psscriptanalyzer.psd1 ``` + +## Appendix: Settings File Documentation + +For detailed documentation on the format of the settings file, see the +[Settings File Documentation](./SettingsFileDocumentation.md) file. + +## References and Links + +- [PSScriptAnalyzer Documentation](https://learn.microsoft.com/powershell/module/psscriptanalyzer/) +- [GitHub Super-Linter](https://github.com/github/super-linter) +- [PSScriptAnalyzer GitHub Repository](https://github.com/PowerShell/PSScriptAnalyzer) +- [Custom Rules in PSScriptAnalyzer](https://docs.microsoft.com/powershell/scripting/developer/hosting/psscriptanalyzer-extensibility) diff --git a/SettingsFileDocumentation.md b/SettingsFileDocumentation.md new file mode 100644 index 0000000..ad5b739 --- /dev/null +++ b/SettingsFileDocumentation.md @@ -0,0 +1,116 @@ +# PSScriptAnalyzer Settings File Format Documentation + +This document describes the format and usage of the hashtable-based settings file +for PSScriptAnalyzer. The file is used by the GitHub action to customize analysis. + +## File Location and Basic Setup + +Place the file at: +```powershell +.github/linters/.powershell-psscriptanalyzer.psd1 +``` +The file is a PowerShell data file (.psd1) that returns a hashtable. For example: +```powershell +@{ + Severity = @('Error','Warning') + ExcludeRules = @('PSAvoidUsingWriteHost') +} +``` +This example sets the severity filter and excludes a specific rule. + +## Key Configuration Options + +- **IncludeRules** + A list of rules to run. Wildcards (e.g. `PSAvoid*`) are supported. + +- **ExcludeRules** + A list of rules to skip. Excludes take precedence over include lists. + +- **Severity** + Filters output by severity. Allowed values include `Error`, `Warning`, and + `Information`. + +- **IncludeDefaultRules** + A Boolean switch to include default rules when using custom rules. + +- **CustomRulePath** + One or more paths to custom rule modules or scripts. These extend PSScriptAnalyzer. + +- **RecurseCustomRulePath** + Boolean to search subdirectories of the custom rule path(s) for more rule files. + +- **Rules** + A nested hashtable for rule-specific settings. Use it to pass parameters to rules. + For example: + ```powershell + Rules = @{ + PSAvoidUsingCmdletAliases = @{ Whitelist = @('ls','gc') } + } + ``` + +## Configuring Custom Rules + +Custom rules are implemented in modules (.psm1) or scripts. They must export +functions that return DiagnosticRecord objects. Specify their location using +**CustomRulePath**. Use **IncludeDefaultRules = $true** if you want to run both +default and custom rules. + +For example: +```powershell +@{ + CustomRulePath = @('.\Modules\MyCustomRules.psm1') + IncludeDefaultRules = $true + IncludeRules = @('PSUseApprovedVerbs', 'Measure-*') +} +``` + +## Advanced Use Cases + +- **Selective Rule Execution** + Use either **IncludeRules** or **ExcludeRules** to control which rules run. + They help reduce noise in the analysis output. + +- **Rule-Specific Parameters** + Configure individual rules via the **Rules** key. Pass any required + parameters to fine-tune rule behavior. + +- **Multiple Settings Files** + In a multi-project repo, use separate settings files for each project and + run PSScriptAnalyzer with the appropriate file. + +- **Dynamic Settings** + Although not recommended, you can include minimal logic in the .psd1 file. + For example, using environment variables to adjust settings dynamically. + +## Automation and CI/CD Integration + +This settings file is designed to be used with automated pipelines. + +- **GitHub Actions** + The Super-Linter action automatically picks up the file from the above path. + Alternatively, use a dedicated PSScriptAnalyzer action with the settings input. + +- **Azure Pipelines** + Run a PowerShell task that installs PSScriptAnalyzer and points to the settings file. + Exit codes can be used to fail the build on errors. + +- **Other CI Tools** + Any CI system can invoke `Invoke-ScriptAnalyzer` with the `-Settings` parameter + to use this configuration. + +## Best Practices + +- **Version Control**: Store the settings file in your repository to keep configuration + consistent across environments. +- **Minimal Exclusions**: Exclude only rules that are not applicable to your project. +- **Documentation**: Use comments in the settings file to explain why certain rules are + included or excluded. +- **Regular Updates**: Update your settings when you upgrade PSScriptAnalyzer or change your + project requirements. + +## Links and References + +- [PSScriptAnalyzer Documentation](https://learn.microsoft.com/powershell/module/psscriptanalyzer/) +- [GitHub Super-Linter](https://github.com/github/super-linter) +- [PSScriptAnalyzer GitHub Repository](https://github.com/PowerShell/PSScriptAnalyzer) +- [Custom Rules in PSScriptAnalyzer](https://docs.microsoft.com/powershell/scripting/developer/hosting/psscriptanalyzer-extensibility) diff --git a/action.yml b/action.yml index 90768f1..2ef0cde 100644 --- a/action.yml +++ b/action.yml @@ -1,27 +1,49 @@ -name: '{{ NAME }}' -description: '{{ DESCRIPTION }}' +name: Invoke-ScriptAnalyzer (by PSModule) +description: Runs PSScriptAnalyzer on the code. author: PSModule branding: - icon: upload-cloud - color: white + icon: check-square + color: gray-dark inputs: - working-directory: - description: The working directory where Terraform will be executed + Path: + description: The path to the code to test. + required: true + default: ${{ github.workspace }} + Settings: + description: The type of tests to run. Can be either 'Module', 'SourceCode' or 'Custom'. required: false - subject: - description: The subject to greet + default: 'Custom' + SettingsFilePath: + description: If 'Custom' is selected, the path to the settings file. required: false - default: World + default: ${{ github.workspace }}/.github/linters/.powershell-psscriptanalyzer.psd1 + +outputs: + passed: + description: If the tests passed. + value: ${{ steps.test.outputs.Passed }} runs: using: composite steps: - - name: '{{ NAME }}' - uses: PSModule/GitHub-Script@v1 + - name: Get test paths + uses: PSModule/Github-Script@v1 + id: paths + env: + GITHUB_ACTION_INVOKE_SCRIPTANALYZER_INPUT_Path: ${{ inputs.Path }} + GITHUB_ACTION_INVOKE_SCRIPTANALYZER_INPUT_Settings: ${{ inputs.Settings }} + GITHUB_ACTION_INVOKE_SCRIPTANALYZER_INPUT_SettingsFilePath: ${{ inputs.SettingsFilePath }} + with: + Script: ${{ github.action_path }}/scripts/main.ps1 + + - name: Invoke-Pester + uses: PSModule/Invoke-Pester@fix + id: test env: - GITHUB_ACTION_INPUT_subject: ${{ inputs.subject }} + Settings: ${{ fromJson(steps.paths.outputs.result).Settings }} + SettingsFilePath: ${{ fromJson(steps.paths.outputs.result).SettingsFilePath }} with: - Script: | - # '{{ NAME }}' - ${{ github.action_path }}\scripts\main.ps1 + TestResult_TestSuiteName: PSScriptAnalyzer + Path: ${{ github.action_path }}/scripts/tests/PSScriptAnalyzer + Run_Path: ${{ inputs.Path }} diff --git a/scripts/main.ps1 b/scripts/main.ps1 index 2779f38..b2822d8 100644 --- a/scripts/main.ps1 +++ b/scripts/main.ps1 @@ -1,24 +1,27 @@ -#Requires -Modules GitHub - -[CmdletBinding()] -param( - [Parameter()] - [string] $Subject = $env:GITHUB_ACTION_INPUT_subject -) - -begin { - $scriptName = $MyInvocation.MyCommand.Name - Write-Debug "[$scriptName] - Start" -} - -process { - try { - Write-Output "Hello, $Subject!" - } catch { - throw $_ - } -} - -end { - Write-Debug "[$scriptName] - End" -} +# If test type is module, the code we ought to test is in the path/name folder, otherwise it's in the path folder. +$settings = $env:GITHUB_ACTION_INVOKE_SCRIPTANALYZER_INPUT_Settings +$testPath = Resolve-Path -Path "$PSScriptRoot/tests/PSScriptAnalyzer" | Select-Object -ExpandProperty Path +$codePath = Resolve-Path -Path $env:GITHUB_ACTION_INVOKE_SCRIPTANALYZER_INPUT_Path | Select-Object -ExpandProperty Path +$settingsFilePath = switch -Regex ($settings) { + 'Module|SourceCode' { + "$testPath/$settings.Settings.psd1" + } + 'Custom' { + Resolve-Path -Path "$env:GITHUB_ACTION_INVOKE_SCRIPTANALYZER_INPUT_SettingsFilePath" | Select-Object -ExpandProperty Path + } + default { + throw "Invalid test type: [$settings]" + } +} + +[pscustomobject]@{ + Settings = $settings + CodePath = $codePath + TestPath = $testPath + SettingsFilePath = $settingsFilePath +} | Format-List + +Set-GitHubOutput -Name Settings -Value $settings +Set-GitHubOutput -Name CodePath -Value $codePath +Set-GitHubOutput -Name TestPath -Value $testPath +Set-GitHubOutput -Name SettingsFilePath -Value $settingsFilePath diff --git a/scripts/tests/PSScriptAnalyzer/Module.Settings.psd1 b/scripts/tests/PSScriptAnalyzer/Module.Settings.psd1 new file mode 100644 index 0000000..62a5818 --- /dev/null +++ b/scripts/tests/PSScriptAnalyzer/Module.Settings.psd1 @@ -0,0 +1,53 @@ +@{ + Rules = @{ + PSAlignAssignmentStatement = @{ + Enable = $true + CheckHashtable = $true + } + PSAvoidLongLines = @{ + Enable = $true + MaximumLineLength = 150 + } + PSAvoidSemicolonsAsLineTerminators = @{ + Enable = $true + } + PSPlaceCloseBrace = @{ + Enable = $true + NewLineAfter = $false + IgnoreOneLineBlock = $true + NoEmptyLineBefore = $false + } + PSPlaceOpenBrace = @{ + Enable = $true + OnSameLine = $true + NewLineAfter = $true + IgnoreOneLineBlock = $true + } + PSProvideCommentHelp = @{ + Enable = $true + ExportedOnly = $false + BlockComment = $true + VSCodeSnippetCorrection = $false + Placement = 'begin' + } + PSUseConsistentIndentation = @{ + Enable = $true + IndentationSize = 4 + PipelineIndentation = 'IncreaseIndentationForFirstPipeline' + Kind = 'space' + } + PSUseConsistentWhitespace = @{ + Enable = $true + CheckInnerBrace = $true + CheckOpenBrace = $true + CheckOpenParen = $true + CheckOperator = $true + CheckPipe = $true + CheckPipeForRedundantWhitespace = $true + CheckSeparator = $true + CheckParameter = $true + IgnoreAssignmentOperatorInsideHashTable = $true + } + } + ExcludeRules = @() +} diff --git a/scripts/tests/PSScriptAnalyzer/PSScriptAnalyzer.Configuration.ps1 b/scripts/tests/PSScriptAnalyzer/PSScriptAnalyzer.Configuration.ps1 new file mode 100644 index 0000000..e495085 --- /dev/null +++ b/scripts/tests/PSScriptAnalyzer/PSScriptAnalyzer.Configuration.ps1 @@ -0,0 +1,5 @@ +@{ + Output = @{ + Verbosity = 'Detailed' + } +} diff --git a/scripts/tests/PSScriptAnalyzer/PSScriptAnalyzer.Container.ps1 b/scripts/tests/PSScriptAnalyzer/PSScriptAnalyzer.Container.ps1 new file mode 100644 index 0000000..404a041 --- /dev/null +++ b/scripts/tests/PSScriptAnalyzer/PSScriptAnalyzer.Container.ps1 @@ -0,0 +1,9 @@ +@{ + Path = Get-ChildItem -Path $PSScriptRoot -Filter *.Tests.ps1 | Select-Object -ExpandProperty FullName + Data = @{ + Path = $env:GITHUB_ACTION_INPUT_Run_Path + SettingsFilePath = $env:SettingsFilePath + Debug = $false + Verbose = $false + } +} diff --git a/scripts/tests/PSScriptAnalyzer/PSScriptAnalyzer.Tests.ps1 b/scripts/tests/PSScriptAnalyzer/PSScriptAnalyzer.Tests.ps1 new file mode 100644 index 0000000..6ff16bb --- /dev/null +++ b/scripts/tests/PSScriptAnalyzer/PSScriptAnalyzer.Tests.ps1 @@ -0,0 +1,68 @@ +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSReviewUnusedParameter', '', + Justification = 'Pester blocks line of sight during analysis.' +)] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseDeclaredVarsMoreThanAssignments', '', + Justification = 'Pester blocks line of sight during analysis.' +)] +[CmdLetBinding()] +Param( + [Parameter(Mandatory)] + [string] $Path, + + [Parameter(Mandatory)] + [string] $SettingsFilePath +) + +BeforeDiscovery { + $settings = Import-PowerShellDataFile -Path $SettingsFilePath + $rules = [Collections.Generic.List[System.Collections.Specialized.OrderedDictionary]]::new() + $ruleObjects = Get-ScriptAnalyzerRule -Verbose:$false | Sort-Object -Property Severity, CommonName + $Severeties = $ruleObjects | Select-Object -ExpandProperty Severity -Unique + foreach ($ruleObject in $ruleObjects) { + $rules.Add( + [ordered]@{ + RuleName = $ruleObject.RuleName + CommonName = $ruleObject.CommonName + Severity = $ruleObject.Severity + Description = $ruleObject.Description + Skip = $ruleObject.RuleName -in $settings.ExcludeRules + <# + RuleName : PSDSCUseVerboseMessageInDSCResource + CommonName : Use verbose message in DSC resource + Description : It is a best practice to emit informative, verbose messages in DSC resource functions. + This helps in debugging issues when a DSC configuration is executed. + SourceType : Builtin + SourceName : PSDSC + Severity : Information + ImplementingType : Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.UseVerboseMessageInDSCResource + #> + } + ) + } + Write-Warning "Discovered [$($rules.Count)] rules" + $relativeSettingsFilePath = $SettingsFilePath.Replace($PSScriptRoot, '').Trim('\').Trim('/') +} + +Describe "PSScriptAnalyzer tests using settings file [$relativeSettingsFilePath]" { + BeforeAll { + $testResults = Invoke-ScriptAnalyzer -Path $Path -Settings $SettingsFilePath -Recurse -Verbose:$false + Write-Warning "Found [$($testResults.Count)] issues" + } + + foreach ($Severety in $Severeties) { + Context "Severity: $Severety" { + foreach ($rule in $rules | Where-Object -Property Severity -EQ $Severety) { + It "$($rule.CommonName) ($($rule.RuleName))" -Skip:$rule.Skip { + $issues = [Collections.Generic.List[string]]::new() + $testResults | Where-Object -Property RuleName -EQ $rule.RuleName | ForEach-Object { + $relativePath = $_.ScriptPath.Replace($Path, '').Trim('\').Trim('/') + $issues.Add(([Environment]::NewLine + " - $relativePath`:L$($_.Line):C$($_.Column)")) + } + $issues -join '' | Should -BeNullOrEmpty -Because $rule.Description + } + } + } + } +} diff --git a/scripts/tests/PSScriptAnalyzer/SourceCode.Settings.psd1 b/scripts/tests/PSScriptAnalyzer/SourceCode.Settings.psd1 new file mode 100644 index 0000000..09cc3d0 --- /dev/null +++ b/scripts/tests/PSScriptAnalyzer/SourceCode.Settings.psd1 @@ -0,0 +1,56 @@ +@{ + Rules = @{ + PSAlignAssignmentStatement = @{ + Enable = $true + CheckHashtable = $true + } + PSAvoidLongLines = @{ + Enable = $true + MaximumLineLength = 150 + } + PSAvoidSemicolonsAsLineTerminators = @{ + Enable = $true + } + PSPlaceCloseBrace = @{ + Enable = $true + NewLineAfter = $false + IgnoreOneLineBlock = $true + NoEmptyLineBefore = $false + } + PSPlaceOpenBrace = @{ + Enable = $true + OnSameLine = $true + NewLineAfter = $true + IgnoreOneLineBlock = $true + } + PSProvideCommentHelp = @{ + Enable = $true + ExportedOnly = $false + BlockComment = $true + VSCodeSnippetCorrection = $false + Placement = 'begin' + } + PSUseConsistentIndentation = @{ + Enable = $true + IndentationSize = 4 + PipelineIndentation = 'IncreaseIndentationForFirstPipeline' + Kind = 'space' + } + PSUseConsistentWhitespace = @{ + Enable = $true + CheckInnerBrace = $true + CheckOpenBrace = $true + CheckOpenParen = $true + CheckOperator = $true + CheckPipe = $true + CheckPipeForRedundantWhitespace = $true + CheckSeparator = $true + CheckParameter = $true + IgnoreAssignmentOperatorInsideHashTable = $true + } + } + ExcludeRules = @( + 'PSMissingModuleManifestField', # This rule is not applicable until the module is built. + 'PSUseToExportFieldsInManifest' + ) +} diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index a570e4d..0000000 --- a/tests/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Placeholder for tests - -Location for tests of the action. diff --git a/tests/outputTestRepo/outputs/docs/PSModuleTest/Get-PSModuleTest.md b/tests/outputTestRepo/outputs/docs/PSModuleTest/Get-PSModuleTest.md new file mode 100644 index 0000000..346f9b9 --- /dev/null +++ b/tests/outputTestRepo/outputs/docs/PSModuleTest/Get-PSModuleTest.md @@ -0,0 +1,72 @@ +--- +external help file: PSModuleTest-help.xml +Module Name: PSModuleTest +online version: +schema: 2.0.0 +--- + +# Get-PSModuleTest + +## SYNOPSIS +Performs tests on a module. + +## SYNTAX + +```powershell +Get-PSModuleTest [-Name] [-ProgressAction ] [] +``` + +## DESCRIPTION +{{ Fill in the Description }} + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Test-PSModule -Name 'World' +``` + +"Hello, World!" + +## PARAMETERS + +### -Name +Name of the person to greet. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ProgressAction +{{ Fill ProgressAction Description }} + +```yaml +Type: ActionPreference +Parameter Sets: (All) +Aliases: proga + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/tests/outputTestRepo/outputs/docs/PSModuleTest/New-PSModuleTest.md b/tests/outputTestRepo/outputs/docs/PSModuleTest/New-PSModuleTest.md new file mode 100644 index 0000000..e3ce5e7 --- /dev/null +++ b/tests/outputTestRepo/outputs/docs/PSModuleTest/New-PSModuleTest.md @@ -0,0 +1,72 @@ +--- +external help file: PSModuleTest-help.xml +Module Name: PSModuleTest +online version: +schema: 2.0.0 +--- + +# New-PSModuleTest + +## SYNOPSIS +Performs tests on a module. + +## SYNTAX + +```powershell +New-PSModuleTest [-Name] [-ProgressAction ] [] +``` + +## DESCRIPTION +{{ Fill in the Description }} + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Test-PSModule -Name 'World' +``` + +"Hello, World!" + +## PARAMETERS + +### -Name +Name of the person to greet. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ProgressAction +{{ Fill ProgressAction Description }} + +```yaml +Type: ActionPreference +Parameter Sets: (All) +Aliases: proga + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/tests/outputTestRepo/outputs/docs/PSModuleTest/Set-PSModuleTest.md b/tests/outputTestRepo/outputs/docs/PSModuleTest/Set-PSModuleTest.md new file mode 100644 index 0000000..6504d6f --- /dev/null +++ b/tests/outputTestRepo/outputs/docs/PSModuleTest/Set-PSModuleTest.md @@ -0,0 +1,72 @@ +--- +external help file: PSModuleTest-help.xml +Module Name: PSModuleTest +online version: +schema: 2.0.0 +--- + +# Set-PSModuleTest + +## SYNOPSIS +Performs tests on a module. + +## SYNTAX + +```powershell +Set-PSModuleTest [-Name] [-ProgressAction ] [] +``` + +## DESCRIPTION +{{ Fill in the Description }} + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Test-PSModule -Name 'World' +``` + +"Hello, World!" + +## PARAMETERS + +### -Name +Name of the person to greet. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ProgressAction +{{ Fill ProgressAction Description }} + +```yaml +Type: ActionPreference +Parameter Sets: (All) +Aliases: proga + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/tests/outputTestRepo/outputs/docs/PSModuleTest/Test-PSModuleTest.md b/tests/outputTestRepo/outputs/docs/PSModuleTest/Test-PSModuleTest.md new file mode 100644 index 0000000..59fc9f8 --- /dev/null +++ b/tests/outputTestRepo/outputs/docs/PSModuleTest/Test-PSModuleTest.md @@ -0,0 +1,72 @@ +--- +external help file: PSModuleTest-help.xml +Module Name: PSModuleTest +online version: +schema: 2.0.0 +--- + +# Test-PSModuleTest + +## SYNOPSIS +Performs tests on a module. + +## SYNTAX + +```powershell +Test-PSModuleTest [-Name] [-ProgressAction ] [] +``` + +## DESCRIPTION +{{ Fill in the Description }} + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Test-PSModule -Name 'World' +``` + +"Hello, World!" + +## PARAMETERS + +### -Name +Name of the person to greet. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ProgressAction +{{ Fill ProgressAction Description }} + +```yaml +Type: ActionPreference +Parameter Sets: (All) +Aliases: proga + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/tests/outputTestRepo/outputs/modules/PSModuleTest/PSModuleTest.psd1 b/tests/outputTestRepo/outputs/modules/PSModuleTest/PSModuleTest.psd1 new file mode 100644 index 0000000..ebe988d --- /dev/null +++ b/tests/outputTestRepo/outputs/modules/PSModuleTest/PSModuleTest.psd1 @@ -0,0 +1,75 @@ +@{ + RootModule = 'PSModuleTest.psm1' + ModuleVersion = '999.0.0' + CompatiblePSEditions = @( + 'Core' + 'Desktop' + ) + GUID = '20b37221-db1c-43db-9cca-f22b33123548' + Author = 'PSModule' + CompanyName = 'PSModule' + Copyright = '(c) 2024 PSModule. All rights reserved.' + Description = 'Process a module from source code to published module.' + PowerShellVersion = '5.1' + ProcessorArchitecture = 'None' + RequiredModules = @( + @{ + ModuleVersion = '1.0' + ModuleName = 'PSSemVer' + } + 'Utilities' + ) + RequiredAssemblies = 'assemblies/LsonLib.dll' + ScriptsToProcess = 'scripts/loader.ps1' + TypesToProcess = @( + 'types/DirectoryInfo.Types.ps1xml' + 'types/FileInfo.Types.ps1xml' + ) + FormatsToProcess = @( + 'formats/CultureInfo.Format.ps1xml' + 'formats/Mygciview.Format.ps1xml' + ) + NestedModules = @( + 'modules/OtherPSModule.psm1' + ) + FunctionsToExport = @( + 'Get-PSModuleTest' + 'New-PSModuleTest' + 'Set-PSModuleTest' + 'Test-PSModuleTest' + ) + CmdletsToExport = @() + VariablesToExport = @() + AliasesToExport = '*' + ModuleList = @( + 'modules/OtherPSModule.psm1' + ) + FileList = @( + 'PSModuleTest.psd1' + 'PSModuleTest.psm1' + 'assemblies/LsonLib.dll' + 'data/Config.psd1' + 'data/Settings.psd1' + 'formats/CultureInfo.Format.ps1xml' + 'formats/Mygciview.Format.ps1xml' + 'modules/OtherPSModule.psm1' + 'scripts/loader.ps1' + 'types/DirectoryInfo.Types.ps1xml' + 'types/FileInfo.Types.ps1xml' + ) + PrivateData = @{ + PSData = @{ + Tags = @( + 'workflow' + 'powershell' + 'powershell-module' + 'PSEdition_Desktop' + 'PSEdition_Core' + ) + LicenseUri = 'https://github.com/PSModule/Process-PSModule/blob/main/LICENSE' + ProjectUri = 'https://github.com/PSModule/Process-PSModule' + IconUri = 'https://raw.githubusercontent.com/PSModule/Process-PSModule/main/icon/icon.png' + } + } +} + diff --git a/tests/outputTestRepo/outputs/modules/PSModuleTest/PSModuleTest.psm1 b/tests/outputTestRepo/outputs/modules/PSModuleTest/PSModuleTest.psm1 new file mode 100644 index 0000000..3553dd9 --- /dev/null +++ b/tests/outputTestRepo/outputs/modules/PSModuleTest/PSModuleTest.psm1 @@ -0,0 +1,392 @@ +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidLongLines', '', Justification = 'Contains long links.')] +[CmdletBinding()] +param() + +$scriptName = $MyInvocation.MyCommand.Name +Write-Verbose "[$scriptName] Importing module" + +#region - Data import +Write-Verbose "[$scriptName] - [data] - Processing folder" +$dataFolder = (Join-Path $PSScriptRoot 'data') +Write-Verbose "[$scriptName] - [data] - [$dataFolder]" +Get-ChildItem -Path "$dataFolder" -Recurse -Force -Include '*.psd1' -ErrorAction SilentlyContinue | ForEach-Object { + Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Importing" + New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force + Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Done" +} + +Write-Verbose "[$scriptName] - [data] - Done" +#endregion - Data import + +#region - From /init +Write-Verbose "[$scriptName] - [/init] - Processing folder" + +#region - From /init/initializer.ps1 +Write-Verbose "[$scriptName] - [/init/initializer.ps1] - Importing" + +Write-Verbose '-------------------------------' +Write-Verbose '--- THIS IS AN INITIALIZER ---' +Write-Verbose '-------------------------------' + +Write-Verbose "[$scriptName] - [/init/initializer.ps1] - Done" +#endregion - From /init/initializer.ps1 + +Write-Verbose "[$scriptName] - [/init] - Done" +#endregion - From /init + +#region - From /classes +Write-Verbose "[$scriptName] - [/classes] - Processing folder" + +#region - From /classes/Book.ps1 +Write-Verbose "[$scriptName] - [/classes/Book.ps1] - Importing" + +class Book { + # Class properties + [string] $Title + [string] $Author + [string] $Synopsis + [string] $Publisher + [datetime] $PublishDate + [int] $PageCount + [string[]] $Tags + # Default constructor + Book() { $this.Init(@{}) } + # Convenience constructor from hashtable + Book([hashtable]$Properties) { $this.Init($Properties) } + # Common constructor for title and author + Book([string]$Title, [string]$Author) { + $this.Init(@{Title = $Title; Author = $Author }) + } + # Shared initializer method + [void] Init([hashtable]$Properties) { + foreach ($Property in $Properties.Keys) { + $this.$Property = $Properties.$Property + } + } + # Method to calculate reading time as 2 minutes per page + [timespan] GetReadingTime() { + if ($this.PageCount -le 0) { + throw 'Unable to determine reading time from page count.' + } + $Minutes = $this.PageCount * 2 + return [timespan]::new(0, $Minutes, 0) + } + # Method to calculate how long ago a book was published + [timespan] GetPublishedAge() { + if ( + $null -eq $this.PublishDate -or + $this.PublishDate -eq [datetime]::MinValue + ) { throw 'PublishDate not defined' } + + return (Get-Date) - $this.PublishDate + } + # Method to return a string representation of the book + [string] ToString() { + return "$($this.Title) by $($this.Author) ($($this.PublishDate.Year))" + } +} + +Write-Verbose "[$scriptName] - [/classes/Book.ps1] - Done" +#endregion - From /classes/Book.ps1 +#region - From /classes/BookList.ps1 +Write-Verbose "[$scriptName] - [/classes/BookList.ps1] - Importing" + +class BookList { + # Static property to hold the list of books + static [System.Collections.Generic.List[Book]] $Books + # Static method to initialize the list of books. Called in the other + # static methods to avoid needing to explicit initialize the value. + static [void] Initialize() { [BookList]::Initialize($false) } + static [bool] Initialize([bool]$force) { + if ([BookList]::Books.Count -gt 0 -and -not $force) { + return $false + } + + [BookList]::Books = [System.Collections.Generic.List[Book]]::new() + + return $true + } + # Ensure a book is valid for the list. + static [void] Validate([book]$Book) { + $Prefix = @( + 'Book validation failed: Book must be defined with the Title,' + 'Author, and PublishDate properties, but' + ) -join ' ' + if ($null -eq $Book) { throw "$Prefix was null" } + if ([string]::IsNullOrEmpty($Book.Title)) { + throw "$Prefix Title wasn't defined" + } + if ([string]::IsNullOrEmpty($Book.Author)) { + throw "$Prefix Author wasn't defined" + } + if ([datetime]::MinValue -eq $Book.PublishDate) { + throw "$Prefix PublishDate wasn't defined" + } + } + # Static methods to manage the list of books. + # Add a book if it's not already in the list. + static [void] Add([Book]$Book) { + [BookList]::Initialize() + [BookList]::Validate($Book) + if ([BookList]::Books.Contains($Book)) { + throw "Book '$Book' already in list" + } + + $FindPredicate = { + param([Book]$b) + + $b.Title -eq $Book.Title -and + $b.Author -eq $Book.Author -and + $b.PublishDate -eq $Book.PublishDate + }.GetNewClosure() + if ([BookList]::Books.Find($FindPredicate)) { + throw "Book '$Book' already in list" + } + + [BookList]::Books.Add($Book) + } + # Clear the list of books. + static [void] Clear() { + [BookList]::Initialize() + [BookList]::Books.Clear() + } + # Find a specific book using a filtering scriptblock. + static [Book] Find([scriptblock]$Predicate) { + [BookList]::Initialize() + return [BookList]::Books.Find($Predicate) + } + # Find every book matching the filtering scriptblock. + static [Book[]] FindAll([scriptblock]$Predicate) { + [BookList]::Initialize() + return [BookList]::Books.FindAll($Predicate) + } + # Remove a specific book. + static [void] Remove([Book]$Book) { + [BookList]::Initialize() + [BookList]::Books.Remove($Book) + } + # Remove a book by property value. + static [void] RemoveBy([string]$Property, [string]$Value) { + [BookList]::Initialize() + $Index = [BookList]::Books.FindIndex({ + param($b) + $b.$Property -eq $Value + }.GetNewClosure()) + if ($Index -ge 0) { + [BookList]::Books.RemoveAt($Index) + } + } +} + +Write-Verbose "[$scriptName] - [/classes/BookList.ps1] - Done" +#endregion - From /classes/BookList.ps1 + +Write-Verbose "[$scriptName] - [/classes] - Done" +#endregion - From /classes + +#region - From /private +Write-Verbose "[$scriptName] - [/private] - Processing folder" + +#region - From /private/Get-InternalPSModule.ps1 +Write-Verbose "[$scriptName] - [/private/Get-InternalPSModule.ps1] - Importing" + +Function Get-InternalPSModule { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} + +Write-Verbose "[$scriptName] - [/private/Get-InternalPSModule.ps1] - Done" +#endregion - From /private/Get-InternalPSModule.ps1 +#region - From /private/Set-InternalPSModule.ps1 +Write-Verbose "[$scriptName] - [/private/Set-InternalPSModule.ps1] - Importing" + +Function Set-InternalPSModule { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function', + Justification = 'Reason for suppressing' + )] + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} + +Write-Verbose "[$scriptName] - [/private/Set-InternalPSModule.ps1] - Done" +#endregion - From /private/Set-InternalPSModule.ps1 + +Write-Verbose "[$scriptName] - [/private] - Done" +#endregion - From /private + +#region - From /public +Write-Verbose "[$scriptName] - [/public] - Processing folder" + +#region - From /public/Get-PSModuleTest.ps1 +Write-Verbose "[$scriptName] - [/public/Get-PSModuleTest.ps1] - Importing" + +#Requires -Modules Utilities + +function Get-PSModuleTest { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Debug 'Debug message' + Write-Verbose 'Verbose message' + Write-Output "Hello, $Name!" +} + +Write-Verbose "[$scriptName] - [/public/Get-PSModuleTest.ps1] - Done" +#endregion - From /public/Get-PSModuleTest.ps1 +#region - From /public/New-PSModuleTest.ps1 +Write-Verbose "[$scriptName] - [/public/New-PSModuleTest.ps1] - Importing" + +#Requires -Modules @{ModuleName='PSSemVer'; ModuleVersion='1.0'} + +function New-PSModuleTest { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function', + Justification = 'Reason for suppressing' + )] + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Debug 'Debug message' + Write-Verbose 'Verbose message' + Write-Output "Hello, $Name!" +} + +Write-Verbose "[$scriptName] - [/public/New-PSModuleTest.ps1] - Done" +#endregion - From /public/New-PSModuleTest.ps1 +#region - From /public/Set-PSModuleTest.ps1 +Write-Verbose "[$scriptName] - [/public/Set-PSModuleTest.ps1] - Importing" + +function Set-PSModuleTest { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function', + Justification = 'Reason for suppressing' + )] + [CmdletBinding(SupportsShouldProcess)] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Debug 'Debug message' + Write-Verbose 'Verbose message' + if ($PSCmdlet.ShouldProcess($Name, 'Set-PSModuleTest')) { + Write-Output "Hello, $Name!" + } +} + +Write-Verbose "[$scriptName] - [/public/Set-PSModuleTest.ps1] - Done" +#endregion - From /public/Set-PSModuleTest.ps1 +#region - From /public/Test-PSModuleTest.ps1 +Write-Verbose "[$scriptName] - [/public/Test-PSModuleTest.ps1] - Importing" + +function Test-PSModuleTest { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Debug 'Debug message' + Write-Verbose 'Verbose message' + Write-Output "Hello, $Name!" +} + +Write-Verbose "[$scriptName] - [/public/Test-PSModuleTest.ps1] - Done" +#endregion - From /public/Test-PSModuleTest.ps1 + +Write-Verbose "[$scriptName] - [/public] - Done" +#endregion - From /public + +#region - From /finally.ps1 +Write-Verbose "[$scriptName] - [/finally.ps1] - Importing" + +Write-Verbose '------------------------------' +Write-Verbose '--- THIS IS A LAST LOADER ---' +Write-Verbose '------------------------------' +Write-Verbose "[$scriptName] - [/finally.ps1] - Done" +#endregion - From /finally.ps1 + +$exports = @{ + Cmdlet = '' + Alias = '*' + Variable = '' + Function = @( + 'Get-PSModuleTest' + 'New-PSModuleTest' + 'Set-PSModuleTest' + 'Test-PSModuleTest' + ) +} +Export-ModuleMember @exports + diff --git a/tests/outputTestRepo/outputs/modules/PSModuleTest/assemblies/LsonLib.dll b/tests/outputTestRepo/outputs/modules/PSModuleTest/assemblies/LsonLib.dll new file mode 100644 index 0000000..3661807 Binary files /dev/null and b/tests/outputTestRepo/outputs/modules/PSModuleTest/assemblies/LsonLib.dll differ diff --git a/tests/outputTestRepo/outputs/modules/PSModuleTest/data/Config.psd1 b/tests/outputTestRepo/outputs/modules/PSModuleTest/data/Config.psd1 new file mode 100644 index 0000000..fea4466 --- /dev/null +++ b/tests/outputTestRepo/outputs/modules/PSModuleTest/data/Config.psd1 @@ -0,0 +1,3 @@ +@{ + RandomKey = 'RandomValue' +} diff --git a/tests/outputTestRepo/outputs/modules/PSModuleTest/data/Settings.psd1 b/tests/outputTestRepo/outputs/modules/PSModuleTest/data/Settings.psd1 new file mode 100644 index 0000000..bcfa7b4 --- /dev/null +++ b/tests/outputTestRepo/outputs/modules/PSModuleTest/data/Settings.psd1 @@ -0,0 +1,3 @@ +@{ + RandomSetting = 'RandomSettingValue' +} diff --git a/tests/outputTestRepo/outputs/modules/PSModuleTest/formats/CultureInfo.Format.ps1xml b/tests/outputTestRepo/outputs/modules/PSModuleTest/formats/CultureInfo.Format.ps1xml new file mode 100644 index 0000000..a715e08 --- /dev/null +++ b/tests/outputTestRepo/outputs/modules/PSModuleTest/formats/CultureInfo.Format.ps1xml @@ -0,0 +1,37 @@ + + + + + System.Globalization.CultureInfo + + System.Globalization.CultureInfo + + + + + 16 + + + 16 + + + + + + + + LCID + + + Name + + + DisplayName + + + + + + + + diff --git a/tests/outputTestRepo/outputs/modules/PSModuleTest/formats/Mygciview.Format.ps1xml b/tests/outputTestRepo/outputs/modules/PSModuleTest/formats/Mygciview.Format.ps1xml new file mode 100644 index 0000000..4c972c2 --- /dev/null +++ b/tests/outputTestRepo/outputs/modules/PSModuleTest/formats/Mygciview.Format.ps1xml @@ -0,0 +1,65 @@ + + + + + mygciview + + System.IO.DirectoryInfo + System.IO.FileInfo + + + PSParentPath + + + + + + 7 + Left + + + + 26 + Right + + + + 26 + Right + + + + 14 + Right + + + + Left + + + + + + + + ModeWithoutHardLink + + + LastWriteTime + + + CreationTime + + + Length + + + Name + + + + + + + + diff --git a/tests/outputTestRepo/outputs/modules/PSModuleTest/modules/OtherPSModule.psm1 b/tests/outputTestRepo/outputs/modules/PSModuleTest/modules/OtherPSModule.psm1 new file mode 100644 index 0000000..9e4353b --- /dev/null +++ b/tests/outputTestRepo/outputs/modules/PSModuleTest/modules/OtherPSModule.psm1 @@ -0,0 +1,19 @@ +Function Get-OtherPSModule { + <# + .SYNOPSIS + Performs tests on a module. + + .DESCRIPTION + A longer description of the function. + + .EXAMPLE + Get-OtherPSModule -Name 'World' + #> + [CmdletBinding()] + param( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/outputTestRepo/outputs/modules/PSModuleTest/scripts/loader.ps1 b/tests/outputTestRepo/outputs/modules/PSModuleTest/scripts/loader.ps1 new file mode 100644 index 0000000..973735a --- /dev/null +++ b/tests/outputTestRepo/outputs/modules/PSModuleTest/scripts/loader.ps1 @@ -0,0 +1,3 @@ +Write-Verbose '-------------------------' +Write-Verbose '--- THIS IS A LOADER ---' +Write-Verbose '-------------------------' diff --git a/tests/outputTestRepo/outputs/modules/PSModuleTest/types/DirectoryInfo.Types.ps1xml b/tests/outputTestRepo/outputs/modules/PSModuleTest/types/DirectoryInfo.Types.ps1xml new file mode 100644 index 0000000..aef538b --- /dev/null +++ b/tests/outputTestRepo/outputs/modules/PSModuleTest/types/DirectoryInfo.Types.ps1xml @@ -0,0 +1,21 @@ + + + + System.IO.FileInfo + + + Status + Success + + + + + System.IO.DirectoryInfo + + + Status + Success + + + + diff --git a/tests/outputTestRepo/outputs/modules/PSModuleTest/types/FileInfo.Types.ps1xml b/tests/outputTestRepo/outputs/modules/PSModuleTest/types/FileInfo.Types.ps1xml new file mode 100644 index 0000000..4cfaf6b --- /dev/null +++ b/tests/outputTestRepo/outputs/modules/PSModuleTest/types/FileInfo.Types.ps1xml @@ -0,0 +1,14 @@ + + + + System.IO.FileInfo + + + Age + + ((Get-Date) - ($this.CreationTime)).Days + + + + + diff --git a/tests/srcTestRepo/README.md b/tests/srcTestRepo/README.md new file mode 100644 index 0000000..b459e35 --- /dev/null +++ b/tests/srcTestRepo/README.md @@ -0,0 +1,3 @@ +# Test module + +This is a test readme. diff --git a/tests/srcTestRepo/icon/icon.png b/tests/srcTestRepo/icon/icon.png new file mode 100644 index 0000000..be83fd5 Binary files /dev/null and b/tests/srcTestRepo/icon/icon.png differ diff --git a/tests/srcTestRepo/mkdocs.yml b/tests/srcTestRepo/mkdocs.yml new file mode 100644 index 0000000..df5e17a --- /dev/null +++ b/tests/srcTestRepo/mkdocs.yml @@ -0,0 +1,75 @@ +site_name: -{{ REPO_NAME }}- +theme: + name: material + language: en + font: + text: Roboto + code: Sono + logo: Assets/icon.png + favicon: Assets/icon.png + palette: + # Palette toggle for automatic mode + - media: "(prefers-color-scheme)" + toggle: + icon: material/link + name: Switch to dark mode + # Palette toggle for dark mode + - media: '(prefers-color-scheme: dark)' + scheme: slate + toggle: + primary: black + accent: green + icon: material/toggle-switch-off-outline + name: Switch to light mode + # Palette toggle for light mode + - media: '(prefers-color-scheme: light)' + scheme: default + toggle: + primary: indigo + accent: green + icon: material/toggle-switch + name: Switch to system preference + icon: + repo: material/github + features: + - navigation.instant + - navigation.instant.progress + - navigation.indexes + - navigation.top + - navigation.tracking + - navigation.expand + - search.suggest + - search.highlight + +repo_name: -{{ REPO_OWNER }}-/-{{ REPO_NAME }}- +repo_url: https://github.com/-{{ REPO_OWNER }}-/-{{ REPO_NAME }}- + +plugins: + - search + +markdown_extensions: + - toc: + permalink: true # Adds a link icon to headings + - attr_list + - admonition + - md_in_html + - pymdownx.details # Enables collapsible admonitions + +extra: + social: + - icon: fontawesome/brands/discord + link: https://discord.gg/jedJWCPAhD + name: -{{ REPO_OWNER }}- on Discord + - icon: fontawesome/brands/github + link: https://github.com/-{{ REPO_OWNER }}-/ + name: -{{ REPO_OWNER }}- on GitHub + consent: + title: Cookie consent + description: >- + We use cookies to recognize your repeated visits and preferences, as well + as to measure the effectiveness of our documentation and whether users + find what they're searching for. With your consent, you're helping us to + make our documentation better. + actions: + - accept + - reject diff --git a/tests/srcTestRepo/src/assemblies/LsonLib.dll b/tests/srcTestRepo/src/assemblies/LsonLib.dll new file mode 100644 index 0000000..3661807 Binary files /dev/null and b/tests/srcTestRepo/src/assemblies/LsonLib.dll differ diff --git a/tests/srcTestRepo/src/classes/private/SecretWriter.ps1 b/tests/srcTestRepo/src/classes/private/SecretWriter.ps1 new file mode 100644 index 0000000..1b1732a --- /dev/null +++ b/tests/srcTestRepo/src/classes/private/SecretWriter.ps1 @@ -0,0 +1,15 @@ +class SecretWriter { + [string] $Alias + [string] $Name + [string] $Secret + + SecretWriter([string] $alias, [string] $name, [string] $secret) { + $this.Alias = $alias + $this.Name = $name + $this.Secret = $secret + } + + [string] GetAlias() { + return $this.Alias + } +} diff --git a/tests/srcTestRepo/src/classes/public/Book.ps1 b/tests/srcTestRepo/src/classes/public/Book.ps1 new file mode 100644 index 0000000..8917d9a --- /dev/null +++ b/tests/srcTestRepo/src/classes/public/Book.ps1 @@ -0,0 +1,147 @@ +class Book { + # Class properties + [string] $Title + [string] $Author + [string] $Synopsis + [string] $Publisher + [datetime] $PublishDate + [int] $PageCount + [string[]] $Tags + # Default constructor + Book() { $this.Init(@{}) } + # Convenience constructor from hashtable + Book([hashtable]$Properties) { $this.Init($Properties) } + # Common constructor for title and author + Book([string]$Title, [string]$Author) { + $this.Init(@{Title = $Title; Author = $Author }) + } + # Shared initializer method + [void] Init([hashtable]$Properties) { + foreach ($Property in $Properties.Keys) { + $this.$Property = $Properties.$Property + } + } + # Method to calculate reading time as 2 minutes per page + [timespan] GetReadingTime() { + if ($this.PageCount -le 0) { + throw 'Unable to determine reading time from page count.' + } + $Minutes = $this.PageCount * 2 + return [timespan]::new(0, $Minutes, 0) + } + # Method to calculate how long ago a book was published + [timespan] GetPublishedAge() { + if ( + $null -eq $this.PublishDate -or + $this.PublishDate -eq [datetime]::MinValue + ) { throw 'PublishDate not defined' } + + return (Get-Date) - $this.PublishDate + } + # Method to return a string representation of the book + [string] ToString() { + return "$($this.Title) by $($this.Author) ($($this.PublishDate.Year))" + } +} + +class BookList { + # Static property to hold the list of books + static [System.Collections.Generic.List[Book]] $Books + # Static method to initialize the list of books. Called in the other + # static methods to avoid needing to explicit initialize the value. + static [void] Initialize() { [BookList]::Initialize($false) } + static [bool] Initialize([bool]$force) { + if ([BookList]::Books.Count -gt 0 -and -not $force) { + return $false + } + + [BookList]::Books = [System.Collections.Generic.List[Book]]::new() + + return $true + } + # Ensure a book is valid for the list. + static [void] Validate([book]$Book) { + $Prefix = @( + 'Book validation failed: Book must be defined with the Title,' + 'Author, and PublishDate properties, but' + ) -join ' ' + if ($null -eq $Book) { throw "$Prefix was null" } + if ([string]::IsNullOrEmpty($Book.Title)) { + throw "$Prefix Title wasn't defined" + } + if ([string]::IsNullOrEmpty($Book.Author)) { + throw "$Prefix Author wasn't defined" + } + if ([datetime]::MinValue -eq $Book.PublishDate) { + throw "$Prefix PublishDate wasn't defined" + } + } + # Static methods to manage the list of books. + # Add a book if it's not already in the list. + static [void] Add([Book]$Book) { + [BookList]::Initialize() + [BookList]::Validate($Book) + if ([BookList]::Books.Contains($Book)) { + throw "Book '$Book' already in list" + } + + $FindPredicate = { + param([Book]$b) + + $b.Title -eq $Book.Title -and + $b.Author -eq $Book.Author -and + $b.PublishDate -eq $Book.PublishDate + }.GetNewClosure() + if ([BookList]::Books.Find($FindPredicate)) { + throw "Book '$Book' already in list" + } + + [BookList]::Books.Add($Book) + } + # Clear the list of books. + static [void] Clear() { + [BookList]::Initialize() + [BookList]::Books.Clear() + } + # Find a specific book using a filtering scriptblock. + static [Book] Find([scriptblock]$Predicate) { + [BookList]::Initialize() + return [BookList]::Books.Find($Predicate) + } + # Find every book matching the filtering scriptblock. + static [Book[]] FindAll([scriptblock]$Predicate) { + [BookList]::Initialize() + return [BookList]::Books.FindAll($Predicate) + } + # Remove a specific book. + static [void] Remove([Book]$Book) { + [BookList]::Initialize() + [BookList]::Books.Remove($Book) + } + # Remove a book by property value. + static [void] RemoveBy([string]$Property, [string]$Value) { + [BookList]::Initialize() + $Index = [BookList]::Books.FindIndex({ + param($b) + $b.$Property -eq $Value + }.GetNewClosure()) + if ($Index -ge 0) { + [BookList]::Books.RemoveAt($Index) + } + } +} + +enum Binding { + Hardcover + Paperback + EBook +} + +enum Genre { + Mystery + Thriller + Romance + ScienceFiction + Fantasy + Horror +} diff --git a/tests/srcTestRepo/src/data/Config.psd1 b/tests/srcTestRepo/src/data/Config.psd1 new file mode 100644 index 0000000..fea4466 --- /dev/null +++ b/tests/srcTestRepo/src/data/Config.psd1 @@ -0,0 +1,3 @@ +@{ + RandomKey = 'RandomValue' +} diff --git a/tests/srcTestRepo/src/data/Settings.psd1 b/tests/srcTestRepo/src/data/Settings.psd1 new file mode 100644 index 0000000..bcfa7b4 --- /dev/null +++ b/tests/srcTestRepo/src/data/Settings.psd1 @@ -0,0 +1,3 @@ +@{ + RandomSetting = 'RandomSettingValue' +} diff --git a/tests/srcTestRepo/src/finally.ps1 b/tests/srcTestRepo/src/finally.ps1 new file mode 100644 index 0000000..d8fc207 --- /dev/null +++ b/tests/srcTestRepo/src/finally.ps1 @@ -0,0 +1,3 @@ +Write-Verbose '------------------------------' +Write-Verbose '--- THIS IS A LAST LOADER ---' +Write-Verbose '------------------------------' diff --git a/tests/srcTestRepo/src/formats/CultureInfo.Format.ps1xml b/tests/srcTestRepo/src/formats/CultureInfo.Format.ps1xml new file mode 100644 index 0000000..a715e08 --- /dev/null +++ b/tests/srcTestRepo/src/formats/CultureInfo.Format.ps1xml @@ -0,0 +1,37 @@ + + + + + System.Globalization.CultureInfo + + System.Globalization.CultureInfo + + + + + 16 + + + 16 + + + + + + + + LCID + + + Name + + + DisplayName + + + + + + + + diff --git a/tests/srcTestRepo/src/formats/Mygciview.Format.ps1xml b/tests/srcTestRepo/src/formats/Mygciview.Format.ps1xml new file mode 100644 index 0000000..4c972c2 --- /dev/null +++ b/tests/srcTestRepo/src/formats/Mygciview.Format.ps1xml @@ -0,0 +1,65 @@ + + + + + mygciview + + System.IO.DirectoryInfo + System.IO.FileInfo + + + PSParentPath + + + + + + 7 + Left + + + + 26 + Right + + + + 26 + Right + + + + 14 + Right + + + + Left + + + + + + + + ModeWithoutHardLink + + + LastWriteTime + + + CreationTime + + + Length + + + Name + + + + + + + + diff --git a/tests/srcTestRepo/src/functions/private/Get-InternalPSModule.ps1 b/tests/srcTestRepo/src/functions/private/Get-InternalPSModule.ps1 new file mode 100644 index 0000000..89f053c --- /dev/null +++ b/tests/srcTestRepo/src/functions/private/Get-InternalPSModule.ps1 @@ -0,0 +1,18 @@ +function Get-InternalPSModule { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/srcTestRepo/src/functions/private/Set-InternalPSModule.ps1 b/tests/srcTestRepo/src/functions/private/Set-InternalPSModule.ps1 new file mode 100644 index 0000000..cf870ba --- /dev/null +++ b/tests/srcTestRepo/src/functions/private/Set-InternalPSModule.ps1 @@ -0,0 +1,22 @@ +function Set-InternalPSModule { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function', + Justification = 'Reason for suppressing' + )] + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/srcTestRepo/src/functions/public/PSModule/Get-PSModuleTest.ps1 b/tests/srcTestRepo/src/functions/public/PSModule/Get-PSModuleTest.ps1 new file mode 100644 index 0000000..57257f1 --- /dev/null +++ b/tests/srcTestRepo/src/functions/public/PSModule/Get-PSModuleTest.ps1 @@ -0,0 +1,23 @@ +#Requires -Modules Utilities +#Requires -Modules @{ ModuleName = 'PSSemVer'; RequiredVersion = '1.1.4' } +#Requires -Modules @{ ModuleName = 'DynamicParams'; ModuleVersion = '1.1.8' } +#Requires -Modules @{ ModuleName = 'Store'; ModuleVersion = '0.3.1' } + +function Get-PSModuleTest { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/srcTestRepo/src/functions/public/PSModule/New-PSModuleTest.ps1 b/tests/srcTestRepo/src/functions/public/PSModule/New-PSModuleTest.ps1 new file mode 100644 index 0000000..5fa16bc --- /dev/null +++ b/tests/srcTestRepo/src/functions/public/PSModule/New-PSModuleTest.ps1 @@ -0,0 +1,37 @@ +#Requires -Modules @{ModuleName='PSSemVer'; ModuleVersion='1.1.4'} + +function New-PSModuleTest { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + + .NOTES + Testing if a module can have a [Markdown based link](https://example.com). + !"#¤%&/()=?`´^¨*'-_+§½{[]}<>|@£$€¥¢:;.," + \[This is a test\] + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function', + Justification = 'Reason for suppressing' + )] + [Alias('New-PSModuleTestAlias1')] + [Alias('New-PSModuleTestAlias2')] + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} + +New-Alias New-PSModuleTestAlias3 New-PSModuleTest +New-Alias -Name New-PSModuleTestAlias4 -Value New-PSModuleTest + + +Set-Alias New-PSModuleTestAlias5 New-PSModuleTest diff --git a/tests/srcTestRepo/src/functions/public/PSModule/PSModule.md b/tests/srcTestRepo/src/functions/public/PSModule/PSModule.md new file mode 100644 index 0000000..79741cf --- /dev/null +++ b/tests/srcTestRepo/src/functions/public/PSModule/PSModule.md @@ -0,0 +1 @@ +# This is PSModule diff --git a/tests/srcTestRepo/src/functions/public/SomethingElse/Set-PSModuleTest.ps1 b/tests/srcTestRepo/src/functions/public/SomethingElse/Set-PSModuleTest.ps1 new file mode 100644 index 0000000..a87ac11 --- /dev/null +++ b/tests/srcTestRepo/src/functions/public/SomethingElse/Set-PSModuleTest.ps1 @@ -0,0 +1,22 @@ +function Set-PSModuleTest { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function', + Justification = 'Reason for suppressing' + )] + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/srcTestRepo/src/functions/public/SomethingElse/SomethingElse.md b/tests/srcTestRepo/src/functions/public/SomethingElse/SomethingElse.md new file mode 100644 index 0000000..d9f7e9e --- /dev/null +++ b/tests/srcTestRepo/src/functions/public/SomethingElse/SomethingElse.md @@ -0,0 +1 @@ +# This is SomethingElse diff --git a/tests/srcTestRepo/src/functions/public/Test-PSModuleTest.ps1 b/tests/srcTestRepo/src/functions/public/Test-PSModuleTest.ps1 new file mode 100644 index 0000000..26be2b9 --- /dev/null +++ b/tests/srcTestRepo/src/functions/public/Test-PSModuleTest.ps1 @@ -0,0 +1,18 @@ +function Test-PSModuleTest { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/srcTestRepo/src/functions/public/completers.ps1 b/tests/srcTestRepo/src/functions/public/completers.ps1 new file mode 100644 index 0000000..6b1adbb --- /dev/null +++ b/tests/srcTestRepo/src/functions/public/completers.ps1 @@ -0,0 +1,8 @@ +Register-ArgumentCompleter -CommandName New-PSModuleTest -ParameterName Name -ScriptBlock { + param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) + $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters + + 'Alice', 'Bob', 'Charlie' | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } +} diff --git a/tests/srcTestRepo/src/header.ps1 b/tests/srcTestRepo/src/header.ps1 new file mode 100644 index 0000000..cc1fde9 --- /dev/null +++ b/tests/srcTestRepo/src/header.ps1 @@ -0,0 +1,3 @@ +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidLongLines', '', Justification = 'Contains long links.')] +[CmdletBinding()] +param() diff --git a/tests/srcTestRepo/src/init/initializer.ps1 b/tests/srcTestRepo/src/init/initializer.ps1 new file mode 100644 index 0000000..28396fb --- /dev/null +++ b/tests/srcTestRepo/src/init/initializer.ps1 @@ -0,0 +1,3 @@ +Write-Verbose '-------------------------------' +Write-Verbose '--- THIS IS AN INITIALIZER ---' +Write-Verbose '-------------------------------' diff --git a/tests/srcTestRepo/src/modules/OtherPSModule.psm1 b/tests/srcTestRepo/src/modules/OtherPSModule.psm1 new file mode 100644 index 0000000..5d6af8e --- /dev/null +++ b/tests/srcTestRepo/src/modules/OtherPSModule.psm1 @@ -0,0 +1,19 @@ +function Get-OtherPSModule { + <# + .SYNOPSIS + Performs tests on a module. + + .DESCRIPTION + A longer description of the function. + + .EXAMPLE + Get-OtherPSModule -Name 'World' + #> + [CmdletBinding()] + param( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/srcTestRepo/src/scripts/loader.ps1 b/tests/srcTestRepo/src/scripts/loader.ps1 new file mode 100644 index 0000000..973735a --- /dev/null +++ b/tests/srcTestRepo/src/scripts/loader.ps1 @@ -0,0 +1,3 @@ +Write-Verbose '-------------------------' +Write-Verbose '--- THIS IS A LOADER ---' +Write-Verbose '-------------------------' diff --git a/tests/srcTestRepo/src/types/DirectoryInfo.Types.ps1xml b/tests/srcTestRepo/src/types/DirectoryInfo.Types.ps1xml new file mode 100644 index 0000000..aef538b --- /dev/null +++ b/tests/srcTestRepo/src/types/DirectoryInfo.Types.ps1xml @@ -0,0 +1,21 @@ + + + + System.IO.FileInfo + + + Status + Success + + + + + System.IO.DirectoryInfo + + + Status + Success + + + + diff --git a/tests/srcTestRepo/src/types/FileInfo.Types.ps1xml b/tests/srcTestRepo/src/types/FileInfo.Types.ps1xml new file mode 100644 index 0000000..4cfaf6b --- /dev/null +++ b/tests/srcTestRepo/src/types/FileInfo.Types.ps1xml @@ -0,0 +1,14 @@ + + + + System.IO.FileInfo + + + Age + + ((Get-Date) - ($this.CreationTime)).Days + + + + + diff --git a/tests/srcTestRepo/src/variables/private/PrivateVariables.ps1 b/tests/srcTestRepo/src/variables/private/PrivateVariables.ps1 new file mode 100644 index 0000000..f1fc2c3 --- /dev/null +++ b/tests/srcTestRepo/src/variables/private/PrivateVariables.ps1 @@ -0,0 +1,47 @@ +$script:HabitablePlanets = @( + @{ + Name = 'Earth' + Mass = 5.97 + Diameter = 12756 + DayLength = 24.0 + }, + @{ + Name = 'Mars' + Mass = 0.642 + Diameter = 6792 + DayLength = 24.7 + }, + @{ + Name = 'Proxima Centauri b' + Mass = 1.17 + Diameter = 11449 + DayLength = 5.15 + }, + @{ + Name = 'Kepler-442b' + Mass = 2.34 + Diameter = 11349 + DayLength = 5.7 + }, + @{ + Name = 'Kepler-452b' + Mass = 5.0 + Diameter = 17340 + DayLength = 20.0 + } +) + +$script:InhabitedPlanets = @( + @{ + Name = 'Earth' + Mass = 5.97 + Diameter = 12756 + DayLength = 24.0 + }, + @{ + Name = 'Mars' + Mass = 0.642 + Diameter = 6792 + DayLength = 24.7 + } +) diff --git a/tests/srcTestRepo/src/variables/public/Moons.ps1 b/tests/srcTestRepo/src/variables/public/Moons.ps1 new file mode 100644 index 0000000..dd0f33c --- /dev/null +++ b/tests/srcTestRepo/src/variables/public/Moons.ps1 @@ -0,0 +1,6 @@ +$script:Moons = @( + @{ + Planet = 'Earth' + Name = 'Moon' + } +) diff --git a/tests/srcTestRepo/src/variables/public/Planets.ps1 b/tests/srcTestRepo/src/variables/public/Planets.ps1 new file mode 100644 index 0000000..736584b --- /dev/null +++ b/tests/srcTestRepo/src/variables/public/Planets.ps1 @@ -0,0 +1,20 @@ +$script:Planets = @( + @{ + Name = 'Mercury' + Mass = 0.330 + Diameter = 4879 + DayLength = 4222.6 + }, + @{ + Name = 'Venus' + Mass = 4.87 + Diameter = 12104 + DayLength = 2802.0 + }, + @{ + Name = 'Earth' + Mass = 5.97 + Diameter = 12756 + DayLength = 24.0 + } +) diff --git a/tests/srcTestRepo/src/variables/public/SolarSystems.ps1 b/tests/srcTestRepo/src/variables/public/SolarSystems.ps1 new file mode 100644 index 0000000..acbcedf --- /dev/null +++ b/tests/srcTestRepo/src/variables/public/SolarSystems.ps1 @@ -0,0 +1,17 @@ +$script:SolarSystems = @( + @{ + Name = 'Solar System' + Planets = $script:Planets + Moons = $script:Moons + }, + @{ + Name = 'Alpha Centauri' + Planets = @() + Moons = @() + }, + @{ + Name = 'Sirius' + Planets = @() + Moons = @() + } +) diff --git a/tests/srcTestRepo/tests/Environment.Tests.ps1 b/tests/srcTestRepo/tests/Environment.Tests.ps1 new file mode 100644 index 0000000..211be94 --- /dev/null +++ b/tests/srcTestRepo/tests/Environment.Tests.ps1 @@ -0,0 +1,15 @@ +Describe 'Environment Variables are available' { + It 'Should be available [<_>]' -ForEach @( + 'TEST_APP_ENT_CLIENT_ID', + 'TEST_APP_ENT_PRIVATE_KEY', + 'TEST_APP_ORG_CLIENT_ID', + 'TEST_APP_ORG_PRIVATE_KEY', + 'TEST_USER_ORG_FG_PAT', + 'TEST_USER_USER_FG_PAT', + 'TEST_USER_PAT' + ) { + $name = $_ + Write-Verbose "Environment variable: [$name]" -Verbose + Get-ChildItem env: | Where-Object { $_.Name -eq $name } | Should -Not -BeNullOrEmpty + } +} diff --git a/tests/srcTestRepo/tests/PSModuleTest.Tests.ps1 b/tests/srcTestRepo/tests/PSModuleTest.Tests.ps1 new file mode 100644 index 0000000..9bf1bb6 --- /dev/null +++ b/tests/srcTestRepo/tests/PSModuleTest.Tests.ps1 @@ -0,0 +1,44 @@ +[CmdletBinding()] +Param( + # Path to the module to test. + [Parameter()] + [string] $Path +) + +Write-Verbose "Path to the module: [$Path]" -Verbose +Describe 'PSModuleTest.Tests.ps1' { + Context 'Function: Test-PSModuleTest' { + It 'Should be able to call the function' { + Write-Verbose (Test-PSModuleTest -Name 'World' | Out-String) -Verbose + Test-PSModuleTest -Name 'World' | Should -Be 'Hello, World!' + } + } + + Context 'Function: Get-PSModuleTest' { + It 'Should be able to call the function' { + Write-Verbose (Get-PSModuleTest -Name 'World' | Out-String) -Verbose + Get-PSModuleTest -Name 'World' | Should -Be 'Hello, World!' + } + } + + Context 'Function: New-PSModuleTest' { + It 'Should be able to call the function' { + Write-Verbose (New-PSModuleTest -Name 'World' | Out-String) -Verbose + New-PSModuleTest -Name 'World' | Should -Be 'Hello, World!' + } + } + + Context 'Function: Set-PSModuleTest' { + It 'Should be able to call the function' { + Write-Verbose (Set-PSModuleTest -Name 'World' | Out-String) -Verbose + Set-PSModuleTest -Name 'World' | Should -Be 'Hello, World!' + } + } + + Context 'Variables' { + It "Exports a variable for SolarSystems that contains 'Solar System'" { + Write-Verbose ($SolarSystems | Out-String) -Verbose + $SolarSystems[0].Name | Should -Be 'Solar System' + } + } +} diff --git a/tests/srcWithManifestTestRepo/README.md b/tests/srcWithManifestTestRepo/README.md new file mode 100644 index 0000000..b459e35 --- /dev/null +++ b/tests/srcWithManifestTestRepo/README.md @@ -0,0 +1,3 @@ +# Test module + +This is a test readme. diff --git a/tests/srcWithManifestTestRepo/icon/icon.png b/tests/srcWithManifestTestRepo/icon/icon.png new file mode 100644 index 0000000..be83fd5 Binary files /dev/null and b/tests/srcWithManifestTestRepo/icon/icon.png differ diff --git a/tests/srcWithManifestTestRepo/mkdocs.yml b/tests/srcWithManifestTestRepo/mkdocs.yml new file mode 100644 index 0000000..df5e17a --- /dev/null +++ b/tests/srcWithManifestTestRepo/mkdocs.yml @@ -0,0 +1,75 @@ +site_name: -{{ REPO_NAME }}- +theme: + name: material + language: en + font: + text: Roboto + code: Sono + logo: Assets/icon.png + favicon: Assets/icon.png + palette: + # Palette toggle for automatic mode + - media: "(prefers-color-scheme)" + toggle: + icon: material/link + name: Switch to dark mode + # Palette toggle for dark mode + - media: '(prefers-color-scheme: dark)' + scheme: slate + toggle: + primary: black + accent: green + icon: material/toggle-switch-off-outline + name: Switch to light mode + # Palette toggle for light mode + - media: '(prefers-color-scheme: light)' + scheme: default + toggle: + primary: indigo + accent: green + icon: material/toggle-switch + name: Switch to system preference + icon: + repo: material/github + features: + - navigation.instant + - navigation.instant.progress + - navigation.indexes + - navigation.top + - navigation.tracking + - navigation.expand + - search.suggest + - search.highlight + +repo_name: -{{ REPO_OWNER }}-/-{{ REPO_NAME }}- +repo_url: https://github.com/-{{ REPO_OWNER }}-/-{{ REPO_NAME }}- + +plugins: + - search + +markdown_extensions: + - toc: + permalink: true # Adds a link icon to headings + - attr_list + - admonition + - md_in_html + - pymdownx.details # Enables collapsible admonitions + +extra: + social: + - icon: fontawesome/brands/discord + link: https://discord.gg/jedJWCPAhD + name: -{{ REPO_OWNER }}- on Discord + - icon: fontawesome/brands/github + link: https://github.com/-{{ REPO_OWNER }}-/ + name: -{{ REPO_OWNER }}- on GitHub + consent: + title: Cookie consent + description: >- + We use cookies to recognize your repeated visits and preferences, as well + as to measure the effectiveness of our documentation and whether users + find what they're searching for. With your consent, you're helping us to + make our documentation better. + actions: + - accept + - reject diff --git a/tests/srcWithManifestTestRepo/src/assemblies/LsonLib.dll b/tests/srcWithManifestTestRepo/src/assemblies/LsonLib.dll new file mode 100644 index 0000000..3661807 Binary files /dev/null and b/tests/srcWithManifestTestRepo/src/assemblies/LsonLib.dll differ diff --git a/tests/srcWithManifestTestRepo/src/classes/private/SecretWriter.ps1 b/tests/srcWithManifestTestRepo/src/classes/private/SecretWriter.ps1 new file mode 100644 index 0000000..1b1732a --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/classes/private/SecretWriter.ps1 @@ -0,0 +1,15 @@ +class SecretWriter { + [string] $Alias + [string] $Name + [string] $Secret + + SecretWriter([string] $alias, [string] $name, [string] $secret) { + $this.Alias = $alias + $this.Name = $name + $this.Secret = $secret + } + + [string] GetAlias() { + return $this.Alias + } +} diff --git a/tests/srcWithManifestTestRepo/src/classes/public/Book.ps1 b/tests/srcWithManifestTestRepo/src/classes/public/Book.ps1 new file mode 100644 index 0000000..8917d9a --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/classes/public/Book.ps1 @@ -0,0 +1,147 @@ +class Book { + # Class properties + [string] $Title + [string] $Author + [string] $Synopsis + [string] $Publisher + [datetime] $PublishDate + [int] $PageCount + [string[]] $Tags + # Default constructor + Book() { $this.Init(@{}) } + # Convenience constructor from hashtable + Book([hashtable]$Properties) { $this.Init($Properties) } + # Common constructor for title and author + Book([string]$Title, [string]$Author) { + $this.Init(@{Title = $Title; Author = $Author }) + } + # Shared initializer method + [void] Init([hashtable]$Properties) { + foreach ($Property in $Properties.Keys) { + $this.$Property = $Properties.$Property + } + } + # Method to calculate reading time as 2 minutes per page + [timespan] GetReadingTime() { + if ($this.PageCount -le 0) { + throw 'Unable to determine reading time from page count.' + } + $Minutes = $this.PageCount * 2 + return [timespan]::new(0, $Minutes, 0) + } + # Method to calculate how long ago a book was published + [timespan] GetPublishedAge() { + if ( + $null -eq $this.PublishDate -or + $this.PublishDate -eq [datetime]::MinValue + ) { throw 'PublishDate not defined' } + + return (Get-Date) - $this.PublishDate + } + # Method to return a string representation of the book + [string] ToString() { + return "$($this.Title) by $($this.Author) ($($this.PublishDate.Year))" + } +} + +class BookList { + # Static property to hold the list of books + static [System.Collections.Generic.List[Book]] $Books + # Static method to initialize the list of books. Called in the other + # static methods to avoid needing to explicit initialize the value. + static [void] Initialize() { [BookList]::Initialize($false) } + static [bool] Initialize([bool]$force) { + if ([BookList]::Books.Count -gt 0 -and -not $force) { + return $false + } + + [BookList]::Books = [System.Collections.Generic.List[Book]]::new() + + return $true + } + # Ensure a book is valid for the list. + static [void] Validate([book]$Book) { + $Prefix = @( + 'Book validation failed: Book must be defined with the Title,' + 'Author, and PublishDate properties, but' + ) -join ' ' + if ($null -eq $Book) { throw "$Prefix was null" } + if ([string]::IsNullOrEmpty($Book.Title)) { + throw "$Prefix Title wasn't defined" + } + if ([string]::IsNullOrEmpty($Book.Author)) { + throw "$Prefix Author wasn't defined" + } + if ([datetime]::MinValue -eq $Book.PublishDate) { + throw "$Prefix PublishDate wasn't defined" + } + } + # Static methods to manage the list of books. + # Add a book if it's not already in the list. + static [void] Add([Book]$Book) { + [BookList]::Initialize() + [BookList]::Validate($Book) + if ([BookList]::Books.Contains($Book)) { + throw "Book '$Book' already in list" + } + + $FindPredicate = { + param([Book]$b) + + $b.Title -eq $Book.Title -and + $b.Author -eq $Book.Author -and + $b.PublishDate -eq $Book.PublishDate + }.GetNewClosure() + if ([BookList]::Books.Find($FindPredicate)) { + throw "Book '$Book' already in list" + } + + [BookList]::Books.Add($Book) + } + # Clear the list of books. + static [void] Clear() { + [BookList]::Initialize() + [BookList]::Books.Clear() + } + # Find a specific book using a filtering scriptblock. + static [Book] Find([scriptblock]$Predicate) { + [BookList]::Initialize() + return [BookList]::Books.Find($Predicate) + } + # Find every book matching the filtering scriptblock. + static [Book[]] FindAll([scriptblock]$Predicate) { + [BookList]::Initialize() + return [BookList]::Books.FindAll($Predicate) + } + # Remove a specific book. + static [void] Remove([Book]$Book) { + [BookList]::Initialize() + [BookList]::Books.Remove($Book) + } + # Remove a book by property value. + static [void] RemoveBy([string]$Property, [string]$Value) { + [BookList]::Initialize() + $Index = [BookList]::Books.FindIndex({ + param($b) + $b.$Property -eq $Value + }.GetNewClosure()) + if ($Index -ge 0) { + [BookList]::Books.RemoveAt($Index) + } + } +} + +enum Binding { + Hardcover + Paperback + EBook +} + +enum Genre { + Mystery + Thriller + Romance + ScienceFiction + Fantasy + Horror +} diff --git a/tests/srcWithManifestTestRepo/src/data/Config.psd1 b/tests/srcWithManifestTestRepo/src/data/Config.psd1 new file mode 100644 index 0000000..fea4466 --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/data/Config.psd1 @@ -0,0 +1,3 @@ +@{ + RandomKey = 'RandomValue' +} diff --git a/tests/srcWithManifestTestRepo/src/data/Settings.psd1 b/tests/srcWithManifestTestRepo/src/data/Settings.psd1 new file mode 100644 index 0000000..bcfa7b4 --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/data/Settings.psd1 @@ -0,0 +1,3 @@ +@{ + RandomSetting = 'RandomSettingValue' +} diff --git a/tests/srcWithManifestTestRepo/src/finally.ps1 b/tests/srcWithManifestTestRepo/src/finally.ps1 new file mode 100644 index 0000000..d8fc207 --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/finally.ps1 @@ -0,0 +1,3 @@ +Write-Verbose '------------------------------' +Write-Verbose '--- THIS IS A LAST LOADER ---' +Write-Verbose '------------------------------' diff --git a/tests/srcWithManifestTestRepo/src/formats/CultureInfo.Format.ps1xml b/tests/srcWithManifestTestRepo/src/formats/CultureInfo.Format.ps1xml new file mode 100644 index 0000000..a715e08 --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/formats/CultureInfo.Format.ps1xml @@ -0,0 +1,37 @@ + + + + + System.Globalization.CultureInfo + + System.Globalization.CultureInfo + + + + + 16 + + + 16 + + + + + + + + LCID + + + Name + + + DisplayName + + + + + + + + diff --git a/tests/srcWithManifestTestRepo/src/formats/Mygciview.Format.ps1xml b/tests/srcWithManifestTestRepo/src/formats/Mygciview.Format.ps1xml new file mode 100644 index 0000000..4c972c2 --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/formats/Mygciview.Format.ps1xml @@ -0,0 +1,65 @@ + + + + + mygciview + + System.IO.DirectoryInfo + System.IO.FileInfo + + + PSParentPath + + + + + + 7 + Left + + + + 26 + Right + + + + 26 + Right + + + + 14 + Right + + + + Left + + + + + + + + ModeWithoutHardLink + + + LastWriteTime + + + CreationTime + + + Length + + + Name + + + + + + + + diff --git a/tests/srcWithManifestTestRepo/src/functions/private/Get-InternalPSModule.ps1 b/tests/srcWithManifestTestRepo/src/functions/private/Get-InternalPSModule.ps1 new file mode 100644 index 0000000..89f053c --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/functions/private/Get-InternalPSModule.ps1 @@ -0,0 +1,18 @@ +function Get-InternalPSModule { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/srcWithManifestTestRepo/src/functions/private/Set-InternalPSModule.ps1 b/tests/srcWithManifestTestRepo/src/functions/private/Set-InternalPSModule.ps1 new file mode 100644 index 0000000..cf870ba --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/functions/private/Set-InternalPSModule.ps1 @@ -0,0 +1,22 @@ +function Set-InternalPSModule { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function', + Justification = 'Reason for suppressing' + )] + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/srcWithManifestTestRepo/src/functions/public/PSModule/Get-PSModuleTest.ps1 b/tests/srcWithManifestTestRepo/src/functions/public/PSModule/Get-PSModuleTest.ps1 new file mode 100644 index 0000000..57257f1 --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/functions/public/PSModule/Get-PSModuleTest.ps1 @@ -0,0 +1,23 @@ +#Requires -Modules Utilities +#Requires -Modules @{ ModuleName = 'PSSemVer'; RequiredVersion = '1.1.4' } +#Requires -Modules @{ ModuleName = 'DynamicParams'; ModuleVersion = '1.1.8' } +#Requires -Modules @{ ModuleName = 'Store'; ModuleVersion = '0.3.1' } + +function Get-PSModuleTest { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/srcWithManifestTestRepo/src/functions/public/PSModule/New-PSModuleTest.ps1 b/tests/srcWithManifestTestRepo/src/functions/public/PSModule/New-PSModuleTest.ps1 new file mode 100644 index 0000000..5fa16bc --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/functions/public/PSModule/New-PSModuleTest.ps1 @@ -0,0 +1,37 @@ +#Requires -Modules @{ModuleName='PSSemVer'; ModuleVersion='1.1.4'} + +function New-PSModuleTest { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + + .NOTES + Testing if a module can have a [Markdown based link](https://example.com). + !"#¤%&/()=?`´^¨*'-_+§½{[]}<>|@£$€¥¢:;.," + \[This is a test\] + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function', + Justification = 'Reason for suppressing' + )] + [Alias('New-PSModuleTestAlias1')] + [Alias('New-PSModuleTestAlias2')] + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} + +New-Alias New-PSModuleTestAlias3 New-PSModuleTest +New-Alias -Name New-PSModuleTestAlias4 -Value New-PSModuleTest + + +Set-Alias New-PSModuleTestAlias5 New-PSModuleTest diff --git a/tests/srcWithManifestTestRepo/src/functions/public/PSModule/PSModule.md b/tests/srcWithManifestTestRepo/src/functions/public/PSModule/PSModule.md new file mode 100644 index 0000000..79741cf --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/functions/public/PSModule/PSModule.md @@ -0,0 +1 @@ +# This is PSModule diff --git a/tests/srcWithManifestTestRepo/src/functions/public/SomethingElse/Set-PSModuleTest.ps1 b/tests/srcWithManifestTestRepo/src/functions/public/SomethingElse/Set-PSModuleTest.ps1 new file mode 100644 index 0000000..a87ac11 --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/functions/public/SomethingElse/Set-PSModuleTest.ps1 @@ -0,0 +1,22 @@ +function Set-PSModuleTest { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseShouldProcessForStateChangingFunctions', '', Scope = 'Function', + Justification = 'Reason for suppressing' + )] + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/srcWithManifestTestRepo/src/functions/public/SomethingElse/SomethingElse.md b/tests/srcWithManifestTestRepo/src/functions/public/SomethingElse/SomethingElse.md new file mode 100644 index 0000000..d9f7e9e --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/functions/public/SomethingElse/SomethingElse.md @@ -0,0 +1 @@ +# This is SomethingElse diff --git a/tests/srcWithManifestTestRepo/src/functions/public/Test-PSModuleTest.ps1 b/tests/srcWithManifestTestRepo/src/functions/public/Test-PSModuleTest.ps1 new file mode 100644 index 0000000..26be2b9 --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/functions/public/Test-PSModuleTest.ps1 @@ -0,0 +1,18 @@ +function Test-PSModuleTest { + <# + .SYNOPSIS + Performs tests on a module. + + .EXAMPLE + Test-PSModule -Name 'World' + + "Hello, World!" + #> + [CmdletBinding()] + param ( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/srcWithManifestTestRepo/src/functions/public/completers.ps1 b/tests/srcWithManifestTestRepo/src/functions/public/completers.ps1 new file mode 100644 index 0000000..6b1adbb --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/functions/public/completers.ps1 @@ -0,0 +1,8 @@ +Register-ArgumentCompleter -CommandName New-PSModuleTest -ParameterName Name -ScriptBlock { + param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) + $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters + + 'Alice', 'Bob', 'Charlie' | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } +} diff --git a/tests/srcWithManifestTestRepo/src/header.ps1 b/tests/srcWithManifestTestRepo/src/header.ps1 new file mode 100644 index 0000000..cc1fde9 --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/header.ps1 @@ -0,0 +1,3 @@ +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidLongLines', '', Justification = 'Contains long links.')] +[CmdletBinding()] +param() diff --git a/tests/srcWithManifestTestRepo/src/init/initializer.ps1 b/tests/srcWithManifestTestRepo/src/init/initializer.ps1 new file mode 100644 index 0000000..28396fb --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/init/initializer.ps1 @@ -0,0 +1,3 @@ +Write-Verbose '-------------------------------' +Write-Verbose '--- THIS IS AN INITIALIZER ---' +Write-Verbose '-------------------------------' diff --git a/tests/srcWithManifestTestRepo/src/manifest.psd1 b/tests/srcWithManifestTestRepo/src/manifest.psd1 new file mode 100644 index 0000000..77348f1 --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/manifest.psd1 @@ -0,0 +1,3 @@ +@{ + Author = 'Author' +} diff --git a/tests/srcWithManifestTestRepo/src/modules/OtherPSModule.psm1 b/tests/srcWithManifestTestRepo/src/modules/OtherPSModule.psm1 new file mode 100644 index 0000000..5d6af8e --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/modules/OtherPSModule.psm1 @@ -0,0 +1,19 @@ +function Get-OtherPSModule { + <# + .SYNOPSIS + Performs tests on a module. + + .DESCRIPTION + A longer description of the function. + + .EXAMPLE + Get-OtherPSModule -Name 'World' + #> + [CmdletBinding()] + param( + # Name of the person to greet. + [Parameter(Mandatory)] + [string] $Name + ) + Write-Output "Hello, $Name!" +} diff --git a/tests/srcWithManifestTestRepo/src/scripts/loader.ps1 b/tests/srcWithManifestTestRepo/src/scripts/loader.ps1 new file mode 100644 index 0000000..973735a --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/scripts/loader.ps1 @@ -0,0 +1,3 @@ +Write-Verbose '-------------------------' +Write-Verbose '--- THIS IS A LOADER ---' +Write-Verbose '-------------------------' diff --git a/tests/srcWithManifestTestRepo/src/types/DirectoryInfo.Types.ps1xml b/tests/srcWithManifestTestRepo/src/types/DirectoryInfo.Types.ps1xml new file mode 100644 index 0000000..aef538b --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/types/DirectoryInfo.Types.ps1xml @@ -0,0 +1,21 @@ + + + + System.IO.FileInfo + + + Status + Success + + + + + System.IO.DirectoryInfo + + + Status + Success + + + + diff --git a/tests/srcWithManifestTestRepo/src/types/FileInfo.Types.ps1xml b/tests/srcWithManifestTestRepo/src/types/FileInfo.Types.ps1xml new file mode 100644 index 0000000..4cfaf6b --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/types/FileInfo.Types.ps1xml @@ -0,0 +1,14 @@ + + + + System.IO.FileInfo + + + Age + + ((Get-Date) - ($this.CreationTime)).Days + + + + + diff --git a/tests/srcWithManifestTestRepo/src/variables/private/PrivateVariables.ps1 b/tests/srcWithManifestTestRepo/src/variables/private/PrivateVariables.ps1 new file mode 100644 index 0000000..f1fc2c3 --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/variables/private/PrivateVariables.ps1 @@ -0,0 +1,47 @@ +$script:HabitablePlanets = @( + @{ + Name = 'Earth' + Mass = 5.97 + Diameter = 12756 + DayLength = 24.0 + }, + @{ + Name = 'Mars' + Mass = 0.642 + Diameter = 6792 + DayLength = 24.7 + }, + @{ + Name = 'Proxima Centauri b' + Mass = 1.17 + Diameter = 11449 + DayLength = 5.15 + }, + @{ + Name = 'Kepler-442b' + Mass = 2.34 + Diameter = 11349 + DayLength = 5.7 + }, + @{ + Name = 'Kepler-452b' + Mass = 5.0 + Diameter = 17340 + DayLength = 20.0 + } +) + +$script:InhabitedPlanets = @( + @{ + Name = 'Earth' + Mass = 5.97 + Diameter = 12756 + DayLength = 24.0 + }, + @{ + Name = 'Mars' + Mass = 0.642 + Diameter = 6792 + DayLength = 24.7 + } +) diff --git a/tests/srcWithManifestTestRepo/src/variables/public/Moons.ps1 b/tests/srcWithManifestTestRepo/src/variables/public/Moons.ps1 new file mode 100644 index 0000000..dd0f33c --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/variables/public/Moons.ps1 @@ -0,0 +1,6 @@ +$script:Moons = @( + @{ + Planet = 'Earth' + Name = 'Moon' + } +) diff --git a/tests/srcWithManifestTestRepo/src/variables/public/Planets.ps1 b/tests/srcWithManifestTestRepo/src/variables/public/Planets.ps1 new file mode 100644 index 0000000..736584b --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/variables/public/Planets.ps1 @@ -0,0 +1,20 @@ +$script:Planets = @( + @{ + Name = 'Mercury' + Mass = 0.330 + Diameter = 4879 + DayLength = 4222.6 + }, + @{ + Name = 'Venus' + Mass = 4.87 + Diameter = 12104 + DayLength = 2802.0 + }, + @{ + Name = 'Earth' + Mass = 5.97 + Diameter = 12756 + DayLength = 24.0 + } +) diff --git a/tests/srcWithManifestTestRepo/src/variables/public/SolarSystems.ps1 b/tests/srcWithManifestTestRepo/src/variables/public/SolarSystems.ps1 new file mode 100644 index 0000000..acbcedf --- /dev/null +++ b/tests/srcWithManifestTestRepo/src/variables/public/SolarSystems.ps1 @@ -0,0 +1,17 @@ +$script:SolarSystems = @( + @{ + Name = 'Solar System' + Planets = $script:Planets + Moons = $script:Moons + }, + @{ + Name = 'Alpha Centauri' + Planets = @() + Moons = @() + }, + @{ + Name = 'Sirius' + Planets = @() + Moons = @() + } +) diff --git a/tests/srcWithManifestTestRepo/tests/Environments/Environment.Tests.ps1 b/tests/srcWithManifestTestRepo/tests/Environments/Environment.Tests.ps1 new file mode 100644 index 0000000..211be94 --- /dev/null +++ b/tests/srcWithManifestTestRepo/tests/Environments/Environment.Tests.ps1 @@ -0,0 +1,15 @@ +Describe 'Environment Variables are available' { + It 'Should be available [<_>]' -ForEach @( + 'TEST_APP_ENT_CLIENT_ID', + 'TEST_APP_ENT_PRIVATE_KEY', + 'TEST_APP_ORG_CLIENT_ID', + 'TEST_APP_ORG_PRIVATE_KEY', + 'TEST_USER_ORG_FG_PAT', + 'TEST_USER_USER_FG_PAT', + 'TEST_USER_PAT' + ) { + $name = $_ + Write-Verbose "Environment variable: [$name]" -Verbose + Get-ChildItem env: | Where-Object { $_.Name -eq $name } | Should -Not -BeNullOrEmpty + } +} diff --git a/tests/srcWithManifestTestRepo/tests/MyTests/PSModuleTest.Tests.ps1 b/tests/srcWithManifestTestRepo/tests/MyTests/PSModuleTest.Tests.ps1 new file mode 100644 index 0000000..9bf1bb6 --- /dev/null +++ b/tests/srcWithManifestTestRepo/tests/MyTests/PSModuleTest.Tests.ps1 @@ -0,0 +1,44 @@ +[CmdletBinding()] +Param( + # Path to the module to test. + [Parameter()] + [string] $Path +) + +Write-Verbose "Path to the module: [$Path]" -Verbose +Describe 'PSModuleTest.Tests.ps1' { + Context 'Function: Test-PSModuleTest' { + It 'Should be able to call the function' { + Write-Verbose (Test-PSModuleTest -Name 'World' | Out-String) -Verbose + Test-PSModuleTest -Name 'World' | Should -Be 'Hello, World!' + } + } + + Context 'Function: Get-PSModuleTest' { + It 'Should be able to call the function' { + Write-Verbose (Get-PSModuleTest -Name 'World' | Out-String) -Verbose + Get-PSModuleTest -Name 'World' | Should -Be 'Hello, World!' + } + } + + Context 'Function: New-PSModuleTest' { + It 'Should be able to call the function' { + Write-Verbose (New-PSModuleTest -Name 'World' | Out-String) -Verbose + New-PSModuleTest -Name 'World' | Should -Be 'Hello, World!' + } + } + + Context 'Function: Set-PSModuleTest' { + It 'Should be able to call the function' { + Write-Verbose (Set-PSModuleTest -Name 'World' | Out-String) -Verbose + Set-PSModuleTest -Name 'World' | Should -Be 'Hello, World!' + } + } + + Context 'Variables' { + It "Exports a variable for SolarSystems that contains 'Solar System'" { + Write-Verbose ($SolarSystems | Out-String) -Verbose + $SolarSystems[0].Name | Should -Be 'Solar System' + } + } +} diff --git a/tests/srcWithManifestTestRepo/tools/1-build.ps1 b/tests/srcWithManifestTestRepo/tools/1-build.ps1 new file mode 100644 index 0000000..e762395 --- /dev/null +++ b/tests/srcWithManifestTestRepo/tools/1-build.ps1 @@ -0,0 +1 @@ +"1 - Build script executed." diff --git a/tests/srcWithManifestTestRepo/tools/2-build.ps1 b/tests/srcWithManifestTestRepo/tools/2-build.ps1 new file mode 100644 index 0000000..d2575a0 --- /dev/null +++ b/tests/srcWithManifestTestRepo/tools/2-build.ps1 @@ -0,0 +1 @@ +"2 - Build script executed."