Add E2E tests for BrowserRouter, plus implement querystring/hash support

This commit is contained in:
Steve Sanderson 2018-02-21 00:16:17 +00:00
parent 8bc7c92683
commit fc9cb1af65
11 changed files with 202 additions and 30 deletions

View File

@ -16,6 +16,8 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Routing
/// </summary>
public class BrowserRouter : IComponent, IDisposable
{
static readonly char[] _queryOrHashStartChar = new[] { '?', '#' };
RenderHandle _renderHandle;
string _baseUriPrefix;
string _locationAbsolute;
@ -74,6 +76,7 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Routing
throw new InvalidOperationException($"No value was specified for {nameof(PagesNamespace)}.");
}
locationPath = StringUntilAny(locationPath, _queryOrHashStartChar);
var componentTypeName = $"{PagesNamespace}{locationPath.Replace('/', '.')}";
if (componentTypeName[componentTypeName.Length - 1] == '.')
{
@ -84,6 +87,14 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Routing
?? throw new InvalidOperationException($"{nameof(BrowserRouter)} cannot find any component type with name {componentTypeName}.");
}
private string StringUntilAny(string str, char[] chars)
{
var firstIndex = str.IndexOfAny(chars);
return firstIndex < 0
? str
: str.Substring(0, firstIndex);
}
private Type FindComponentTypeInAssemblyOrReferences(Assembly assembly, string typeName)
=> assembly.GetType(typeName, throwOnError: false, ignoreCase: true)
?? assembly.GetReferencedAssemblies()

View File

