Example #1
0
        protected TagBuilder GenerateLabel(ModelExpression modelExpression)
        {
            TagBuilder tb = new TagBuilder("label");

              tb.MergeAttribute("for", this.GetIdentity(modelExpression));
              tb.SetInnerText(modelExpression.Metadata.DisplayName);
              return tb;
        }
        private TagBuilder GenerateText(ModelExpression modelExpression)
        {
            TagBuilder tb = new TagBuilder("span");

              tb.Attributes.Add("class", "text");
              tb.SetInnerText(modelExpression.Metadata.DisplayName);
              return tb;
        }
        private TagBuilder GenerateText(ViewContext viewContext, ModelExpression modelExpression, IEnumerable<OptionViewModel> options)
        {
            TagBuilder tb = new TagBuilder("a");

              tb.AddCssClass("text");
              tb.Attributes.Add("href", "#");
              tb.SetInnerText(this.GetText(viewContext, modelExpression, options));
              return tb;
        }
        private string GetValue(ViewContext viewContext, ModelExpression modelExpression)
        {
            string value = base.GetValue(viewContext, modelExpression);

              if (string.IsNullOrEmpty(value))
            return false.ToString().ToLower();

              return value.ToLower();
        }
Example #5
0
        protected TagBuilder GenerateLabel(ModelExpression modelExpression)
        {
            TagBuilder tb = new TagBuilder("label");

              tb.MergeAttribute("for", this.GetIdentity(modelExpression));
              tb.InnerHtml.Clear();
              tb.InnerHtml.AppendEncoded(modelExpression.Metadata.DisplayName);
              return tb;
        }
        private TagBuilder GenerateText(ModelExpression modelExpression)
        {
            TagBuilder tb = new TagBuilder("span");

              tb.Attributes.Add("class", "text");
              tb.InnerHtml.Clear();
              tb.InnerHtml.AppendEncoded(modelExpression.Metadata.DisplayName);
              return tb;
        }
        private TagBuilder GenerateInput(ViewContext viewContext, ModelExpression modelExpression, IEnumerable<OptionViewModel> options)
        {
            TagBuilder tb = new TagBuilder("input");

              tb.Attributes.Add("name", this.GetIdentity(modelExpression));
              tb.Attributes.Add("type", "hidden");
              tb.Attributes.Add("value", this.GetValue(viewContext, modelExpression, options));
              return tb;
        }
        private TagBuilder GenerateIndicator(ViewContext viewContext, ModelExpression modelExpression)
        {
            TagBuilder tb = new TagBuilder("span");

              if (this.GetValue(viewContext, modelExpression) == true.ToString().ToLower())
            tb.Attributes.Add("class", "indicator checked");

              else tb.Attributes.Add("class", "indicator");

              return tb;
        }
Example #9
0
        protected void HandleAttributes(TagBuilder tb, ModelExpression modelExpression)
        {
            foreach (object validatorMetadata in modelExpression.Metadata.ValidatorMetadata)
              {
            if (validatorMetadata is RequiredAttribute)
              this.HandleRequiredAttribute(tb, validatorMetadata as RequiredAttribute);

            else if (validatorMetadata is StringLengthAttribute)
              this.HandleStringLengthAttribute(tb, validatorMetadata as StringLengthAttribute);
              }
        }
Example #10
0
        protected string GetValue(ViewContext viewContext, ModelExpression modelExpression, LocalizationViewModel localization = null)
        {
            ModelState modelState;

              if (viewContext.ModelState.TryGetValue(this.GetIdentity(modelExpression, localization), out modelState) && modelState.Value != null)
            return modelState.Value.AttemptedValue;

              if (localization != null)
            return localization.Value;

              return modelExpression.Model == null ? null : modelExpression.Model.ToString();
        }
