@@ -25,6 +25,113 @@ public void Preview_For_WebAssembly_Project_Creates_Plan_Without_Writing_Files()
2525 Assert . False ( File . Exists ( Path . Combine ( projectDirectory , "Pages" , "LineChartPage.razor" ) ) ) ;
2626 }
2727
28+ [ Fact ]
29+ public void Preview_Accepts_Exact_Project_File_Path ( )
30+ {
31+ using var workspace = new TemporaryWorkspace ( ) ;
32+ var projectDirectory = workspace . CreateWebAssemblyProject ( ) ;
33+ var projectFilePath = Path . Combine ( projectDirectory , "Sample.csproj" ) ;
34+ var service = new ProjectIntegrationService ( new ChartExampleGenerator ( ) ) ;
35+
36+ var plan = service . Preview ( new PreviewIntegrationRequest
37+ {
38+ TargetProjectPath = projectFilePath ,
39+ Chart = new ChartGenerationRequest { ChartType = "Line" } ,
40+ } ) ;
41+
42+ Assert . Equal ( projectFilePath , plan . ProjectFilePath ) ;
43+ }
44+
45+ [ Fact ]
46+ public void Preview_Resolves_Repo_Folder_With_One_Nested_Blazor_App ( )
47+ {
48+ using var workspace = new TemporaryWorkspace ( ) ;
49+ var projectDirectory = workspace . CreateWebAssemblyProject ( Path . Combine ( "src" , "App" ) , "App" ) ;
50+ var service = new ProjectIntegrationService ( new ChartExampleGenerator ( ) ) ;
51+
52+ var plan = service . Preview ( new PreviewIntegrationRequest
53+ {
54+ TargetProjectPath = workspace . Root ,
55+ Chart = new ChartGenerationRequest { ChartType = "Line" } ,
56+ } ) ;
57+
58+ Assert . Equal ( Path . Combine ( projectDirectory , "App.csproj" ) , plan . ProjectFilePath ) ;
59+ }
60+
61+ [ Fact ]
62+ public void Preview_Rejects_Repo_Folder_With_Multiple_Blazor_App_Candidates ( )
63+ {
64+ using var workspace = new TemporaryWorkspace ( ) ;
65+ var firstProjectDirectory = workspace . CreateWebAssemblyProject ( Path . Combine ( "src" , "FirstApp" ) , "FirstApp" ) ;
66+ var secondProjectDirectory = workspace . CreateWebAssemblyProject ( Path . Combine ( "src" , "SecondApp" ) , "SecondApp" ) ;
67+ var service = new ProjectIntegrationService ( new ChartExampleGenerator ( ) ) ;
68+
69+ var exception = Assert . Throws < ToolInputException > ( ( ) => service . Preview ( new PreviewIntegrationRequest
70+ {
71+ TargetProjectPath = workspace . Root ,
72+ Chart = new ChartGenerationRequest { ChartType = "Line" } ,
73+ } ) ) ;
74+
75+ Assert . Equal ( "targetProjectPath" , exception . Field ) ;
76+ Assert . Contains ( Path . Combine ( firstProjectDirectory , "FirstApp.csproj" ) , exception . Details ) ;
77+ Assert . Contains ( Path . Combine ( secondProjectDirectory , "SecondApp.csproj" ) , exception . Details ) ;
78+ }
79+
80+ [ Fact ]
81+ public void Preview_Rejects_Folder_With_Only_NonBlazor_Projects ( )
82+ {
83+ using var workspace = new TemporaryWorkspace ( ) ;
84+ var projectDirectory = workspace . CreateLibraryProject ( Path . Combine ( "src" , "Library" ) , "Library" ) ;
85+ var service = new ProjectIntegrationService ( new ChartExampleGenerator ( ) ) ;
86+
87+ var exception = Assert . Throws < ToolInputException > ( ( ) => service . Preview ( new PreviewIntegrationRequest
88+ {
89+ TargetProjectPath = workspace . Root ,
90+ Chart = new ChartGenerationRequest { ChartType = "Line" } ,
91+ } ) ) ;
92+
93+ Assert . Equal ( "targetProjectPath" , exception . Field ) ;
94+ Assert . Contains ( Path . Combine ( projectDirectory , "Library.csproj" ) , exception . Details ) ;
95+ }
96+
97+ [ Fact ]
98+ public void Preview_Resolves_Solution_With_One_Blazor_App ( )
99+ {
100+ using var workspace = new TemporaryWorkspace ( ) ;
101+ var projectDirectory = workspace . CreateWebAssemblyProject ( Path . Combine ( "src" , "App" ) , "App" ) ;
102+ var libraryDirectory = workspace . CreateLibraryProject ( Path . Combine ( "src" , "Library" ) , "Library" ) ;
103+ var solutionPath = workspace . CreateSolution ( "Sample.sln" , Path . Combine ( projectDirectory , "App.csproj" ) , Path . Combine ( libraryDirectory , "Library.csproj" ) ) ;
104+ var service = new ProjectIntegrationService ( new ChartExampleGenerator ( ) ) ;
105+
106+ var plan = service . Preview ( new PreviewIntegrationRequest
107+ {
108+ TargetProjectPath = solutionPath ,
109+ Chart = new ChartGenerationRequest { ChartType = "Line" } ,
110+ } ) ;
111+
112+ Assert . Equal ( Path . Combine ( projectDirectory , "App.csproj" ) , plan . ProjectFilePath ) ;
113+ }
114+
115+ [ Fact ]
116+ public void Preview_Rejects_Solution_With_Multiple_Blazor_App_Candidates ( )
117+ {
118+ using var workspace = new TemporaryWorkspace ( ) ;
119+ var firstProjectDirectory = workspace . CreateWebAssemblyProject ( Path . Combine ( "src" , "FirstApp" ) , "FirstApp" ) ;
120+ var secondProjectDirectory = workspace . CreateWebAssemblyProject ( Path . Combine ( "src" , "SecondApp" ) , "SecondApp" ) ;
121+ var solutionPath = workspace . CreateSolution ( "Sample.sln" , Path . Combine ( firstProjectDirectory , "FirstApp.csproj" ) , Path . Combine ( secondProjectDirectory , "SecondApp.csproj" ) ) ;
122+ var service = new ProjectIntegrationService ( new ChartExampleGenerator ( ) ) ;
123+
124+ var exception = Assert . Throws < ToolInputException > ( ( ) => service . Preview ( new PreviewIntegrationRequest
125+ {
126+ TargetProjectPath = solutionPath ,
127+ Chart = new ChartGenerationRequest { ChartType = "Line" } ,
128+ } ) ) ;
129+
130+ Assert . Equal ( "targetProjectPath" , exception . Field ) ;
131+ Assert . Contains ( Path . Combine ( firstProjectDirectory , "FirstApp.csproj" ) , exception . Details ) ;
132+ Assert . Contains ( Path . Combine ( secondProjectDirectory , "SecondApp.csproj" ) , exception . Details ) ;
133+ }
134+
28135 [ Fact ]
29136 public void Apply_Writes_Files_For_Matching_Preview ( )
30137 {
@@ -102,35 +209,78 @@ private sealed class TemporaryWorkspace : IDisposable
102209 {
103210 private readonly string root = Path . Combine ( Path . GetTempPath ( ) , $ "bex-chartjs-mcp-tests-{ Guid . NewGuid ( ) : N} ") ;
104211
105- public string CreateWebAssemblyProject ( )
212+ public string Root => root ;
213+
214+ public string CreateWebAssemblyProject ( string relativePath = "" , string projectName = "Sample" )
106215 {
107- Directory . CreateDirectory ( root ) ;
108- Directory . CreateDirectory ( Path . Combine ( root , "wwwroot" ) ) ;
109- Directory . CreateDirectory ( Path . Combine ( root , "Pages" ) ) ;
110- Directory . CreateDirectory ( Path . Combine ( root , "Shared" ) ) ;
216+ var projectRoot = string . IsNullOrWhiteSpace ( relativePath ) ? root : Path . Combine ( root , relativePath ) ;
217+ Directory . CreateDirectory ( projectRoot ) ;
218+ Directory . CreateDirectory ( Path . Combine ( projectRoot , "wwwroot" ) ) ;
219+ Directory . CreateDirectory ( Path . Combine ( projectRoot , "Pages" ) ) ;
220+ Directory . CreateDirectory ( Path . Combine ( projectRoot , "Shared" ) ) ;
111221
112- File . WriteAllText ( Path . Combine ( root , "Sample .csproj") , """
222+ File . WriteAllText ( Path . Combine ( projectRoot , $ " { projectName } .csproj") , """
113223 <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
114224 <PropertyGroup>
115225 <TargetFramework>net10.0</TargetFramework>
116226 </PropertyGroup>
117227 </Project>
118228 """ ) ;
119- File . WriteAllText ( Path . Combine ( root , "_Imports.razor" ) , "@using Microsoft.AspNetCore.Components" + Environment . NewLine ) ;
120- File . WriteAllText ( Path . Combine ( root , "wwwroot" , "index.html" ) , """
229+ File . WriteAllText ( Path . Combine ( projectRoot , "_Imports.razor" ) , "@using Microsoft.AspNetCore.Components" + Environment . NewLine ) ;
230+ File . WriteAllText ( Path . Combine ( projectRoot , "wwwroot" , "index.html" ) , """
121231 <html>
122232 <body>
123233 <div id="app"></div>
124234 </body>
125235 </html>
126236 """ ) ;
127- File . WriteAllText ( Path . Combine ( root , "Shared" , "NavMenu.razor" ) , """
237+ File . WriteAllText ( Path . Combine ( projectRoot , "Shared" , "NavMenu.razor" ) , """
128238 <nav>
129239 <NavLink class="nav-link" href="">Home</NavLink>
130240 </nav>
131241 """ ) ;
132242
133- return root ;
243+ return projectRoot ;
244+ }
245+
246+ public string CreateLibraryProject ( string relativePath , string projectName )
247+ {
248+ var projectRoot = Path . Combine ( root , relativePath ) ;
249+ Directory . CreateDirectory ( projectRoot ) ;
250+ File . WriteAllText ( Path . Combine ( projectRoot , $ "{ projectName } .csproj") , """
251+ <Project Sdk="Microsoft.NET.Sdk">
252+ <PropertyGroup>
253+ <TargetFramework>net10.0</TargetFramework>
254+ </PropertyGroup>
255+ </Project>
256+ """ ) ;
257+
258+ return projectRoot ;
259+ }
260+
261+ public string CreateSolution ( string fileName , params string [ ] projectPaths )
262+ {
263+ Directory . CreateDirectory ( root ) ;
264+ var solutionPath = Path . Combine ( root , fileName ) ;
265+ var lines = new List < string >
266+ {
267+ "Microsoft Visual Studio Solution File, Format Version 12.00" ,
268+ "# Visual Studio Version 17" ,
269+ } ;
270+
271+ foreach ( var projectPath in projectPaths )
272+ {
273+ var relativePath = Path . GetRelativePath ( root , projectPath ) ;
274+ var projectName = Path . GetFileNameWithoutExtension ( projectPath ) ;
275+ lines . Add ( $ "Project(\" {{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}\" ) = \" { projectName } \" , \" { relativePath } \" , \" {{{Guid.NewGuid():D}}}\" ") ;
276+ lines . Add ( "EndProject" ) ;
277+ }
278+
279+ lines . Add ( "Global" ) ;
280+ lines . Add ( "EndGlobal" ) ;
281+ File . WriteAllText ( solutionPath , string . Join ( Environment . NewLine , lines ) ) ;
282+
283+ return solutionPath ;
134284 }
135285
136286 public void Dispose ( )
0 commit comments