1
1
// Licensed to the .NET Foundation under one or more agreements.
2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
- using System ;
5
- using System . IO ;
6
4
using System . Runtime . InteropServices ;
7
- using System . Threading . Tasks ;
8
5
using Microsoft . AspNetCore . BrowserTesting ;
6
+ using Microsoft . Playwright ;
9
7
using Templates . Test . Helpers ;
10
- using Xunit ;
11
8
12
9
namespace BlazorTemplates . Tests ;
13
10
@@ -18,14 +15,19 @@ public abstract class BlazorTemplateTest : BrowserTestBase
18
15
public BlazorTemplateTest ( ProjectFactoryFixture projectFactory )
19
16
{
20
17
ProjectFactory = projectFactory ;
21
- Microsoft . Playwright . Program . Main ( new [ ] { "install" } ) ;
18
+ Microsoft . Playwright . Program . Main ( [ "install" ] ) ;
22
19
}
23
20
24
21
public ProjectFactoryFixture ProjectFactory { get ; set ; }
25
22
26
23
public abstract string ProjectType { get ; }
27
24
28
- protected async Task < Project > CreateBuildPublishAsync ( string auth = null , string [ ] args = null , string targetFramework = null , bool serverProject = false , bool onlyCreate = false )
25
+ protected async Task < Project > CreateBuildPublishAsync (
26
+ string auth = null ,
27
+ string [ ] args = null ,
28
+ string targetFramework = null ,
29
+ Func < Project , Project > getTargetProject = null ,
30
+ bool onlyCreate = false )
29
31
{
30
32
// Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278
31
33
Environment . SetEnvironmentVariable ( "EnableDefaultScopedCssItems" , "true" ) ;
@@ -38,21 +40,17 @@ protected async Task<Project> CreateBuildPublishAsync(string auth = null, string
38
40
39
41
await project . RunDotNetNewAsync ( ProjectType , auth : auth , args : args ) ;
40
42
43
+ project = getTargetProject ? . Invoke ( project ) ?? project ;
44
+
41
45
if ( ! onlyCreate )
42
46
{
43
- var targetProject = project ;
44
- if ( serverProject )
45
- {
46
- targetProject = GetSubProject ( project , "Server" , $ "{ project . ProjectName } .Server") ;
47
- }
48
-
49
- await targetProject . RunDotNetPublishAsync ( noRestore : false ) ;
47
+ await project . RunDotNetPublishAsync ( noRestore : false ) ;
50
48
51
49
// Run dotnet build after publish. The reason is that one uses Config = Debug and the other uses Config = Release
52
50
// The output from publish will go into bin/Release/netcoreappX.Y/publish and won't be affected by calling build
53
51
// later, while the opposite is not true.
54
52
55
- await targetProject . RunDotNetBuildAsync ( ) ;
53
+ await project . RunDotNetBuildAsync ( ) ;
56
54
}
57
55
58
56
return project ;
@@ -83,6 +81,138 @@ public static bool TryValidateBrowserRequired(BrowserKind browserKind, bool isRe
83
81
return isRequired ;
84
82
}
85
83
84
+ protected async Task TestBasicInteractionInNewPageAsync (
85
+ BrowserKind browserKind ,
86
+ string listeningUri ,
87
+ string appName ,
88
+ BlazorTemplatePages pagesToExclude = BlazorTemplatePages . None ,
89
+ bool usesAuth = false )
90
+ {
91
+ if ( ! BrowserManager . IsAvailable ( browserKind ) )
92
+ {
93
+ EnsureBrowserAvailable ( browserKind ) ;
94
+ return ;
95
+ }
96
+
97
+ await using var browser = await BrowserManager . GetBrowserInstance ( browserKind , BrowserContextInfo ) ;
98
+ var page = await browser . NewPageAsync ( ) ;
99
+
100
+ Output . WriteLine ( $ "Opening browser at { listeningUri } ...") ;
101
+ await page . GotoAsync ( listeningUri , new ( ) { WaitUntil = WaitUntilState . NetworkIdle } ) ;
102
+
103
+ await TestBasicInteractionAsync ( page , appName , pagesToExclude , usesAuth ) ;
104
+
105
+ await page . CloseAsync ( ) ;
106
+ }
107
+
108
+ protected async Task TestBasicInteractionAsync (
109
+ IPage page ,
110
+ string appName ,
111
+ BlazorTemplatePages pagesToExclude = BlazorTemplatePages . None ,
112
+ bool usesAuth = false )
113
+ {
114
+ await page . WaitForSelectorAsync ( "nav" ) ;
115
+
116
+ if ( ! pagesToExclude . HasFlag ( BlazorTemplatePages . Home ) )
117
+ {
118
+ // Initially displays the home page
119
+ await page . WaitForSelectorAsync ( "h1 >> text=Hello, world!" ) ;
120
+
121
+ Assert . Equal ( "Home" , ( await page . TitleAsync ( ) ) . Trim ( ) ) ;
122
+ }
123
+
124
+ if ( ! pagesToExclude . HasFlag ( BlazorTemplatePages . Counter ) )
125
+ {
126
+ // Can navigate to the counter page
127
+ await Task . WhenAll (
128
+ page . WaitForNavigationAsync ( new ( ) { UrlString = "**/counter" } ) ,
129
+ page . WaitForSelectorAsync ( "h1 >> text=Counter" ) ,
130
+ page . WaitForSelectorAsync ( "p >> text=Current count: 0" ) ,
131
+ page . ClickAsync ( "a[href=counter]" ) ) ;
132
+
133
+ // Clicking the counter button works
134
+ await IncrementCounterAsync ( page ) ;
135
+ }
136
+
137
+ if ( usesAuth )
138
+ {
139
+ await Task . WhenAll (
140
+ page . WaitForNavigationAsync ( new ( ) { UrlString = "**/Identity/Account/Login**" , WaitUntil = WaitUntilState . NetworkIdle } ) ,
141
+ page . ClickAsync ( "text=Log in" ) ) ;
142
+
143
+ await Task . WhenAll (
144
+ page . WaitForSelectorAsync ( "[name=\" Input.Email\" ]" ) ,
145
+ page . WaitForNavigationAsync ( new ( ) { UrlString = "**/Identity/Account/Register**" , WaitUntil = WaitUntilState . NetworkIdle } ) ,
146
+ page . ClickAsync ( "text=Register as a new user" ) ) ;
147
+
148
+ var userName = $ "{ Guid . NewGuid ( ) } @example.com";
149
+ var password = "[PLACEHOLDER]-1a" ;
150
+
151
+ await page . TypeAsync ( "[name=\" Input.Email\" ]" , userName ) ;
152
+ await page . TypeAsync ( "[name=\" Input.Password\" ]" , password ) ;
153
+ await page . TypeAsync ( "[name=\" Input.ConfirmPassword\" ]" , password ) ;
154
+
155
+ // We will be redirected to the RegisterConfirmation
156
+ await Task . WhenAll (
157
+ page . WaitForNavigationAsync ( new ( ) { UrlString = "**/Identity/Account/RegisterConfirmation**" , WaitUntil = WaitUntilState . NetworkIdle } ) ,
158
+ page . ClickAsync ( "#registerSubmit" ) ) ;
159
+
160
+ // We will be redirected to the ConfirmEmail
161
+ await Task . WhenAll (
162
+ page . WaitForNavigationAsync ( new ( ) { UrlString = "**/Identity/Account/ConfirmEmail**" , WaitUntil = WaitUntilState . NetworkIdle } ) ,
163
+ page . ClickAsync ( "text=Click here to confirm your account" ) ) ;
164
+
165
+ // Now we can login
166
+ await page . ClickAsync ( "text=Login" ) ;
167
+ await page . WaitForSelectorAsync ( "[name=\" Input.Email\" ]" ) ;
168
+ await page . TypeAsync ( "[name=\" Input.Email\" ]" , userName ) ;
169
+ await page . TypeAsync ( "[name=\" Input.Password\" ]" , password ) ;
170
+ await page . ClickAsync ( "#login-submit" ) ;
171
+
172
+ // Need to navigate to fetch page
173
+ await page . GotoAsync ( new Uri ( page . Url ) . GetLeftPart ( UriPartial . Authority ) ) ;
174
+ Assert . Equal ( appName . Trim ( ) , ( await page . TitleAsync ( ) ) . Trim ( ) ) ;
175
+ }
176
+
177
+ if ( ! pagesToExclude . HasFlag ( BlazorTemplatePages . Weather ) )
178
+ {
179
+ await page . ClickAsync ( "a[href=weather]" ) ;
180
+ await page . WaitForSelectorAsync ( "h1 >> text=Weather" ) ;
181
+
182
+ // Asynchronously loads and displays the table of weather forecasts
183
+ await page . WaitForSelectorAsync ( "table>tbody>tr" ) ;
184
+ Assert . Equal ( 5 , await page . Locator ( "p+table>tbody>tr" ) . CountAsync ( ) ) ;
185
+ }
186
+
187
+ static async Task IncrementCounterAsync ( IPage page )
188
+ {
189
+ // Allow multiple click attempts because some interactive render modes
190
+ // won't be immediately available
191
+ const int MaxIncrementAttempts = 5 ;
192
+ const float IncrementTimeoutMilliseconds = 3000f ;
193
+ for ( var i = 0 ; i < MaxIncrementAttempts ; i ++ )
194
+ {
195
+ await page . ClickAsync ( "p+button >> text=Click me" ) ;
196
+ try
197
+ {
198
+ await page . WaitForSelectorAsync ( "p >> text=Current count: 1" , new ( )
199
+ {
200
+ Timeout = IncrementTimeoutMilliseconds ,
201
+ } ) ;
202
+
203
+ // The counter successfully incremented, so we're done
204
+ return ;
205
+ }
206
+ catch ( TimeoutException )
207
+ {
208
+ // The counter did not increment; try again
209
+ }
210
+ }
211
+
212
+ Assert . Fail ( $ "The counter did not increment after { MaxIncrementAttempts } attempts") ;
213
+ }
214
+ }
215
+
86
216
protected void EnsureBrowserAvailable ( BrowserKind browserKind )
87
217
{
88
218
Assert . False (
@@ -92,4 +222,14 @@ protected void EnsureBrowserAvailable(BrowserKind browserKind)
92
222
out var errorMessage ) ,
93
223
errorMessage ) ;
94
224
}
225
+
226
+ [ Flags ]
227
+ protected enum BlazorTemplatePages
228
+ {
229
+ None = 0 ,
230
+ Home = 1 ,
231
+ Counter = 2 ,
232
+ Weather = 4 ,
233
+ All = ~ 0 ,
234
+ }
95
235
}
0 commit comments