Example #11
0
        protected string GetIdentity(ModelExpression modelExpression, LocalizationViewModel localization = null)
        {
            string identity = string.Empty;

              if (modelExpression.Name.Length == 1)
            identity = modelExpression.Name[0].ToString().ToLower();

              identity = modelExpression.Name.Remove(1).ToLower() + modelExpression.Name.Substring(1);

              if (localization != null)
            identity += localization.Culture.Code;

              return identity;
        }
        protected TagBuilder GenerateCheckBox(ViewContext viewContext, ModelExpression modelExpression)
        {
            TagBuilder tb = new TagBuilder("a");

              tb.AddCssClass("checkbox");
              tb.MergeAttribute("id", this.GetIdentity(modelExpression));
              tb.MergeAttribute("href", "#");
              tb.InnerHtml = new CompositeHtmlContent(
            this.GenerateIndicator(viewContext, modelExpression),
            this.GenerateText(modelExpression),
            this.GenerateInput(viewContext, modelExpression)
              );

              return tb;
        }
        protected TagBuilder GenerateInput(ViewContext viewContext, ModelExpression modelExpression, Localization localization = null, string type = "text")
        {
            TagBuilder tb = new TagBuilder("input");

              if (!this.IsValid(viewContext, modelExpression, localization))
            tb.AddCssClass("input-validation-error");

              tb.MergeAttribute("id", this.GetIdentity(modelExpression, localization));
              tb.MergeAttribute("name", this.GetIdentity(modelExpression, localization));
              tb.MergeAttribute("type", type);
              tb.MergeAttribute("value", this.GetValue(viewContext, modelExpression, localization));
              this.HandleAttributes(tb, modelExpression);
              tb.TagRenderMode = TagRenderMode.SelfClosing;
              return tb;
        }
        private OptionViewModel GetOption(ViewContext viewContext, ModelExpression modelExpression, IEnumerable<OptionViewModel> options)
        {
            string value = this.GetValue(viewContext, modelExpression);
              OptionViewModel option = null;

              if (!string.IsNullOrEmpty(value))
            option = options.FirstOrDefault(o => o.Value == value);

              if (option == null)
            if (modelExpression.Model != null)
              option = options.FirstOrDefault(o => o.Value == modelExpression.Model.ToString());

              if (option == null)
            option = options.FirstOrDefault();

              return option;
        }
        protected TagBuilder GenerateDropDownList(ViewContext viewContext, ModelExpression modelExpression, IEnumerable<OptionViewModel> options)
        {
            TagBuilder tb = new TagBuilder("div");

              tb.AddCssClass("drop-down-list");

              if (!this.IsValid(viewContext, modelExpression))
            tb.AddCssClass("input-validation-error");

              tb.MergeAttribute("id", this.GetIdentity(modelExpression));
              this.HandleAttributes(tb, modelExpression);
              tb.InnerHtml = new CompositeHtmlContent(
            this.GenerateText(viewContext, modelExpression, options),
            this.GenerateDropDownListItems(options),
            this.GenerateInput(viewContext, modelExpression, options)
              );

              return tb;
        }
        private string GetValue(ViewContext viewContext, ModelExpression modelExpression, IEnumerable<OptionViewModel> options)
        {
            OptionViewModel option = this.GetOption(viewContext, modelExpression, options);

              if (option == null)
            return null;

              return option.Value;
        }
Example #17
0
        private static InputTagHelper GetTagHelper(
            IHtmlGenerator htmlGenerator,
            object container,
            Type containerType,
            object model,
            string propertyName,
            string expressionName,
            IModelMetadataProvider metadataProvider = null)
        {
            if (metadataProvider == null)
            {
                metadataProvider = new TestModelMetadataProvider();
            }

            var containerMetadata = metadataProvider.GetMetadataForType(containerType);
            var containerExplorer = metadataProvider.GetModelExplorerForType(containerType, container);

            var propertyMetadata = metadataProvider.GetMetadataForProperty(containerType, propertyName);
            var modelExplorer = containerExplorer.GetExplorerForExpression(propertyMetadata, model);

            var modelExpression = new ModelExpression(expressionName, modelExplorer);
            var viewContext = TestableHtmlGenerator.GetViewContext(container, htmlGenerator, metadataProvider);
            var inputTagHelper = new InputTagHelper(htmlGenerator)
            {
                For = modelExpression,
                ViewContext = viewContext,
            };

            return inputTagHelper;
        }