@ -61,14 +61,22 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Routing
/// <returns>A relative URI path.</returns>
public static string ToBaseRelativePath(string baseUriPrefix, string absoluteUri)
{
// The absolute URI must be of the form "{baseUriPrefix}/something",
// and from that we return "/something" (also stripping any querystring
// and/or hash value)
if (absoluteUri.StartsWith(baseUriPrefix, StringComparison.Ordinal)
if (absoluteUri.Equals(baseUriPrefix, StringComparison.Ordinal))
{
// Special case: if you're exactly at the base URI, treat it as if you
// were at "{baseUriPrefix}/" (i.e., with a following slash). It's a bit
// ambiguous because we don't know whether the server would return the
// same page whether or not the slash is present, but ASP.NET Core at
// least does by default when using PathBase.
return "/";
}
else if (absoluteUri.StartsWith(baseUriPrefix, StringComparison.Ordinal)
&& absoluteUri.Length > baseUriPrefix.Length
&& absoluteUri[baseUriPrefix.Length] == '/')
{
// TODO: Remove querystring and/or hash
// The absolute URI must be of the form "{baseUriPrefix}/something",
// and from that we return "/something" (also stripping any querystring
// and/or hash value)
return absoluteUri.Substring(baseUriPrefix.Length);
}

View File

@ -26,6 +26,7 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Test
[InlineData("scheme://host", "scheme://host/path", "/path")]
[InlineData("scheme://host/path", "scheme://host/path/", "/")]
[InlineData("scheme://host/path", "scheme://host/path/more", "/more")]
[InlineData("scheme://host/path", "scheme://host/path", "/")]
public void ComputesCorrectValidBaseRelativePaths(string baseUriPrefix, string absoluteUri, string expectedResult)
{
var actualResult = UriHelper.ToBaseRelativePath(baseUriPrefix, absoluteUri);
@ -35,7 +36,6 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Test
[Theory]
[InlineData("scheme://host", "otherscheme://host/")] // Mismatched prefix is error
[InlineData("scheme://host", "scheme://otherhost/")] // Mismatched prefix is error
[InlineData("scheme://host/path", "scheme://host/path")] // URI isn't within base URI space
public void ThrowsForInvalidBaseRelativePaths(string baseUriPrefix, string absoluteUri)
{
var ex = Assert.Throws<ArgumentException>(() =>

View File

@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using BasicTestApp;
using Microsoft.AspNetCore.Blazor.Components;
using Microsoft.AspNetCore.Blazor.E2ETest.Infrastructure.ServerFixtures;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System;
namespace Microsoft.AspNetCore.Blazor.E2ETest.Infrastructure
{
public class BasicTestAppTestBase : ServerTestBase<DevHostServerFixture<Program>>
{
public const string ServerPathBase = "/subdir";
public BasicTestAppTestBase(BrowserFixture browserFixture, DevHostServerFixture<Program> serverFixture)
: base(browserFixture, serverFixture)
{
serverFixture.PathBase = ServerPathBase;
}
protected IWebElement MountTestComponent<TComponent>() where TComponent : IComponent
{
var componentTypeName = typeof(TComponent).FullName;
WaitUntilDotNetRunningInBrowser();
((IJavaScriptExecutor)Browser).ExecuteScript(
$"mountTestComponent('{componentTypeName}')");
return Browser.FindElement(By.TagName("app"));
}
protected void WaitUntilDotNetRunningInBrowser()
{
new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until(driver =>
{
return ((IJavaScriptExecutor)driver)
.ExecuteScript("return window.isTestReady;");
});
}
}
}

View File

@ -8,23 +8,19 @@ using System.Linq;
using System.Numerics;
using BasicTestApp;
using BasicTestApp.HierarchicalImportsTest.Subdir;
using Microsoft.AspNetCore.Blazor.Components;
using Microsoft.AspNetCore.Blazor.E2ETest.Infrastructure;
using Microsoft.AspNetCore.Blazor.E2ETest.Infrastructure.ServerFixtures;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using Xunit;
namespace Microsoft.AspNetCore.Blazor.E2ETest.Tests
{
public class ComponentRenderingTest
: ServerTestBase<DevHostServerFixture<BasicTestApp.Program>>
public class ComponentRenderingTest : BasicTestAppTestBase
{
public ComponentRenderingTest(BrowserFixture browserFixture, DevHostServerFixture<Program> serverFixture)
: base(browserFixture, serverFixture)
{
serverFixture.PathBase = "/subdir";
Navigate("/subdir", noReload: true);
Navigate(ServerPathBase, noReload: true);
}
[Fact]
@ -203,23 +199,5 @@ namespace Microsoft.AspNetCore.Blazor.E2ETest.Tests
elem => Assert.Equal(typeof(Complex).FullName, elem.Text),
elem => Assert.Equal(typeof(AssemblyHashAlgorithm).FullName, elem.Text));
}
private IWebElement MountTestComponent<TComponent>() where TComponent: IComponent
{
var componentTypeName = typeof(TComponent).FullName;
WaitUntilDotNetRunningInBrowser();
((IJavaScriptExecutor)Browser).ExecuteScript(
$"mountTestComponent('{componentTypeName}')");
return Browser.FindElement(By.TagName("app"));
}
private void WaitUntilDotNetRunningInBrowser()
{
new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until(driver =>
{
return ((IJavaScriptExecutor)driver)
.ExecuteScript("return window.isTestReady;");
});
}
}
}

View File

@ -0,0 +1,114 @@
using System;
using BasicTestApp;
using BasicTestApp.RouterTest;
using Microsoft.AspNetCore.Blazor.E2ETest.Infrastructure;
using Microsoft.AspNetCore.Blazor.E2ETest.Infrastructure.ServerFixtures;
using OpenQA.Selenium;
using Xunit;
namespace Microsoft.AspNetCore.Blazor.E2ETest.Tests
{
public class RoutingTest : BasicTestAppTestBase, IDisposable
{
private readonly ServerFixture _server;
public RoutingTest(BrowserFixture browserFixture, DevHostServerFixture<Program> serverFixture)
: base(browserFixture, serverFixture)
{
_server = serverFixture;
Navigate(ServerPathBase, noReload: true);
}
[Fact]
public void CanArriveAtDefaultPage()
{
SetUrlViaPushState($"{ServerPathBase}/RouterTest/");
var app = MountTestComponent<TestRouter>();
Assert.Equal("This is the default page.", app.FindElement(By.Id("test-info")).Text);
}
[Fact]
public void CanArriveAtNonDefaultPage()
{
SetUrlViaPushState($"{ServerPathBase}/RouterTest/Other");
var app = MountTestComponent<TestRouter>();
Assert.Equal("This is another page.", app.FindElement(By.Id("test-info")).Text);
}
[Fact]
public void CanFollowLinkToOtherPage()
{
SetUrlViaPushState($"{ServerPathBase}/RouterTest/");
var app = MountTestComponent<TestRouter>();
app.FindElement(By.LinkText("Other")).Click();
Assert.Equal("This is another page.", app.FindElement(By.Id("test-info")).Text);
}
[Fact]
public void CanFollowLinkToDefaultPage()
{
SetUrlViaPushState($"{ServerPathBase}/RouterTest/Other");
var app = MountTestComponent<TestRouter>();
app.FindElement(By.LinkText("Default")).Click();
Assert.Equal("This is the default page.", app.FindElement(By.Id("test-info")).Text);
}
[Fact]
public void CanFollowLinkToOtherPageWithQueryString()
{
SetUrlViaPushState($"{ServerPathBase}/RouterTest/");
var app = MountTestComponent<TestRouter>();
app.FindElement(By.LinkText("Other with query")).Click();
Assert.Equal("This is another page.", app.FindElement(By.Id("test-info")).Text);
}
[Fact]
public void CanFollowLinkToDefaultPageWithQueryString()
{
SetUrlViaPushState($"{ServerPathBase}/RouterTest/Other");
var app = MountTestComponent<TestRouter>();
app.FindElement(By.LinkText("Default with query")).Click();
Assert.Equal("This is the default page.", app.FindElement(By.Id("test-info")).Text);
}
[Fact]
public void CanFollowLinkToOtherPageWithHash()
{
SetUrlViaPushState($"{ServerPathBase}/RouterTest/");
var app = MountTestComponent<TestRouter>();
app.FindElement(By.LinkText("Other with hash")).Click();
Assert.Equal("This is another page.", app.FindElement(By.Id("test-info")).Text);
}
[Fact]
public void CanFollowLinkToDefaultPageWithHash()
{
SetUrlViaPushState($"{ServerPathBase}/RouterTest/Other");
var app = MountTestComponent<TestRouter>();
app.FindElement(By.LinkText("Default with hash")).Click();
Assert.Equal("This is the default page.", app.FindElement(By.Id("test-info")).Text);
}
public void Dispose()
{
// Clear any existing state
SetUrlViaPushState(ServerPathBase);
MountTestComponent<TextOnlyComponent>();
}
private void SetUrlViaPushState(string relativeUri)
{
var jsExecutor = (IJavaScriptExecutor)Browser;
var absoluteUri = new Uri(_server.RootUri, relativeUri);
jsExecutor.ExecuteScript($"history.pushState(null, '', '{absoluteUri.ToString()}')");
}
}
}

View File

@ -11,6 +11,7 @@
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "http://localhost:63796/subdir",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
@ -18,6 +19,7 @@
"BasicTestApp": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "http://localhost:63797/subdir",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},

View File

@ -0,0 +1,3 @@
@using BasicTestApp.RouterTest
<div id="test-info">This is the default page.</div>
<c:Links />

View File

@ -0,0 +1,8 @@
<ul>
<li><a href="/subdir/RouterTest/">Default</a></li>
<li><a href="/subdir/RouterTest/?abc=123">Default with query</a></li>
<li><a href="/subdir/RouterTest/#blah">Default with hash</a></li>
<li><a href="/subdir/RouterTest/Other">Other</a></li>
<li><a href="/subdir/RouterTest/Other?abc=123">Other with query</a></li>
<li><a href="/subdir/RouterTest/Other#blah">Other with hash</a></li>
</ul>

View File

@ -0,0 +1,3 @@
@using BasicTestApp.RouterTest
<div id="test-info">This is another page.</div>
<c:Links />

View File

@ -0,0 +1,4 @@
@using Microsoft.AspNetCore.Blazor.Browser.Routing
<c:BrowserRouter AppAssembly=@(typeof(BasicTestApp.Program).Assembly)
PagesNamespace=@nameof(BasicTestApp)
DefaultComponentName=@("Default") />