Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feature/231 focus async #260

Merged
merged 5 commits into from
Nov 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ jobs:
with:
setAllVars: true

- name: Setting VERSION
run: echo "::set-env name=VERSION::$NBGV_NuGetPackageVersion"
- name: Setting VERSION env
run: echo "VERSION=$NBGV_NuGetPackageVersion" >> $GITHUB_ENV

- name: Update tokens in project files
uses: cschleiden/replace-tokens@v1
with:
Expand Down
138 changes: 68 additions & 70 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@ jobs:

verify-template:
if: github.event_name != 'schedule'
env:
BRANCH: ''
VERSION: ''
runs-on: ubuntu-latest
steps:
- name: Checkout repository
Expand All @@ -84,10 +81,11 @@ jobs:
with:
setAllVars: true

- uses: nelonoel/[email protected]
- name: Setting VERSION and BRANCH env
run: |
echo "::set-env name=VERSION::$NBGV_NuGetPackageVersion"
echo "::set-env name=BRANCH::$(echo ${GITHUB_REF#refs/heads/} | sed 's/\//_/g')"
echo "VERSION=$NBGV_NuGetPackageVersion" >> $GITHUB_ENV
echo "BRANCH=$BRANCH_NAME" >> $GITHUB_ENV

- name: Update tokens in project files
uses: cschleiden/replace-tokens@v1
Expand Down Expand Up @@ -121,79 +119,78 @@ jobs:
if: github.event_name != 'schedule' && github.event_name != 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0w

- uses: dotnet/nbgv@master
with:
setAllVars: true

- name: Setting VERSION and BRANCH env
run: |
echo "::set-env name=VERSION::$NBGV_NuGetPackageVersion"
echo "::set-env name=BRANCH::$(echo ${GITHUB_REF#refs/heads/} | sed 's/\//_/g')"

- uses: actions/setup-dotnet@v1
with:
dotnet-version: '3.1.x'
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'

- name: Install dotnet-sonarscanner
run: dotnet tool install --global dotnet-sonarscanner
- name: Running dotnet-sonarscanner
shell: bash
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
dotnet sonarscanner begin /k:"egil_bUnit" /o:"egil" /n:"bUnit" /v:"$NBGV_NuGetPackageVersion" /d:sonar.login="$SONAR_TOKEN" /d:sonar.host.url=https://sonarcloud.io /d:sonar.branch.name="$BRANCH" /d:sonar.verbose="true" /d:sonar.cs.opencover.reportsPaths="tests/**/coverage/*.opencover.xml"
dotnet build
dotnet test tests/bunit.core.tests/ /p:CollectCoverage=true /p:CoverletOutput=./coverage/ /p:CoverletOutputFormat=opencover /p:ExcludeByAttribute=\"Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute\"
dotnet test tests/bunit.web.tests/ /p:CollectCoverage=true /p:CoverletOutput=./coverage/ /p:CoverletOutputFormat=opencover /p:ExcludeByAttribute=\"Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute\"
dotnet test tests/bunit.xunit.tests/ /p:CollectCoverage=true /p:CoverletOutput=./coverage/ /p:CoverletOutputFormat=opencover /p:ExcludeByAttribute=\"Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute\"
dotnet sonarscanner end /d:sonar.login="$SONAR_TOKEN"
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0

- uses: dotnet/nbgv@master
with:
setAllVars: true

- uses: nelonoel/[email protected]

- name: Setting VERSION and BRANCH env
run: |
echo "VERSION=$NBGV_NuGetPackageVersion" >> $GITHUB_ENV
echo "BRANCH=$BRANCH_NAME" >> $GITHUB_ENV

- uses: actions/setup-dotnet@v1
with:
dotnet-version: '3.1.x'
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'

- name: Install dotnet-sonarscanner
run: dotnet tool install --global dotnet-sonarscanner
- name: Running dotnet-sonarscanner
shell: bash
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
dotnet sonarscanner begin /k:"egil_bUnit" /o:"egil" /n:"bUnit" /v:"$NBGV_NuGetPackageVersion" /d:sonar.login="$SONAR_TOKEN" /d:sonar.host.url=https://sonarcloud.io /d:sonar.branch.name="$BRANCH" /d:sonar.verbose="true" /d:sonar.cs.opencover.reportsPaths="tests/**/coverage/*.opencover.xml"
dotnet build
dotnet test tests/bunit.core.tests/ /p:CollectCoverage=true /p:CoverletOutput=./coverage/ /p:CoverletOutputFormat=opencover /p:ExcludeByAttribute=\"Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute\"
dotnet test tests/bunit.web.tests/ /p:CollectCoverage=true /p:CoverletOutput=./coverage/ /p:CoverletOutputFormat=opencover /p:ExcludeByAttribute=\"Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute\"
dotnet test tests/bunit.xunit.tests/ /p:CollectCoverage=true /p:CoverletOutput=./coverage/ /p:CoverletOutputFormat=opencover /p:ExcludeByAttribute=\"Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute\"
dotnet sonarscanner end /d:sonar.login="$SONAR_TOKEN"

code-ql:
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 0

