Skip to content

Commit e619116

Browse files
committed
fix: align example scaffold tests with source coverage
1 parent b6167db commit e619116

15 files changed

Lines changed: 170 additions & 53 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
2222
- The command cheat sheet now stacks the three "Set up a project" cards vertically so the section reads top-to-bottom without a missing grid slot.
2323
- The Agentic Copilot starter shipped by `nova init` no longer tells generated projects to use a `run.ps1` quality wrapper that the template does not include.
2424
- The scaffold now points generated documentation and agent guidance to the repository quality loop wording plus the existing analyzer and `Test-NovaBuild` entrypoints instead.
25+
- `nova init --example` now ships the packaged example with source-mirrored tests and `Pester.CodeCoverage.Enabled = true` by default.
26+
- The example scaffold now runs `Test-NovaBuild` against `src/**/*.ps1` without requiring a prior build, and the bundled docs describe that source-first coverage flow.
2527

2628
### Security
2729

RELEASE_NOTE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This file summarizes the release notes for NovaModuleTools. **UNRELEASED** chang
1515
### Fixed
1616

1717
- The Agentic Copilot starter shipped by `nova init` no longer tells generated projects to use a `run.ps1` quality wrapper that the template does not include.
18+
- `nova init --example` now ships the packaged example with source-mirrored tests and code coverage enabled by default, so `Test-NovaBuild` works before a build and measures `src/**/*.ps1`.
1819

1920
### Security
2021

