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
48 changes: 21 additions & 27 deletions plan.md

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.12" />
<PackageVersion Include="Markdig" Version="1.4.0" />
<PackageVersion Include="TUnit" Version="1.70.1" />
<PackageVersion Include="Verify" Version="33.1.5" />
<PackageVersion Include="Verify.TUnit" Version="33.1.5" />
<PackageVersion Include="Verify.DiffPlex" Version="3.3.1" />
<PackageVersion Include="Verify" Version="33.2.0" />
<PackageVersion Include="Verify.TUnit" Version="33.2.0" />
<PackageVersion Include="Verify.AngleSharp" Version="5.1.2" />
<PackageVersion Include="bunit" Version="2.11.3" />
<PackageVersion Include="Verify.Bunit" Version="14.1.0" />
<PackageVersion Include="Microsoft.Playwright" Version="1.63.0" />
<PackageVersion Include="Verify.Playwright" Version="3.1.1" />
<PackageVersion Include="DiffEngine" Version="20.5.2" />
<PackageVersion Include="DiffEngine" Version="20.6.0" />
</ItemGroup>
</Project>
44 changes: 11 additions & 33 deletions src/Wizard.Core/Generation/PluginTestFiles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,14 @@
/// </summary>
public static class PluginTestFiles
{
public static IEnumerable<GeneratedFile> For(Plan plan, bool windows)
{
foreach (var plugin in plan.PluginsIn(windows).Where(_ => _.Samples.Count > 0))
{
yield return new($"Plugins/{plugin.TestClass}.cs", Build(plan, plugin));

// With inline snapshots a known snapshot is a literal in the sample instead.
if (plan.Inline)
{
continue;
}

// A sample whose snapshot is known ships it, so it passes on the first run. Verified text
// files are UTF-8 with a BOM and no trailing newline.
foreach (var sample in plugin.Samples.Where(_ => _.VerifiedOutput != null))
{
yield return new(
$"Plugins/{plugin.TestClass}.{sample.Name}.verified.txt",
sample.VerifiedOutput!,
Bom: true);
}
}
}
/// <remarks>
/// No plugin sample ships a verified file (plan D6): what a plugin writes depends on its version
/// and the machine, so each writes a received file on its first run instead.
/// </remarks>
public static IEnumerable<GeneratedFile> For(Plan plan, bool windows) =>
plan.PluginsIn(windows)
.Where(_ => _.Samples.Count > 0)
.Select(_ => new GeneratedFile($"Plugins/{_.TestClass}.cs", Build(plan, _)));

static string Build(Plan plan, ResolvedPlugin plugin)
{
Expand Down Expand Up @@ -73,7 +58,7 @@ static string Build(Plan plan, ResolvedPlugin plugin)
}

first = false;
AppendSample(builder, framework, sample, plan.Inline);
AppendSample(builder, framework, sample);
}

foreach (var member in plugin.Samples.SelectMany(_ => _.Members))
Expand All @@ -86,7 +71,7 @@ static string Build(Plan plan, ResolvedPlugin plugin)
return builder.ToString();
}