Example #18
0
        public async Task ProcessAsync_WithItems_GeneratesExpectedOutput_DoesNotChangeSelectList(
            object model,
            Type containerType,
            Func<object> modelAccessor,
            NameAndId nameAndId,
            string expectedOptions)
        {
            // Arrange
            var originalAttributes = new TagHelperAttributeList
            {
                { "class", "form-control" },
            };
            var originalPostContent = "original content";

            var expectedAttributes = new TagHelperAttributeList(originalAttributes)
            {
                { "id", nameAndId.Id },
                { "name", nameAndId.Name },
                { "valid", "from validation attributes" },
            };
            var expectedPreContent = "original pre-content";
            var expectedContent = "original content";
            var expectedPostContent = originalPostContent + expectedOptions;
            var expectedTagName = "select";

            var metadataProvider = new TestModelMetadataProvider();

            var containerMetadata = metadataProvider.GetMetadataForType(containerType);
            var containerExplorer = metadataProvider.GetModelExplorerForType(containerType, model);

            var propertyMetadata = metadataProvider.GetMetadataForProperty(containerType, "Text");
            var modelExplorer = containerExplorer.GetExplorerForExpression(propertyMetadata, modelAccessor());

            var modelExpression = new ModelExpression(nameAndId.Name, modelExplorer);

            var tagHelperContext = new TagHelperContext(
                allAttributes: new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
                    Enumerable.Empty<IReadOnlyTagHelperAttribute>()),
                items: new Dictionary<object, object>(),
                uniqueId: "test");
            var output = new TagHelperOutput(
                expectedTagName,
                originalAttributes,
                getChildContentAsync: useCachedResult =>
                {
                    var tagHelperContent = new DefaultTagHelperContent();
                    tagHelperContent.AppendHtml("Something");
                    return Task.FromResult<TagHelperContent>(tagHelperContent);
                })
            {
                TagMode = TagMode.SelfClosing,
            };
            output.PreContent.AppendHtml(expectedPreContent);
            output.Content.AppendHtml(expectedContent);
            output.PostContent.AppendHtml(originalPostContent);

            var htmlGenerator = new TestableHtmlGenerator(metadataProvider)
            {
                ValidationAttributes =
                {
                    {  "valid", "from validation attributes" },
                }
            };
            var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);

            var items = new SelectList(new[] { "", "outer text", "inner text", "other text" });
            var savedDisabled = items.Select(item => item.Disabled).ToList();
            var savedGroup = items.Select(item => item.Group).ToList();
            var savedSelected = items.Select(item => item.Selected).ToList();
            var savedText = items.Select(item => item.Text).ToList();
            var savedValue = items.Select(item => item.Value).ToList();

            var tagHelper = new SelectTagHelper(htmlGenerator)
            {
                For = modelExpression,
                Items = items,
                ViewContext = viewContext,
            };

            // Act
            tagHelper.Init(tagHelperContext);
            await tagHelper.ProcessAsync(tagHelperContext, output);

            // Assert
            Assert.Equal(TagMode.SelfClosing, output.TagMode);
            Assert.Equal(expectedAttributes, output.Attributes);
            Assert.Equal(expectedPreContent, output.PreContent.GetContent());
            Assert.Equal(expectedContent, output.Content.GetContent());
            Assert.Equal(expectedPostContent, HtmlContentUtilities.HtmlContentToString(output.PostContent));
            Assert.Equal(expectedTagName, output.TagName);

            Assert.Single(
                tagHelperContext.Items,
                entry => (Type)entry.Key == typeof(SelectTagHelper));

            Assert.Equal(savedDisabled, items.Select(item => item.Disabled));
            Assert.Equal(savedGroup, items.Select(item => item.Group));
            Assert.Equal(savedSelected, items.Select(item => item.Selected));
            Assert.Equal(savedText, items.Select(item => item.Text));
            Assert.Equal(savedValue, items.Select(item => item.Value));
        }