docs/NovaModuleTools/en-US/Initialize-NovaModule.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ PS> Initialize-NovaModule [-Path <string>] [-Example] [-WhatIf] [-Confirm] [<Com
2727

2828
`Initialize-NovaModule` creates a new project folder, the standard `src/` layout, and a starter `project.json` file.
2929

30-
The starter `project.json` includes Nova's standard Pester defaults, including an opt-in `CodeCoverage` block with `Enabled=false`, common `src/` coverage paths, JaCoCo output in `artifacts/coverage.xml`, and a `90` percent target that can be enabled later.
30+
The standard scaffold `project.json` includes Nova's standard Pester defaults, including an opt-in `CodeCoverage` block with `Enabled=false`, common `src/` coverage paths, JaCoCo output in `artifacts/coverage.xml`, and a `90` percent target that can be enabled later.
3131

3232
The command collects project details interactively, including the module name, description, version, author, minimum PowerShell version, Git initialization, and an optional Agentic Copilot starter package. Before the first interactive question, Nova checks whether a newer `NovaModuleTools` release is available and warns without blocking the scaffold flow. When Git initialization is enabled, Nova also creates or updates a default `.gitignore` in the generated project root so common local and CI artifact paths such as `artifacts/`, `dist/`, `run.ps1`, and `reload.ps1` are ignored from the start. Existing `.gitignore` files are preserved and only receive the missing Nova-managed entries. When you enable the Agentic package, Nova also asks for a project short name used in generated guidance placeholders such as `Invoke-<ShortName>*`; for NovaModuleTools the short name is `Nova`, but it could also have been
3333
`NMT`. The Agentic package follows Nova's maintained agentic guidance through a filtered starter mirror, including Nova build/test/package expectations, `project.json`
@@ -40,7 +40,7 @@ Use this command when you want to start a new module in the NovaModuleTools stru
4040

4141
Use `-Path` when you want to create the project under a specific base directory. Positional path syntax is no longer supported.
4242

43-
Use `-Example` when you want the scaffold to start from the packaged example project instead of the minimal default layout. The example flow keeps the example source, resource, and test files, skips the Pester enable/disable question, and applies the interactive metadata values to the copied `project.json`, including the same disabled-by-default `CodeCoverage` block that the standard scaffold uses. The standard and example flows share the same upfront Nova update warning, inline validation and retry behavior for interactive answers, and optional Agentic Copilot setup prompt after the Git question. When Git is enabled in either flow, Nova also ensures the generated project root has the default `.gitignore` entries for local and CI artifacts without overwriting any existing `.gitignore` content. When you answer
43+
Use `-Example` when you want the scaffold to start from the packaged example project instead of the minimal default layout. The example flow keeps the example source, resource, and source-mirrored test files, skips the Pester enable/disable question, and applies the interactive metadata values to the copied `project.json`, including an enabled-by-default `CodeCoverage` block that targets `src/**/*.ps1`. The packaged example can run `Test-NovaBuild` before `Invoke-NovaBuild`, and the same test flow writes JaCoCo coverage to `artifacts/coverage.xml`. The standard and example flows share the same upfront Nova update warning, inline validation and retry behavior for interactive answers, and optional Agentic Copilot setup prompt after the Git question. When Git is enabled in either flow, Nova also ensures the generated project root has the default `.gitignore` entries for local and CI artifacts without overwriting any existing `.gitignore` content. When you answer
4444
`Yes`, Nova asks for the short project name, adds the same starter package to either scaffold style, and merges the example README instead of replacing it with the generic starter README outright.
4545

4646
This command supports `-WhatIf` and `-Confirm` through PowerShell `SupportsShouldProcess`. Use `-WhatIf` to preview the scaffold target after the interactive answers have been collected, without creating folders, writing `project.json`, or initializing Git.
@@ -142,7 +142,7 @@ This cmdlet does not emit an output object.
142142
143143
## NOTES
144144
145-
Generated projects start with NovaModuleTools defaults for recursive discovery, source markers, duplicate-function validation, an opt-in Pester `CodeCoverage` block (`Enabled=false`, shared `src/` paths, JaCoCo output in `artifacts/coverage.xml`, and a `90` percent target), and—when Git is enabled—a default `.gitignore` for common local and CI artifact paths. The packaged example scaffold keeps its bundled example source and tests, while still updating prompt-driven metadata such as project name, description, version, author, and PowerShell host version. The optional Agentic Copilot starter package adds repository-local Copilot workflow files under the project root and `.github/` that tell agents to keep build/test/package workflows on Nova, treat `project.json` `Manifest.PowerShellHostVersion` as the PowerShell compatibility target, run ScriptAnalyzer before build/test through the repo-standard `Invoke-ScriptAnalyzerCI.ps1` /
145+
Generated projects start with NovaModuleTools defaults for recursive discovery, source markers, duplicate-function validation, and—when Git is enabled—a default `.gitignore` for common local and CI artifact paths. The standard scaffold keeps Pester `CodeCoverage` opt-in (`Enabled=false`, shared `src/` paths, JaCoCo output in `artifacts/coverage.xml`, and a `90` percent target). The packaged example scaffold keeps its bundled example source and source-mirrored tests, while also enabling that same `CodeCoverage` block by default so `Test-NovaBuild` measures `src/**/*.ps1` without requiring a prior build. Both scaffold styles still update prompt-driven metadata such as project name, description, version, author, and PowerShell host version. The optional Agentic Copilot starter package adds repository-local Copilot workflow files under the project root and `.github/` that tell agents to keep build/test/package workflows on Nova, treat `project.json` `Manifest.PowerShellHostVersion` as the PowerShell compatibility target, run ScriptAnalyzer before build/test through the repo-standard `Invoke-ScriptAnalyzerCI.ps1` /
146146
`run.ps1` workflow, fix any ScriptAnalyzer findings before handoff, run project tests through `Test-NovaBuild` instead of direct `Invoke-Pester`, leave `.psm1` / `.psd1` files to generated
147147
`dist` output, generate and validate command help with the Microsoft.PowerShell.PlatyPS cmdlets instead of hand-written Markdown structure, require a matching help file for every new public entry point in the same change, mirror new tests to source files, and add a text-file-formatting guardrail helper plus a Pester guardrail test when project tests are enabled.
148148

docs/commands.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,9 @@ <h3>Start a new project</h3>
179179
<li><strong>Use when:</strong> you are creating a fresh NovaModuleTools project</li>
180180
<li><strong>Includes:</strong> a pre-question Nova release warning, best-effort
181181
<code>.gitignore</code> creation or update when Git is enabled, and Nova's default
182-
<code>Pester.CodeCoverage</code> block in <code>project.json</code> with coverage still
183-
disabled until you choose to turn it on</li>
182+
<code>Pester.CodeCoverage</code> block in <code>project.json</code>; the minimal
183+
scaffold keeps coverage opt-in, while the packaged example scaffold starts with coverage
184+
enabled against <code>src/**/*.ps1</code></li>
184185
<li><strong>Optional:</strong> add the <strong>Agentic Copilot</strong> starter package for Nova-maintained
185186
build, test, analyzer, help, and guidance files; if you enable it, Nova also asks for a
186187
short project name for placeholders such as <code>Invoke-&lt;ShortName&gt;*</code></li>

docs/core-workflows.html

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,11 @@ <h2>Create the project</h2>
147147
<code>Test-MarkdownCommandHelp</code>, with a matching help file required for every new public
148148
entry point, and a strict file-ending rule for changed or generated text files.</p>
149149
<p>The generated <code>project.json</code> also starts with Nova's standard <code>Pester</code>
150-
defaults, including a disabled <code>CodeCoverage</code> block with explicit source coverage paths
151-
for <code>src/public/</code>, nested <code>src/private/</code> helper folders, and
152-
<code>src/classes/</code>, so you can enable coverage later without hand-authoring the structure.</p>
150+
defaults, including explicit source coverage paths for <code>src/public/</code>, nested
151+
<code>src/private/</code> helper folders, and <code>src/classes/</code>. The minimal scaffold keeps
152+
<code>CodeCoverage</code> disabled until you opt in, while the packaged example scaffold enables it
153+
by default so <code>Test-NovaBuild</code> can measure <code>src/**/*.ps1</code> without requiring a
154+
prior build.</p>
153155
<p>When you answer <code>Yes</code>, Nova also asks for a short project name used in generated guidance
154156
placeholders such as <code>Invoke-&lt;ShortName&gt;*</code>. For NovaModuleTools the short name is
155157
<code>Nova</code>, but it could also have been <code>NMT</code>.</p>

docs/getting-started.html

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,12 @@ <h2>4. Scaffold the packaged example</h2>
218218
entry point, and a strict file-ending rule for changed or generated text files.</p>
219219
<p>Nova then creates a project folder under the path you selected.</p>
220220
<p>The generated <code>project.json</code> also starts with Nova's standard <code>Pester</code>
221-
defaults, including an opt-in <code>CodeCoverage</code> block with <code>Enabled=false</code>,
222-
explicit source coverage paths for <code>src/public/</code>, nested <code>src/private/</code>
223-
helper folders, and <code>src/classes/</code>, JaCoCo output in <code>artifacts/coverage.xml</code>,
224-
and a <code>90</code> percent target you can enable later.</p>
221+
defaults, including explicit source coverage paths for <code>src/public/</code>, nested
222+
<code>src/private/</code> helper folders, and <code>src/classes/</code>, JaCoCo output in
223+
<code>artifacts/coverage.xml</code>, and a <code>90</code> percent target. The minimal scaffold
224+
keeps that <code>CodeCoverage</code> block opt-in with <code>Enabled=false</code>, while the
225+
packaged example scaffold enables coverage by default so <code>Test-NovaBuild</code> measures
226+
<code>src/**/*.ps1</code> before any build step.</p>
225227
<p>If you enable the Agentic package, Nova also asks for a short project name used in generated guidance
226228
placeholders such as <code>Invoke-&lt;ShortName&gt;*</code>. For NovaModuleTools the short name is
227229
<code>Nova</code>, but it could also have been <code>NMT</code>.</p>

docs/project-json-reference.html

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -675,10 +675,11 @@ <h2><code>Pester</code> settings</h2>
675675
</table>
676676
</div>
677677
<p class="tiny-note">Starter projects now include the full <code>Pester.CodeCoverage</code> block with
678-
<code>Enabled=false</code> and the explicit default source globs shown above, so turning coverage on
679-
usually means changing only that switch. At runtime, <code>Test-NovaBuild</code> also lets you
680-
override verbosity and render mode temporarily with <code>-OutputVerbosity</code> and
681-
<code>-OutputRenderMode</code>.</p>
678+
the explicit default source globs shown above. The minimal scaffold keeps
679+
<code>Enabled=false</code>, while the packaged example scaffold sets <code>Enabled=true</code> so
680+
coverage works against <code>src/**/*.ps1</code> immediately. At runtime,
681+
<code>Test-NovaBuild</code> also lets you override verbosity and render mode temporarily with
682+
<code>-OutputVerbosity</code> and <code>-OutputRenderMode</code>.</p>
682683
<p>When you enable coverage, Nova writes JaCoCo output to <code>artifacts/coverage.xml</code> and fails
683684
the test workflow if the measured percentage is lower than
684685
<code>Pester.CodeCoverage.CoveragePercentTarget</code>.</p>
@@ -698,8 +699,9 @@ <h3>Minimal module project</h3>
698699
}
699700
}</code></pre>
700701
<p>Use this when you want the smallest hand-authored Nova project. Scaffolded projects start with
701-
additional <code>Pester</code> defaults, including a disabled <code>CodeCoverage</code> block, but
702-
Nova can still resolve its normal defaults when those sections are omitted.</p>
702+
additional <code>Pester</code> defaults; the minimal scaffold keeps <code>CodeCoverage</code>
703+
disabled until you opt in, while the packaged example scaffold enables it by default. Nova can
704+
still resolve its normal defaults when those sections are omitted.</p>
703705