static void AppendSample(StringBuilder builder, TestFrameworkInfo framework, Sample sample, bool inline)
static void AppendSample(StringBuilder builder, TestFrameworkInfo framework, Sample sample)
{
foreach (var line in sample.Comment)
{
Expand Down Expand Up @@ -124,14 +109,7 @@ static void AppendSample(StringBuilder builder, TestFrameworkInfo framework, Sam

var signature = sample.Async ? "async Task" : "Task";
builder.Append($" {visibility} {signature} {sample.Name}()\n {{\n");
var body = sample.Body;
if (inline &&
sample.VerifiedOutput is { } verified)
{
body = InlineSnapshots.AddSnapshot(body, verified);
}

builder.Append(ModuleInitializerGenerator.Indent(body, " "));
builder.Append(ModuleInitializerGenerator.Indent(sample.Body, " "));
builder.Append(" }\n");
}

Expand Down
2 changes: 1 addition & 1 deletion src/Wizard.Core/Generation/TestFrameworkInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// Verify's usages/*NugetUsage projects; sample and VerifyChecks code match the snippets in the Verify
/// repo, which the old docs/wiz pages rendered.
/// </summary>
/// <param name="Packages">Test project packages, in the order the Verify docs list them. Verify.DiffPlex is added separately.</param>
/// <param name="Packages">Test project packages, in the order the Verify docs list them.</param>
/// <param name="Properties">Test project properties that switch on Microsoft.Testing.Platform.</param>
/// <param name="UsesTestingPlatform">False for Fixie, which has no MTP runner and keeps VSTest (plan D10).</param>
/// <param name="SampleVerifiedFile">The name of the core sample's snapshot, which is shipped (plan D6).</param>
Expand Down
22 changes: 5 additions & 17 deletions src/Wizard.Core/Model/WizardState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,11 @@ public sealed record WizardState
/// so the url and the generated output do not depend on insertion order. Collections are replaced
/// rather than mutated, so <c>with { }</c> copies do not share them.
/// </summary>
public IReadOnlySet<string> SelectedPlugins { get; set; } = new HashSet<string>(DefaultPlugins(Flow.New), StringComparer.Ordinal);

/// <summary>
/// In a new project Verify.DiffPlex is selected until it is deselected: an inline diff on a failed
/// text snapshot helps in any project, and the wizard has recommended it unconditionally since the
/// old pages. Adding to an existing project starts from nothing, because the project already has
/// whatever it had.
/// </summary>
public static IReadOnlyList<string> DefaultPlugins(Flow flow)
{
if (flow == Flow.New)
{
return [Plugins.DiffPlexId];
}

return [];
}
/// <remarks>
/// Starts empty in every flow. A new project used to start with Verify.DiffPlex selected, until
/// Verify showed a text diff in the failure message itself.
/// </remarks>
public IReadOnlySet<string> SelectedPlugins { get; set; } = new HashSet<string>(StringComparer.Ordinal);

/// <summary>
/// Plugins the project already has (plan 7.2 step 2). They are never generated, but they take
Expand Down
28 changes: 12 additions & 16 deletions src/Wizard.Core/Model/WizardStateUrl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ public static class WizardStateUrl
public const string ExistingKey = "have";
public const string InlineKey = "inline";

/// <summary>The <see cref="PluginsKey"/> value meaning "nothing at all", as opposed to "unset".</summary>
/// <summary>
/// The <see cref="PluginsKey"/> value older links used for "nothing at all", when an absent key
/// meant a default selection. Only read now: an absent key means nothing.
/// </summary>
public const string NoPlugins = "none";

public const string SponsorKey = "sponsor";
Expand Down Expand Up @@ -85,11 +88,7 @@ void Add(string key, string? value)
.Where(_ => state.Has(_.Id))
.Select(_ => _.Id)
.ToList();
if (!selected.SequenceEqual(WizardState.DefaultPlugins(state.Flow), StringComparer.Ordinal))
{
// A link that selects nothing still has to say so, or it would read as the default.
Add(PluginsKey, selected.Count == 0 ? NoPlugins : string.Join(',', selected));
}
Add(PluginsKey, string.Join(',', selected));

Add(MinimalKey, string.Join(',', selected.Where(_ => state.DepthOf(_) == Depth.Minimal)));
Add(ChoicesKey, string.Join(',', state.Choices.OrderBy(_ => _.Key, StringComparer.Ordinal).Select(_ => $"{_.Key}:{_.Value}")));
Expand Down Expand Up @@ -161,7 +160,7 @@ public static WizardState Parse(Flow flow, string? query)
BuildServer = ParseEnum<BuildServer>(Get(BuildServerKey)),
SolutionName = Get(NameKey) ?? WizardState.DefaultSolutionName,
InlineSnapshots = Get(InlineKey) == "true",
SelectedPlugins = ParsePlugins(flow, Get(PluginsKey)),
SelectedPlugins = ParsePlugins(Get(PluginsKey)),
ExistingPlugins = new HashSet<string>(SplitList(Get(ExistingKey)), StringComparer.Ordinal),
Techs = new HashSet<string>(SplitList(Get(TechKey)), StringComparer.Ordinal),
Choices = ParseChoices(Get(ChoicesKey)),
Expand Down Expand Up @@ -227,17 +226,14 @@ static string Escape(string value) =>
.Replace("%3A", ":");

/// <summary>
/// An absent key means the default selection, so a link made before a plugin existed still
/// means what it meant. <see cref="NoPlugins"/> is how "nothing selected" is written.
/// An absent key means nothing is selected. <see cref="NoPlugins"/> is still read, because it is
/// how links wrote "nothing selected" while a new project started with Verify.DiffPlex selected,
/// and one of those links should still mean what it meant.
/// </summary>
static HashSet<string> ParsePlugins(Flow flow, string? value)
static HashSet<string> ParsePlugins(string? value)
{
if (value == null)
{
return [with(StringComparer.Ordinal), .. WizardState.DefaultPlugins(flow)];
}

if (value == NoPlugins)
if (value == null ||
value == NoPlugins)
{
return [with(StringComparer.Ordinal)];
}
Expand Down
11 changes: 0 additions & 11 deletions src/Wizard.Core/Registry/InteractionRules.Data.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,17 +292,6 @@ public static partial class InteractionRules
RetiredBy = "U25"
},
new()
{
Id = "diffplex-default-comparer",
Severity = Severity.Info,
All = ["DiffPlex"],
Any = ["AngleSharp", "Bunit", "ImageMagick", "Quibble"],
Message =
"Verify.DiffPlex is the default comparer for text snapshots. The plugins selected here " +
"register comparers for specific plugins (html, json, svg), which take precedence over it.",
Notes = ["A per test `UseDiffPlex()` overrides those comparers again, for that test only."]
},
new()
{
Id = "readable-expressions-priority",
Severity = Severity.Info,
Expand Down
7 changes: 0 additions & 7 deletions src/Wizard.Core/Registry/PluginDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,13 +308,6 @@ public sealed record Sample(string Name, string Body)

/// <summary>Extra members the body needs, emitted after the method.</summary>
public IReadOnlyList<string> Members { get; init; } = [];

/// <summary>
/// The snapshot this sample produces, when it is known exactly and does not depend on the package
/// version or the machine. Shipping it means the sample passes on the first run instead of writing
/// a received file; most samples cannot (plan D6), and the guide explains what to do with those.
/// </summary>
public string? VerifiedOutput { get; init; }
}

/// <param name="Path">Relative to the project the file belongs to.</param>
Expand Down
60 changes: 0 additions & 60 deletions src/Wizard.Core/Registry/Plugins.A.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1270,66 +1270,6 @@ static string BuildCsv() =>
]
},
new()
{
Id = "DiffPlex",
DisplayName = "Verify.DiffPlex",
RepoUrl = "https://github.com/VerifyTests/Verify.DiffPlex",
Description = "Shows an inline diff in the failure message of a text snapshot, instead of both files in full.",
Category = PluginCategory.DeveloperExperience,
Packages = [new("Verify.DiffPlex")],
PluginType = "VerifyDiffPlex",
Phase = InitializePhase.Comparers,
Initialize =
[
new(
"VerifyDiffPlex.Initialize(OutputType.{diffplex-output});",
"Verify.DiffPlex: when a text snapshot does not match, the failure message shows an inline",
"diff instead of the whole received and verified text.",
"OutputType.Compact prints only the changed lines, with a line of context either side.",
"Alternatives: OutputType.Full, OutputType.Minimal.")
],
InitializeUsings = ["VerifyTests.DiffPlex"],
Usings = ["VerifyTests.DiffPlex"],
Choices =
[
new(
"diffplex-output",
"Failure message detail",
"How much of the text a failed comparison prints.",
[
new("Compact", "Compact", "Only changed lines, with one line of context and its line number."),
new("Full", "Full", "The whole received text, with + and - markers on the changed lines."),
new("Minimal", "Minimal", "Only the changed lines.")
])
],
MinimalSamples =
[
new(
"PerTestOutput",
"""
var target = "The text";
return Verify(target)
.UseDiffPlex(OutputType.Full);
""")
{
Comment =
[
"The module initializer sets the mode for every test. UseDiffPlex overrides it for one,",
"which is useful when a single snapshot is easier to read in full."
],
// Verifying a string writes it verbatim, so the snapshot is known and can ship.
// Verify.DiffPlex is selected by default, and a download whose first test run fails
// is a poor way to meet a tool.
VerifiedOutput = "The text"
}
],
Notes =
[
"This changes only the failure message; what is written to a `.verified.` file is unaffected.",
"It is the default comparer for text. A plugin that registers a comparer for a specific file extension, such as html or json, takes precedence for that extension."
]
},
new()
{
Id = "DocNet",
DisplayName = "Verify.DocNet",
Expand Down
2 changes: 1 addition & 1 deletion src/Wizard.Core/Registry/Plugins.C.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,7 @@ static IDocument GenerateDocument() =>
[
"`VerifierSettings.UseStrictJson()` is mandatory and has to run first. `Initialize()` sets `Initialized` before it throws about strict json, so a failed call cannot be retried.",
"Strict json changes every snapshot in the project from Verify's relaxed format to real json, so it is not a drop in for an existing suite.",
"It registers a string comparer for `json`, so it takes precedence over Verify.DiffPlex for json snapshots. A per test `UseDiffPlex()` overrides it again.",
"It registers a string comparer for `json`, so a mismatched json snapshot shows Quibble's message rather than Verify's text diff.",
"An empty verified file is treated as `{}`.",
"Quibble is an F# library, so `FSharp.Core` arrives transitively.",
"There are no settings methods or per test toggles: the only configuration is the `UseStrictJson()` call."
Expand Down
6 changes: 0 additions & 6 deletions src/Wizard.Core/Registry/Plugins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@ static IEnumerable<PluginDefinition> Build() =>
public static bool Contains(string id) =>
ById.ContainsKey(id);

/// <summary>
/// Always pre-checked: an inline diff on a failed text snapshot is useful in every project, and the
/// old wizard's pages recommended it unconditionally.
/// </summary>
public const string DiffPlexId = "DiffPlex";

/// <summary>The selected plugins in registry order, ignoring ids the registry does not have.</summary>
public static IReadOnlyList<PluginDefinition> Selected(WizardState state) =>
[.. All.Where(_ => state.Has(_.Id))];
Expand Down
3 changes: 1 addition & 2 deletions src/Wizard.Core/Registry/TechSuggestions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static class TechSuggestions
/// <summary>
/// The plugins the stack suggests, in registry order. A Windows-only plugin is left out when
/// the chosen OS is not Windows, since it cannot be selected there.
/// Verify.DiffPlex is recommended in every new project, and Verify.Terminal is listed alongside it.
/// Verify.Terminal is listed as related in every new project.
/// </summary>
public static IReadOnlyList<Suggestion> For(WizardState state)
{
Expand Down Expand Up @@ -40,7 +40,6 @@ static void Note(Dictionary<string, List<string>> into, string pluginId, string

if (state.Flow == Flow.New)
{
Note(recommended, Plugins.DiffPlexId, "every project");
Note(related, "Terminal", "every project");
}

Expand Down
2 changes: 1 addition & 1 deletion src/Wizard.Core/Registry/Techs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,5 @@ public static bool Contains(string id) =>
/// Plugins no tech suggests: they are useful whatever the stack, so they are listed under
/// "Everything else" rather than tied to one (plan 9.2).
/// </summary>
public static IReadOnlyList<string> NotSuggestedByTech { get; } = [Plugins.DiffPlexId, "Terminal"];
public static IReadOnlyList<string> NotSuggestedByTech { get; } = ["Terminal"];
}
15 changes: 7 additions & 8 deletions src/Wizard.Core/Versions/package-versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"Serilog": "4.4.0",
"System.Drawing.Common": "10.0.12",
"TUnit": "1.69.0",
"Verify": "33.1.1",
"Verify": "33.2.0",
"Verify.AngleSharp": "5.1.2",
"Verify.AspNetCore": "5.0.0",
"Verify.Aspose": "5.28.0",
Expand All @@ -65,14 +65,13 @@
"Verify.Cosmos": "3.0.0",
"Verify.CsvHelper": "0.2.0",
"Verify.Diagnostics": "1.0.0",
"Verify.DiffPlex": "3.3.1",
"Verify.DocNet": "3.6.0",
"Verify.EmailPreviewServices": "1.0.0",
"Verify.EntityFramework": "15.5.0",
"Verify.EntityFrameworkClassic": "15.5.0",
"Verify.Expecto": "33.1.1",
"Verify.Expecto": "33.2.0",
"Verify.FakeItEasy": "2.1.0",
"Verify.Fixie": "33.1.1",
"Verify.Fixie": "33.2.0",
"Verify.Flurl": "2.0.0",
"Verify.Http": "7.5.1",
"Verify.ICSharpCode.Decompiler": "3.4.1",
Expand All @@ -85,13 +84,13 @@
"Verify.MicrosoftLogging": "5.0.0",
"Verify.Mockly": "1.0.1",
"Verify.Moq": "2.2.0",
"Verify.MSTest": "33.1.1",
"Verify.MSTest": "33.2.0",
"Verify.NewtonsoftJson": "1.1.0",
"Verify.NodaTime": "2.3.0",
"Verify.NServiceBus": "12.2.0",
"Verify.NSubstitute": "2.1.0",
"Verify.NUlid": "1.0.1",
"Verify.NUnit": "33.1.1",
"Verify.NUnit": "33.2.0",
"Verify.OpenTelemetry": "1.0.0",
"Verify.OpenXml": "1.27.0",
"Verify.Pandoc": "0.1.1",
Expand All @@ -115,12 +114,12 @@
"Verify.Syncfusion": "2.7.4",
"Verify.SystemJson": "1.4.1",
"verify.tool": "0.9.1",
"Verify.TUnit": "33.1.1",
"Verify.TUnit": "33.2.0",
"Verify.Ulid": "1.0.1",
"Verify.WinForms": "6.0.0",
"Verify.Wolverine": "3.2.0",
"Verify.Xaml": "5.0.0",
"Verify.XunitV3": "33.1.1",
"Verify.XunitV3": "33.2.0",
"Verify.Yaml": "0.1.0",
"Verify.ZeroLog": "2.0.0",
"WolverineFx": "6.39.1",
Expand Down
Loading