Example #19
0
        public async Task ProcessAsync_GeneratesExpectedOutput(
            object model,
            Type containerType,
            Func<object> modelAccessor,
            NameAndId nameAndId,
            string ignored)
        {
            // Arrange
            var originalAttributes = new TagHelperAttributeList
            {
                { "class", "form-control" },
            };
            var originalPostContent = "original content";

            var expectedAttributes = new TagHelperAttributeList(originalAttributes)
            {
                { "id", nameAndId.Id },
                { "name", nameAndId.Name },
                { "valid", "from validation attributes" },
            };
            var expectedPreContent = "original pre-content";
            var expectedContent = "original content";
            var expectedPostContent = originalPostContent;
            var expectedTagName = "not-select";

            var metadataProvider = new TestModelMetadataProvider();
            var containerMetadata = metadataProvider.GetMetadataForType(containerType);
            var containerExplorer = metadataProvider.GetModelExplorerForType(containerType, model);

            var propertyMetadata = metadataProvider.GetMetadataForProperty(containerType, "Text");
            var modelExplorer = containerExplorer.GetExplorerForExpression(propertyMetadata, modelAccessor());

            var modelExpression = new ModelExpression(nameAndId.Name, modelExplorer);

            var tagHelperContext = new TagHelperContext(
                allAttributes: new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
                    Enumerable.Empty<IReadOnlyTagHelperAttribute>()),
                items: new Dictionary<object, object>(),
                uniqueId: "test");
            var output = new TagHelperOutput(
                expectedTagName,
                originalAttributes,
                getChildContentAsync: useCachedResult =>
                {
                    var tagHelperContent = new DefaultTagHelperContent();
                    tagHelperContent.SetContent("Something");
                    return Task.FromResult<TagHelperContent>(tagHelperContent);
                })
            {
                TagMode = TagMode.SelfClosing,
            };
            output.PreContent.SetContent(expectedPreContent);
            output.Content.SetContent(expectedContent);
            output.PostContent.SetContent(originalPostContent);

            var htmlGenerator = new TestableHtmlGenerator(metadataProvider)
            {
                ValidationAttributes =
                {
                    {  "valid", "from validation attributes" },
                }
            };
            var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
            var tagHelper = new SelectTagHelper(htmlGenerator)
            {
                For = modelExpression,
                ViewContext = viewContext,
            };

            // Act
            tagHelper.Init(tagHelperContext);
            await tagHelper.ProcessAsync(tagHelperContext, output);

            // Assert
            Assert.Equal(TagMode.SelfClosing, output.TagMode);
            Assert.Equal(expectedAttributes, output.Attributes);
            Assert.Equal(expectedPreContent, output.PreContent.GetContent());
            Assert.Equal(expectedContent, output.Content.GetContent());
            Assert.Equal(expectedPostContent, output.PostContent.GetContent());
            Assert.Equal(expectedTagName, output.TagName);

            Assert.Single(
                tagHelperContext.Items,
                entry => (Type)entry.Key == typeof(SelectTagHelper));
        }
Example #20
0
        public async Task Process_GeneratesExpectedOutput(
            object container,
            Type containerType,
            object model,
            NameAndId nameAndId,
            string expectedContent)
        {
            // Arrange
            var expectedAttributes = new TagHelperAttributeList
            {
                { "class", "form-control" },
                { "id", nameAndId.Id },
                { "name", nameAndId.Name },
                {  "valid", "from validation attributes" },
            };
            var expectedTagName = "not-textarea";

            var metadataProvider = new TestModelMetadataProvider();

            var containerMetadata = metadataProvider.GetMetadataForType(containerType);
            var containerExplorer = metadataProvider.GetModelExplorerForType(containerType, container);

            var propertyMetadata = metadataProvider.GetMetadataForProperty(containerType, "Text");
            var modelExplorer = containerExplorer.GetExplorerForExpression(propertyMetadata, model);

            var htmlGenerator = new TestableHtmlGenerator(metadataProvider)
            {
                ValidationAttributes =
                {
                    {  "valid", "from validation attributes" },
                }
            };

            // Property name is either nameof(Model.Text) or nameof(NestedModel.Text).
            var modelExpression = new ModelExpression(nameAndId.Name, modelExplorer);
            var tagHelper = new TextAreaTagHelper(htmlGenerator)
            {
                For = modelExpression,
            };

            var tagHelperContext = new TagHelperContext(
                allAttributes: new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
                    Enumerable.Empty<IReadOnlyTagHelperAttribute>()),
                items: new Dictionary<object, object>(),
                uniqueId: "test",
                getChildContentAsync: useCachedResult =>
                {
                    var tagHelperContent = new DefaultTagHelperContent();
                    tagHelperContent.SetContent("Something");
                    return Task.FromResult<TagHelperContent>(tagHelperContent);
                });
            var htmlAttributes = new TagHelperAttributeList
            {
                { "class", "form-control" },
            };
            var output = new TagHelperOutput(expectedTagName, htmlAttributes)
            {
                TagMode = TagMode.SelfClosing,
            };
            output.Content.SetContent("original content");

            var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
            tagHelper.ViewContext = viewContext;

            // Act
            await tagHelper.ProcessAsync(tagHelperContext, output);

            // Assert
            Assert.Equal(TagMode.SelfClosing, output.TagMode);
            Assert.Equal(expectedAttributes, output.Attributes);
            Assert.Equal(expectedContent, HtmlContentUtilities.HtmlContentToString(output.Content));
            Assert.Equal(expectedTagName, output.TagName);
        }
