ScopedCssIntegrationTests.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. using System.Text.RegularExpressions;
  4. using Microsoft.AspNetCore.StaticWebAssets.Tasks;
  5. namespace Microsoft.NET.Sdk.Razor.Tests
  6. {
  7. public class ScopedCssIntegrationTest : AspNetSdkBaselineTest
  8. {
  9. public ScopedCssIntegrationTest(ITestOutputHelper log) : base(log, GenerateBaselines) { }
  10. [Fact]
  11. public void Build_NoOps_WhenScopedCssIsDisabled()
  12. {
  13. var testAsset = "RazorComponentApp";
  14. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  15. var build = new BuildCommand(projectDirectory);
  16. build.Execute("/p:ScopedCssEnabled=false").Should().Pass();
  17. var intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().ToString(), "Debug", DefaultTfm);
  18. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css")).Should().NotExist();
  19. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Index.razor.rz.scp.css")).Should().NotExist();
  20. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "bundle", "ComponentApp.styles.css")).Should().NotExist();
  21. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "FetchData.razor.rz.scp.css")).Should().NotExist();
  22. }
  23. [Fact]
  24. public void Build_NoOps_ForMvcApp_WhenScopedCssIsDisabled()
  25. {
  26. var testAsset = "RazorSimpleMvc";
  27. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  28. var build = new BuildCommand(projectDirectory);
  29. build.Execute("/p:ScopedCssEnabled=false").Should().Pass();
  30. var intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().ToString(), "Debug", DefaultTfm);
  31. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Views", "Home", "Index.cshtml.rz.scp.css")).Should().NotExist();
  32. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Views", "Home", "Contact.cshtml.rz.scp.css")).Should().NotExist();
  33. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "bundle", "SimpleMvc.styles.css")).Should().NotExist();
  34. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Views", "Home", "About.cshtml.rz.scp.css")).Should().NotExist();
  35. }
  36. [Fact]
  37. public void CanDisableDefaultDiscoveryConvention()
  38. {
  39. var testAsset = "RazorComponentApp";
  40. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  41. var build = new BuildCommand(projectDirectory);
  42. build.Execute("/p:EnableDefaultScopedCssItems=false").Should().Pass();
  43. var intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().ToString(), "Debug", DefaultTfm);
  44. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css")).Should().NotExist();
  45. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Index.razor.rz.scp.css")).Should().NotExist();
  46. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "bundle", "ComponentApp.styles.css")).Should().NotExist();
  47. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "FetchData.razor.rz.scp.css")).Should().NotExist();
  48. }
  49. [CoreMSBuildOnlyFact]
  50. public void CanOverrideScopeIdentifiers()
  51. {
  52. var testAsset = "RazorComponentApp";
  53. var projectDirectory = CreateAspNetSdkTestAsset(testAsset)
  54. .WithProjectChanges(project =>
  55. {
  56. var ns = project.Root.Name.Namespace;
  57. var itemGroup = new XElement(ns + "ItemGroup");
  58. var element = new XElement("ScopedCssInput", new XAttribute("Include", @"Styles\Pages\Counter.css"));
  59. element.Add(new XElement("RazorComponent", @"Components\Pages\Counter.razor"));
  60. element.Add(new XElement("CssScope", "b-overriden"));
  61. itemGroup.Add(element);
  62. project.Root.Add(itemGroup);
  63. });
  64. var stylesFolder = Path.Combine(projectDirectory.Path, "Styles", "Pages");
  65. Directory.CreateDirectory(stylesFolder);
  66. var styles = Path.Combine(stylesFolder, "Counter.css");
  67. File.Move(Path.Combine(projectDirectory.Path, "Components", "Pages", "Counter.razor.css"), styles);
  68. var build = new BuildCommand(projectDirectory);
  69. build.Execute("/p:EnableDefaultScopedCssItems=false", "/p:EmitCompilerGeneratedFiles=true").Should().Pass();
  70. var intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().ToString(), "Debug", DefaultTfm);
  71. var scoped = Path.Combine(intermediateOutputPath, "scopedcss", "Styles", "Pages", "Counter.rz.scp.css");
  72. new FileInfo(scoped).Should().Exist();
  73. new FileInfo(scoped).Should().Contain("b-overriden");
  74. var generated = Path.Combine(intermediateOutputPath, "generated", "Microsoft.CodeAnalysis.Razor.Compiler", "Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator", "Components_Pages_Counter_razor.g.cs");
  75. new FileInfo(generated).Should().Exist();
  76. new FileInfo(generated).Should().Contain("b-overriden");
  77. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Index.razor.rz.scp.css")).Should().NotExist();
  78. }
  79. [Fact]
  80. public void Build_GeneratesTransformedFilesAndBundle_ForComponentsWithScopedCss()
  81. {
  82. var testAsset = "RazorComponentApp";
  83. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  84. var build = new BuildCommand(projectDirectory);
  85. build.Execute().Should().Pass();
  86. var intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().ToString(), "Debug", DefaultTfm);
  87. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css")).Should().Exist();
  88. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Index.razor.rz.scp.css")).Should().Exist();
  89. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "bundle", "ComponentApp.styles.css")).Should().Exist();
  90. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "projectbundle", "ComponentApp.bundle.scp.css")).Should().Exist();
  91. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "FetchData.razor.rz.scp.css")).Should().NotExist();
  92. }
  93. [Fact]
  94. public void Build_GeneratesTransformedFilesAndBundle_ForViewsWithScopedCss()
  95. {
  96. var testAsset = "RazorSimpleMvc";
  97. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  98. var build = new BuildCommand(projectDirectory);
  99. build.Execute().Should().Pass();
  100. var intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().ToString(), "Debug", DefaultTfm);
  101. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Views", "Home", "Index.cshtml.rz.scp.css")).Should().Exist();
  102. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Views", "Home", "Contact.cshtml.rz.scp.css")).Should().Exist();
  103. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "bundle", "SimpleMvc.styles.css")).Should().Exist();
  104. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "projectbundle", "SimpleMvc.bundle.scp.css")).Should().Exist();
  105. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Views", "Home", "About.cshtml.rz.scp.css")).Should().Exist();
  106. }
  107. [Fact]
  108. public void Build_ScopedCssFiles_ContainsUniqueScopesPerFile()
  109. {
  110. var testAsset = "RazorComponentApp";
  111. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  112. var build = new BuildCommand(projectDirectory);
  113. build.Execute().Should().Pass();
  114. var intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().ToString(), "Debug", DefaultTfm);
  115. var generatedCounter = Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css");
  116. new FileInfo(generatedCounter).Should().Exist();
  117. var generatedIndex = Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Index.razor.rz.scp.css");
  118. new FileInfo(generatedIndex).Should().Exist();
  119. var counterContent = File.ReadAllText(generatedCounter);
  120. var indexContent = File.ReadAllText(generatedIndex);
  121. var counterScopeMatch = Regex.Match(counterContent, ".*button\\[(.*)\\].*", RegexOptions.Multiline | RegexOptions.IgnoreCase);
  122. Assert.True(counterScopeMatch.Success, "Couldn't find a scope id in the generated Counter scoped css file.");
  123. var counterScopeId = counterScopeMatch.Groups[1].Captures[0].Value;
  124. var indexScopeMatch = Regex.Match(indexContent, ".*h1\\[(.*)\\].*", RegexOptions.Multiline | RegexOptions.IgnoreCase);
  125. Assert.True(indexScopeMatch.Success, "Couldn't find a scope id in the generated Index scoped css file.");
  126. var indexScopeId = indexScopeMatch.Groups[1].Captures[0].Value;
  127. Assert.NotEqual(counterScopeId, indexScopeId);
  128. }
  129. [Fact]
  130. public void Build_ScopedCssViews_ContainsUniqueScopesPerView()
  131. {
  132. var testAsset = "RazorSimpleMvc";
  133. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  134. var build = new BuildCommand(projectDirectory);
  135. build.Execute().Should().Pass();
  136. var intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().ToString(), "Debug", DefaultTfm);
  137. var generatedIndex = Path.Combine(intermediateOutputPath, "scopedcss", "Views", "Home", "Index.cshtml.rz.scp.css");
  138. new FileInfo(generatedIndex).Should().Exist();
  139. var generatedAbout = Path.Combine(intermediateOutputPath, "scopedcss", "Views", "Home", "About.cshtml.rz.scp.css");
  140. new FileInfo(generatedAbout).Should().Exist();
  141. var generatedContact = Path.Combine(intermediateOutputPath, "scopedcss", "Views", "Home", "Contact.cshtml.rz.scp.css");
  142. new FileInfo(generatedContact).Should().Exist();
  143. var indexContent = File.ReadAllText(generatedIndex);
  144. var aboutContent = File.ReadAllText(generatedAbout);
  145. var contactContent = File.ReadAllText(generatedContact);
  146. var indexScopeMatch = Regex.Match(indexContent, ".*p\\[(.*)\\].*", RegexOptions.Multiline | RegexOptions.IgnoreCase);
  147. Assert.True(indexScopeMatch.Success, "Couldn't find a scope id in the generated Index scoped css file.");
  148. var indexScopeId = indexScopeMatch.Groups[1].Captures[0].Value;
  149. var aboutScopeMatch = Regex.Match(aboutContent, ".*h2\\[(.*)\\].*", RegexOptions.Multiline | RegexOptions.IgnoreCase);
  150. Assert.True(aboutScopeMatch.Success, "Couldn't find a scope id in the generated About scoped css file.");
  151. var aboutScopeId = aboutScopeMatch.Groups[1].Captures[0].Value;
  152. var contactScopeMatch = Regex.Match(contactContent, ".*a\\[(.*)\\].*", RegexOptions.Multiline | RegexOptions.IgnoreCase);
  153. Assert.True(contactScopeMatch.Success, "Couldn't find a scope id in the generated Contact scoped css file.");
  154. var contactScopeId = contactScopeMatch.Groups[1].Captures[0].Value;
  155. Assert.NotEqual(indexScopeId, aboutScopeId);
  156. Assert.NotEqual(indexScopeId, contactScopeId);
  157. Assert.NotEqual(aboutScopeId, contactScopeId);
  158. }
  159. [Fact]
  160. public void Build_WorksWhenViewsAndComponentsArePartOfTheSameProject_ContainsUniqueScopesPerFile()
  161. {
  162. var testAsset = "RazorMvcWithComponents";
  163. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  164. var build = new BuildCommand(projectDirectory);
  165. build.Execute().Should().Pass();
  166. var intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().ToString(), "Debug", DefaultTfm);
  167. var generatedIndex = Path.Combine(intermediateOutputPath, "scopedcss", "Views", "Home", "Index.cshtml.rz.scp.css");
  168. new FileInfo(generatedIndex).Should().Exist();
  169. var generatedCounter = Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Counter.razor.rz.scp.css");
  170. new FileInfo(generatedCounter).Should().Exist();
  171. var indexContent = File.ReadAllText(generatedIndex);
  172. var counterContent = File.ReadAllText(generatedCounter);
  173. var indexScopeMatch = Regex.Match(indexContent, ".*p\\[(.*)\\].*", RegexOptions.Multiline | RegexOptions.IgnoreCase);
  174. Assert.True(indexScopeMatch.Success, "Couldn't find a scope id in the generated Index scoped css file.");
  175. var indexScopeId = indexScopeMatch.Groups[1].Captures[0].Value;
  176. var counterScopeMatch = Regex.Match(counterContent, ".*div\\[(.*)\\].*", RegexOptions.Multiline | RegexOptions.IgnoreCase);
  177. Assert.True(counterScopeMatch.Success, "Couldn't find a scope id in the generated Counter scoped css file.");
  178. var counterScopeId = counterScopeMatch.Groups[1].Captures[0].Value;
  179. Assert.NotEqual(indexScopeId, counterScopeId);
  180. }
  181. [Fact]
  182. public void Publish_PublishesBundleToTheRightLocation()
  183. {
  184. var testAsset = "RazorComponentApp";
  185. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  186. var publish = new PublishCommand(projectDirectory);
  187. publish.WithWorkingDirectory(projectDirectory.TestRoot);
  188. publish.Execute().Should().Pass();
  189. var publishOutputPath = publish.GetOutputDirectory(DefaultTfm, "Debug").ToString();
  190. new FileInfo(Path.Combine(publishOutputPath, "wwwroot", "ComponentApp.styles.css")).Should().Exist();
  191. new FileInfo(Path.Combine(publishOutputPath, "wwwroot", "_content", "ComponentApp", "Components", "Pages", "Index.razor.rz.scp.css")).Should().NotExist();
  192. new FileInfo(Path.Combine(publishOutputPath, "wwwroot", "_content", "ComponentApp", "Components", "Pages", "Counter.razor.rz.scp.css")).Should().NotExist();
  193. }
  194. [Fact]
  195. public void Publish_NoBuild_PublishesBundleToTheRightLocation()
  196. {
  197. var testAsset = "RazorComponentApp";
  198. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  199. var build = new BuildCommand(projectDirectory);
  200. build.WithWorkingDirectory(projectDirectory.Path);
  201. var buildResult = build.Execute();
  202. buildResult.Should().Pass();
  203. var publish = new PublishCommand(projectDirectory);
  204. publish.Execute("/p:NoBuild=true").Should().Pass();
  205. var publishOutputPath = publish.GetOutputDirectory(DefaultTfm, "Debug").ToString();
  206. new FileInfo(Path.Combine(publishOutputPath, "wwwroot", "ComponentApp.styles.css")).Should().Exist();
  207. new FileInfo(Path.Combine(publishOutputPath, "wwwroot", "_content", "ComponentApp", "Components", "Pages", "Index.razor.rz.scp.css")).Should().NotExist();
  208. new FileInfo(Path.Combine(publishOutputPath, "wwwroot", "_content", "ComponentApp", "Components", "Pages", "Counter.razor.rz.scp.css")).Should().NotExist();
  209. }
  210. [Fact]
  211. public void Publish_DoesNotPublishAnyFile_WhenThereAreNoScopedCssFiles()
  212. {
  213. var testAsset = "RazorComponentApp";
  214. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  215. File.Delete(Path.Combine(projectDirectory.Path, "Components", "Pages", "Counter.razor.css"));
  216. File.Delete(Path.Combine(projectDirectory.Path, "Components", "Pages", "Index.razor.css"));
  217. var publish = new PublishCommand(Log, projectDirectory.TestRoot);
  218. publish.Execute().Should().Pass();
  219. var publishOutputPath = publish.GetOutputDirectory(DefaultTfm, "Debug").ToString();
  220. new FileInfo(Path.Combine(publishOutputPath, "wwwroot", "_content", "ComponentApp", "_framework", "scoped.styles.css")).Should().NotExist();
  221. }
  222. [Fact]
  223. public void Publish_Publishes_IndividualScopedCssFiles_WhenNoBundlingIsEnabled()
  224. {
  225. var testAsset = "RazorComponentApp";
  226. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  227. var publish = new PublishCommand(projectDirectory);
  228. publish.WithWorkingDirectory(projectDirectory.TestRoot);
  229. publish.Execute("/p:DisableScopedCssBundling=true").Should().Pass();
  230. var publishOutputPath = publish.GetOutputDirectory(DefaultTfm, "Debug").ToString();
  231. new FileInfo(Path.Combine(publishOutputPath, "wwwroot", "_content", "ComponentApp", "ComponentApp.styles.css")).Should().NotExist();
  232. new FileInfo(Path.Combine(publishOutputPath, "wwwroot", "Components", "Pages", "Index.razor.rz.scp.css")).Should().Exist();
  233. new FileInfo(Path.Combine(publishOutputPath, "wwwroot", "Components", "Pages", "Counter.razor.rz.scp.css")).Should().Exist();
  234. }
  235. [CoreMSBuildOnlyFact]
  236. public void Build_RemovingScopedCssAndBuilding_UpdatesGeneratedCodeAndBundle()
  237. {
  238. var testAsset = "RazorComponentApp";
  239. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  240. var build = new BuildCommand(projectDirectory);
  241. build.Execute("/p:EmitCompilerGeneratedFiles=true").Should().Pass();
  242. var intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().ToString(), "Debug", DefaultTfm);
  243. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css")).Should().Exist();
  244. var generatedBundle = Path.Combine(intermediateOutputPath, "scopedcss", "bundle", "ComponentApp.styles.css");
  245. new FileInfo(generatedBundle).Should().Exist();
  246. var generatedProjectBundle = Path.Combine(intermediateOutputPath, "scopedcss", "projectbundle", "ComponentApp.bundle.scp.css");
  247. new FileInfo(generatedProjectBundle).Should().Exist();
  248. var generatedCounter = Path.Combine(intermediateOutputPath, "generated", "Microsoft.CodeAnalysis.Razor.Compiler", "Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator", "Components_Pages_Counter_razor.g.cs");
  249. new FileInfo(generatedCounter).Should().Exist();
  250. var componentThumbprint = FileThumbPrint.Create(generatedCounter);
  251. var bundleThumbprint = FileThumbPrint.Create(generatedBundle);
  252. File.Delete(Path.Combine(projectDirectory.Path, "Components", "Pages", "Counter.razor.css"));
  253. build = new BuildCommand(projectDirectory);
  254. build.Execute("/p:EmitCompilerGeneratedFiles=true").Should().Pass();
  255. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css")).Should().NotExist();
  256. new FileInfo(generatedCounter).Should().Exist();
  257. var newComponentThumbprint = FileThumbPrint.Create(generatedCounter);
  258. var newBundleThumbprint = FileThumbPrint.Create(generatedBundle);
  259. Assert.NotEqual(componentThumbprint, newComponentThumbprint);
  260. Assert.NotEqual(bundleThumbprint, newBundleThumbprint);
  261. }
  262. [Fact]
  263. public void Does_Nothing_WhenThereAreNoScopedCssFiles()
  264. {
  265. var testAsset = "RazorComponentApp";
  266. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  267. File.Delete(Path.Combine(projectDirectory.Path, "Components", "Pages", "Counter.razor.css"));
  268. File.Delete(Path.Combine(projectDirectory.Path, "Components", "Pages", "Index.razor.css"));
  269. var build = new BuildCommand(projectDirectory);
  270. build.Execute().Should().Pass();
  271. var intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().ToString(), "Debug", DefaultTfm);
  272. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css")).Should().NotExist();
  273. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Index.razor.rz.scp.css")).Should().NotExist();
  274. new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "_framework", "scoped.styles.css")).Should().NotExist();
  275. }
  276. [Fact]
  277. public void Build_ScopedCssTransformation_AndBundling_IsIncremental()
  278. {
  279. // Arrange
  280. var thumbprintLookup = new Dictionary<string, FileThumbPrint>();
  281. var testAsset = "RazorComponentApp";
  282. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  283. // Act & Assert 1
  284. var build = new BuildCommand(projectDirectory);
  285. build.Execute().Should().Pass();
  286. var intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().ToString(), "Debug", DefaultTfm);
  287. var directoryPath = Path.Combine(intermediateOutputPath, "scopedcss");
  288. var files = Directory.GetFiles(directoryPath, "*", SearchOption.AllDirectories);
  289. foreach (var file in files)
  290. {
  291. var thumbprint = FileThumbPrint.Create(file);
  292. thumbprintLookup[file] = thumbprint;
  293. }
  294. // Act & Assert 2
  295. for (var i = 0; i < 2; i++)
  296. {
  297. build = new BuildCommand(projectDirectory);
  298. build.Execute().Should().Pass();
  299. foreach (var file in files)
  300. {
  301. var thumbprint = FileThumbPrint.Create(file);
  302. Assert.Equal(thumbprintLookup[file], thumbprint);
  303. }
  304. }
  305. }
  306. [Fact]
  307. public void BuildProjectWithReferences_CorrectlyBundlesScopedCssFiles()
  308. {
  309. var testAsset = "RazorAppWithPackageAndP2PReference";
  310. ProjectDirectory = CreateAspNetSdkTestAsset(testAsset);
  311. var build = new BuildCommand(ProjectDirectory, "AppWithPackageAndP2PReference");
  312. build.WithWorkingDirectory(ProjectDirectory.TestRoot);
  313. build.Execute("/bl").Should().Pass();
  314. var intermediateOutputPath = build.GetIntermediateDirectory(DefaultTfm, "Debug").ToString();
  315. var outputPath = build.GetOutputDirectory(DefaultTfm, "Debug").ToString();
  316. // GenerateStaticWebAssetsManifest should copy the file to the output folder.
  317. var finalPath = Path.Combine(outputPath, "AppWithPackageAndP2PReference.staticwebassets.runtime.json");
  318. new FileInfo(finalPath).Should().Exist();
  319. var buildManifest = StaticWebAssetsManifest.FromJsonBytes(File.ReadAllBytes(Path.Combine(intermediateOutputPath, "staticwebassets.build.json")));
  320. AssertManifest(
  321. buildManifest,
  322. LoadBuildManifest());
  323. AssertBuildAssets(
  324. buildManifest,
  325. outputPath,
  326. intermediateOutputPath);
  327. var appBundle = new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "bundle", "AppWithPackageAndP2PReference.styles.css"));
  328. appBundle.Should().Exist();
  329. appBundle.Should().Contain("_content/ClassLibrary/ClassLibrary.bundle.scp.css");
  330. appBundle.Should().Contain("_content/PackageLibraryDirectDependency/PackageLibraryDirectDependency.bundle.scp.css");
  331. }
  332. [Fact]
  333. public void ScopedCss_IsBackwardsCompatible_WithPreviousVersions()
  334. {
  335. var testAsset = "RazorAppWithPackageAndP2PReference";
  336. ProjectDirectory = CreateAspNetSdkTestAsset(testAsset)
  337. .WithProjectChanges((project, document) =>
  338. {
  339. if (Path.GetFileName(project) == "AnotherClassLib.csproj")
  340. {
  341. document.Descendants("TargetFramework").Single().ReplaceNodes("net5.0");
  342. }
  343. if (Path.GetFileName(project) == "ClassLibrary.csproj")
  344. {
  345. document.Descendants("TargetFramework").Single().ReplaceNodes("net5.0");
  346. }
  347. });
  348. var build = new BuildCommand(ProjectDirectory, "AppWithPackageAndP2PReference");
  349. build.WithWorkingDirectory(ProjectDirectory.TestRoot);
  350. build.Execute("/bl").Should().Pass();
  351. var intermediateOutputPath = build.GetIntermediateDirectory(DefaultTfm, "Debug").ToString();
  352. var outputPath = build.GetOutputDirectory(DefaultTfm, "Debug").ToString();
  353. // GenerateStaticWebAssetsManifest should copy the file to the output folder.
  354. var finalPath = Path.Combine(outputPath, "AppWithPackageAndP2PReference.staticwebassets.runtime.json");
  355. new FileInfo(finalPath).Should().Exist();
  356. var manifest = StaticWebAssetsManifest.FromJsonBytes(File.ReadAllBytes(Path.Combine(intermediateOutputPath, "staticwebassets.build.json")));
  357. AssertManifest(
  358. manifest,
  359. LoadBuildManifest());
  360. AssertBuildAssets(
  361. manifest,
  362. outputPath,
  363. intermediateOutputPath);
  364. var appBundle = new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "bundle", "AppWithPackageAndP2PReference.styles.css"));
  365. appBundle.Should().Exist();
  366. appBundle.Should().Contain("_content/ClassLibrary/ClassLibrary.bundle.scp.css");
  367. appBundle.Should().Contain("_content/PackageLibraryDirectDependency/PackageLibraryDirectDependency.bundle.scp.css");
  368. }
  369. [Fact]
  370. public void ScopedCss_PublishIsBackwardsCompatible_WithPreviousVersions()
  371. {
  372. var testAsset = "RazorAppWithPackageAndP2PReference";
  373. ProjectDirectory = CreateAspNetSdkTestAsset(testAsset)
  374. .WithProjectChanges((project, document) =>
  375. {
  376. if (Path.GetFileName(project) == "AnotherClassLib.csproj")
  377. {
  378. document.Descendants("TargetFramework").Single().ReplaceNodes("net5.0");
  379. }
  380. if (Path.GetFileName(project) == "ClassLibrary.csproj")
  381. {
  382. document.Descendants("TargetFramework").Single().ReplaceNodes("net5.0");
  383. }
  384. });
  385. var build = new PublishCommand(ProjectDirectory, "AppWithPackageAndP2PReference");
  386. build.WithWorkingDirectory(ProjectDirectory.TestRoot);
  387. build.Execute().Should().Pass();
  388. var intermediateOutputPath = build.GetIntermediateDirectory(DefaultTfm, "Debug").ToString();
  389. var outputPath = build.GetOutputDirectory(DefaultTfm, "Debug").ToString();
  390. var finalPath = Path.Combine(intermediateOutputPath, "staticwebassets.publish.json");
  391. new FileInfo(finalPath).Should().Exist();
  392. var publishManifest = StaticWebAssetsManifest.FromJsonBytes(File.ReadAllBytes(Path.Combine(intermediateOutputPath, "staticwebassets.publish.json")));
  393. AssertManifest(
  394. publishManifest,
  395. LoadPublishManifest());
  396. AssertPublishAssets(
  397. publishManifest,
  398. outputPath,
  399. intermediateOutputPath);
  400. var appBundle = new FileInfo(Path.Combine(outputPath, "wwwroot", "AppWithPackageAndP2PReference.styles.css"));
  401. appBundle.Should().Exist();
  402. appBundle.Should().Contain("_content/ClassLibrary/ClassLibrary.bundle.scp.css");
  403. appBundle.Should().Contain("_content/PackageLibraryDirectDependency/PackageLibraryDirectDependency.bundle.scp.css");
  404. }
  405. // This test verifies if the targets that VS calls to update scoped css works to update these files
  406. [Fact]
  407. public void RegeneratingScopedCss_ForProject()
  408. {
  409. // Arrange
  410. var testAsset = "RazorComponentApp";
  411. ProjectDirectory = CreateAspNetSdkTestAsset(testAsset);
  412. var build = new BuildCommand(ProjectDirectory);
  413. build.Execute().Should().Pass();
  414. var intermediateOutputPath = build.GetIntermediateDirectory(DefaultTfm, "Debug").ToString();
  415. var bundlePath = Path.Combine(intermediateOutputPath, "scopedcss", "bundle", "ComponentApp.styles.css");
  416. new FileInfo(bundlePath).Should().Exist();
  417. // Make an edit
  418. var scopedCssFile = Path.Combine(ProjectDirectory.TestRoot, "Components", "Pages", "Index.razor.css");
  419. File.WriteAllLines(scopedCssFile, File.ReadAllLines(scopedCssFile).Concat(new[] { "body { background-color: orangered; }" }));
  420. build = new BuildCommand(ProjectDirectory);
  421. build.Execute("/t:UpdateStaticWebAssetsDesignTime").Should().Pass();
  422. var fileInfo = new FileInfo(bundlePath);
  423. fileInfo.Should().Exist();
  424. // Verify the generated file contains newly added css
  425. fileInfo.ReadAllText().Should().Contain("background-color: orangered");
  426. }
  427. // Regression test for https://github.com/dotnet/aspnetcore/issues/37592
  428. [Fact]
  429. public void RegeneratingScopedCss_ForProjectWithReferences()
  430. {
  431. var testAsset = "RazorAppWithPackageAndP2PReference";
  432. ProjectDirectory = CreateAspNetSdkTestAsset(testAsset);
  433. var scopedCssFile = Path.Combine(ProjectDirectory.Path, "AppWithPackageAndP2PReference", "Index.razor.css");
  434. File.WriteAllText(scopedCssFile, "/* Empty css */");
  435. File.WriteAllText(Path.Combine(ProjectDirectory.Path, "AppWithPackageAndP2PReference", "Index.razor"), "This is a test razor component.");
  436. var build = new BuildCommand(ProjectDirectory, "AppWithPackageAndP2PReference");
  437. build.Execute().Should().Pass();
  438. var intermediateOutputPath = build.GetIntermediateDirectory(DefaultTfm, "Debug").ToString();
  439. var bundlePath = Path.Combine(intermediateOutputPath, "scopedcss", "bundle", "AppWithPackageAndP2PReference.styles.css");
  440. new FileInfo(bundlePath).Should().Exist();
  441. // Make an edit to a scoped css file
  442. File.WriteAllLines(scopedCssFile, File.ReadAllLines(scopedCssFile).Concat(new[] { "body { background-color: orangered; }" }));
  443. build = new BuildCommand(ProjectDirectory, "AppWithPackageAndP2PReference");
  444. build.Execute("/t:UpdateStaticWebAssetsDesignTime").Should().Pass();
  445. var fileInfo = new FileInfo(bundlePath);
  446. fileInfo.Should().Exist();
  447. // Verify the generated file contains newly added css
  448. var text = fileInfo.ReadAllText();
  449. text.Should().Contain("background-color: orangered");
  450. text.Should().Contain("@import '_content/ClassLibrary/ClassLibrary.bundle.scp.css");
  451. }
  452. }
  453. }