1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+
4
+ using System ;
5
+ using System . Collections . Generic ;
6
+ using System . IO ;
7
+ using System . Runtime . Versioning ;
8
+ using System . Text ;
9
+ using System . Threading . Tasks ;
10
+ using Common ;
11
+ using Microsoft . Identity . Lab . Api ;
12
+ using Microsoft . Playwright ;
13
+ using Xunit ;
14
+ using Xunit . Abstractions ;
15
+ using Process = System . Diagnostics . Process ;
16
+ using TC = Common . TestConstants ;
17
+ using TH = Common . UiTestHelpers ;
18
+
19
+ namespace MultipleApiUiTest
20
+ {
21
+ public class MultiApiTest : IClassFixture < InstallPlaywrightBrowserFixture >
22
+ {
23
+ private const string SignOutPageUriPath = @"/MicrosoftIdentity/Account/SignedOut" ;
24
+ private const uint ClientPort = 44321 ;
25
+ private const string TraceFileClassName = "OpenIDConnect" ;
26
+ private readonly LocatorAssertionsToBeVisibleOptions _assertVisibleOptions = new ( ) { Timeout = 5000 } ;
27
+ private readonly string _sampleAppPath = "3-WebApp-multi-APIs" + Path . DirectorySeparatorChar . ToString ( ) ;
28
+ private readonly string _testAssemblyLocation = typeof ( MultiApiTest ) . Assembly . Location ;
29
+ private readonly ITestOutputHelper _output ;
30
+
31
+ public MultiApiTest ( ITestOutputHelper output )
32
+ {
33
+ _output = output ;
34
+ }
35
+
36
+ [ Fact ]
37
+ [ SupportedOSPlatform ( "windows" ) ]
38
+ public async Task ChallengeUser_MicrosoftIdFlow_LocalApp_ValidEmailPasswordCreds_LoginLogout ( )
39
+ {
40
+ // Setup web app and api environmental variables.
41
+ var clientEnvVars = new Dictionary < string , string >
42
+ {
43
+ { "ASPNETCORE_ENVIRONMENT" , "Development" } ,
44
+ { TC . KestrelEndpointEnvVar , TC . HttpsStarColon + ClientPort }
45
+ } ;
46
+
47
+ Dictionary < string , Process > ? processes = null ;
48
+
49
+ // Arrange Playwright setup, to see the browser UI set Headless = false.
50
+ const string TraceFileName = TraceFileClassName + "_LoginLogout" ;
51
+ using IPlaywright playwright = await Playwright . CreateAsync ( ) ;
52
+ IBrowser browser = await playwright . Chromium . LaunchAsync ( new ( ) { Headless = false } ) ;
53
+ IBrowserContext context = await browser . NewContextAsync ( new BrowserNewContextOptions { IgnoreHTTPSErrors = true } ) ;
54
+ await context . Tracing . StartAsync ( new ( ) { Screenshots = true , Snapshots = true , Sources = true } ) ;
55
+ IPage page = await context . NewPageAsync ( ) ;
56
+ string uriWithPort = TC . LocalhostUrl + ClientPort ;
57
+
58
+ try
59
+ {
60
+ // Start the web app and api processes.
61
+ // The delay before starting client prevents transient devbox issue where the client fails to load the first time after rebuilding
62
+ var clientProcessOptions = new ProcessStartOptions ( _testAssemblyLocation , _sampleAppPath , TC . s_oidcWebAppExe , clientEnvVars ) ;
63
+
64
+ bool areProcessesRunning = TH . StartAndVerifyProcessesAreRunning ( [ clientProcessOptions ] , out processes ) ;
65
+
66
+ if ( ! areProcessesRunning )
67
+ {
68
+ _output . WriteLine ( "Process not started after 3 attempts." ) ;
69
+ StringBuilder runningProcesses = new StringBuilder ( ) ;
70
+ foreach ( var process in processes )
71
+ {
72
+ #pragma warning disable CA1305 // Specify IFormatProvider
73
+ runningProcesses . AppendLine ( $ "Is { process . Key } running: { TH . ProcessIsAlive ( process . Value ) } ") ;
74
+ #pragma warning restore CA1305 // Specify IFormatProvider
75
+ }
76
+ Assert . Fail ( TC . WebAppCrashedString + " " + runningProcesses . ToString ( ) ) ;
77
+ }
78
+
79
+ LabResponse labResponse = await LabUserHelper . GetSpecificUserAsync ( TC . OIDCUser ) ;
80
+
81
+ // Initial sign in
82
+ _output . WriteLine ( "Starting web app sign-in flow." ) ;
83
+ string email = labResponse . User . Upn ;
84
+ await TH . NavigateToWebApp ( uriWithPort , page ) ;
85
+ await TH . EnterEmailAsync ( page , email , _output ) ;
86
+ await TH . EnterPasswordAsync ( page , labResponse . User . GetOrFetchPassword ( ) , _output ) ;
87
+ await Assertions . Expect ( page . GetByText ( "Integrating Azure AD V2" ) ) . ToBeVisibleAsync ( _assertVisibleOptions ) ;
88
+ await Assertions . Expect ( page . GetByText ( email ) ) . ToBeVisibleAsync ( _assertVisibleOptions ) ;
89
+ _output . WriteLine ( "Web app sign-in flow successful." ) ;
90
+
91
+ // Sign out
92
+ _output . WriteLine ( "Starting web app sign-out flow." ) ;
93
+ await page . GetByRole ( AriaRole . Link , new ( ) { Name = "Sign out" } ) . ClickAsync ( ) ;
94
+ await TH . PerformSignOut_MicrosoftIdFlow ( page , email , TC . LocalhostUrl + ClientPort + SignOutPageUriPath , _output ) ;
95
+ _output . WriteLine ( "Web app sign out successful." ) ;
96
+ }
97
+ catch ( Exception ex )
98
+ {
99
+ //Adding guid incase of multiple test runs. This will allow screenshots to be matched to their appropriet test runs.
100
+ var guid = Guid . NewGuid ( ) . ToString ( ) ;
101
+ try
102
+ {
103
+ if ( page != null )
104
+ {
105
+ await page . ScreenshotAsync ( new PageScreenshotOptions ( ) { Path = $ "ChallengeUser_MicrosoftIdFlow_LocalApp_ValidEmailPasswordCreds_TodoAppFunctionsCorrectlyScreenshotFail{ guid } .png", FullPage = true } ) ;
106
+ }
107
+ }
108
+ catch
109
+ {
110
+ _output . WriteLine ( "No Screenshot." ) ;
111
+ }
112
+
113
+ string runningProcesses = TH . GetRunningProcessAsString ( processes ) ;
114
+ Assert . Fail ( $ "the UI automation failed: { ex } output: { ex . Message } .\n { runningProcesses } \n Test run: { guid } ") ;
115
+ }
116
+ finally
117
+ {
118
+ // Add the following to make sure all processes and their children are stopped.
119
+ TH . EndProcesses ( processes ) ;
120
+
121
+ // Stop tracing and export it into a zip archive.
122
+ string path = TH . GetTracePath ( _testAssemblyLocation , TraceFileName ) ;
123
+ await context . Tracing . StopAsync ( new ( ) { Path = path } ) ;
124
+ _output . WriteLine ( $ "Trace data for { TraceFileName } recorded to { path } .") ;
125
+
126
+ // Close the browser and stop Playwright.
127
+ await browser . CloseAsync ( ) ;
128
+ playwright . Dispose ( ) ;
129
+ }
130
+ }
131
+
132
+ }
133
+ }
0 commit comments