Example #21
0
        public async Task ProcessAsync_GeneratesExpectedOutput(
            object model,
            Type containerType,
            Func<object> modelAccessor,
            string propertyPath,
            TagHelperOutputContent tagHelperOutputContent)
        {
            // Arrange
            var expectedTagName = "not-label";
            var expectedAttributes = new TagHelperAttributeList
            {
                { "class", "form-control" },
                { "for", tagHelperOutputContent.ExpectedId }
            };
            var metadataProvider = new TestModelMetadataProvider();

            var containerMetadata = metadataProvider.GetMetadataForType(containerType);
            var containerExplorer = metadataProvider.GetModelExplorerForType(containerType, model);

            var propertyMetadata = metadataProvider.GetMetadataForProperty(containerType, "Text");
            var modelExplorer = containerExplorer.GetExplorerForExpression(propertyMetadata, modelAccessor());
            var htmlGenerator = new TestableHtmlGenerator(metadataProvider);

            var modelExpression = new ModelExpression(propertyPath, modelExplorer);
            var tagHelper = new LabelTagHelper(htmlGenerator)
            {
                For = modelExpression,
            };
            var expectedPreContent = "original pre-content";
            var expectedPostContent = "original post-content";

            var tagHelperContext = new TagHelperContext(
                allAttributes: new ReadOnlyTagHelperAttributeList<IReadOnlyTagHelperAttribute>(
                    Enumerable.Empty<IReadOnlyTagHelperAttribute>()),
                items: new Dictionary<object, object>(),
                uniqueId: "test",
                getChildContentAsync: useCachedResult =>
                {
                    var tagHelperContent = new DefaultTagHelperContent();
                    tagHelperContent.SetContent(tagHelperOutputContent.OriginalChildContent);
                    return Task.FromResult<TagHelperContent>(tagHelperContent);
                });
            var htmlAttributes = new TagHelperAttributeList
            {
                { "class", "form-control" },
            };
            var output = new TagHelperOutput(expectedTagName, htmlAttributes);
            output.PreContent.SetContent(expectedPreContent);
            output.PostContent.SetContent(expectedPostContent);

            // LabelTagHelper checks IsContentModified so we don't want to forcibly set it if
            // tagHelperOutputContent.OriginalContent is going to be null or empty.
            if (!string.IsNullOrEmpty(tagHelperOutputContent.OriginalContent))
            {
                output.Content.SetContent(tagHelperOutputContent.OriginalContent);
            }

            var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
            tagHelper.ViewContext = viewContext;

            // Act
            await tagHelper.ProcessAsync(tagHelperContext, output);

            // Assert
            Assert.Equal(expectedAttributes, output.Attributes);
            Assert.Equal(expectedPreContent, output.PreContent.GetContent());
            Assert.Equal(
                tagHelperOutputContent.ExpectedContent,
                HtmlContentUtilities.HtmlContentToString(output.Content));
            Assert.Equal(expectedPostContent, output.PostContent.GetContent());
            Assert.Equal(TagMode.StartTagAndEndTag, output.TagMode);
            Assert.Equal(expectedTagName, output.TagName);
        }