704706
<h3>Create both NuGet and Zip artifacts, plus latest aliases</h3>
705707
<pre><code>{

src/resources/example/README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ It is meant to help a new user understand the smallest useful setup that can:
77
- build a module
88
- include a private helper
99
- include a resource file
10-
- run Pester tests against the built module
10+
- run Pester tests against source files with code coverage enabled
1111
- show the full `project.json` configuration surface in one place
1212

1313
## What is in this example?
@@ -22,7 +22,8 @@ It is meant to help a new user understand the smallest useful setup that can:
2222
- `src/public/Get-ExampleGreeting.ps1` – a public function exported from the built module
2323
- `src/private/Get-ExampleConfiguration.ps1` – a private helper used by the public function
2424
- `src/resources/greeting-config.json` – a resource file bundled into the built module
25-
- `tests/Pester.Some.Tests.ps1` – tests that import the built module and verify its behavior
25+
- `tests/public/Get-ExampleGreeting.Tests.ps1` – source-mirrored tests for the public function
26+
- `tests/private/Get-ExampleConfiguration.Tests.ps1` – source-mirrored tests for the private helper and resource resolution
2627

2728
## Quick start
2829

@@ -35,8 +36,8 @@ If `./dist/NovaModuleTools` is not available yet, build `NovaModuleTools` from t
3536
```text
3637
PS> Import-Module ./dist/NovaModuleTools -Force
3738
PS> Set-Location ./src/resources/example
38-
PS> Invoke-NovaBuild
3939
PS> Test-NovaBuild
40+
PS> Invoke-NovaBuild
4041
PS> New-NovaModulePackage
4142
PS> $project = Get-NovaProjectInfo
4243
PS> Import-Module $project.OutputModuleDir -Force
@@ -51,8 +52,8 @@ PS> Install-Module NovaModuleTools
5152
PS> Import-Module NovaModuleTools
5253
PS> $module = Get-Module NovaModuleTools -ListAvailable | Select-Object -First 1
5354
PS> Set-Location (Join-Path $module.ModuleBase 'resources/example')
54-
PS> Invoke-NovaBuild
5555
PS> Test-NovaBuild
56+
PS> Invoke-NovaBuild
5657
PS> New-NovaModulePackage
5758
PS> $project = Get-NovaProjectInfo
5859
PS> Import-Module $project.OutputModuleDir -Force
@@ -61,6 +62,12 @@ PS> Get-ExampleGreeting
6162

6263
## Expected result
6364

65+
After `Test-NovaBuild`, the example tests run against `src/**/*.ps1` and JaCoCo coverage is written to:
66+
67+
```text
68+
src/resources/example/artifacts/coverage.xml
69+
```
70+
6471
After `Invoke-NovaBuild`, the built module is written to:
6572

6673
```text
@@ -112,7 +119,7 @@ This example is intentionally small, but it demonstrates the most important Nova
112119
- how module-level preamble lines from `project.json` are emitted before the generated source markers and module code
113120
- how public and private functions are combined into one module
114121
- how resource files are copied and used at runtime
115-
- how tests should import the built module from `dist/`
122+
- how tests can run against source files before a build while still producing coverage
116123
- where the current package, packaging, and raw-upload configuration keys live in `project.json`
117124

118125
If you want a new project scaffold, use `PS> Initialize-NovaModule` (`% nova init`). If you want a concrete project you can inspect, run, or copy through `% nova init --example` / `% nova init -e`, use this example folder.

src/resources/example/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
},
8484
"Pester": {
8585
"CodeCoverage": {
86-
"Enabled": false,
86+
"Enabled": true,
8787
"Path": [
8888
"src/public/*.ps1",
8989
"src/private/*.ps1",

src/resources/example/src/private/Get-ExampleConfiguration.ps1

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
1-
function Get-ExampleConfiguration {
1+
function Get-ExampleConfigurationPath {
22
[CmdletBinding()]
3-
param()
3+
param(
4+
[string]$BasePath = $PSScriptRoot
5+
)
6+
7+
$candidatePathList = @(
8+
[System.IO.Path]::GetFullPath((Join-Path $BasePath 'resources/greeting-config.json'))
9+
[System.IO.Path]::GetFullPath((Join-Path $BasePath '../resources/greeting-config.json'))
10+
) | Select-Object -Unique
411

5-
$configurationPath = Join-Path $PSScriptRoot 'resources/greeting-config.json'
6-
if (-not (Test-Path -LiteralPath $configurationPath)) {
7-
Stop-NovaOperation -Message "Example configuration not found: $configurationPath" -ErrorId 'Nova.Environment.ExampleConfigurationNotFound' -Category ObjectNotFound -TargetObject $configurationPath
12+
foreach ($candidatePath in $candidatePathList) {
13+
if (Test-Path -LiteralPath $candidatePath) {
14+
return $candidatePath
15+
}
816
}
917

18+
Stop-NovaOperation -Message "Example configuration not found. Checked: $( $candidatePathList -join ', ' )" -ErrorId 'Nova.Environment.ExampleConfigurationNotFound' -Category ObjectNotFound -TargetObject 'resources/greeting-config.json'
19+
}
20+
21+
function Get-ExampleConfiguration {
22+
[CmdletBinding()]
23+
param()
24+
25+
$configurationPath = Get-ExampleConfigurationPath
1026
$configuration = Get-Content -LiteralPath $configurationPath -Raw | ConvertFrom-Json
1127
return [pscustomobject]@{
1228
GreetingPrefix = $configuration.GreetingPrefix

0 commit comments

Comments
 (0)