diff --git a/src/Framework/Framework/Compilation/ControlTree/DefaultControlResolver.cs b/src/Framework/Framework/Compilation/ControlTree/DefaultControlResolver.cs index 6757086bba..4736b7e1ec 100644 --- a/src/Framework/Framework/Compilation/ControlTree/DefaultControlResolver.cs +++ b/src/Framework/Framework/Compilation/ControlTree/DefaultControlResolver.cs @@ -22,13 +22,13 @@ public class DefaultControlResolver : ControlResolverBase private readonly DotvvmConfiguration configuration; private readonly IControlBuilderFactory controlBuilderFactory; private readonly CompiledAssemblyCache compiledAssemblyCache; + private readonly Dictionary? controlNameMappings; private static object locker = new object(); private static bool isInitialized = false; private static object dotvvmLocker = new object(); private static bool isDotvvmInitialized = false; - private static Dictionary? controlNameMappings; public DefaultControlResolver(DotvvmConfiguration configuration, IControlBuilderFactory controlBuilderFactory, CompiledAssemblyCache compiledAssemblyCache) : base(configuration.Markup) { diff --git a/src/Framework/Framework/Compilation/DefaultControlBuilderFactory.cs b/src/Framework/Framework/Compilation/DefaultControlBuilderFactory.cs index bc2a015642..59f710498f 100644 --- a/src/Framework/Framework/Compilation/DefaultControlBuilderFactory.cs +++ b/src/Framework/Framework/Compilation/DefaultControlBuilderFactory.cs @@ -31,7 +31,7 @@ public class DefaultControlBuilderFactory : IControlBuilderFactory public DefaultControlBuilderFactory(DotvvmConfiguration configuration, IMarkupFileLoader markupFileLoader, CompiledAssemblyCache compiledAssemblyCache) { this.configuration = configuration; - this.allowReload = configuration.Debug; + this.allowReload = configuration.Runtime.ReloadMarkupFiles.Enabled ?? configuration.Debug; // WORKAROUND: there is a circular dependency // TODO: get rid of that diff --git a/src/Framework/Framework/Configuration/Dotvvm3StateFeatureFlag.cs b/src/Framework/Framework/Configuration/Dotvvm3StateFeatureFlag.cs new file mode 100644 index 0000000000..70e51ed3d3 --- /dev/null +++ b/src/Framework/Framework/Configuration/Dotvvm3StateFeatureFlag.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using Newtonsoft.Json; + +namespace DotVVM.Framework.Configuration +{ + /// Overrides an automatically enabled feature by always enabling or disabling it for the entire application or only for certain routes. + public class Dotvvm3StateFeatureFlag: IDotvvmFeatureFlagAdditiveConfiguration + { + [JsonIgnore] + public string FlagName { get; } + + public Dotvvm3StateFeatureFlag(string flagName) + { + FlagName = flagName; + } + + /// Default state of this feature flag. true = enabled, false = disabled, null = enabled automatically based on other conditions (usually running in Development/Production environment) + [JsonProperty("enabled")] + public bool? Enabled + { + get => _enabled; + set + { + ThrowIfFrozen(); + _enabled = value; + } + } + private bool? _enabled = null; + + /// List of routes where the feature flag is always enabled. + [JsonProperty("includedRoutes")] + public ISet IncludedRoutes + { + get => _includedRoutes; + set + { + ThrowIfFrozen(); + _includedRoutes = value; + } + } + private ISet _includedRoutes = new FreezableSet(comparer: StringComparer.OrdinalIgnoreCase); + + /// List of routes where the feature flag is always disabled. + [JsonProperty("excludedRoutes")] + public ISet ExcludedRoutes + { + get => _excludedRoutes; + set + { + ThrowIfFrozen(); + _excludedRoutes = value; + } + } + private ISet _excludedRoutes = new FreezableSet(comparer: StringComparer.OrdinalIgnoreCase); + + /// Resets the feature flag to its default state. + public IDotvvmFeatureFlagAdditiveConfiguration Reset() + { + ThrowIfFrozen(); + IncludedRoutes.Clear(); + ExcludedRoutes.Clear(); + Enabled = null; + return this; + } + + /// Enables the feature flag for all routes, even if it has been previously disabled. + public IDotvvmFeatureFlagAdditiveConfiguration EnableForAllRoutes() + { + ThrowIfFrozen(); + IncludedRoutes.Clear(); + ExcludedRoutes.Clear(); + Enabled = true; + return this; + } + + /// Disables the feature flag for all routes, even if it has been previously enabled. + public IDotvvmFeatureFlagAdditiveConfiguration DisableForAllRoutes() + { + ThrowIfFrozen(); + IncludedRoutes.Clear(); + ExcludedRoutes.Clear(); + Enabled = false; + return this; + } + + /// Enables the feature flag only for the specified routes, and disables for all other (Clears any previous rules). + public void EnableForRoutes(params string[] routes) + { + ThrowIfFrozen(); + Enabled = false; + ExcludedRoutes.Clear(); + + IncludedRoutes.Clear(); + foreach (var route in routes) + { + IncludedRoutes.Add(route); + } + } + + /// Enables the feature flag for all routes except the specified ones (Clears any previous rules). + public void EnableForAllRoutesExcept(params string[] routes) + { + ThrowIfFrozen(); + Enabled = true; + IncludedRoutes.Clear(); + + ExcludedRoutes.Clear(); + foreach (var route in routes) + { + ExcludedRoutes.Add(route); + } + } + + /// Include the specified route in this feature flag. Enables the feature for the route. + public IDotvvmFeatureFlagAdditiveConfiguration IncludeRoute(string routeName) + { + ThrowIfFrozen(); + if (Enabled == true) + throw new InvalidOperationException($"Cannot include route '{routeName}' because the feature flag {this.FlagName} is enabled by default."); + if (ExcludedRoutes.Contains(routeName)) + throw new InvalidOperationException($"Cannot include route '{routeName}' because it is already in the list of excluded routes."); + IncludedRoutes.Add(routeName); + return this; + } + /// Include the specified routes in this feature flag. Enables the feature for the routes. + public IDotvvmFeatureFlagAdditiveConfiguration IncludeRoutes(params string[] routeNames) + { + foreach (var routeName in routeNames) + { + IncludeRoute(routeName); + } + return this; + } + /// Exclude the specified route from this feature flag. Disables the feature for the route. + public IDotvvmFeatureFlagAdditiveConfiguration ExcludeRoute(string routeName) + { + ThrowIfFrozen(); + if (Enabled == false) + throw new InvalidOperationException($"Cannot exclude route '{routeName}' because the feature flag {this.FlagName} is disabled by default."); + if (IncludedRoutes.Contains(routeName)) + throw new InvalidOperationException($"Cannot exclude route '{routeName}' because it is already in the list of included routes."); + ExcludedRoutes.Add(routeName); + return this; + } + /// Exclude the specified routes from this feature flag. Disables the feature for the routes. + public IDotvvmFeatureFlagAdditiveConfiguration ExcludeRoutes(params string[] routeNames) + { + foreach (var routeName in routeNames) + { + ExcludeRoute(routeName); + } + return this; + } + + /// Return true if there exists a route where this feature flag is enabled. + public bool IsEnabledForAnyRoute(bool defaultValue) + { + if (Enabled == false && IncludedRoutes.Count == 0) + return false; + return defaultValue || Enabled == true || IncludedRoutes.Count > 0; + } + + /// Returns true/false if this feature flag has been explicitly enabled/disabled for the specified route. + public bool? IsEnabledForRoute(string? routeName) + { + if (IncludedRoutes.Contains(routeName!)) + return true; + if (ExcludedRoutes.Contains(routeName!)) + return false; + return Enabled; + } + + /// Returns if this feature flag has been explicitly for the specified route. + public bool IsEnabledForRoute(string? routeName, bool defaultValue) + { + defaultValue = Enabled ?? defaultValue; + if (defaultValue) + return !ExcludedRoutes.Contains(routeName!); + else + return IncludedRoutes.Contains(routeName!); + } + + private bool isFrozen = false; + private void ThrowIfFrozen() + { + if (isFrozen) + throw FreezableUtils.Error($"{nameof(DotvvmFeatureFlag)} {this.FlagName}"); + } + public void Freeze() + { + this.isFrozen = true; + FreezableSet.Freeze(ref this._excludedRoutes); + FreezableSet.Freeze(ref this._includedRoutes); + } + + public override string ToString() + { + var defaultStr = Enabled switch { null => "Default state", true => "Enabled by default", false => "Disabled by default" }; + var enabledStr = IncludedRoutes.Count > 0 ? $", enabled for routes: [{string.Join(", ", IncludedRoutes)}]" : null; + var disabledStr = ExcludedRoutes.Count > 0 ? $", disabled for routes: [{string.Join(", ", ExcludedRoutes)}]" : null; + return $"Feature flag {this.FlagName}: {defaultStr}{enabledStr}{disabledStr}"; + } + } +} diff --git a/src/Framework/Framework/Configuration/DotvvmConfiguration.cs b/src/Framework/Framework/Configuration/DotvvmConfiguration.cs index e777b96c61..76f3043031 100644 --- a/src/Framework/Framework/Configuration/DotvvmConfiguration.cs +++ b/src/Framework/Framework/Configuration/DotvvmConfiguration.cs @@ -158,6 +158,7 @@ private void ThrowIfFrozen() public void Freeze() { isFrozen = true; + Diagnostics.Freeze(); Markup.Freeze(); RouteTable.Freeze(); Resources.Freeze(); diff --git a/src/Framework/Framework/Configuration/DotvvmControlConfiguration.cs b/src/Framework/Framework/Configuration/DotvvmControlConfiguration.cs index f333eb220a..5ab3607ddb 100644 --- a/src/Framework/Framework/Configuration/DotvvmControlConfiguration.cs +++ b/src/Framework/Framework/Configuration/DotvvmControlConfiguration.cs @@ -93,7 +93,7 @@ public void Validate() private void ThrowIfFrozen() { if (isFrozen) - FreezableUtils.Error(nameof(DotvvmControlConfiguration)); + throw FreezableUtils.Error(nameof(DotvvmControlConfiguration)); } public void Freeze() { diff --git a/src/Framework/Framework/Configuration/DotvvmFeatureFlag.cs b/src/Framework/Framework/Configuration/DotvvmFeatureFlag.cs index fab9d6c32f..ee44f61e44 100644 --- a/src/Framework/Framework/Configuration/DotvvmFeatureFlag.cs +++ b/src/Framework/Framework/Configuration/DotvvmFeatureFlag.cs @@ -166,7 +166,7 @@ public bool IsEnabledForRoute(string? routeName) private void ThrowIfFrozen() { if (isFrozen) - FreezableUtils.Error($"{nameof(DotvvmFeatureFlag)} {this.FlagName}"); + throw FreezableUtils.Error($"{nameof(DotvvmFeatureFlag)} {this.FlagName}"); } public void Freeze() { diff --git a/src/Framework/Framework/Configuration/DotvvmGlobal3StateFeatureFlag.cs b/src/Framework/Framework/Configuration/DotvvmGlobal3StateFeatureFlag.cs new file mode 100644 index 0000000000..5237f9deda --- /dev/null +++ b/src/Framework/Framework/Configuration/DotvvmGlobal3StateFeatureFlag.cs @@ -0,0 +1,64 @@ +using System; +using Newtonsoft.Json; + +namespace DotVVM.Framework.Configuration +{ + /// Overrides an automatically enabled feature by always enabling or disabling it for the entire application. + public class DotvvmGlobal3StateFeatureFlag + { + [JsonIgnore] + public string FlagName { get; } + + public DotvvmGlobal3StateFeatureFlag(string flagName) + { + FlagName = flagName; + } + + /// Gets or sets whether the feature is enabled or disabled. + [JsonProperty("enabled")] + public bool? Enabled + { + get => _enabled; + set + { + ThrowIfFrozen(); + _enabled = value; + } + } + private bool? _enabled = null; + + /// Resets the feature flag to its default state. + public void Reset() + { + ThrowIfFrozen(); + Enabled = null; + } + + /// Enables the feature for this application. + public void Enable() + { + ThrowIfFrozen(); + Enabled = true; + } + /// Disables the feature for this application + public void Disable() + { + ThrowIfFrozen(); + Enabled = false; + } + + private bool isFrozen = false; + private void ThrowIfFrozen() + { + if (isFrozen) + throw FreezableUtils.Error($"{nameof(DotvvmGlobalFeatureFlag)} {this.FlagName}"); + } + + public void Freeze() + { + this.isFrozen = true; + } + + public override string ToString() => $"Feature flag {FlagName}: {Enabled switch { null => "Default", true => "Enabled", false => "Disabled"}}"; + } +} diff --git a/src/Framework/Framework/Configuration/DotvvmGlobalFeatureFlag.cs b/src/Framework/Framework/Configuration/DotvvmGlobalFeatureFlag.cs index fe1689c93a..eb5fff9a71 100644 --- a/src/Framework/Framework/Configuration/DotvvmGlobalFeatureFlag.cs +++ b/src/Framework/Framework/Configuration/DotvvmGlobalFeatureFlag.cs @@ -47,7 +47,7 @@ public void Disable() private void ThrowIfFrozen() { if (isFrozen) - FreezableUtils.Error($"{nameof(DotvvmGlobalFeatureFlag)} {this.FlagName}"); + throw FreezableUtils.Error($"{nameof(DotvvmGlobalFeatureFlag)} {this.FlagName}"); } public void Freeze() diff --git a/src/Framework/Framework/Configuration/DotvvmMarkupConfiguration.cs b/src/Framework/Framework/Configuration/DotvvmMarkupConfiguration.cs index 3f3ed0bff5..d399b2e60e 100644 --- a/src/Framework/Framework/Configuration/DotvvmMarkupConfiguration.cs +++ b/src/Framework/Framework/Configuration/DotvvmMarkupConfiguration.cs @@ -196,8 +196,9 @@ public void Freeze() foreach (var t in this.HtmlAttributeTransforms) t.Value.Freeze(); _defaultDirectives.Freeze(); - + FreezableList.Freeze(ref _importedNamespaces); JavascriptTranslator.Freeze(); + FreezableList.Freeze(ref _defaultExtensionParameters); } } } diff --git a/src/Framework/Framework/Configuration/DotvvmRuntimeConfiguration.cs b/src/Framework/Framework/Configuration/DotvvmRuntimeConfiguration.cs index e024df0b94..359a5dcd26 100644 --- a/src/Framework/Framework/Configuration/DotvvmRuntimeConfiguration.cs +++ b/src/Framework/Framework/Configuration/DotvvmRuntimeConfiguration.cs @@ -3,7 +3,6 @@ using System.Linq; using Newtonsoft.Json; using DotVVM.Framework.Runtime.Filters; -using DotVVM.Framework.Runtime.Tracing; namespace DotVVM.Framework.Configuration { @@ -16,6 +15,13 @@ public class DotvvmRuntimeConfiguration public IList GlobalFilters => _globalFilters; private IList _globalFilters; + /// + /// When enabled, dothtml files are reloaded and recompiled after a change. Note that resources (CSS, JS) are not controlled by this option. + /// By default, reloading is only enabled in debug mode. + /// + [JsonProperty("reloadMarkupFiles")] + public DotvvmGlobal3StateFeatureFlag ReloadMarkupFiles { get; } = new("Dotvvm3StateFeatureFlag.ReloadMarkupFiles"); + /// /// Initializes a new instance of the class. /// @@ -35,6 +41,7 @@ public void Freeze() { this.isFrozen = true; FreezableList.Freeze(ref _globalFilters); + ReloadMarkupFiles.Freeze(); } } } diff --git a/src/Tests/Runtime/ConfigurationValidationTests.cs b/src/Tests/Runtime/ConfigurationValidationTests.cs index 9d5b6f76a2..86878eec7b 100644 --- a/src/Tests/Runtime/ConfigurationValidationTests.cs +++ b/src/Tests/Runtime/ConfigurationValidationTests.cs @@ -6,6 +6,10 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using DotVVM.Framework.Testing; using DotVVM.Framework.Routing; +using DotVVM.Framework.ResourceManagement; +using System.Threading.Tasks; +using DotVVM.Framework.Compilation; +using DotVVM.Framework.Compilation.Styles; namespace DotVVM.Framework.Tests.Runtime { @@ -62,6 +66,112 @@ public void FeatureFlag_InvalidExclude() XAssert.Equal("Cannot exclude route 'a' because the feature flag myFlag is disabled by default.", e.Message); } + [TestMethod] + public void FeatureFlag3State_InvalidExclude() + { + var flag = new Dotvvm3StateFeatureFlag("myFlag"); + flag.Enabled = false; + var e = XAssert.ThrowsAny(() => flag.ExcludeRoute("a")); + XAssert.Equal("Cannot exclude route 'a' because the feature flag myFlag is disabled by default.", e.Message); + } + + [TestMethod] + public void FeatureFlag3State_ValidOperations() + { + var flag = new Dotvvm3StateFeatureFlag("myFlag"); + + Assert.IsNull(flag.Enabled); + Assert.IsNull(flag.IsEnabledForRoute("a")); + Assert.IsTrue(flag.IsEnabledForRoute("a", true)); + + flag.EnableForAllRoutes(); + Assert.IsTrue(flag.Enabled); + Assert.IsTrue(flag.IsEnabledForRoute("a", false)); + XAssert.Empty(flag.IncludedRoutes); + XAssert.Empty(flag.ExcludedRoutes); + + flag.ExcludeRoute("a"); + Assert.IsTrue(flag.Enabled); + XAssert.Contains("a", flag.ExcludedRoutes); + Assert.IsFalse(flag.IsEnabledForRoute("a")); + Assert.IsTrue(flag.IsEnabledForRoute("b")); + + flag.DisableForAllRoutes() + .IncludeRoute("a") + .IncludeRoute("b"); + Assert.IsFalse(flag.Enabled); + XAssert.Contains("a", flag.IncludedRoutes); + XAssert.Contains("b", flag.IncludedRoutes); + XAssert.Empty(flag.ExcludedRoutes); + + flag.EnableForRoutes("x", "y"); + Assert.IsFalse(flag.Enabled); + XAssert.Equal(new [] { "x", "y" }, flag.IncludedRoutes); + XAssert.Empty(flag.ExcludedRoutes); + + flag.Reset().IncludeRoute("always-enabled").ExcludeRoute("always-disabled"); + Assert.IsNull(flag.IsEnabledForRoute("a")); + Assert.IsTrue(flag.IsEnabledForRoute("always-enabled")); + Assert.IsFalse(flag.IsEnabledForRoute("always-disabled")); + } + + [TestMethod] + public void FeatureFlagGlobal3State_ValidOperations() + { + var flag = new DotvvmGlobal3StateFeatureFlag("myFlag"); + Assert.IsNull(flag.Enabled); + flag.Enable(); + Assert.IsTrue(flag.Enabled); + flag.Disable(); + Assert.IsFalse(flag.Enabled); + flag.Reset(); + Assert.IsNull(flag.Enabled); + flag.Freeze(); + XAssert.ThrowsAny(() => flag.Enabled = true); + } + + [TestMethod] + public void Freezing() + { + var config = DotvvmTestHelper.CreateConfiguration(); + config.Freeze(); + XAssert.ThrowsAny(() => config.RouteTable.Add("a", "a", null, null, presenterFactory: _ => throw new NotImplementedException())); + XAssert.ThrowsAny(() => config.RouteTable.AddRouteRedirection("b", "url", "a")); + XAssert.ThrowsAny(() => config.ApplicationPhysicalPath = "a"); + XAssert.ThrowsAny(() => config.Debug = true); + XAssert.ThrowsAny(() => config.DefaultCulture = "a"); + XAssert.ThrowsAny(() => config.Diagnostics.PerfWarnings.BigViewModelBytes = 1); + XAssert.ThrowsAny(() => config.Diagnostics.PerfWarnings.IsEnabled = false); + XAssert.ThrowsAny(() => config.Diagnostics.CompilationPage.AuthorizationPredicate = _ => Task.FromResult(true)); + XAssert.ThrowsAny(() => config.Diagnostics.CompilationPage.RouteName = "a"); + XAssert.ThrowsAny(() => config.Resources.DefaultResourceProcessors.Add(new SpaModeResourceProcessor(config))); + // adding resources is actually explicitly allowed as they are stored in a ConcurrentDictionary + config.Resources.Register("my-test-resource", new InlineScriptResource("alert(1)")); + XAssert.ThrowsAny(() => config.RouteConstraints.Add("a", new GenericRouteParameterType(_ => "..", (_, _) => new ParameterParseResult(true, "yes")))); + XAssert.ThrowsAny(() => config.Markup.Assemblies.Add("aa")); + XAssert.ThrowsAny(() => config.Markup.ViewCompilation.CompileInParallel = false); + XAssert.ThrowsAny(() => config.Markup.ImportedNamespaces.Add(new("ns1"))); + XAssert.ThrowsAny(() => config.Markup.DefaultDirectives.Add(new("import", "ABC"))); + XAssert.ThrowsAny(() => config.Markup.HtmlAttributeTransforms.Remove(new HtmlTagAttributePair { TagName = "a", AttributeName = "href" })); + XAssert.ThrowsAny(() => config.Runtime.ReloadMarkupFiles.Enable()); + XAssert.ThrowsAny(() => config.Runtime.GlobalFilters.Add(null)); + XAssert.ThrowsAny(() => config.Security.ContentTypeOptionsHeader.IncludedRoutes.Add("abc")); + XAssert.ThrowsAny(() => config.Security.ContentTypeOptionsHeader.ExcludedRoutes.Add("abc")); + XAssert.ThrowsAny(() => config.Security.FrameOptionsCrossOrigin.Enabled = true); + XAssert.ThrowsAny(() => config.Security.FrameOptionsSameOrigin.Enabled = true); + XAssert.ThrowsAny(() => config.Security.FrameOptionsSameOrigin.Enabled = true); + XAssert.ThrowsAny(() => config.Security.RequireSecFetchHeaders.Enabled = true); + XAssert.ThrowsAny(() => config.Security.VerifySecFetchForCommands.Enabled = true); + XAssert.ThrowsAny(() => config.Security.XssProtectionHeader.Enabled = true); + XAssert.ThrowsAny(() => config.Security.ReferrerPolicyValue = "aa"); + XAssert.ThrowsAny(() => config.Security.SessionIdCookieName = "aa"); + XAssert.ThrowsAny(() => config.Styles.RegisterForTag("div").SetAttribute("data-a", "b")); + XAssert.ThrowsAny(() => config.Styles.Styles = new List()); + XAssert.ThrowsAny(() => config.Styles.Styles.RemoveAt(0)); + XAssert.ThrowsAny(() => config.Styles.Styles.Add(null)); + + } + [TestMethod] public void ValidateMissingRoutes() { diff --git a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/BaseTypeDirectiveTests.cs b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/BaseTypeDirectiveTests.cs index e57a53b185..f325b905b3 100644 --- a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/BaseTypeDirectiveTests.cs +++ b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/BaseTypeDirectiveTests.cs @@ -7,6 +7,7 @@ using DotVVM.Framework.Binding.Expressions; using System.Threading.Tasks; using DotVVM.Framework.Compilation; +using DotVVM.Framework.Testing; namespace DotVVM.Framework.Tests.Runtime.ControlTree { @@ -57,29 +58,31 @@ @baseType controlAlias [TestMethod] public void ResolvedTree_BaseTypeDirective_CorrectBindings_UsingGlobalAliasedType() { - configuration.Markup.ImportedNamespaces.Add(new NamespaceImport($"DotVVM.Framework.Tests.Runtime.ControlTree.{nameof(TestControl)}", "controlAlias")); - var root = ParseSource(@$" + var config = DotvvmTestHelper.CreateConfiguration(); + config.Markup.ImportedNamespaces.Add(new NamespaceImport($"DotVVM.Framework.Tests.Runtime.ControlTree.{nameof(TestControl)}", "controlAlias")); + var root = DotvvmTestHelper.ParseResolvedTree(@$" @viewModel object @baseType controlAlias -"); +", configuration: config); CheckServiceAndBinding(root); } [TestMethod] public void ResolvedTree_BaseTypeDirective_CorrectBindings_UsingGlobalImportedNamespace() { - configuration.Markup.ImportedNamespaces.Add(new NamespaceImport("DotVVM.Framework.Tests.Runtime.ControlTree")); + var config = DotvvmTestHelper.CreateConfiguration(); + config.Markup.ImportedNamespaces.Add(new NamespaceImport("DotVVM.Framework.Tests.Runtime.ControlTree")); - var root = ParseSource(@$" + var root = DotvvmTestHelper.ParseResolvedTree(@$" @viewModel object @baseType {nameof(TestControl)} -"); +", configuration: config); CheckServiceAndBinding(root); } diff --git a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/MarkupDeclaredPropertyTests.cs b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/MarkupDeclaredPropertyTests.cs index b42ccdd5e4..21701f8e03 100644 --- a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/MarkupDeclaredPropertyTests.cs +++ b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/MarkupDeclaredPropertyTests.cs @@ -3,6 +3,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using DotVVM.Framework.Utils; using System; +using DotVVM.Framework.Testing; using System.Collections.Generic; using DotVVM.Framework.Compilation; @@ -103,10 +104,11 @@ @property List Items [TestMethod] public void ResolvedTree_MarkupDeclaredProperty_GlobalImportUsed() { - configuration.Markup.ImportedNamespaces.Add(new NamespaceImport("System.Collections.Generic")); - configuration.Markup.ImportedNamespaces.Add(new NamespaceImport("System")); + var config = DotvvmTestHelper.CreateConfiguration(); + config.Markup.ImportedNamespaces.Add(new NamespaceImport("System.Collections.Generic")); + config.Markup.ImportedNamespaces.Add(new NamespaceImport("System")); - var root = ParseSource(@$" + var root = DotvvmTestHelper.ParseResolvedTree(@$" @viewModel object @property List Items @@ -114,7 +116,7 @@ @property List Items
  • {{{{value: _this}}}}
  • ", -fileName: "control.dotcontrol"); +fileName: "control.dotcontrol", configuration: config); CheckPropertyAndBinding(root, typeof(List), "Items"); } diff --git a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/ServiceDirectiveTests.cs b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/ServiceDirectiveTests.cs index a776685f22..e7f98ee30b 100644 --- a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/ServiceDirectiveTests.cs +++ b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/ServiceDirectiveTests.cs @@ -5,6 +5,7 @@ using DotVVM.Framework.Tests.Runtime.ControlTree.DefaultControlTreeResolver; using DotVVM.Framework.Compilation.ControlTree; using DotVVM.Framework.Compilation; +using DotVVM.Framework.Testing; namespace DotVVM.Framework.Tests.Runtime.ControlTree { @@ -52,30 +53,32 @@ @viewModel object [TestMethod] public void ResolvedTree_ServiceDirective_CorrectBindingFromInjectedService_UsingGlobalImportedAliasedNamespace() { - configuration.Markup.ImportedNamespaces.Add(new NamespaceImport( + var config = DotvvmTestHelper.CreateConfiguration(); + config.Markup.ImportedNamespaces.Add(new NamespaceImport( $"DotVVM.Framework.Tests.Runtime.ControlTree.DefaultControlTreeResolver.{nameof(TestService)}", "testServiceAlias")); - var root = ParseSource(@$" + var root = DotvvmTestHelper.ParseResolvedTree(@$" @viewModel object @service testService = testServiceAlias -"); +", configuration: config); CheckServiceAndBinding(root); } [TestMethod] public void ResolvedTree_ServiceDirective_CorrectBindingFromInjectedService_UsingGlobalImportedNamespace() { - configuration.Markup.ImportedNamespaces.Add(new NamespaceImport($"DotVVM.Framework.Tests.Runtime.ControlTree.DefaultControlTreeResolver")); + var config = DotvvmTestHelper.CreateConfiguration(); + config.Markup.ImportedNamespaces.Add(new NamespaceImport($"DotVVM.Framework.Tests.Runtime.ControlTree.DefaultControlTreeResolver")); - var root = ParseSource(@$" + var root = DotvvmTestHelper.ParseResolvedTree(@$" @viewModel object @service testService = {nameof(TestService)} -"); +", configuration: config); CheckServiceAndBinding(root); } diff --git a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/ViewModelDirectiveTest.cs b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/ViewModelDirectiveTest.cs index 4176eb21b9..78eefccd23 100644 --- a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/ViewModelDirectiveTest.cs +++ b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/ViewModelDirectiveTest.cs @@ -4,6 +4,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using DotVVM.Framework.Compilation.Binding; using DotVVM.Framework.Compilation; +using DotVVM.Framework.Testing; namespace DotVVM.Framework.Tests.Runtime.ControlTree { @@ -70,9 +71,10 @@ @viewModel viewModelAlias [TestMethod] public void ResolvedTree_ViewModel_TypeFromGlobalImportedAliasedType() { - configuration.Markup.ImportedNamespaces.Add(new NamespaceImport("DotVVM.Framework.Tests.Runtime.ControlTree.TestViewModel", "viewModelAlias")); + var config = DotvvmTestHelper.CreateConfiguration(); + config.Markup.ImportedNamespaces.Add(new NamespaceImport("DotVVM.Framework.Tests.Runtime.ControlTree.TestViewModel", "viewModelAlias")); - var root = ParseSource(@"@viewModel viewModelAlias"); + var root = DotvvmTestHelper.ParseResolvedTree(@"@viewModel viewModelAlias", configuration: config); Assert.IsFalse(root.Directives.Any(d => d.Value.Any(dd => dd.DothtmlNode.HasNodeErrors))); Assert.AreEqual(typeof(TestViewModel), root.DataContextTypeStack.DataContextType); @@ -81,9 +83,10 @@ public void ResolvedTree_ViewModel_TypeFromGlobalImportedAliasedType() [TestMethod] public void ResolvedTree_ViewModel_TypeFromGlobalImportedNamespace() { - configuration.Markup.ImportedNamespaces.Add(new NamespaceImport("DotVVM.Framework.Tests.Runtime.ControlTree")); + var config = DotvvmTestHelper.CreateConfiguration(); + config.Markup.ImportedNamespaces.Add(new NamespaceImport("DotVVM.Framework.Tests.Runtime.ControlTree")); - var root = ParseSource(@"@viewModel TestViewModel"); + var root = DotvvmTestHelper.ParseResolvedTree(@"@viewModel TestViewModel", configuration: config); Assert.IsFalse(root.Directives.Any(d => d.Value.Any(dd => dd.DothtmlNode.HasNodeErrors))); Assert.AreEqual(typeof(TestViewModel), root.DataContextTypeStack.DataContextType); diff --git a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.AuxOptions.json b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.AuxOptions.json index a3052db825..03804aaaef 100644 --- a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.AuxOptions.json +++ b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.AuxOptions.json @@ -33,7 +33,9 @@ "enabled": true } }, - "runtime": {}, + "runtime": { + "reloadMarkupFiles": {} + }, "defaultCulture": "cs-CZ", "clientSideValidation": false, "experimentalFeatures": {}, diff --git a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.ExperimentalFeatures.json b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.ExperimentalFeatures.json index 22fb50bbd8..e9fab14acf 100644 --- a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.ExperimentalFeatures.json +++ b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.ExperimentalFeatures.json @@ -32,7 +32,9 @@ "enabled": true } }, - "runtime": {}, + "runtime": { + "reloadMarkupFiles": {} + }, "defaultCulture": "en-US", "experimentalFeatures": { "lazyCsrfToken": { diff --git a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.Markup.json b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.Markup.json index 3c276f5e06..e28d691727 100644 --- a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.Markup.json +++ b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.Markup.json @@ -74,7 +74,9 @@ "enabled": true } }, - "runtime": {}, + "runtime": { + "reloadMarkupFiles": {} + }, "defaultCulture": "en-US", "experimentalFeatures": {}, "debug": false, diff --git a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.RestAPI.json b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.RestAPI.json index 988f7ad020..d8998c75b9 100644 --- a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.RestAPI.json +++ b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.RestAPI.json @@ -64,7 +64,9 @@ "enabled": true } }, - "runtime": {}, + "runtime": { + "reloadMarkupFiles": {} + }, "defaultCulture": "en-US", "experimentalFeatures": {}, "debug": false, diff --git a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeDefaultConfig.json b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeDefaultConfig.json index 9f0d245af5..63a2baef13 100644 --- a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeDefaultConfig.json +++ b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeDefaultConfig.json @@ -147,7 +147,9 @@ "enabled": true } }, - "runtime": {}, + "runtime": { + "reloadMarkupFiles": {} + }, "defaultCulture": "en-US", "experimentalFeatures": { "explicitAssemblyLoading": { diff --git a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeEmptyConfig.json b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeEmptyConfig.json index b0aceb4faa..cdb7088016 100644 --- a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeEmptyConfig.json +++ b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeEmptyConfig.json @@ -32,7 +32,9 @@ "enabled": true } }, - "runtime": {}, + "runtime": { + "reloadMarkupFiles": {} + }, "defaultCulture": "en-US", "experimentalFeatures": {}, "debug": false, diff --git a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeResources.json b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeResources.json index a2a4e53c45..2b4dc1067b 100644 --- a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeResources.json +++ b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeResources.json @@ -115,7 +115,9 @@ "enabled": true } }, - "runtime": {}, + "runtime": { + "reloadMarkupFiles": {} + }, "defaultCulture": "en-US", "experimentalFeatures": {}, "debug": false, diff --git a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeRoutes.json b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeRoutes.json index f3923f8745..fb5d40095e 100644 --- a/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeRoutes.json +++ b/src/Tests/Runtime/config-tests/ConfigurationSerializationTests.SerializeRoutes.json @@ -63,7 +63,9 @@ "enabled": true } }, - "runtime": {}, + "runtime": { + "reloadMarkupFiles": {} + }, "defaultCulture": "en-US", "experimentalFeatures": {}, "debug": false,