55using Microsoft . AspNetCore . Http . Connections . Client ;
66using Microsoft . AspNetCore . Mvc . Testing ;
77using Microsoft . AspNetCore . SignalR . Client ;
8+ using Microsoft . Extensions . Configuration ;
89using Microsoft . Extensions . DependencyInjection ;
910using Microsoft . Extensions . Hosting ;
1011using Microsoft . Playwright ;
1112
1213namespace ManagedCode . IntegrationTestBaseKit ;
1314
14- public abstract class BaseTestApp < TEntryPoint > : WebApplicationFactory < TEntryPoint >
15- where TEntryPoint : class
15+ public abstract class BaseTestApp < TEntryPoint > : WebApplicationFactory < TEntryPoint > where TEntryPoint : class
1616{
1717 private IHost ? _host ;
1818
19+ private readonly ConfigurationBuilder ConfigurationBuilder = new ( ) ;
20+
21+ protected virtual bool UsePlaywright { get ; } = true ;
1922 private PlaywrightWrapper Fixture { get ; } = new ( ) ;
2023
2124 protected Dictionary < string , DockerContainer > Containers { get ; } = new ( ) ;
@@ -62,18 +65,44 @@ public T GetContainer<T>() where T : DockerContainer
6265 public virtual async Task InitializeAsync ( )
6366 {
6467 await ConfigureTestContainers ( ) ;
65- await Fixture . InitializeAsync ( ) ;
68+
69+ if ( UsePlaywright )
70+ await Fixture . InitializeAsync ( ) ;
71+
6672 foreach ( var container in Containers )
6773 await container . Value . StartAsync ( ) ;
6874 }
6975
7076 protected override IHost CreateHost ( IHostBuilder builder )
7177 {
78+ ConfigureConfiguration ( ) ;
79+ var configuration = ConfigurationBuilder . Build ( ) ;
80+ builder . ConfigureWebHost ( hostBuilder =>
81+ {
82+ foreach ( var setting in configuration . AsEnumerable ( true ) )
83+ hostBuilder . UseSetting ( setting . Key , setting . Value ) ;
84+ } ) ;
85+
86+ // Create the host for TestServer now before we
87+ // modify the builder to use Kestrel instead.
7288 var testHost = builder . Build ( ) ;
89+
90+ // Modify the host builder to use Kestrel instead
91+ // of TestServer so we can listen on a real address.
7392 builder . ConfigureWebHost ( hostBuilder => hostBuilder . UseKestrel ( ) ) ;
74- _host = builder . Build ( ) ;
93+
94+ // Create and start the Kestrel server before the test server,
95+ // otherwise due to the way the deferred host builder works
96+ // for minimal hosting, the server will not get "initialized
97+ // enough" for the address it is listening on to be available.
98+ // See https://github.com/dotnet/aspnetcore/issues/33846.
99+ _host = builder . Build ( ) ; //base.CreateHost(builder);
75100 _host . Start ( ) ;
76101
102+ // Extract the selected dynamic port out of the Kestrel server
103+ // and assign it onto the client options for convenience so it
104+ // "just works" as otherwise it'll be the default http://localhost
105+ // URL, which won't route to the Kestrel-hosted HTTP server.
77106 var server = _host . Services . GetRequiredService < IServer > ( ) ;
78107 var addressFeature = server . Features . Get < IServerAddressesFeature > ( ) ;
79108 ClientOptions . BaseAddress = addressFeature ! . Addresses
@@ -84,13 +113,9 @@ protected override IHost CreateHost(IHostBuilder builder)
84113 return testHost ;
85114 }
86115
87- protected override void ConfigureWebHost ( IWebHostBuilder builder )
88- {
89- builder . UseEnvironment ( "Development" ) ;
90- }
91-
92116 public override async ValueTask DisposeAsync ( )
93117 {
118+ _host ? . Dispose ( ) ;
94119 await Fixture . DisposeAsync ( ) ;
95120 foreach ( var container in Containers )
96121 {
@@ -125,6 +150,9 @@ public HubConnection CreateSignalRClient(string hubUrl, Action<HubConnectionBuil
125150
126151 public async Task < IPage > OpenNewPage ( string url )
127152 {
153+ if ( ! UsePlaywright )
154+ throw new InvalidOperationException ( "Playwright is not enabled" ) ;
155+
128156 var fullUrl = new Uri ( ServerUri , url ) . ToString ( ) ;
129157 var context = await Browser . NewContextAsync ( ) ;
130158 var page = await context . NewPageAsync ( ) ;
@@ -144,4 +172,10 @@ protected void AddContainer(DockerContainer container)
144172 }
145173
146174 protected abstract Task ConfigureTestContainers ( ) ;
175+ protected abstract void ConfigureConfiguration ( ) ;
176+
177+ protected void SetConfigurationValue ( string key , string value )
178+ {
179+ ConfigurationBuilder . AddInMemoryCollection ( new Dictionary < string , string > { { key , value } } ! ) ;
180+ }
147181}
0 commit comments