Begin E2E testing for BasicTestApp and component rendering in browser
This commit is contained in:
parent
ce04fde7bd
commit
dfd6c4a1c2
|
|
@ -1,6 +1,7 @@
|
|||
import { platform } from './Environment';
|
||||
import { getAssemblyNameFromUrl } from './Platform/DotNet';
|
||||
import './Rendering/Renderer';
|
||||
import './GlobalExports';
|
||||
|
||||
async function boot() {
|
||||
// Read startup config from the <script> element that's importing this file
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { platform } from './Environment'
|
||||
import { registerFunction } from './RegisteredFunction';
|
||||
|
||||
// This file defines an export that, when the library is loaded in a browser via a
|
||||
// <script> element, will be attached to the global namespace
|
||||
const blazorInstance = {
|
||||
platform: platform,
|
||||
registerFunction: registerFunction
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window['Blazor'] = blazorInstance;
|
||||
}
|
||||
|
|
@ -18,11 +18,15 @@ export function registerFunction(identifier: string, implementation: Function) {
|
|||
registerFunction('__blazor_InvokeJson', (identifier: System_String, ...argsJson: System_String[]) => {
|
||||
const identifierJsString = platform.toJavaScriptString(identifier);
|
||||
if (!(registeredFunctions && registeredFunctions.hasOwnProperty(identifierJsString))) {
|
||||
throw new Error(`Could not find registered function with name "${identifier}".`);
|
||||
throw new Error(`Could not find registered function with name "${identifierJsString}".`);
|
||||
}
|
||||
const funcInstance = registeredFunctions[identifierJsString];
|
||||
const args = argsJson.map(json => JSON.parse(platform.toJavaScriptString(json)));
|
||||
const result = funcInstance.apply(null, args);
|
||||
const resultJson = JSON.stringify(result);
|
||||
return platform.toDotNetString(resultJson);
|
||||
if (result !== null && result !== undefined) {
|
||||
const resultJson = JSON.stringify(result);
|
||||
return platform.toDotNetString(resultJson);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -29,10 +29,21 @@ namespace Microsoft.Blazor.Build.Core.FileSystem
|
|||
|
||||
private static string GetIndexHtmlContents(string htmlTemplate, string assemblyName, IEnumerable<IFileInfo> binFiles)
|
||||
{
|
||||
// TODO: Consider parsing the HTML properly so for example we don't insert into
|
||||
// the wrong place if there was also '</body>' in a JavaScript string literal
|
||||
// TODO: Instead of inserting the script as the first element in <body>,
|
||||
// consider either:
|
||||
// [1] Inserting it just before the first <script> in the <body>, so that
|
||||
// developers can still put other <script> elems after (and therefore
|
||||
// reference Blazor JS APIs from them) but we don't block the page
|
||||
// rendering while fetching that script. Note that adding async/defer
|
||||
// alone isn't enough because that doesn't help older browsers that
|
||||
// don't suppor them.
|
||||
// [2] Or possibly better, don't insert the <script> magically at all.
|
||||
// Instead, just insert a block of configuration data at the top of
|
||||
// <body> (e.g., <script type='blazor-config'>{ ...json... }</script>)
|
||||
// and then let the developer manually place the tag that loads blazor.js
|
||||
// wherever they want (adding their own async/defer if they want).
|
||||
return htmlTemplate
|
||||
.Replace("</body>", CreateBootMarkup(assemblyName, binFiles) + "\n</body>");
|
||||
.Replace("<body>", "<body>\n" + CreateBootMarkup(assemblyName, binFiles));
|
||||
}
|
||||
|
||||
private static string CreateBootMarkup(string assemblyName, IEnumerable<IFileInfo> binFiles)
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ namespace Microsoft.Blazor.Server.Test
|
|||
public void InsertsScriptTagReferencingAssemblyAndDependencies()
|
||||
{
|
||||
// Arrange
|
||||
var htmlTemplate = "<html><body>Hello</body></html>";
|
||||
var htmlTemplate = "<html><body><h1>Hello</h1>Some text</body></html>";
|
||||
var dependencies = new IFileInfo[]
|
||||
{
|
||||
new TestFileInfo("System.Abc.dll"),
|
||||
|
|
@ -80,7 +80,7 @@ namespace Microsoft.Blazor.Server.Test
|
|||
// Act
|
||||
var file = instance.GetFileInfo("/index.html");
|
||||
var parsedHtml = new HtmlParser().Parse(ReadString(file));
|
||||
var scriptElem = parsedHtml.Body.LastElementChild;
|
||||
var scriptElem = parsedHtml.Body.FirstElementChild;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("script", scriptElem.TagName.ToLowerInvariant());
|
||||
|
|
|
|||
|
|
@ -23,9 +23,7 @@ namespace Microsoft.Blazor.E2ETest.Infrastructure.ServerFixtures
|
|||
$"No value was provided for {nameof(BuildWebHostMethod)}");
|
||||
}
|
||||
|
||||
var sampleSitePath = Path.Combine(
|
||||
FindSolutionDir(),
|
||||
"samples",
|
||||
var sampleSitePath = FindSampleOrTestSitePath(
|
||||
BuildWebHostMethod.Method.DeclaringType.Assembly.GetName().Name);
|
||||
|
||||
return BuildWebHostMethod(new[]
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@ namespace Microsoft.Blazor.E2ETest.Infrastructure.ServerFixtures
|
|||
{
|
||||
protected override IWebHost CreateWebHost()
|
||||
{
|
||||
var sampleSitePath = Path.Combine(
|
||||
FindSolutionDir(),
|
||||
"samples",
|
||||
var sampleSitePath = FindSampleOrTestSitePath(
|
||||
typeof(TProgram).Assembly.GetName().Name);
|
||||
|
||||
return DevHostServerProgram.BuildWebHost(new string[]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.Blazor.E2ETest.Infrastructure.ServerFixtures
|
||||
|
|
@ -30,6 +31,19 @@ namespace Microsoft.Blazor.E2ETest.Infrastructure.ServerFixtures
|
|||
Path.GetDirectoryName(typeof(ServerFixture).Assembly.Location));
|
||||
}
|
||||
|
||||
protected static string FindSampleOrTestSitePath(string projectName)
|
||||
{
|
||||
var solutionDir = FindSolutionDir();
|
||||
var possibleLocations = new[]
|
||||
{
|
||||
Path.Combine(solutionDir, "samples", projectName),
|
||||
Path.Combine(solutionDir, "test", "testapps", projectName)
|
||||
};
|
||||
|
||||
return possibleLocations.FirstOrDefault(Directory.Exists)
|
||||
?? throw new ArgumentException($"Cannot find a sample or test site with name '{projectName}'.");
|
||||
}
|
||||
|
||||
private static string FindClosestDirectoryContaining(
|
||||
string filename,
|
||||
string startDirectory)
|
||||
|
|
|
|||
|
|
@ -23,10 +23,7 @@ namespace Microsoft.Blazor.E2ETest.Infrastructure.ServerFixtures
|
|||
throw new InvalidOperationException($"No value was provided for {nameof(SampleSiteName)}");
|
||||
}
|
||||
|
||||
var sampleSitePath = Path.Combine(
|
||||
FindSolutionDir(),
|
||||
"samples",
|
||||
SampleSiteName);
|
||||
var sampleSitePath = FindSampleOrTestSitePath(SampleSiteName);
|
||||
|
||||
return new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
<ProjectReference Include="..\..\samples\MonoSanity\MonoSanity.csproj" />
|
||||
<ProjectReference Include="..\..\samples\StandaloneApp\StandaloneApp.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.Blazor.DevHost\Microsoft.Blazor.DevHost.csproj" />
|
||||
<ProjectReference Include="..\testapps\BasicTestApp\BasicTestApp.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
// 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 System;
|
||||
using BasicTestApp;
|
||||
using Microsoft.Blazor.E2ETest.Infrastructure;
|
||||
using Microsoft.Blazor.E2ETest.Infrastructure.ServerFixtures;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Support.UI;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.Blazor.E2ETest.Tests
|
||||
{
|
||||
public class ComponentRenderingTest
|
||||
: ServerTestBase<DevHostServerFixture<BasicTestApp.Program>>
|
||||
{
|
||||
public ComponentRenderingTest(BrowserFixture browserFixture, DevHostServerFixture<Program> serverFixture)
|
||||
: base(browserFixture, serverFixture)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BasicTestAppCanBeServed()
|
||||
{
|
||||
Navigate("/", noReload: true);
|
||||
Assert.Equal("Basic test app", Browser.Title);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanRenderTextOnlyComponent()
|
||||
{
|
||||
Navigate("/", noReload: true);
|
||||
MountTestComponent("BasicTestApp.TextOnlyComponent");
|
||||
|
||||
var appElement = Browser.FindElement(By.TagName("app"));
|
||||
Assert.Equal("Hello from TextOnlyComponent", appElement.Text);
|
||||
}
|
||||
|
||||
private void MountTestComponent(string componentTypeName)
|
||||
{
|
||||
WaitUntilDotNetRunningInBrowser();
|
||||
((IJavaScriptExecutor)Browser).ExecuteScript(
|
||||
$"mountTestComponent('{componentTypeName}')");
|
||||
}
|
||||
|
||||
private void WaitUntilDotNetRunningInBrowser()
|
||||
{
|
||||
new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until(driver =>
|
||||
{
|
||||
return ((IJavaScriptExecutor)driver)
|
||||
.ExecuteScript("return window.isTestReady;");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,26 @@
|
|||
// 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 Microsoft.Blazor.Browser;
|
||||
using Microsoft.Blazor.Browser.Interop;
|
||||
using Microsoft.Blazor.Components;
|
||||
using System;
|
||||
|
||||
namespace BasicTestApp
|
||||
{
|
||||
class Program
|
||||
public class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("BasicTestApp entrypoint invoked");
|
||||
// Signal to tests that we're ready
|
||||
RegisteredFunction.Invoke<object>("testReady");
|
||||
}
|
||||
|
||||
public static void MountTestComponent(string componentTypeName)
|
||||
{
|
||||
var componentType = Type.GetType(componentTypeName);
|
||||
var componentInstance = (IComponent)Activator.CreateInstance(componentType);
|
||||
Renderer.Render(componentInstance, "app");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// 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 Microsoft.Blazor.Components;
|
||||
using Microsoft.Blazor.UITree;
|
||||
|
||||
namespace BasicTestApp
|
||||
{
|
||||
public class TextOnlyComponent : IComponent
|
||||
{
|
||||
public void Render(UITreeBuilder builder)
|
||||
{
|
||||
builder.AddText($"Hello from {nameof(TextOnlyComponent)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,5 +6,17 @@
|
|||
</head>
|
||||
<body>
|
||||
<app>Loading...</app>
|
||||
|
||||
<script>
|
||||
// The client-side .NET code calls this when it is ready to be called from test code
|
||||
// The Xunit test code polls until it sees the flag is set
|
||||
Blazor.registerFunction('testReady', function () { window.isTestReady = true; });
|
||||
|
||||
// The Xunit test code calls this when setting up tests for specific components
|
||||
function mountTestComponent(typeName) {
|
||||
var method = Blazor.platform.findMethod('BasicTestApp', 'BasicTestApp', 'Program', 'MountTestComponent');
|
||||
Blazor.platform.callMethod(method, null, [Blazor.platform.toDotNetString(typeName)]);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in New Issue