Example #22
0
        protected bool IsValid(ViewContext viewContext, ModelExpression modelExpression, LocalizationViewModel localization = null)
        {
            ModelState modelState;

              if (viewContext.ModelState.TryGetValue(this.GetIdentity(modelExpression, localization), out modelState))
            return modelState.ValidationState != ModelValidationState.Invalid;

              return true;
        }
        public async Task TagHelper_CallsGeneratorWithExpectedValues_RealModelType(
            Type modelType,
            object model,
            bool allowMultiple)
        {
            // Arrange
            var contextAttributes = new Dictionary<string, object>();
            var originalAttributes = new Dictionary<string, object>();
            var propertyName = "Property1";
            var tagName = "select";

            var tagHelperContext = new TagHelperContext(
                contextAttributes,
                items: new Dictionary<object, object>(),
                uniqueId: "test",
                getChildContentAsync: () =>
                {
                    var tagHelperContent = new DefaultTagHelperContent();
                    tagHelperContent.SetContent("Something");
                    return Task.FromResult<TagHelperContent>(tagHelperContent);
                });
            var output = new TagHelperOutput(tagName, originalAttributes);
            var metadataProvider = new EmptyModelMetadataProvider();
            var modelExplorer = metadataProvider.GetModelExplorerForType(modelType, model);
            var modelExpression = new ModelExpression(propertyName, modelExplorer);

            var htmlGenerator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
            var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator.Object, metadataProvider);
            var currentValues = new string[0];
            htmlGenerator
                .Setup(real => real.GetCurrentValues(
                    viewContext,
                    modelExplorer,
                    propertyName,   // expression
                    allowMultiple))
                .Returns(currentValues)
                .Verifiable();
            htmlGenerator
                .Setup(real => real.GenerateSelect(
                    viewContext,
                    modelExplorer,
                    null,           // optionLabel
                    propertyName,   // expression
                    It.IsAny<IEnumerable<SelectListItem>>(),
                    currentValues,
                    allowMultiple,
                    null))          // htmlAttributes
                .Returns((TagBuilder)null)
                .Verifiable();

            var tagHelper = new SelectTagHelper
            {
                For = modelExpression,
                Generator = htmlGenerator.Object,
                ViewContext = viewContext,
            };

            // Act
            await tagHelper.ProcessAsync(tagHelperContext, output);

            // Assert
            htmlGenerator.Verify();

            Assert.NotNull(viewContext.FormContext?.FormData);
            var keyValuePair = Assert.Single(
                viewContext.FormContext.FormData,
                entry => entry.Key == SelectTagHelper.SelectedValuesFormDataKey);
            Assert.Same(currentValues, keyValuePair.Value);
        }
        public async Task ProcessAsync_CallsGeneratorWithExpectedValues_ItemsAndAttribute(
            IEnumerable<SelectListItem> inputItems,
            string attributeName,
            string attributeValue,
            IEnumerable<SelectListItem> expectedItems)
        {
            // Arrange
            var contextAttributes = new Dictionary<string, object>
            {
                // Provided for completeness. Select tag helper does not confirm AllAttributes set is consistent.
                { attributeName, attributeValue },
            };
            var originalAttributes = new Dictionary<string, object>
            {
                { attributeName, attributeValue },
            };
            var propertyName = "Property1";
            var expectedTagName = "select";

            var tagHelperContext = new TagHelperContext(
                contextAttributes,
                items: new Dictionary<object, object>(),
                uniqueId: "test",
                getChildContentAsync: () =>
                {
                    var tagHelperContent = new DefaultTagHelperContent();
                    tagHelperContent.SetContent("Something");
                    return Task.FromResult<TagHelperContent>(tagHelperContent);
                });

            var output = new TagHelperOutput(expectedTagName, originalAttributes);
            var metadataProvider = new EmptyModelMetadataProvider();
            string model = null;
            var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(string), model);

            var htmlGenerator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
            var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator.Object, metadataProvider);

            // Simulate a (model => model) scenario. E.g. the calling helper may appear in a low-level template.
            var modelExpression = new ModelExpression(string.Empty, modelExplorer);
            viewContext.ViewData.TemplateInfo.HtmlFieldPrefix = propertyName;

            var currentValues = new string[0];
            htmlGenerator
                .Setup(real => real.GetCurrentValues(
                    viewContext,
                    modelExplorer,
                    string.Empty,   // expression
                    false))         // allowMultiple
                .Returns(currentValues)
                .Verifiable();
            htmlGenerator
                .Setup(real => real.GenerateSelect(
                    viewContext,
                    modelExplorer,
                    null,           // optionLabel
                    string.Empty,   // expression
                    expectedItems,
                    currentValues,
                    false,          // allowMultiple
                    null))          // htmlAttributes
                .Returns((TagBuilder)null)
                .Verifiable();

            var tagHelper = new SelectTagHelper
            {
                For = modelExpression,
                Items = inputItems,
                Generator = htmlGenerator.Object,
                ViewContext = viewContext,
            };

            // Act
            await tagHelper.ProcessAsync(tagHelperContext, output);

            // Assert
            htmlGenerator.Verify();

            Assert.NotNull(viewContext.FormContext?.FormData);
            var keyValuePair = Assert.Single(
                viewContext.FormContext.FormData,
                entry => entry.Key == SelectTagHelper.SelectedValuesFormDataKey);
            Assert.Same(currentValues, keyValuePair.Value);
        }
        public async Task ProcessAsyncInTemplate_WithItems_GeneratesExpectedOutput_DoesNotChangeSelectList(
            object model,
            Type containerType,
            Func<object> modelAccessor,
            NameAndId nameAndId,
            string expectedOptions)
        {
            // Arrange
            var originalAttributes = new Dictionary<string, object>
            {
                { "class", "form-control" },
            };
            var originalPostContent = "original content";

            var expectedAttributes = new Dictionary<string, object>(originalAttributes)
            {
                { "id", nameAndId.Id },
                { "name", nameAndId.Name },
                { "valid", "from validation attributes" },
            };
            var expectedPreContent = "original pre-content";
            var expectedContent = "original content";
            var expectedPostContent = originalPostContent + expectedOptions;
            var expectedTagName = "select";

            var metadataProvider = new TestModelMetadataProvider();

            var containerMetadata = metadataProvider.GetMetadataForType(containerType);
            var containerExplorer = metadataProvider.GetModelExplorerForType(containerType, model);

            var propertyMetadata = metadataProvider.GetMetadataForProperty(containerType, "Text");
            var modelExplorer = containerExplorer.GetExplorerForExpression(propertyMetadata, modelAccessor());

            var modelExpression = new ModelExpression(name: string.Empty, modelExplorer: modelExplorer);

            var tagHelperContext = new TagHelperContext(
                allAttributes: new Dictionary<string, object>(),
                items: new Dictionary<object, object>(),
                uniqueId: "test",
                getChildContentAsync: () =>
                {
                    var tagHelperContent = new DefaultTagHelperContent();
                    tagHelperContent.SetContent("Something");
                    return Task.FromResult<TagHelperContent>(tagHelperContent);
                });
            var output = new TagHelperOutput(expectedTagName, originalAttributes)
            {
                SelfClosing = true
            };
            output.PreContent.SetContent(expectedPreContent);
            output.Content.SetContent(expectedContent);
            output.PostContent.SetContent(originalPostContent);

            var htmlGenerator = new TestableHtmlGenerator(metadataProvider)
            {
                ValidationAttributes =
                {
                    {  "valid", "from validation attributes" },
                }
            };
            var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
            viewContext.ViewData.TemplateInfo.HtmlFieldPrefix = nameAndId.Name;

            var items = new SelectList(new[] { "", "outer text", "inner text", "other text" });
            var savedDisabled = items.Select(item => item.Disabled).ToList();
            var savedGroup = items.Select(item => item.Group).ToList();
            var savedSelected = items.Select(item => item.Selected).ToList();
            var savedText = items.Select(item => item.Text).ToList();
            var savedValue = items.Select(item => item.Value).ToList();
            var tagHelper = new SelectTagHelper
            {
                For = modelExpression,
                Generator = htmlGenerator,
                Items = items,
                ViewContext = viewContext,
            };

            // Act
            await tagHelper.ProcessAsync(tagHelperContext, output);

            // Assert
            Assert.True(output.SelfClosing);
            Assert.Equal(expectedAttributes, output.Attributes);
            Assert.Equal(expectedPreContent, output.PreContent.GetContent());
            Assert.Equal(expectedContent, output.Content.GetContent());
            Assert.Equal(expectedPostContent, output.PostContent.GetContent());
            Assert.Equal(expectedTagName, output.TagName);

            Assert.NotNull(viewContext.FormContext?.FormData);
            Assert.Single(
                viewContext.FormContext.FormData,
                entry => entry.Key == SelectTagHelper.SelectedValuesFormDataKey);

            Assert.Equal(savedDisabled, items.Select(item => item.Disabled));
            Assert.Equal(savedGroup, items.Select(item => item.Group));
            Assert.Equal(savedSelected, items.Select(item => item.Selected));
            Assert.Equal(savedText, items.Select(item => item.Text));
            Assert.Equal(savedValue, items.Select(item => item.Value));
        }
        public async Task ProcessAsync_GeneratesExpectedOutput(
            object model,
            Type containerType,
            Func<object> modelAccessor,
            NameAndId nameAndId,
            string ignored)
        {
            // Arrange
            var originalAttributes = new Dictionary<string, object>
            {
                { "class", "form-control" },
            };
            var originalPostContent = "original content";

            var expectedAttributes = new Dictionary<string, object>(originalAttributes)
            {
                { "id", nameAndId.Id },
                { "name", nameAndId.Name },
                { "valid", "from validation attributes" },
            };
            var expectedPreContent = "original pre-content";
            var expectedContent = "original content";
            var expectedPostContent = originalPostContent;
            var expectedTagName = "not-select";

            var metadataProvider = new TestModelMetadataProvider();
            var containerMetadata = metadataProvider.GetMetadataForType(containerType);
            var containerExplorer = metadataProvider.GetModelExplorerForType(containerType, model);

            var propertyMetadata = metadataProvider.GetMetadataForProperty(containerType, "Text");
            var modelExplorer = containerExplorer.GetExplorerForExpression(propertyMetadata, modelAccessor());

            var modelExpression = new ModelExpression(nameAndId.Name, modelExplorer);

            var tagHelperContext = new TagHelperContext(
                allAttributes: new Dictionary<string, object>(),
                items: new Dictionary<object, object>(),
                uniqueId: "test",
                getChildContentAsync: () =>
                {
                    var tagHelperContent = new DefaultTagHelperContent();
                    tagHelperContent.SetContent("Something");
                    return Task.FromResult<TagHelperContent>(tagHelperContent);
                });
            var output = new TagHelperOutput(expectedTagName, originalAttributes)
            {
                SelfClosing = true,
            };
            output.PreContent.SetContent(expectedPreContent);
            output.Content.SetContent(expectedContent);
            output.PostContent.SetContent(originalPostContent);

            var htmlGenerator = new TestableHtmlGenerator(metadataProvider)
            {
                ValidationAttributes =
                {
                    {  "valid", "from validation attributes" },
                }
            };
            var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
            var tagHelper = new SelectTagHelper
            {
                For = modelExpression,
                Generator = htmlGenerator,
                ViewContext = viewContext,
            };

            // Act
            await tagHelper.ProcessAsync(tagHelperContext, output);

            // Assert
            Assert.True(output.SelfClosing);
            Assert.Equal(expectedAttributes, output.Attributes);
            Assert.Equal(expectedPreContent, output.PreContent.GetContent());
            Assert.Equal(expectedContent, output.Content.GetContent());
            Assert.Equal(expectedPostContent, output.PostContent.GetContent());
            Assert.Equal(expectedTagName, output.TagName);

            Assert.NotNull(viewContext.FormContext?.FormData);
            Assert.Single(
                viewContext.FormContext.FormData,
                entry => entry.Key == SelectTagHelper.SelectedValuesFormDataKey);
        }