Skip to content

Commit 326e329

Browse files
Run components E2E tests in CI (#5158)
* Auto-install local copy of selenium-standalone on build * Automatically start/stop selenium-standalone when running E2E tests * Update after rebase * Exclude node_modules from E2ETests project * Avoid deadlocks * Include E2E tests when running all tests in src/Components * Be more forgiving about waiting for selenium-server to be ready * Update usage of shared source file
1 parent c60d1e1 commit 326e329

File tree

6 files changed

+760
-10
lines changed

6 files changed

+760
-10
lines changed

src/Components/build/repo.props

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,6 @@
2727
<ProjectsToPack Include="$(RepositoryRoot)blazor\src\*\*.csproj" />
2828
</ItemGroup>
2929

30-
<!--
31-
By default, this excludes the end-to-end tests from the repo-level build command.
32-
The CI will script these directly by passing BlazorAllTests=true
33-
-->
34-
<ItemGroup Condition="'$(BlazorAllTests)'!='true'">
35-
<ExcludeFromTest Include="$(RepositoryRoot)test\Microsoft.AspNetCore.Components.E2ETest\Microsoft.AspNetCore.Components.E2ETest.csproj" />
36-
</ItemGroup>
37-
3830
<ItemGroup>
3931
<DotNetCoreRuntime Include="$(MicrosoftNETCoreAppPackageVersion)" />
4032

src/Components/test/Microsoft.AspNetCore.Components.E2ETest/Infrastructure/BrowserFixture.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using OpenQA.Selenium;
@@ -38,7 +38,7 @@ public BrowserFixture()
3838

3939
try
4040
{
41-
var driver = new RemoteWebDriver(opts);
41+
var driver = new RemoteWebDriver(SeleniumStandaloneServer.Instance.Uri, opts);
4242
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1);
4343
Browser = driver;
4444
Logs = new RemoteLogs(driver);
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.Extensions.Internal;
5+
using System;
6+
using System.Diagnostics;
7+
using System.Net;
8+
using System.Net.Http;
9+
using System.Net.Sockets;
10+
using System.Threading;
11+
using System.Threading.Tasks;
12+
13+
namespace Microsoft.AspNetCore.Components.E2ETest.Infrastructure
14+
{
15+
class SeleniumStandaloneServer
16+
{
17+
private static object _instanceCreationLock = new object();
18+
private static SeleniumStandaloneServer _instance;
19+
20+
public Uri Uri { get; }
21+
22+
public static SeleniumStandaloneServer Instance
23+
{
24+
get
25+
{
26+
lock (_instanceCreationLock)
27+
{
28+
if (_instance == null)
29+
{
30+
_instance = new SeleniumStandaloneServer();
31+
}
32+
}
33+
34+
return _instance;
35+
}
36+
}
37+
38+
private SeleniumStandaloneServer()
39+
{
40+
var port = FindAvailablePort();
41+
Uri = new UriBuilder("http", "localhost", port, "/wd/hub").Uri;
42+
43+
var process = Process.Start(new ProcessStartInfo
44+
{
45+
FileName = "npm",
46+
Arguments = $"run selenium-standalone start -- -- -port {port}",
47+
UseShellExecute = true,
48+
});
49+
50+
PollUntilProcessStarted();
51+
52+
AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
53+
{
54+
if (!process.HasExited)
55+
{
56+
process.KillTree(TimeSpan.FromSeconds(10));
57+
process.Dispose();
58+
}
59+
};
60+
}
61+
62+
private void PollUntilProcessStarted()
63+
{
64+
var timeoutAt = DateTime.Now.AddSeconds(30);
65+
Exception lastException = null;
66+
while (true)
67+
{
68+
if (DateTime.Now > timeoutAt)
69+
{
70+
throw new TimeoutException($"The selenium server instance did not start accepting requests at {Uri} before the timeout occurred. The last exception was: {lastException?.ToString() ?? "NULL"}");
71+
}
72+
73+
var httpClient = new HttpClient();
74+
try
75+
{
76+
var timeoutAfter1Second = new CancellationTokenSource(3000);
77+
var response = httpClient.GetAsync(
78+
Uri, timeoutAfter1Second.Token).Result;
79+
response.EnsureSuccessStatusCode();
80+
return;
81+
}
82+
catch (Exception ex)
83+
{
84+
lastException = ex;
85+
}
86+
87+
Thread.Sleep(1000);
88+
}
89+
}
90+
91+
static int FindAvailablePort()
92+
{
93+
var listener = new TcpListener(IPAddress.Loopback, 0);
94+
95+
try
96+
{
97+
listener.Start();
98+
return ((IPEndPoint)listener.LocalEndpoint).Port;
99+
}
100+
finally
101+
{
102+
listener.Stop();
103+
}
104+
}
105+
}
106+
}

src/Components/test/Microsoft.AspNetCore.Components.E2ETest/Microsoft.AspNetCore.Components.E2ETest.csproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33
<PropertyGroup>
44
<TargetFramework>netcoreapp3.0</TargetFramework>
55
<IsPackable>false</IsPackable>
6+
<DefaultItemExcludes>${DefaultItemExcludes};node_modules\**</DefaultItemExcludes>
67
<!-- WebDriver is not strong-named signed -->
78
<SignAssembly>false</SignAssembly>
89
</PropertyGroup>
910

11+
<Target Name="EnsureNpmRestored" BeforeTargets="Build" Condition="!Exists('node_modules')">
12+
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
13+
<Exec Command="npm ci" />
14+
</Target>
15+
1016
<ItemGroup>
1117
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(AspNetCorePackageVersion)" />
1218
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(AspNetCorePackageVersion)" />
@@ -32,4 +38,8 @@
3238
<ProjectReference Include="..\testapps\TestServer\TestServer.csproj" />
3339
</ItemGroup>
3440

41+
<ItemGroup>
42+
<Compile Include="..\..\..\Shared\Process\ProcessExtensions.cs" Link="Shared\ProcessExtensions.cs" />
43+
</ItemGroup>
44+
3545
</Project>

0 commit comments

Comments
 (0)