- uses: actions/setup-dotnet@v1
with:
dotnet-version: '3.1.x'
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'

- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: csharp
config-file: ./.github/codeql/codeql-config.yml

- name: Building library
run: dotnet build -p:ContinuousIntegrationBuild=true

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 0

- uses: actions/setup-dotnet@v1
with:
dotnet-version: '3.1.x'
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'

- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: csharp
config-file: ./.github/codeql/codeql-config.yml

- name: Building library
run: dotnet build -p:ContinuousIntegrationBuild=true

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

publish-nuget-packages:
if: (github.event_name == 'push' && github.ref == 'refs/heads/dev') || github.event_name == 'release'
needs: [dotnet-build, dotnet-test, verify-template]
runs-on: ubuntu-latest
env:
VERSION: ''
BRANCH: ''
steps:
- name: Checkout Repository
uses: actions/checkout@v2
Expand All @@ -204,10 +201,11 @@ jobs:
with:
setAllVars: true

- uses: nelonoel/[email protected]
- name: Setting VERSION and BRANCH env
run: |
echo "::set-env name=VERSION::$NBGV_NuGetPackageVersion"
echo "::set-env name=BRANCH::$(echo ${GITHUB_REF#refs/heads/} | sed 's/\//_/g')"
echo "VERSION=$NBGV_NuGetPackageVersion" >> $GITHUB_ENV
echo "BRANCH=$BRANCH_NAME" >> $GITHUB_ENV

- name: Update tokens in project files
uses: cschleiden/replace-tokens@v1
Expand Down
23 changes: 22 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,21 @@ The following section list all changes in 1.0.0 preview 01.
### Added
List of new features.

