diff --git a/readme.md b/readme.md index 55da972..f12e549 100644 --- a/readme.md +++ b/readme.md @@ -20,6 +20,7 @@ PVM (PHP Version Manager) is a lightweight PowerShell tool for Windows that make - [Aliases](#command-aliases) - [Build Types](#build-types) - [Namespaced Commands](#namespaced-commands) + - [Scripts](#scripts) - [Data Storage](#data-storage) - [Running Tests](#running-tests) - [Requirements](#requirements-1) @@ -378,6 +379,37 @@ pvm cache pvm cache: ``` +### Scripts + +PVM includes predefined scripts for common test scenarios, similar to npm scripts in package.json or composer scripts in composer.json. These scripts provide shortcuts for running tests with specific configurations without typing all the options each time. + +```sh +# Run a specific script +pvm run + +# Examples: +pvm run test # ............... Runs all tests with default settings +pvm run test:quiet # ......... Runs tests with verbosity set to None +pvm run test:cov # ........... Runs tests with 80% coverage target +pvm run test:cov80 # ......... Runs tests with 80% coverage target +pvm run test:cov90 # ......... Runs tests with 90% coverage target +pvm run test:full # .......... Runs tests with coverage and detailed output +``` + +**Available Scripts:** + +| Script Name | Command | Description | +|-------------|---------|-------------| +| `test:quiet` | `pvm test --verbosity=None` | Runs tests with verbosity set to None | +| `test:cov` | `pvm test --coverage=75` | Runs tests with 75% coverage target | +| `test:cov80` | `pvm test --coverage=80` | Runs tests with 80% coverage target | +| `test:cov90` | `pvm test --coverage=90` | Runs tests with 90% coverage target | +| `test:full` | `pvm test --coverage=85 --verbosity=Detailed` | Runs tests with coverage and detailed output | + +**Customizing Scripts:** + +You can customize scripts by editing the `scripts` section in `src/helpers/config.ps1`. The scripts are defined in the `$PVMConfig.defaults.scripts` hashtable. Add or modify scripts to suit your workflow. + ## Data Storage | Item | Location | diff --git a/src/actions/setup.ps1 b/src/actions/setup.ps1 index 390de38..04b78f4 100644 --- a/src/actions/setup.ps1 +++ b/src/actions/setup.ps1 @@ -101,6 +101,15 @@ function Initialize-PVMFiles { Write-Host -Object "`nFailed to create aliases list." -ForegroundColor DarkYellow } + $codes += $code = Set-Scripts-List + if ($code -eq 0) { + Write-Host -Object "`nScripts list created successfully at '$($PVMConfig.paths.scriptsList)'." -ForegroundColor DarkGreen + Write-Host -Object "- Use 'pvm scripts' to see available scripts." -ForegroundColor Gray + Write-Host -Object "- Feel free to modify it." -ForegroundColor Gray + } else { + Write-Host -Object "`nFailed to create scripts list." -ForegroundColor DarkYellow + } + return $codes } diff --git a/src/core/handlers.ps1 b/src/core/handlers.ps1 index f89f497..656ade6 100644 --- a/src/core/handlers.ps1 +++ b/src/core/handlers.ps1 @@ -505,3 +505,53 @@ function Invoke-Update { Display-Msg-By-ExitCode -result $result return $result.code } + +function Invoke-Run { + param ($arguments) + + $scriptName = $arguments[0] + $scripts = Get-Scripts + + if ([string]::IsNullOrWhiteSpace($scriptName)) { + Write-Host -Object "`nPlease provide a script name to run: pvm run " -ForegroundColor Yellow + Write-Host -Object "`nAvailable scripts:`n" -ForegroundColor Cyan + + $maxNameLength = ($scripts.Keys | Measure-Object -Maximum Length).Maximum + ($PVMConfig.env.MIN_PAD_RIGHT_LENGTH * 2) + $scripts.Keys | ForEach-Object { + $name = "$_ ".PadRight($maxNameLength, '.') + $command = $scripts[$_] + Write-Host -Object " $name $command" + } + return -1 + } + + if (-not $scripts.Contains($scriptName)) { + Write-Host -Object "`nScript '$scriptName' not found." -ForegroundColor Yellow + Write-Host -Object "`nAvailable scripts:`n" -ForegroundColor Cyan + $maxNameLength = ($scripts.Keys | Measure-Object -Maximum Length).Maximum + ($PVMConfig.env.MIN_PAD_RIGHT_LENGTH * 2) + $scripts.Keys | ForEach-Object { + $name = "$_ ".PadRight($maxNameLength, '.') + $command = $scripts[$_] + Write-Host -Object " $name $command" + } + return -1 + } + + $scriptCommand = $scripts[$scriptName] + Write-Host -Object "`nRunning script: $scriptName" -ForegroundColor Cyan + Write-Host -Object "Command: pvm $scriptCommand`n" -ForegroundColor Gray + + $parts = $scriptCommand -split ' ' + $command = $parts[0] + $scriptArgs = if ($parts.Count -gt 1) { $parts[1..($parts.Count - 1)] } else { @() } + + $actions = Get-Actions -arguments $scriptArgs + + if (-not $actions.Contains($command)) { + Write-Host -Object "`nInvalid command in script: $command" -ForegroundColor Yellow + return -1 + } + + $result = $($actions[$command].action.Invoke()) + return $result +} diff --git a/src/core/router.ps1 b/src/core/router.ps1 index b846fbe..b0cb873 100644 --- a/src/core/router.ps1 +++ b/src/core/router.ps1 @@ -350,5 +350,24 @@ function Get-Actions { } action = { return Invoke-Update -arguments $script:arguments } } + 'run' = @{ + command = 'pvm run '; + description = 'Run a predefined script from the scripts configuration.'; + usage = [ordered]@{ + USAGE = 'pvm run ' + DESCRIPTION = @( + 'Runs a predefined script from the scripts configuration.', + 'Scripts are shortcuts for common commands with predefined options.' + ) + EXAMPLES = @( + 'pvm run test:quiet ......... Runs tests with verbosity set to None' + 'pvm run test:cov ........... Runs tests with 75% coverage target' + 'pvm run test:cov80 ......... Runs tests with 80% coverage target' + 'pvm run test:cov90 ......... Runs tests with 90% coverage target' + 'pvm run test:full .......... Runs tests with coverage and detailed output' + ) + } + action = { return Invoke-Run -arguments $script:arguments } + } } } diff --git a/src/helpers/config.ps1 b/src/helpers/config.ps1 index 8fd4d3a..77eabe0 100644 --- a/src/helpers/config.ps1 +++ b/src/helpers/config.ps1 @@ -69,6 +69,35 @@ function Get-FlagMap { return $PVMConfig.defaults.flags } +function Set-Scripts-List { + try { + $jsonContent = $PVMConfig.defaults.scripts | ConvertTo-Json -Depth 10 + Set-Content -Path $PVMConfig.paths.scriptsList -Value $jsonContent -Encoding UTF8 + + return 0 + } catch { + $null = Log-Data -data @{ header = "$($MyInvocation.MyCommand.Name) - Failed to create scripts list"; exception = $_ } + return -1 + } +} + +function Get-Scripts { + try { + if (Is-File-Exists -path $PVMConfig.paths.scriptsList) { + $data = (Get-Content -Path $PVMConfig.paths.scriptsList -Raw | ConvertFrom-Json) + if ($null -ne $data) { + $ordered = [ordered]@{} + $data.PSObject.Properties | ForEach-Object { $ordered[$_.Name] = $_.Value } + if ($ordered.Count -gt 0) { return $ordered } + } + } + } catch { + $null = Log-Data -data @{ header = "$($MyInvocation.MyCommand.Name) - Failed to get scripts list"; exception = $_ } + } + + return $PVMConfig.defaults.scripts +} + function Get-Config { param([string] $rootPath) @@ -104,6 +133,7 @@ function Get-Config { profileTemplate = "$templates\profile-template.json" zendExtensionsList = "$templates\zend_extensions.json" aliasesList = "$templates\aliases.json" + scriptsList = "$templates\scripts.json" log = $logs logError = "$logs\error.log" pathVarBackup = "$logs\path.bak.log" @@ -157,7 +187,6 @@ function Get-Config { 'a' = 'add'; '+' = 'add'; 'rm' = 'remove'; '-' = 'remove' 'del' = 'delete'; 'cls' = 'clear' 'logs' = 'log'; 'upgrade' = 'update' - 'run' = 'test' 'fix' = 'repair'; } flags = [ordered]@{ @@ -166,6 +195,13 @@ function Get-Config { '--help' = 'help' '-h' = 'help' } + scripts = [ordered]@{ + 'test:quiet' = 'test --verbosity=None' + 'test:cov' = 'test --coverage=75' + 'test:cov80' = 'test --coverage=80' + 'test:cov90' = 'test --coverage=90' + 'test:full' = 'test --coverage=85 --verbosity=Detailed --sort=coverage --group=folder' + } } } } diff --git a/tests/actions/setup.tests.ps1 b/tests/actions/setup.tests.ps1 index 3247966..4434b37 100644 --- a/tests/actions/setup.tests.ps1 +++ b/tests/actions/setup.tests.ps1 @@ -225,6 +225,7 @@ Describe "Initialize-PVMFiles" { Mock Create-Profile-Template { return 0 } Mock Set-Zend-Extensions-List { return 0 } Mock Set-Aliases-List { return 0 } + Mock Set-Scripts-List { return 0 } } It "Returns -1 when the example profile creation fails" { @@ -250,6 +251,12 @@ Describe "Initialize-PVMFiles" { $result = Setup-Environment-Directories-And-Files $result | Should -Be -1 } + + It "Returns -1 when the scripts file creation fails" { + Mock Set-Scripts-List { return -1 } + $result = Setup-Environment-Directories-And-Files + $result | Should -Be -1 + } } Describe "Setup-Environment-Directories-And-Files" { diff --git a/tests/core/handlers.tests.ps1 b/tests/core/handlers.tests.ps1 index 7cc7bfa..2c193de 100644 --- a/tests/core/handlers.tests.ps1 +++ b/tests/core/handlers.tests.ps1 @@ -1443,3 +1443,43 @@ Describe "Invoke-Update Tests" { Should -Invoke Update-PVM -Times 1 } } + +Describe "Invoke-Run Tests" { + BeforeAll { + Mock Get-Actions { + [ordered]@{ + 'install' = @{ action = { return 0 } } + 'list' = @{ action = { return 0 } } + 'ini' = @{ action = { return 0 } } + 'cache' = @{ action = { return 0 } } + 'test' = @{ action = { return 0 } } + } + } + } + + It "Should return -1 when no arguments are provided" { + $result = Invoke-Run -arguments @() + $result | Should -Be -1 + } + + It "Should return -1 when an unknown script is provided" { + Mock Get-Scripts { return @{ 'script1' = 'command1'; 'script2' = 'command2' } } + + $result = Invoke-Run -arguments @('unknown') + $result | Should -Be -1 + } + + It "Should return -1 when an unknown command is provided" { + Mock Get-Scripts { return @{ 'cmd1:arg1' = 'command1'; 'cmd2:arg2' = 'command2' } } + + $result = Invoke-Run -arguments @('cmd1:arg1') + $result | Should -Be -1 + } + + It "Should return 0 when a valid script is provided" { + Mock Get-Scripts { return @{ 'test:cov' = 'test --coverage'; 'test:quiet' = 'test --verbosity=None' } } + + $result = Invoke-Run -arguments @('test:cov') + $result | Should -Be 0 + } +} diff --git a/tests/core/router.tests.ps1 b/tests/core/router.tests.ps1 index b928a9b..c7bfea1 100644 --- a/tests/core/router.tests.ps1 +++ b/tests/core/router.tests.ps1 @@ -22,6 +22,7 @@ Describe "Get-Actions Tests" { Mock Invoke-Cache { } Mock Invoke-Aliases { } Mock Invoke-Update { } + Mock Invoke-Run { } } It "Should return ordered hashtable with all actions" { @@ -44,6 +45,7 @@ Describe "Get-Actions Tests" { $actions.Keys | Should -Contain 'test' $actions.Keys | Should -Contain 'log' $actions.Keys | Should -Contain 'update' + $actions.Keys | Should -Contain 'run' } It "Should set script-level arguments variable" { @@ -174,6 +176,13 @@ Describe "Get-Actions Tests" { Should -Invoke Invoke-Update -Times 1 } + + It "Should execute run action" { + $actions = Get-Actions -arguments @() + $actions['run'].action.Invoke() + + Should -Invoke Invoke-Run -Times 1 + } } } diff --git a/tests/helpers/config.tests.ps1 b/tests/helpers/config.tests.ps1 index f2790dd..4dcf7cf 100644 --- a/tests/helpers/config.tests.ps1 +++ b/tests/helpers/config.tests.ps1 @@ -188,7 +188,7 @@ Describe "Set-Aliases-List" { Describe "Get-Aliases" { BeforeAll { New-Item -ItemType Directory -Force -Path $TEMPLATES_PATH | Out-Null - $testContent = [ordered]@{'?' = 'help'; 'i' = 'install'; 'init' = 'setup'} + $testContent = [ordered]@{'?' = 'help'; 'i' = 'install'; 'init' = 'setup'} $testContent | ConvertTo-Json -Depth 10 | Set-Content -Path $ALIASES_LIST_PATH $script:DEFAULT_ALIASES = $PVMConfig.defaults.aliases } @@ -222,6 +222,60 @@ Describe "Get-FlagMap" { } } +Describe "Set-Scripts-List" { + BeforeAll { + $script:TEMPLATES_PATH = $PVMConfig.paths.templates = 'TestDrive:\\storage\data\templates' + $PVMConfig.paths.scriptsList = "$TEMPLATES_PATH\scripts.json" + New-Item -ItemType Directory -Force -Path $script:TEMPLATES_PATH | Out-Null + $script:DEFAULT_SCRIPTS = $PVMConfig.defaults.scripts + } + + It "Creates scripts.json" { + $result = Set-Scripts-List + $result | Should -Be 0 + + $result = Get-Scripts + $result.Count | Should -Be $DEFAULT_SCRIPTS.Count + } + + It "Returns -1 when exception is thrown" { + Mock Set-Content { throw 'Test exception' } + $result = Set-Scripts-List + $result | Should -Be -1 + } +} + +Describe "Get-Scripts" { + BeforeAll { + $script:TEMPLATES_PATH = $PVMConfig.paths.templates = 'TestDrive:\\storage\data\templates' + $script:SCRIPTS_LIST_PATH = $PVMConfig.paths.scriptsList = "$TEMPLATES_PATH\scripts.json" + New-Item -ItemType Directory -Path $script:TEMPLATES_PATH | Out-Null + $testContent = [ordered]@{'test:quiet' = 'test --verbosity=None'; 'test:cov' = 'test --coverage=75'} + $testContent | ConvertTo-Json -Depth 10 | Set-Content -Path $SCRIPTS_LIST_PATH + $script:DEFAULT_SCRIPTS = $PVMConfig.defaults.scripts + } + + It "Returns scripts from scripts.json or PVMConfig.defaults.scripts" { + $result = Get-Scripts + $result.Count | Should -Be 2 + $result['test:quiet'] | Should -Be 'test --verbosity=None' + $result['test:cov'] | Should -Be 'test --coverage=75' + } + + It "Falls back to DEFAULT_SCRIPTS value" { + Remove-Item -Path "$script:TEMPLATES_PATH\scripts.json" + $result = Get-Scripts + $result.Count | Should -Be $DEFAULT_SCRIPTS.Count + } + + It "Returns default value when exception is thrown" { + Mock Is-File-Exists { return $true } + Mock Get-Content { throw 'Test exception' } + $result = Get-Scripts + $result.Count | Should -Be $DEFAULT_SCRIPTS.Count + } +} + Describe "Get-Config" { Context "When .env file exists" { BeforeAll {