Skip to content

Shallow Rendering based on Component Factories #396

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
6 tasks
egil opened this issue May 17, 2021 · 6 comments · Fixed by #397
Closed
6 tasks

Shallow Rendering based on Component Factories #396

egil opened this issue May 17, 2021 · 6 comments · Fixed by #397

Comments

@egil
Copy link
Member

egil commented May 17, 2021

The goal is to make shallow rendering possible, with as few caveats as possible.

It should:

  • Mimic shallow rendering principles from other component testing frameworks, e.g. React
  • Replace all child components of the component under test with stub components
  • Not replace components added to TestContext.RenderTree
  • Stub components should be represented in the DOM tree as the component they have replaced, with correct casing.
  • Shallow rendering does not replace CascadingValue<T> which are part of the render fragment being rendered.
  • Enable stub/shallow options to be passed to shallow render methods to control how removed components are replaced.

Limitations:

  • ShallowRender will only render the first top level component. Other components, including its children will be stubbed.
  • Once a test context has been used to perform a shallow render, its Render, ShallowRender, and RenderComponent methods will all throw an exception, with the message The test context has previously been used to perform a shallow render of a component or render fragment. Rendering another component or render fragment with the same test context is not currently supported, since it can result in unpredictable results.. This limitation might be removed in the future, but right now I do not see a neat way to achieve this.

Replaces #17.

@egil
Copy link
Member Author

egil commented May 17, 2021

@FlorianRappl, it seems AngleSharp is converting all elements to lowercase by default, e.g. this raw markup string:

<MyComponent />

gets the localname in mycomponent in the DOM tree. Is it possible to control this in AngleSharp?

I would like to keep the original casing when printing out the DOM nodes if possible, but think that it might not be how the HTML5 standard works.

@FlorianRappl
Copy link

FlorianRappl commented May 17, 2021

All DOM nodes are lowercase.

image

So this is nothing from AngleSharp, but rather from the HTML spec.

I would like to keep the original casing when printing out the DOM nodes if possible, but think that it might not be how the HTML5 standard works.

Correct!

I think one way around is would be to keep the source references and to have a custom markup formatter, which looks for source references and prefers them.

Quick example (sorry its a mess, just to get the idea out):

void Main()
{
	var config = Configuration.Default;
	var context = BrowsingContext.New(config);
	var parser = new HtmlParser(new AngleSharp.Html.Parser.HtmlParserOptions
	{
		IsKeepingSourceReferences = true,
	}, context);
	var document = parser.ParseDocument("<FooBar>Test</FooBar>");
	
	document.ToHtml(new MyMarkupFormatter()).Dump();
	
	
}

class MyMarkupFormatter : AngleSharp.Html.HtmlMarkupFormatter
{
	private string GetOriginalName(AngleSharp.Dom.IElement element)
	{
		if (element.SourceReference != null && element.SourceReference.Position.Index != -1)
		{
			element.Owner.Source.Index = element.SourceReference.Position.Index + 1;
			var originalName = element.Owner.Source.ReadCharacters(element.LocalName.Length);
			return originalName;
		}
		
		return null;
	}

	public override string OpenTag(AngleSharp.Dom.IElement element, bool selfClosing)
	{
		var content = base.OpenTag(element, selfClosing);
		var originalName = GetOriginalName(element);
		
		if (originalName != null)
		{
			return $"<{originalName}{content.Substring(originalName.Length + 1)}";
		}
		
		return content;
	}

	public override string CloseTag(AngleSharp.Dom.IElement element, bool selfClosing)
	{
		var content = base.CloseTag(element, selfClosing);
		var originalName = GetOriginalName(element);

		if (content.Length > 0 && originalName != null)
		{
			return $"</{originalName}{content.Substring(originalName.Length + 2)}";
		}

		return content;
	}
}

Result:

<html><head></head><body><FooBar>Test</FooBar></body></html>

Potentially, we could introduce a special parser option to keep the original names. As usual, my recommendation would be to stick to the standards, as deviating (e.g., via enabling a flag) could cause unwanted side-effects.

@egil egil linked a pull request May 17, 2021 that will close this issue
9 tasks
@egil
Copy link
Member Author

egil commented May 17, 2021

Thanks @FlorianRappl. Yeah, I dont really want to change the default behavior of the DOM tree, its likely that some users of bUnit depend on that. The use case is for when we print out error messages when AngleSharp Diffing detects a diff between expected and actual markup, so I think your suggestion is just right.

This is what I ended up with: 2b857ed

@egil egil closed this as completed in #397 May 18, 2021
@egil egil reopened this May 18, 2021
@egil egil closed this as completed May 18, 2021
@egil egil reopened this May 18, 2021
@egil
Copy link
Member Author

egil commented May 18, 2021

Ill keep this issue open for now to see if there is any feedback from the upcoming preview release.

@egil egil closed this as completed May 21, 2021
@egil egil reopened this Jul 9, 2021
@egil
Copy link
Member Author

egil commented Jul 9, 2021

Reopened due to bug in implementation.

@egil
Copy link
Member Author

egil commented Dec 18, 2021

I'll close this again. There is rudimentary support for shallow rendering now, documented in the next release of the docs.

@egil egil closed this as completed Dec 18, 2021
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants