146 lines
4.3 KiB
C#
146 lines
4.3 KiB
C#
// 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 System.Diagnostics;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Net.Sockets;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Testing;
|
|
using Microsoft.Extensions.Internal;
|
|
|
|
namespace Microsoft.AspNetCore.Components.E2ETest.Infrastructure
|
|
{
|
|
class SeleniumStandaloneServer
|
|
{
|
|
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(30);
|
|
private static readonly object _instanceCreationLock = new object();
|
|
private static SeleniumStandaloneServer _instance;
|
|
|
|
public Uri Uri { get; }
|
|
|
|
public static SeleniumStandaloneServer Instance
|
|
{
|
|
get
|
|
{
|
|
lock (_instanceCreationLock)
|
|
{
|
|
if (_instance == null)
|
|
{
|
|
_instance = new SeleniumStandaloneServer();
|
|
}
|
|
}
|
|
|
|
return _instance;
|
|
}
|
|
}
|
|
|
|
private SeleniumStandaloneServer()
|
|
{
|
|
var port = FindAvailablePort();
|
|
Uri = new UriBuilder("http", "localhost", port, "/wd/hub").Uri;
|
|
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = "npm",
|
|
Arguments = $"run selenium-standalone start -- -- -port {port}",
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
};
|
|
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
{
|
|
psi.FileName = "cmd";
|
|
psi.Arguments = $"/c npm {psi.Arguments}";
|
|
}
|
|
|
|
var process = Process.Start(psi);
|
|
|
|
var builder = new StringBuilder();
|
|
process.OutputDataReceived += LogOutput;
|
|
process.ErrorDataReceived += LogOutput;
|
|
|
|
process.BeginOutputReadLine();
|
|
process.BeginErrorReadLine();
|
|
|
|
// The Selenium sever has to be up for the entirety of the tests and is only shutdown when the application (i.e. the test) exits.
|
|
AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
|
|
{
|
|
if (!process.HasExited)
|
|
{
|
|
process.KillTree(TimeSpan.FromSeconds(10));
|
|
process.Dispose();
|
|
}
|
|
};
|
|
|
|
void LogOutput(object sender, DataReceivedEventArgs e)
|
|
{
|
|
lock (builder)
|
|
{
|
|
builder.AppendLine(e.Data);
|
|
}
|
|
}
|
|
|
|
var waitForStart = Task.Run(async () =>
|
|
{
|
|
var httpClient = new HttpClient
|
|
{
|
|
Timeout = TimeSpan.FromSeconds(1),
|
|
};
|
|
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
var responseTask = httpClient.GetAsync(Uri);
|
|
|
|
var response = await responseTask;
|
|
if (response.StatusCode == HttpStatusCode.OK)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
|
|
}
|
|
await Task.Delay(1000);
|
|
}
|
|
});
|
|
|
|
try
|
|
{
|
|
waitForStart.TimeoutAfter(Timeout).Wait(1000);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
string output;
|
|
lock (builder)
|
|
{
|
|
output = builder.ToString();
|
|
}
|
|
|
|
throw new InvalidOperationException($"Failed to start selenium sever. {Environment.NewLine}{output}", ex.GetBaseException());
|
|
}
|
|
}
|
|
|
|
static int FindAvailablePort()
|
|
{
|
|
var listener = new TcpListener(IPAddress.Loopback, 0);
|
|
|
|
try
|
|
{
|
|
listener.Start();
|
|
return ((IPEndPoint)listener.LocalEndpoint).Port;
|
|
}
|
|
finally
|
|
{
|
|
listener.Stop();
|
|
}
|
|
}
|
|
}
|
|
}
|