Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ The full suite exercises real Visio COM calls with no mock/fake layer. CI runs o
- **Releases:** NuGet `VisioAutomation2010` **3.1.0** (tag `VisioAutomation_3.1.0`) and Visio PowerShell module **4.7.3** (tag `VisioPS_4.7.3`) shipped on 2026-09-30. The Release-build change for the NuGet flow ([#181](https://github.com/saveenr/VisioAutomation/issues/181)) is done and closed.
- **Repo:** `master` at `92dec356` (PRs up to #227 merged), no open PRs, working trees clean. The older `claude/*` and `experiment/linq-shapesheet` branches are leftovers from earlier sessions.
- **Unreleased changes** (`NuGet/CHANGELOG.md` and the module's `CHANGELOG.md`, `[Unreleased]`; `Get-VisioPage -ID` is now a real page ID and `-Index` takes positions, [#232](https://github.com/saveenr/VisioAutomation/issues/232)): the first tier of [#225](https://github.com/saveenr/VisioAutomation/issues/225) is Added (more directed graph XML options: `<documentoptions>`, shape `width`/`height`, `<hyperlink>`, `<cells>`, typed `<customprop>`, and per-connector `connectortype`, `<cells>` and `<customprop>`); the rest is Fixed: `DrawDataTable` honors widths and heights, `DrawDataTableModel` draws on the target page, `DrawXmlModel` roots the tree at the document element and now has an undo scope, `DrawDirectedGraphDocument` honors its `DirectedGraphStyling`, and `DrawOrgChart` no longer resizes the page you pass it. The next version is not decided; these are fixes only, so a patch release is the likely fit.
- **Tests:** 337 tests across four projects (VTest 108, VTest.Models 104, VTest.Scripting 43, VTest.PowerShell 82). The last full Release run (256) was on 2026-09-30; the newest tests are the regression tests for [#197](https://github.com/saveenr/VisioAutomation/issues/197), [#222](https://github.com/saveenr/VisioAutomation/issues/222) and [#219](https://github.com/saveenr/VisioAutomation/issues/219), the 23 tests in `DirectedGraphXmlOptionsTests` for [#225](https://github.com/saveenr/VisioAutomation/issues/225), and the 46 cmdlet-binding tests of the second slice of [#173](https://github.com/saveenr/VisioAutomation/issues/173).
- **Tests:** 344 tests across four projects (VTest 108, VTest.Models 104, VTest.Scripting 48, VTest.PowerShell 84). The last full Release run (256) was on 2026-09-30; the newest tests are the regression tests for [#197](https://github.com/saveenr/VisioAutomation/issues/197), [#222](https://github.com/saveenr/VisioAutomation/issues/222) and [#219](https://github.com/saveenr/VisioAutomation/issues/219), the 23 tests in `DirectedGraphXmlOptionsTests` for [#225](https://github.com/saveenr/VisioAutomation/issues/225), and the 46 cmdlet-binding tests of the second slice of [#173](https://github.com/saveenr/VisioAutomation/issues/173).
- **Docs:** the .NET GitBook's Diagram models section was reorganized on 2026-10-01 (docs repo PRs 8 and 9, live). Every model page now has a "Where the output goes" section; the DOM page has a render performance page beneath it. The 2026-09 entries on the docs-updates page record each change. The "Models docs follow-ups" entry in [`docs/futures/docs.md`](docs/futures/docs.md) lists which doc statements to revisit when each source issue below is decided, and the release-gated "unreleased" notes to turn into version statements at the next release. The [#219](https://github.com/saveenr/VisioAutomation/issues/219) fix and the new directed graph XML options ([#225](https://github.com/saveenr/VisioAutomation/issues/225)) are described in the docs as unreleased changes after 3.1.0; the fixes for [#197](https://github.com/saveenr/VisioAutomation/issues/197) and [#222](https://github.com/saveenr/VisioAutomation/issues/222) are not yet described.
- **Docs conventions:** no em-dashes in either GitBook. The docs repos publish when a PR merges to `main`, and each PR gets a GitBook preview. Cite the issue number on any page that describes behavior an open issue may change.
- **Docs viewer:** the local GitBook viewer now lives in its own private repo, `TheSevenPens/LocalGitbookviewer` (cloned at `C:/Users/savee/Documents/GitHub/LocalGitbookviewer`). Run `view.cmd <docs folder>` from that checkout. Its backlog is in that repo's `BACKLOG.md`.
Expand Down Expand Up @@ -187,7 +187,7 @@ Roughly in the order worth doing. Pick from the top. ([#219](https://github.com/

1. **Decide the three public types with no real caller, together:** [#218](https://github.com/saveenr/VisioAutomation/issues/218) Box layout, [#220](https://github.com/saveenr/VisioAutomation/issues/220) form page model, [#221](https://github.com/saveenr/VisioAutomation/issues/221) `ContainerLayout` (draws plain rectangles; the `ContainerMaster` option is never read). The options are in each issue; the docs side of each is listed in the "Models docs follow-ups" backlog entry.
2. **Cut the next release when ready.** Versions are not decided (fixes only, so likely 3.1.1 and a PS module patch). The flow is in [`docs/futures/releases.md`](docs/futures/releases.md). At release time also turn the "unreleased" notes in both GitBooks into version statements and add the new versions to both version-compatibility pages.
3. **Design discussions, not yet quick:** [#223](https://github.com/saveenr/VisioAutomation/issues/223) `client.Model` draw methods are inconsistent, [#225](https://github.com/saveenr/VisioAutomation/issues/225) the rest of the directed graph XML options (the first tier shipped in the unreleased changes; per-edge master and stencil, renderer and styling from XML, and an XSD remain), [#229](https://github.com/saveenr/VisioAutomation/issues/229) `NewDocumentFromTemplate` does not base the document on the template (affects `New-VisioDocument -Template`), and [#198](https://github.com/saveenr/VisioAutomation/issues/198) / [#199](https://github.com/saveenr/VisioAutomation/issues/199) drawing a single directed graph onto the current page.
3. **Design discussions, not yet quick:** [#223](https://github.com/saveenr/VisioAutomation/issues/223) `client.Model` draw methods are inconsistent, [#225](https://github.com/saveenr/VisioAutomation/issues/225) the rest of the directed graph XML options (the first tier shipped in the unreleased changes; per-edge master and stencil, renderer and styling from XML, and an XSD remain), and [#198](https://github.com/saveenr/VisioAutomation/issues/198) / [#199](https://github.com/saveenr/VisioAutomation/issues/199) drawing a single directed graph onto the current page.
4. **Earlier backlog:** [#191](https://github.com/saveenr/VisioAutomation/issues/191) migrate cmdlets to `ThrowTerminatingError` (makes `InvokeScriptStrict` redundant), [#190](https://github.com/saveenr/VisioAutomation/issues/190) the `VTest.PowerShell` two-Client mismatch, [#183](https://github.com/saveenr/VisioAutomation/issues/183) VisioScripting dead-method removal (CY27Q1; re-verify zero callers first), [#173](https://github.com/saveenr/VisioAutomation/issues/173) more cmdlet-binding tests (the second slice covered 13 cmdlets; roughly 40 are still untested, see `docs/futures/test-coverage-gaps.md`), [#163](https://github.com/saveenr/VisioAutomation/issues/163) and [#164](https://github.com/saveenr/VisioAutomation/issues/164) (the `PSCmdlet` question; see the memory note on why cmdlets derive from `Cmdlet`).

**Calendar-bound**
Expand Down
3 changes: 2 additions & 1 deletion NuGet/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ The format follows [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/

### Added
- More of the directed graph model can now be set from the directed graph XML format. Every addition is optional, so existing XML loads as before. Part of [#225](https://github.com/saveenr/VisioAutomation/issues/225):
- `<documentoptions template="..." borderwidth="..." borderheight="..." />` as a child of `<directedgraph>` sets `DirectedGraphDocument.Template` and `BorderSize`. If only one border attribute is given, the other keeps its default. (`Template` is passed to `NewDocumentFromTemplate`, which currently opens the template as a docked stencil instead of basing the drawing on it; see [#229](https://github.com/saveenr/VisioAutomation/issues/229).)
- `<documentoptions template="..." borderwidth="..." borderheight="..." />` as a child of `<directedgraph>` sets `DirectedGraphDocument.Template` and `BorderSize`. If only one border attribute is given, the other keeps its default.
- On `<shape>`: `width` and `height` (both or neither) set the node `Size`; `<hyperlink name address subaddress description />` children add hyperlinks (the `url` attribute's link stays first); a `<cells><cell name="..." value="..." /></cells>` child sets any cell of `VisioAutomation.Models.Dom.ShapeCells` by property name (for example `FillForeground`, `LineColor`, `CharSize`); and `<customprop>` takes optional `type` (`string`, `number`, `boolean` or `date`), `label`, `prompt` and `format` attributes.
- On `<connector>`: a per-edge `connectortype` that overrides the page setting, a `<cells>` child (explicit cells win over the `color`, `weight` and arrow defaults), and `<customprop>` children.

### Fixed
- `Client.Document.NewDocumentFromTemplate` now creates the drawing from the template (Visio's `AddEx` with no flags): the drawing gets the template's page setup, styles and settings, and the stencils in the template's workspace are opened and docked. Previously it created a blank drawing and opened the template as a separate docked stencil (an empty one in Visio 2013 and later), so the drawing was not based on the template. This is what `New-VisioDocument -Template` and a directed graph document's `Template` (the XML `<documentoptions template="..." />`) use. A null, empty or whitespace template still gives a blank drawing; a template file that does not exist throws without leaving a stray blank drawing; and passing a stencil (`.vss` or `.vssx`) now throws an `ArgumentException` that says so, before anything is created (it used to fail with an opaque COM error after leaving a blank drawing open). Closes [#229](https://github.com/saveenr/VisioAutomation/issues/229).
- `Client.Page.SetFormatCells` now writes the page format cells to the page sheet. Previously it committed them to the `Page` object, which always threw a `COMException`, so `Format-VisioPage -Width` / `-Height` could not work. Found by the cmdlet-binding tests ([#173](https://github.com/saveenr/VisioAutomation/issues/173)).
- `HyperlinkHelper.Add` (and so `Client.Hyperlink.AddHyperlink`) now applies the `NewWindow`, `Default`, `Invisible` and `SortKey` cells of the `HyperlinkCells` it is given. Previously it ignored them, so the cells always stayed `FALSE` (or unset).
- `Client.Model.DrawOrgChart`, and so `Out-VisioApplication -OrgChart`, no longer resizes the page passed as its `TargetPage`. The chart is drawn in a new document, and each of its pages is already sized by the render, but the call also resized the caller's own page to fit its contents, which shrank that page for no reason. The `TargetPage` now only supplies the application. Closes [#219](https://github.com/saveenr/VisioAutomation/issues/219).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,19 +296,20 @@ public void Draw_DocumentBorderFromXml_ChangesThePageSize()
}

[MUT.TestMethod]
public void Draw_DocumentTemplateFromXml_IsAcceptedAndTheGraphStillDraws()
public void Draw_DocumentTemplateFromXml_CreatesTheDrawingFromThatTemplate()
{
// DirectedGraphDocument.Template goes to NewDocumentFromTemplate, which currently opens the template as a
// docked stencil and returns a blank document instead of basing the document on it (#229). This test only
// checks that the option is accepted and the graph is still drawn.
// Regression (#229): the template used to be opened as a docked stencil beside a blank drawing,
// so the drawing was not based on it.
var client = this.GetScriptingClient();
string template = client.Application.ApplicationVersion.Major >= 15 ? "basflo_u.vstx" : "basflo_u.vst";

var dgdoc = this.draw(this.build_xml(root_children: "<documentoptions template='" + template + "' />"));

var shape = dgdoc.Layouts[0].Nodes["n1"].VisioShape;
MUT.Assert.IsNotNull(shape);
shape.ContainingPage.Document.Close(true);
var doc = shape.ContainingPage.Document;
MUT.Assert.IsTrue(doc.Template.ToLowerInvariant().Contains("basflo_u"), "the drawing should be based on the template: '" + doc.Template + "'");
doc.Close(true);
}

private double draw_and_get_page_width(string xml)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,26 @@ public void GetVisioDocument_ActiveDocumentWithName_FailsToResolveAParameterSet(
MUT.StringAssert.Contains(CmdletScriptExtensions.MessageOf(ex), "Parameter set cannot be resolved");
}

// -- New-VisioDocument: -Template creates the drawing from the template (#229) ---------------

[MUT.TestMethod]
public void NewVisioDocument_Template_CreatesTheDrawingFromThatTemplate()
{
var results = DocumentWindowCmdletBindingTests.Session.InvokeScriptStrict<string>(
"$ver = (Get-VisioApplication).Version; " +
"$name = if ([int]($ver -split '[.,]')[0] -ge 15) { 'basflo_u.vstx' } else { 'basflo_u.vst' }; " +
"$d = New-VisioDocument -Template $name; " +
"try { $d.Template } finally { try { $d.Saved = $true; $d.Close() } catch { } }");
MUT.Assert.IsTrue(results.Single().ToLowerInvariant().Contains("basflo_u"), "the new drawing should be based on the template: '" + results.Single() + "'");
}

[MUT.TestMethod]
public void NewVisioDocument_TemplateThatIsAStencil_FailsWithAClearMessage()
{
var ex = DocumentWindowCmdletBindingTests.Session.ExpectFailureInNewDocument("New-VisioDocument -Template 'basic_u.vss'");
MUT.StringAssert.Contains(CmdletScriptExtensions.MessageOf(ex), "is a stencil, not a template");
}

// -- Format-VisioWindow: three parameter sets ----------------------------------

[MUT.TestMethod]
Expand Down
108 changes: 108 additions & 0 deletions VisioAutomation_2010/VTest.Scripting/DocumentTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using MUT=Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using VisioAutomation.Extensions;

namespace VTest.Scripting
Expand Down Expand Up @@ -28,5 +29,112 @@ public void ActivateDocument_AmongMultipleOpenDocs_UpdatesActiveDocument()
doc2.Close(true);
doc3.Close(true);
}

// -- NewDocumentFromTemplate (#229) ------------------------------------------------------
// Visio 2013 and later ship XML templates (.vstx); older versions ship binary ones (.vst).

private static string flowchart_template(VisioScripting.Client client)
{
return client.Application.ApplicationVersion.Major >= 15 ? "basflo_u.vstx" : "basflo_u.vst";
}

private static int count_empty_stencils(Microsoft.Office.Interop.Visio.Application app)
{
// A document that is a stencil and holds no masters is the stray window the old code left behind
return app.Documents.Cast<Microsoft.Office.Interop.Visio.Document>().Count(
d => d.Type == Microsoft.Office.Interop.Visio.VisDocumentTypes.visTypeStencil && d.Masters.Count == 0);
}

[MUT.TestMethod]
public void NewDocumentFromTemplate_RealTemplate_CreatesTheDocumentFromTheTemplate()
{
// Regression: the template was opened as a docked stencil next to a blank drawing, so the
// drawing was not based on it and an empty stencil was left open.
var client = this.GetScriptingClient();
var app = client.Application.GetApplication();
int empty_stencils_before = count_empty_stencils(app);

var doc = client.Document.NewDocumentFromTemplate(flowchart_template(client));

MUT.Assert.AreEqual(Microsoft.Office.Interop.Visio.VisDocumentTypes.visTypeDrawing, doc.Type);
MUT.Assert.IsTrue(doc.Template.ToLowerInvariant().Contains("basflo_u"), "the drawing should be based on the template: '" + doc.Template + "'");
MUT.Assert.AreEqual(empty_stencils_before, count_empty_stencils(app), "no empty stencil should be left open");

doc.Close(true);
}

[MUT.TestMethod]
public void NewDocumentFromTemplate_NullTemplate_CreatesABlankDrawing()
{
var client = this.GetScriptingClient();
var app = client.Application.GetApplication();
int docs_before = app.Documents.Count;

var doc = client.Document.NewDocumentFromTemplate(null);

MUT.Assert.AreEqual(Microsoft.Office.Interop.Visio.VisDocumentTypes.visTypeDrawing, doc.Type);
MUT.Assert.AreEqual(string.Empty, doc.Template);
MUT.Assert.AreEqual(docs_before + 1, app.Documents.Count, "exactly one document should be added");

doc.Close(true);
}

[MUT.TestMethod]
public void NewDocumentFromTemplate_EmptyOrWhitespaceTemplate_CreatesABlankDrawing()
{
var client = this.GetScriptingClient();
var app = client.Application.GetApplication();

foreach (string template in new[] { "", " " })
{
int docs_before = app.Documents.Count;
var doc = client.Document.NewDocumentFromTemplate(template);

MUT.Assert.AreEqual(Microsoft.Office.Interop.Visio.VisDocumentTypes.visTypeDrawing, doc.Type);
MUT.Assert.AreEqual(docs_before + 1, app.Documents.Count, "exactly one document should be added for '" + template + "'");

doc.Close(true);
}
}

[MUT.TestMethod]
public void NewDocumentFromTemplate_StencilFile_ThrowsAndCreatesNoDocument()
{
// A stencil is not a template. The old code threw an opaque COM error after leaving a blank drawing open.
var client = this.GetScriptingClient();
var app = client.Application.GetApplication();

foreach (string stencil in new[] { "basic_u.vss", "BASIC_U.VSSX", @"C:\Stencils\mine.vssx" })
{
int docs_before = app.Documents.Count;

var ex = MUT.Assert.ThrowsExactly<System.ArgumentException>(
() => client.Document.NewDocumentFromTemplate(stencil));

MUT.StringAssert.Contains(ex.Message.ToLowerInvariant(), "stencil");
MUT.Assert.AreEqual(docs_before, app.Documents.Count, "no document should be created for '" + stencil + "'");
}
}

[MUT.TestMethod]
public void NewDocumentFromTemplate_MissingTemplate_ThrowsAndCreatesNoDocument()
{
var client = this.GetScriptingClient();
var app = client.Application.GetApplication();
int docs_before = app.Documents.Count;

bool threw = false;
try
{
client.Document.NewDocumentFromTemplate("no_such_template_for_vtest.vstx");
}
catch (System.Exception)
{
threw = true;
}

MUT.Assert.IsTrue(threw, "a template that does not exist should throw");
MUT.Assert.AreEqual(docs_before, app.Documents.Count, "no stray blank drawing should be left open");
}
}
}
Loading
Loading