diff --git a/.github/ISSUE_TEMPLATE/10-feature.yml b/.github/ISSUE_TEMPLATE/10-feature.yml index 9d090e2..19d6abb 100644 --- a/.github/ISSUE_TEMPLATE/10-feature.yml +++ b/.github/ISSUE_TEMPLATE/10-feature.yml @@ -8,16 +8,26 @@ body: - type: markdown attributes: value: | - Thank you for suggesting a new feature. Please provide as much - information as possible so we can understand your request better. + Describe what you need and the result you expect. + You do not need to propose a technical solution + or use a special format. + + See [issue planning][planning] and [issue routing][routing]. + + This form collects feedback. Maintainers record internal plans + in separate issues linked to the feedback. + + [planning]: https://msx.no/docs/Ways-of-Working/Issues/Process/Planning/ + [routing]: https://msx.no/docs/Ways-of-Working/Issues/Types/Hierarchy/ - type: textarea id: description attributes: - label: Description + label: Describe the need description: | - Please describe your idea/request - Feel free to add suggestions how you would like to see this - implemented. - Are there alternatives you've considered? + Describe what you need to do and who benefits. + Explain why this matters. Describe the problem or opportunity. + Describe the expected behavior. Give a short example of the situation, + the action you take, and the result you expect. + If a failure or limit changes what you expect, include an example. validations: required: true diff --git a/.github/tests/FeatureIntake.Tests.ps1 b/.github/tests/FeatureIntake.Tests.ps1 new file mode 100644 index 0000000..46d4a55 --- /dev/null +++ b/.github/tests/FeatureIntake.Tests.ps1 @@ -0,0 +1,119 @@ +#Requires -Version 7.4 +#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '6.0.0'; MaximumVersion = '6.*' } + +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseDeclaredVarsMoreThanAssignments', '', + Justification = 'Pester executes setup and test blocks in separate scopes.' +)] +[CmdletBinding()] +param() + +$ErrorActionPreference = 'Stop' + +BeforeAll { + $formPath = Join-Path -Path $PSScriptRoot -ChildPath '..\ISSUE_TEMPLATE\10-feature.yml' + $content = [IO.File]::ReadAllText($formPath) -replace '\r\n?', "`n" + $shape = @' +\A--- +# GitHub issue form syntax and schema\. +name: (?[^\n]+) +description: (?[^\n]+) +title: '(?[^'\n]+)' +labels: \['feature', 'triage'\] +body: + - type: markdown + attributes: + value: \| +(?<Guidance>(?: [^\n]*\n|\n)+) - type: textarea + id: description + attributes: + label: (?<Label>[^\n]+) + description: \| +(?<Prompt>(?: [^\n]*\n)+) validations: + required: true\n\z +'@ + $form = [regex]::Match($content, ($shape -replace '\r\n?', "`n")) + $introduction = $form.Groups['Guidance'].Value -replace '(?m)^ {8}', '' + $guidance = ($introduction -replace '\s+', ' ').Trim() + $prompt = ($form.Groups['Prompt'].Value -replace '\s+', ' ').Trim() + $configurationPath = Join-Path -Path $PSScriptRoot -ChildPath '..\ISSUE_TEMPLATE\config.yml' + $configuration = [IO.File]::ReadAllText($configurationPath) -replace '\r\n?', "`n" +} + +Describe 'Organization feature intake' { + It 'preserves the feature identity and triage routing without assigning a native type' { + $form.Groups['Name'].Value | Should -BeExactly "`u{1F680} [Feature]" + $form.Groups['Description'].Value | Should -BeExactly 'Suggest/request a new feature' + $form.Groups['Title'].Value | Should -BeExactly "`u{1F680}[Feature]: " + $content | Should -Match "(?m)^labels: \['feature', 'triage'\]$" + $content | Should -Not -Match '(?m)^type:' + } + + It 'retains the supported whole-form metadata and markdown and textarea structure' { + $form.Success | Should -BeTrue + $guidance | Should -Not -BeNullOrEmpty + $form.Groups['Label'].Value | Should -Not -BeNullOrEmpty + $prompt | Should -Not -BeNullOrEmpty + } + + It 'requires only the existing free-text description field' { + [regex]::Matches($content, '(?m)^ - type: (?!markdown$)').Count | Should -Be 1 + $content | Should -Match '(?m)^ id: description$' + $content | Should -Match '(?m)^ required: true$' + $form.Success | Should -BeTrue + } + + It 'keeps blank feedback intake available' { + $configuration | Should -BeExactly "blank_issues_enabled: true`n" + } + + It 'asks for the consumer need and who benefits' { + $form.Groups['Label'].Value | Should -BeExactly 'Describe the need' + $prompt | Should -Match ([regex]::Escape('Describe what you need to do and who benefits.')) + } + + It 'asks why the need matters' { + $prompt | Should -Match ([regex]::Escape('Explain why this matters. Describe the problem or opportunity.')) + } + + It 'asks for a plain-language situation, action, and expected result' { + $scenario = 'Describe the expected behavior. Give a short example of the situation, ' + + 'the action you take, and the result you expect.' + $prompt | Should -Match ([regex]::Escape($scenario)) + "$guidance $prompt" | Should -Not -Match '\bGherkin\b|\bGiven\s*/\s*When\s*/\s*Then\b' + } + + It 'asks for a failure or boundary example only when it matters' { + $boundary = 'If a failure or limit changes what you expect, include an example.' + $prompt | Should -Match ([regex]::Escape($boundary)) + } + + It 'links canonical planning and native routing instead of copying the guidance' { + $guidance | Should -Match ([regex]::Escape('See [issue planning][planning] and [issue routing][routing].')) + $planningLink = '[planning]: https://msx.no/docs/Ways-of-Working/Issues/Process/Planning/' + $guidance | Should -Match ([regex]::Escape($planningLink)) + $routingLink = '[routing]: https://msx.no/docs/Ways-of-Working/Issues/Types/Hierarchy/' + $guidance | Should -Match ([regex]::Escape($routingLink)) + + $html = (ConvertFrom-Markdown -InputObject $introduction).Html + $links = [regex]::Matches($html, '<a href="([^"]+)"') + $links.Count | Should -Be 2 + $links[0].Groups[1].Value | Should -BeExactly 'https://msx.no/docs/Ways-of-Working/Issues/Process/Planning/' + $links[1].Groups[1].Value | Should -BeExactly 'https://msx.no/docs/Ways-of-Working/Issues/Types/Hierarchy/' + } + + It 'keeps external feedback separate from internal planning work' { + $routing = 'This form collects feedback. Maintainers record internal plans ' + + 'in separate issues linked to the feedback.' + $guidance | Should -Match ([regex]::Escape($routing)) + } + + It 'does not require a technical solution or a special submission format' { + $reassurance = 'You do not need to propose a technical solution or use a special format.' + $guidance | Should -Match ([regex]::Escape($reassurance)) + } + + It 'does not solicit implementation choices or alternatives' { + $prompt | Should -Not -Match '\b(implement(?:ation|ed)?|alternatives?)\b' + } +}