8
8
using System . Net ;
9
9
using System . Net . Http ;
10
10
using System . Threading . Tasks ;
11
+ using AngleSharp . Dom . Html ;
12
+ using AngleSharp . Parser . Html ;
11
13
using Microsoft . AspNetCore . Certificates . Generation ;
12
14
using Microsoft . Extensions . CommandLineUtils ;
13
15
using OpenQA . Selenium ;
@@ -21,17 +23,19 @@ namespace Templates.Test.Helpers
21
23
public class AspNetProcess : IDisposable
22
24
{
23
25
private const string ListeningMessagePrefix = "Now listening on: " ;
24
- private readonly Uri _listeningUri ;
25
26
private readonly HttpClient _httpClient ;
26
27
private readonly ITestOutputHelper _output ;
27
28
29
+ internal readonly Uri ListeningUri ;
28
30
internal ProcessEx Process { get ; }
29
31
30
32
public AspNetProcess (
31
33
ITestOutputHelper output ,
32
34
string workingDirectory ,
33
35
string dllPath ,
34
- IDictionary < string , string > environmentVariables )
36
+ IDictionary < string , string > environmentVariables ,
37
+ bool published = true ,
38
+ bool hasListeningUri = true )
35
39
{
36
40
_output = output ;
37
41
_httpClient = new HttpClient ( new HttpClientHandler ( )
@@ -48,18 +52,20 @@ public AspNetProcess(
48
52
var now = DateTimeOffset . Now ;
49
53
new CertificateManager ( ) . EnsureAspNetCoreHttpsDevelopmentCertificate ( now , now . AddYears ( 1 ) ) ;
50
54
51
-
52
55
output . WriteLine ( "Running ASP.NET application..." ) ;
53
56
54
- Process = ProcessEx . Run ( output , workingDirectory , DotNetMuxer . MuxerPathOrDefault ( ) , $ "exec { dllPath } ", envVars : environmentVariables ) ;
55
- _listeningUri = GetListeningUri ( output ) ;
57
+ var arguments = published ? $ "exec { dllPath } " : "run" ;
58
+ Process = ProcessEx . Run ( output , workingDirectory , DotNetMuxer . MuxerPathOrDefault ( ) , arguments , envVars : environmentVariables ) ;
59
+ if ( hasListeningUri )
60
+ {
61
+ ListeningUri = GetListeningUri ( output ) ;
62
+ }
56
63
}
57
64
58
-
59
65
public void VisitInBrowser ( IWebDriver driver )
60
66
{
61
- _output . WriteLine ( $ "Opening browser at { _listeningUri } ...") ;
62
- driver . Navigate ( ) . GoToUrl ( _listeningUri ) ;
67
+ _output . WriteLine ( $ "Opening browser at { ListeningUri } ...") ;
68
+ driver . Navigate ( ) . GoToUrl ( ListeningUri ) ;
63
69
64
70
if ( driver is EdgeDriver )
65
71
{
@@ -75,7 +81,7 @@ public void VisitInBrowser(IWebDriver driver)
75
81
{
76
82
_output . WriteLine ( $ "Clicking on link '{ continueLink . Text } ' to skip invalid certificate error page.") ;
77
83
continueLink . Click ( ) ;
78
- driver . Navigate ( ) . GoToUrl ( _listeningUri ) ;
84
+ driver . Navigate ( ) . GoToUrl ( ListeningUri ) ;
79
85
}
80
86
else
81
87
{
@@ -85,6 +91,58 @@ public void VisitInBrowser(IWebDriver driver)
85
91
}
86
92
}
87
93
94
+ public async Task AssertPagesOk ( IEnumerable < Page > pages )
95
+ {
96
+ foreach ( var page in pages )
97
+ {
98
+ await AssertOk ( page . Url ) ;
99
+ await ContainsLinks ( page ) ;
100
+ }
101
+ }
102
+
103
+ public async Task ContainsLinks ( Page page )
104
+ {
105
+ var request = new HttpRequestMessage (
106
+ HttpMethod . Get ,
107
+ new Uri ( ListeningUri , page . Url ) ) ;
108
+
109
+ var response = await _httpClient . SendAsync ( request ) ;
110
+
111
+ Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
112
+ var parser = new HtmlParser ( ) ;
113
+ var html = await parser . ParseAsync ( await response . Content . ReadAsStreamAsync ( ) ) ;
114
+
115
+ foreach ( IHtmlLinkElement styleSheet in html . GetElementsByTagName ( "link" ) )
116
+ {
117
+ Assert . Equal ( "stylesheet" , styleSheet . Relation ) ;
118
+ await AssertOk ( styleSheet . Href . Replace ( "about://" , string . Empty ) ) ;
119
+ }
120
+ foreach ( var script in html . Scripts )
121
+ {
122
+ if ( ! string . IsNullOrEmpty ( script . Source ) )
123
+ {
124
+ await AssertOk ( script . Source ) ;
125
+ }
126
+ }
127
+
128
+ Assert . True ( html . Links . Length == page . Links . Count ( ) , $ "Expected { page . Url } to have { page . Links . Count ( ) } links but it had { html . Links . Length } ") ;
129
+ foreach ( ( var link , var expectedLink ) in html . Links . Zip ( page . Links , Tuple . Create ) )
130
+ {
131
+ IHtmlAnchorElement anchor = ( IHtmlAnchorElement ) link ;
132
+ if ( string . Equals ( anchor . Protocol , "about:" ) )
133
+ {
134
+ Assert . True ( anchor . PathName . EndsWith ( expectedLink ) , $ "Expected next link on { page . Url } to be { expectedLink } but it was { anchor . PathName } .") ;
135
+ await AssertOk ( anchor . PathName ) ;
136
+ }
137
+ else
138
+ {
139
+ Assert . True ( string . Equals ( anchor . Href , expectedLink ) , $ "Expected next link to be { expectedLink } but it was { anchor . Href } .") ;
140
+ var result = await _httpClient . GetAsync ( anchor . Href ) ;
141
+ Assert . True ( IsSuccessStatusCode ( result ) , $ "{ anchor . Href } is a broken link!") ;
142
+ }
143
+ }
144
+ }
145
+
88
146
private Uri GetListeningUri ( ITestOutputHelper output )
89
147
{
90
148
// Wait until the app is accepting HTTP requests
@@ -113,6 +171,11 @@ private Uri GetListeningUri(ITestOutputHelper output)
113
171
}
114
172
}
115
173
174
+ private bool IsSuccessStatusCode ( HttpResponseMessage response )
175
+ {
176
+ return response . IsSuccessStatusCode || response . StatusCode == HttpStatusCode . Redirect ;
177
+ }
178
+
116
179
public Task AssertOk ( string requestUrl )
117
180
=> AssertStatusCode ( requestUrl , HttpStatusCode . OK ) ;
118
181
@@ -121,22 +184,22 @@ public Task AssertNotFound(string requestUrl)
121
184
122
185
internal Task < HttpResponseMessage > SendRequest ( string path )
123
186
{
124
- return _httpClient . GetAsync ( new Uri ( _listeningUri , path ) ) ;
187
+ return _httpClient . GetAsync ( new Uri ( ListeningUri , path ) ) ;
125
188
}
126
189
127
190
public async Task AssertStatusCode ( string requestUrl , HttpStatusCode statusCode , string acceptContentType = null )
128
191
{
129
192
var request = new HttpRequestMessage (
130
193
HttpMethod . Get ,
131
- new Uri ( _listeningUri , requestUrl ) ) ;
194
+ new Uri ( ListeningUri , requestUrl ) ) ;
132
195
133
196
if ( ! string . IsNullOrEmpty ( acceptContentType ) )
134
197
{
135
198
request . Headers . Add ( "Accept" , acceptContentType ) ;
136
199
}
137
200
138
201
var response = await _httpClient . SendAsync ( request ) ;
139
- Assert . Equal ( statusCode , response . StatusCode ) ;
202
+ Assert . True ( statusCode == response . StatusCode , $ "Expected { requestUrl } to have status ' { statusCode } ' but it was ' { response . StatusCode } '." ) ;
140
203
}
141
204
142
205
public void Dispose ( )
@@ -153,7 +216,7 @@ public override string ToString()
153
216
{
154
217
if ( ! Process . HasExited )
155
218
{
156
- result += $ "(Listening on { _listeningUri . OriginalString } ) PID: { Process . Id } ";
219
+ result += $ "(Listening on { ListeningUri . OriginalString } ) PID: { Process . Id } ";
157
220
}
158
221
else
159
222
{
@@ -164,4 +227,10 @@ public override string ToString()
164
227
return result ;
165
228
}
166
229
}
230
+
231
+ public class Page
232
+ {
233
+ public string Url { get ; set ; }
234
+ public IEnumerable < string > Links { get ; set ; }
235
+ }
167
236
}
0 commit comments