- Added support for triggering `@ontoggle` event handlers through a dedicated `Toggle()` method. By [@egil](https://github.com/egil) in [#248](https://github.com/egil/bUnit/pull/248).
- Added support for triggering `@ontoggle` event handlers through a dedicated `Toggle()` method. By [@egil](https://github.com/egil) in [#256](https://github.com/egil/bUnit/pull/256).

- Added support for components that call `ElementReference.FocusAsync`. These calls are handled by the bUnits JSInterop, that also allows you to verify that `FocusAsync` has been called for a specific element. For example, if a component has rendered an `<input>` element, then the following code will verify that it has been focused using `FocusAsync`:

```csharp
var cut = RenderComponent<FocusingComponent>();

var input = cut.Find("input");

JSInterop.VerifyFocusAsyncInvoke()
.Arguments[0] // the first argument is the ElemenetReference
.ShouldBeElementReferenceTo(input);
```

By [@egil](https://github.com/egil) in [#260](https://github.com/egil/bUnit/pull/260).

### Changed
List of changes in existing functionality.
Expand All @@ -24,6 +38,13 @@ List of changes in existing functionality.

Learn more [issue #237](https://github.com/egil/bUnit/issues/237). By [@egil](https://github.com/egil) in [#247](https://github.com/egil/bUnit/pull/247).

- The `Setup<TResult>(string identifier, Func<IReadOnlyList<object?>, bool> argumentsMatcher)` and `SetupVoid(string identifier, Func<IReadOnlyList<object?>, bool> argumentsMatcher)` methods in bUnits JSInterop/MockJSRuntime has a new second parameter, an `InvocationMatcher`.

The `InvocationMatcher` type is a delegate that receives a `JSRuntimeInvoation` and returns true. The `JSRuntimeInvoation` type contains the arguments of the invocation and the identifier for the invocation. This means old code using the `Setup` and `SetupVoid` methods should be updated to use the arguments list in `JSRuntimeInvoation`, e.g., change the following call:

`ctx.JSInterop.Setup<string>("foo", args => args.Count == 2)` to this:
`ctx.JSInterop.Setup<string>("foo", invocation => invocation.Arguments.Count == 2)`.

### Removed
List of now removed features.

Expand Down
38 changes: 38 additions & 0 deletions src/bunit.core/RazorTesting/FragmentContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;

namespace Bunit.RazorTesting
{
/// <summary>
/// Creates an instance of the <see cref="FragmentContainer"/>, which is used
/// when a fragment is rendered inside a test contexts render tree.
/// It is primarily used to be able to find the starting point to return.
/// </summary>
internal sealed class FragmentContainer : ComponentBase
{
/// <summary>
/// The content to wrap.
/// </summary>
[Parameter] public RenderFragment? ChildContent { get; set; }

/// <inheritdoc/>
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddContent(0, ChildContent);
}

/// <summary>
/// Wraps the <paramref name="wrappingTarget"/> in a <see cref="FragmentContainer"/>.
/// </summary>
internal static RenderFragment Wrap(RenderFragment wrappingTarget)
{
return builder =>
{
builder.OpenComponent<FragmentContainer>(0);
builder.AddAttribute(1, nameof(ChildContent), wrappingTarget);
builder.CloseComponent();
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public bool TryAdd<TComponent>(Action<ComponentParameterCollectionBuilder<TCompo
/// </summary>
/// <param name="target"><see cref="RenderFragment"/> to render inside the render tree.</param>
/// <returns>A <see cref="RenderFragment"/> that renders the <paramref name="target"/> inside this <see cref="RootRenderTree"/> render tree.</returns>
internal RenderFragment Wrap(RenderFragment target)
public RenderFragment Wrap(RenderFragment target)
{
// Wrap from the last added to the first added, as we start with the
// target and goes from inside to out.
Expand All @@ -94,7 +94,7 @@ internal RenderFragment Wrap(RenderFragment target)
/// </summary>
/// <typeparam name="TComponent">Component type to count.</typeparam>
/// <returns>Number of components of type <typeparamref name="TComponent"/> in render tree.</returns>
internal int GetCountOf<TComponent>() where TComponent : IComponent
public int GetCountOf<TComponent>() where TComponent : IComponent
{
var result = 0;
var countType = typeof(TComponent);
Expand Down
10 changes: 9 additions & 1 deletion src/bunit.core/Rendering/TestRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,15 @@ private void AssertNoUnhandledExceptions()
{
_unhandledException = null;
LogUnhandledException(unhandled);
ExceptionDispatchInfo.Capture(unhandled).Throw();

if (unhandled is AggregateException aggregateException && aggregateException.InnerExceptions.Count == 1)
{
ExceptionDispatchInfo.Capture(aggregateException.InnerExceptions[0]).Throw();
}
else
{
ExceptionDispatchInfo.Capture(unhandled).Throw();
}
}
}

Expand Down
55 changes: 53 additions & 2 deletions src/bunit.core/TestContextBase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using Bunit.RazorTesting;
using Bunit.Rendering;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;

namespace Bunit
Expand Down Expand Up @@ -33,13 +35,62 @@ public ITestRenderer Renderer
/// </summary>
public TestServiceProvider Services { get; }

/// <summary>
/// Gets the <see cref="RootRenderTree"/> that all components rendered with the
/// <c>RenderComponent&lt;TComponent&gt;()</c> methods, are rendered inside.
/// </summary>
/// <remarks>
/// Use this to add default layout- or root-components which a component under test
/// should be rendered under.
/// </remarks>
public RootRenderTree RenderTree { get; } = new RootRenderTree();

/// <summary>
/// Creates a new instance of the <see cref="TestContextBase"/> class.
/// </summary>
protected TestContextBase()
{
Services = new TestServiceProvider();
Services.AddSingleton<ITestRenderer, TestRenderer>();
Services = new TestServiceProvider();
}

/// <summary>
/// Renders a component, declared in the <paramref name="renderFragment"/>, inside the <see cref="RenderTree"/>.
/// </summary>
/// <typeparam name="TComponent">The type of component to render.</typeparam>
/// <param name="renderFragment">The <see cref="RenderFragmentBase"/> that contains a declaration of the component.</param>
/// <returns>A <see cref="IRenderedComponentBase{TComponent}"/>.</returns>
protected IRenderedComponentBase<TComponent> RenderComponentBase<TComponent>(RenderFragment renderFragment) where TComponent : IComponent
{
// Wrap TComponent in any layout components added to the test context.
// If one of the layout components is the same type as TComponent,
// make sure to return the rendered component, not the layout component.
var resultBase = Renderer.RenderFragment(RenderTree.Wrap(renderFragment));

// This ensures that the correct component is returned, in case an added layout component
// is of type TComponent.
var renderTreeTComponentCount = RenderTree.GetCountOf<TComponent>();
var result = renderTreeTComponentCount > 0
? Renderer.FindComponents<TComponent>(resultBase)[renderTreeTComponentCount]
: Renderer.FindComponent<TComponent>(resultBase);

return result;
}

/// <summary>
/// Renders a fragment, declared in the <paramref name="renderFragment"/>, inside the <see cref="RenderTree"/>.
/// </summary>
/// <param name="renderFragment">The <see cref="RenderFragmentBase"/> to render.</param>
/// <returns>A <see cref="IRenderedFragmentBase"/>.</returns>
protected IRenderedFragmentBase RenderFragmentBase(RenderFragment renderFragment)
{
// Wrap fragment in a FragmentContainer so the start of the test supplied
// razor fragment can be found after, and then wrap in any layout components
// added to the test context.
var wrappedInFragmentContainer = FragmentContainer.Wrap(renderFragment);
var wrappedInRenderTree = RenderTree.Wrap(wrappedInFragmentContainer);
var resultBase = Renderer.RenderFragment(wrappedInRenderTree);

return Renderer.FindComponent<FragmentContainer>(resultBase);
}

/// <inheritdoc/>
Expand Down
1 change: 1 addition & 0 deletions src/bunit.web/Extensions/TestServiceProviderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public static IServiceCollection AddDefaultTestContextServices(this IServiceColl
services.AddSingleton<IStringLocalizer, PlaceholderStringLocalization>();

// bUnit specific services
services.AddSingleton<ITestRenderer, WebTestRenderer>();
services.AddSingleton<HtmlComparer>();
services.AddSingleton<BunitHtmlParser>();
services.AddSingleton<IRenderedComponentActivator, RenderedComponentActivator>();
Expand Down
Loading