diff --git a/NET10/BlazorInteractiveServer/Components/Demo/ComplexComponentsDemo.razor b/NET10/BlazorInteractiveServer/Components/Demo/ComplexComponentsDemo.razor index bf45f91..b74ef99 100644 --- a/NET10/BlazorInteractiveServer/Components/Demo/ComplexComponentsDemo.razor +++ b/NET10/BlazorInteractiveServer/Components/Demo/ComplexComponentsDemo.razor @@ -333,7 +333,7 @@ + CurrentPageChanged="HandlePageChange" /> diff --git a/README.md b/README.md index 4315130..755dfbf 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ ShellUI is pre-1.0, so APIs and generated output can still change between minor ## Capabilities -- The CLI commands are `init`, `add`, `list`, `remove`, `update` and `doctor`, plus `theme init`, `theme apply`, and `theme update`. +- The CLI commands are `init`, `add`, `list`, `remove`, `update`, `doctor` and `migrate`, plus `theme init`, `theme apply`, and `theme update`. - The component registry has **254 entries**: **98 direct install targets** and **156 hidden dependency entries**. `list` shows direct targets; `add` resolves hidden dependencies. - `0.4.0-alpha.1` adds `kbd`, `aspect-ratio`, `button-group`, `toggle-group`, `input-group`, `number-input`, `stat-card`, `timeline`, `tree-view`, `qr-code`, `image-viewer`, `chat`, `chat-message`, `chat-input`, and the `auth-01`, `auth-02` and `auth-03` blocks. - `0.3.0` added `typed-select`, `command-palette`, `data-picker`, `multi-select`, `tag-input`, `donut-chart`, `radar-chart`, and `radial-chart`. diff --git a/ShellUI.Tests/MigrateTests.cs b/ShellUI.Tests/MigrateTests.cs new file mode 100644 index 0000000..73fd534 --- /dev/null +++ b/ShellUI.Tests/MigrateTests.cs @@ -0,0 +1,126 @@ +using ShellUI.CLI.Services; + +namespace ShellUI.Tests; + +public class MigrateTests : IDisposable +{ + private readonly string _root = Path.Combine(Path.GetTempPath(), $"migrate-{Guid.NewGuid():N}"); + + public MigrateTests() + { + Directory.CreateDirectory(Path.Combine(_root, "Components", "UI")); + Directory.CreateDirectory(Path.Combine(_root, "Components", "Pages")); + Directory.CreateDirectory(Path.Combine(_root, "obj")); + File.WriteAllText(Path.Combine(_root, "MyApp.csproj"), ""); + } + + public void Dispose() + { + try { Directory.Delete(_root, recursive: true); } catch (IOException) { } + } + + private string Page(string name, string content) + { + var path = Path.Combine(_root, "Components", "Pages", name); + File.WriteAllText(path, content); + return path; + } + + private MigrationEdit SingleEdit(params string[] components) => Assert.Single(MigrateService.Plan(_root, components).Edits); + + [Fact] + public void Input_DropsTheNestedVariantEnum() + { + Page("Login.razor", "\n@code { var v = Input.InputVariant.Default; }"); + + var edit = SingleEdit("input"); + + Assert.Equal("\n@code { var v = InputVariant.Default; }", edit.After); + } + + [Fact] + public void DatePicker_RenamesSelectedDateButLeavesCalendarAlone() + { + Page("Booking.razor", + "\n" + + " b\" />\n" + + ""); + + var edit = SingleEdit("date-picker"); + + Assert.Equal( + "\n" + + " b\" />\n" + + "", edit.After); + } + + [Fact] + public void TimePicker_RenamesSelectedTime() + { + Page("Alarm.razor", ""); + + Assert.Equal("", SingleEdit("time-picker").After); + } + + [Fact] + public void Pagination_RenamesOnPageChangeUnlessCurrentPageIsBound() + { + Page("List.razor", + "\n" + + ""); + + var edit = SingleEdit("pagination"); + + Assert.Equal( + "\n" + + "", edit.After); + } + + [Fact] + public void Plan_SkipsInstalledComponentsAndBuildOutput() + { + File.WriteAllText(Path.Combine(_root, "Components", "UI", "Input.razor"), "Input.InputVariant.Error"); + File.WriteAllText(Path.Combine(_root, "obj", "Generated.cs"), "Input.InputVariant.Error"); + var page = Page("Form.razor", "Input.InputVariant.Error"); + + var plan = MigrateService.Plan(_root); + + Assert.Equal(page, Assert.Single(plan.Edits).Path); + } + + [Fact] + public void Plan_WithNoComponentsRunsEveryMigrationAndListsWhatItApplied() + { + Page("All.razor", "\n"); + + var edit = SingleEdit(); + + Assert.Equal(2, edit.Applied.Count); + Assert.Equal("\n", edit.After); + } + + [Fact] + public void Calendar_FlagsHandlersThatTakeADateTime() + { + var page = Page("Plan.razor", + "\n@code {\n void OnDay(DateTime day) { }\n void Other(DateTime d) { }\n}"); + + var plan = MigrateService.Plan(_root); + + Assert.Empty(plan.Edits); + var note = Assert.Single(plan.Notes); + Assert.Equal(page, note.Path); + Assert.Equal(3, note.Line); + } + + [Fact] + public void Plan_IsEmptyForAnUpToDateApp() + { + Page("Fine.razor", "\n\n@code { void OnDay(DateTime? d) { } }"); + + var plan = MigrateService.Plan(_root); + + Assert.Empty(plan.Edits); + Assert.Empty(plan.Notes); + } +} diff --git a/docs/CLI_SYNTAX.md b/docs/CLI_SYNTAX.md index 396d15f..ded3ded 100644 --- a/docs/CLI_SYNTAX.md +++ b/docs/CLI_SYNTAX.md @@ -22,6 +22,7 @@ shellui list [--installed|--available] shellui remove shellui update [name...] [--all] [--force] shellui doctor +shellui migrate [name...] [--dry-run] [--yes] shellui theme init [--force] [--style