Skip to content

Commit a09af28

Browse files
authored
Enable integration test web application (#12714)
* Create TestServices project
1 parent 86bf444 commit a09af28

30 files changed

+725
-204
lines changed

.ado/jobs/desktop.yml

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ parameters:
6060
- X86ReleaseFabric:
6161
BuildConfiguration: Release
6262
BuildPlatform: x86
63-
UseFabric: true
63+
UseFabric: true
6464

6565
jobs:
6666
- ${{ each config in parameters.buildMatrix }}:
@@ -78,11 +78,14 @@ jobs:
7878

7979
#5059 - Disable failing or intermittent tests (IntegrationTestHarness,WebSocket,Logging).
8080
#10732 - WebSocketIntegrationTest::SendReceiveSsl fails on Windows Server 2022.
81+
#12714 - Disable for first deployment of test website.
82+
# RNTesterIntegrationTests::WebSocket
83+
# RNTesterIntegrationTests::WebSocketBlob
8184
- name: Desktop.IntegrationTests.Filter
8285
value: >
83-
(FullyQualifiedName!=RNTesterIntegrationTests::Blob)&
8486
(FullyQualifiedName!=RNTesterIntegrationTests::IntegrationTestHarness)&
85-
(FullyQualifiedName!=WebSocketResourcePerformanceTest::ProcessThreadsPerResource)&
87+
(FullyQualifiedName!=RNTesterIntegrationTests::WebSocket)&
88+
(FullyQualifiedName!=RNTesterIntegrationTests::WebSocketBlob)&
8689
(FullyQualifiedName!=WebSocketIntegrationTest::SendReceiveSsl)&
8790
(FullyQualifiedName!=Microsoft::React::Test::HttpOriginPolicyIntegrationTest)
8891
#6799 -
@@ -100,6 +103,31 @@ jobs:
100103
cancelTimeoutInMinutes: 5 # how much time to give 'run always even if cancelled tasks' before killing them
101104

102105
steps:
106+
# Set up IIS tests {
107+
- pwsh: |
108+
Install-WindowsFeature -Name Web-Server, Web-Scripting-Tools
109+
displayName: Install IIS
110+
111+
- pwsh: |
112+
Invoke-WebRequest `
113+
-Uri 'https://download.visualstudio.microsoft.com/download/pr/20598243-c38f-4538-b2aa-af33bc232f80/ea9b2ca232f59a6fdc84b7a31da88464/dotnet-hosting-8.0.3-win.exe' `
114+
-OutFile dotnet-hosting-8.0.3-win.exe
115+
116+
Write-Host 'Installing .NET hosting bundle'
117+
Start-Process -Wait -FilePath .\dotnet-hosting-8.0.3-win.exe -ArgumentList '/INSTALL', '/QUIET', '/NORESTART'
118+
Write-Host 'Installed .NET hosting bundle'
119+
120+
Invoke-WebRequest `
121+
-Uri 'https://download.visualstudio.microsoft.com/download/pr/f2ec926e-0d98-4a8b-8c70-722ccc2ca0e5/b59941b0c60f16421679baafdb7e9338/dotnet-sdk-7.0.407-win-x64.exe' `
122+
-OutFile dotnet-sdk-7.0.407-win-x64.exe
123+
124+
Write-Host 'Installing .NET 7 SDK'
125+
Start-Process -Wait -FilePath .\dotnet-sdk-7.0.407-win-x64.exe -ArgumentList '/INSTALL', '/QUIET', '/NORESTART'
126+
Write-Host 'Installed .NET 7 SDK'
127+
displayName: Install the .NET Core Hosting Bundle
128+
129+
# } Set up IIS tests
130+
103131
- template: ../templates/checkout-shallow.yml
104132

105133
- template: ../templates/prepare-js-env.yml
@@ -168,11 +196,89 @@ jobs:
168196
filePath: $(Build.SourcesDirectory)\vnext\Scripts\Tfs\Start-TestServers.ps1
169197
arguments: -SourcesDirectory $(Build.SourcesDirectory)\vnext -Preload -SleepSeconds 120
170198

199+
- task: DotNetCoreCLI@2
200+
displayName: Publish Test Website
201+
inputs:
202+
command: publish
203+
publishWebProjects: false
204+
zipAfterPublish: false
205+
projects: $(Build.SourcesDirectory)\vnext\TestWebsite\Microsoft.ReactNative.Test.Website.csproj
206+
arguments: --configuration ${{ matrix.BuildConfiguration }}
207+
208+
- pwsh: |
209+
# Create and make available to IIS
210+
$cert = New-SelfSignedCertificate `
211+
-Type SSLServerAuthentication `
212+
-KeyExportPolicy Exportable `
213+
-Subject 'CN=localhost' `
214+
-NotAfter ([DateTime]::Now).AddHours(2) `
215+
-CertStoreLocation Cert:\LocalMachine\My\
216+
217+
$certPath = "${env:TEMP}\localhost.pfx"
218+
$certPass = -join ('a'..'z' | Get-Random -Count 32) | ConvertTo-SecureString -AsPlainText -Force
219+
$certHash = $cert.Thumbprint
220+
Write-Host "##vso[task.setvariable variable=TestWebsiteCertificateThumbprint]$certHash"
221+
222+
# Export PFX
223+
$cert | Export-PfxCertificate -FilePath $certPath -Password $certPass
224+
225+
# Trust globally
226+
Import-PfxCertificate `
227+
-Exportable `
228+
-FilePath $certPath `
229+
-Password $certPass `
230+
-CertStoreLocation Cert:\LocalMachine\Root\
231+
displayName: Install SSL Certificate
232+
233+
- task: IISWebAppManagementOnMachineGroup@0
234+
displayName: Create Test Website
235+
inputs:
236+
EnableIIS: false
237+
IISDeploymentType: IISWebsite
238+
ActionIISWebsite: CreateOrUpdateWebsite
239+
SSLCertThumbPrint: $(TestWebsiteCertificateThumbprint)
240+
# IIS Website
241+
WebsiteName: RNW Test Website
242+
WebsitePhysicalPath: $(Build.SourcesDirectory)\vnext\target\${{ matrix.BuildPlatform }}\${{ matrix.BuildConfiguration }}\Microsoft.ReactNative.Test.Website\Publish
243+
WebsitePhysicalPathAuth: WebsiteUserPassThrough
244+
CreateOrUpdateAppPoolForWebsite: false
245+
ConfigureAuthenticationForWebsite: false
246+
# IIS Application pool
247+
AppPoolNameForWebsite: DefaultAppPool
248+
# IIS Bindings
249+
# See https://stackoverflow.com/questions/60089756
250+
AddBinding: true
251+
Bindings: |
252+
{
253+
bindings: [
254+
{
255+
"protocol": "http",
256+
"ipAddress": "*",
257+
"port": "5555",
258+
"sslThumbprint": "",
259+
"sniFlag": false
260+
},
261+
{
262+
"protocol": "https",
263+
"ipAddress": "*",
264+
"port": "5543",
265+
"sslThumbprint": "$(TestWebsiteCertificateThumbprint)",
266+
"sniFlag": false
267+
}
268+
]
269+
}
270+
171271
- task: PowerShell@2
172-
displayName: Check the metro bundle server
272+
displayName: Ensure servers readiness
173273
inputs:
174274
targetType: 'inline'
175-
script: Invoke-WebRequest -UseBasicParsing -Uri "http://localhost:8081/IntegrationTests/IntegrationTestsApp.bundle?platform=windows&dev=true"
275+
script: |
276+
# Test website
277+
Invoke-WebRequest -Uri 'http://localhost:5555'
278+
Invoke-WebRequest -Uri 'https://localhost:5543'
279+
280+
# Bundler
281+
Invoke-WebRequest -UseBasicParsing -Uri "http://localhost:8081/IntegrationTests/IntegrationTestsApp.bundle?platform=windows&dev=true"
176282
177283
- task: VSTest@2
178284
displayName: Run Desktop Integration Tests

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ package-lock.json
140140
!.vscode/tasks.json
141141
!.vscode/launch.json
142142
!.vscode/extensions.json
143+
vnext/.vscode/*
143144

144145
#JavaScript files
145146
*.jsbundle

Directory.Build.targets

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
>
3636
<Output TaskParameter="ExitCode" ItemName="_ReactNativeYarnExitCode"/>
3737
</Exec>
38-
38+
3939
<Message Text="yarn install: Succeeded" Condition="'%(_ReactNativeYarnExitCode.Identity)' == '0'" Importance="High" />
4040
<Message Text="yarn install: Failed" Condition="'%(_ReactNativeYarnExitCode.Identity)' != '0'" Importance="High" />
4141

@@ -91,8 +91,10 @@
9191
VCTargetsPath;
9292
UserRootDir;
9393
ProjectHome;
94+
BaseIntermediateOutputPath;
9495
IntermediateOutputPath;
9596
OutputPath;
97+
OS;
9698
" />
9799
<_VarsCustom Include="
98100
RootDir;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "Implement test website",
4+
"packageName": "react-native-windows",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

vnext/Desktop.IntegrationTests/HttpOriginPolicyIntegrationTest.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,46 @@ TEST_CLASS(HttpOriginPolicyIntegrationTest)
743743
TestOriginPolicy(serverArgs, clientArgs, s_shouldFail);
744744
} // FullCors304ForSimpleGetFails
745745

746+
TEST_METHOD(OfficeDev_OfficeJS_4144)
747+
{
748+
SetRuntimeOptionString("Http.GlobalOrigin", "http://orig.in");
749+
SetRuntimeOptionInt("Http.OriginPolicy", static_cast<int32_t>(OriginPolicy::CrossOriginResourceSharing));
750+
751+
ClientParams clientArgs(http::verb::get, {} /*headers*/);
752+
auto resource = IHttpResource::Make();
753+
resource->SetOnResponse([&clientArgs](int64_t, IHttpResource::Response&& res)
754+
{
755+
clientArgs.Response = std::move(res);
756+
});
757+
resource->SetOnData([&clientArgs](int64_t, string&& content)
758+
{
759+
clientArgs.ResponseContent = std::move(content);
760+
clientArgs.ContentPromise.set_value();
761+
});
762+
resource->SetOnError([&clientArgs](int64_t, string&& message, bool)
763+
{
764+
clientArgs.ErrorMessage = std::move(message);
765+
clientArgs.ContentPromise.set_value();
766+
});
767+
768+
resource->SendRequest(
769+
string { http::to_string(clientArgs.Method).data() },
770+
string { "http://localhost:5555/officedev/office-js/issues/4144"},
771+
0, /*requestId*/
772+
std::move(clientArgs.RequestHeaders),
773+
{ { "string", "" } }, /*data*/
774+
"text",
775+
false, /*useIncrementalUpdates*/
776+
0, /*timeout*/
777+
clientArgs.WithCredentials, /*withCredentials*/
778+
[](int64_t) {} /*reactCallback*/
779+
);
780+
781+
clientArgs.ContentPromise.get_future().wait();
782+
783+
Assert::AreEqual({}, clientArgs.ErrorMessage);
784+
}
785+
746786
TEST_METHOD(FullCorsPreflightSucceeds)
747787
{
748788
ServerParams serverArgs(s_port);

vnext/Desktop.IntegrationTests/RNTesterIntegrationTests.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ TEST_CLASS (RNTesterIntegrationTests) {
209209
}
210210

211211
BEGIN_TEST_METHOD_ATTRIBUTE(WebSocketBlob)
212-
TEST_IGNORE()
213212
END_TEST_METHOD_ATTRIBUTE()
214213
TEST_METHOD(WebSocketBlob) {
215214
auto result = m_runner.RunTest("IntegrationTests/WebSocketBlobTest", "WebSocketBlobTest");

vnext/Desktop.IntegrationTests/WebSocketIntegrationTest.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,44 @@ TEST_CLASS (WebSocketIntegrationTest)
280280
END_TEST_METHOD_ATTRIBUTE()
281281
TEST_METHOD(SendReceiveSsl)
282282
{
283-
SendReceiveCloseBase(/*isSecure*/ true);
283+
auto ws = IWebSocketResource::Make();
284+
promise<size_t> sentSizePromise;
285+
ws->SetOnSend([&sentSizePromise](size_t size)
286+
{
287+
sentSizePromise.set_value(size);
288+
});
289+
promise<string> receivedPromise;
290+
ws->SetOnMessage([&receivedPromise](size_t size, const string& message, bool isBinary)
291+
{
292+
receivedPromise.set_value(message);
293+
});
294+
string clientError{};
295+
ws->SetOnError([&clientError, &sentSizePromise, &receivedPromise](Error err)
296+
{
297+
clientError = err.Message;
298+
sentSizePromise.set_value(0);
299+
receivedPromise.set_value("");
300+
});
301+
302+
string sent = "prefix";
303+
auto expectedSize = sent.size();
304+
ws->Connect("wss://localhost:5543/rnw/websockets/echosuffix");
305+
ws->Send(std::move(sent));
306+
307+
// Block until response is received. Fail in case of a remote endpoint failure.
308+
auto sentSizeFuture = sentSizePromise.get_future();
309+
sentSizeFuture.wait();
310+
auto sentSize = sentSizeFuture.get();
311+
auto receivedFuture = receivedPromise.get_future();
312+
receivedFuture.wait();
313+
string received = receivedFuture.get();
314+
Assert::AreEqual({}, clientError);
315+
316+
ws->Close(CloseCode::Normal, "Closing after reading");
317+
318+
Assert::AreEqual({}, clientError);
319+
Assert::AreEqual(expectedSize, sentSize);
320+
Assert::AreEqual({"prefix_response"}, received);
284321
}
285322

286323
BEGIN_TEST_METHOD_ATTRIBUTE(SendBinary)

vnext/Desktop.IntegrationTests/WebSocketResourcePerformanceTests.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ TEST_CLASS (WebSocketResourcePerformanceTest) {
5959
/// several times, then measure the amount of allocated threads. Important. This
6060
/// test must be run in isolation (no other tests running concurrently).
6161
///
62+
BEGIN_TEST_METHOD_ATTRIBUTE(ProcessThreadsPerResource)
63+
TEST_IGNORE()
64+
END_TEST_METHOD_ATTRIBUTE()
6265
TEST_METHOD(ProcessThreadsPerResource) {
6366
// About 3 seconds total running time.
6467
// 6, if we increase this value to 100.
@@ -71,11 +74,6 @@ TEST_CLASS (WebSocketResourcePerformanceTest) {
7174
bool errorFound = false;
7275
string errorMessage;
7376

74-
auto server = std::make_shared<Test::WebSocketServer>(s_port);
75-
server->SetMessageFactory([](string &&message) { return message + "_response"; });
76-
// TODO: #4493 - Allow TestWebSocketServer to handle multiple incoming messages.
77-
// server->Start();
78-
7977
// WebSocket resources scope.
8078
{
8179
vector<shared_ptr<IWebSocketResource>> resources;
@@ -112,7 +110,7 @@ TEST_CLASS (WebSocketResourcePerformanceTest) {
112110
errorFound = true;
113111
errorMessage = error.Message;
114112
});
115-
ws->Connect("ws://localhost:" + std::to_string(s_port));
113+
ws->Connect("ws://localhost:5555");
116114

117115
resources.push_back(std::move(ws));
118116
} // Create and store WS resources.
@@ -127,8 +125,6 @@ TEST_CLASS (WebSocketResourcePerformanceTest) {
127125
ws->Close();
128126
}
129127
}
130-
// TODO: #4493 - Allow TestWebSocketServer to handle multiple incoming messages.
131-
// server->Stop();
132128

133129
int32_t finalThreadCount = threadCount.load();
134130
int64_t threadsPerResource = (finalThreadCount - startThreadCount) / resourceTotal;

vnext/Directory.Build.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
[MSBuild]::NormalizeDirectory on relative paths since cwd is not always correct. This logic should prefer to operate
3131
on full paths and avoid extra normalization.
3232
-->
33-
<PropertyGroup>
33+
<PropertyGroup Label="NodeNativeDeps" Condition="'$(IgnoreNodeNativeDeps)' != 'true'">
3434
<ReactNativeWindowsDir Condition="'$(ReactNativeWindowsDir)' == ''">$(MSBuildThisFileDirectory)</ReactNativeWindowsDir>
3535

3636
<ReactNativeDir Condition="'$(ReactNativeDir)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'node_modules\react-native\package.json'))\node_modules\react-native\</ReactNativeDir>
@@ -80,4 +80,4 @@
8080
<RestoreUseStaticGraphEvaluation Condition="'$(BuildingInsideVisualStudio)' == 'true' AND $([MSBuild]::VersionLessThan('$(MSBuildVersion)', '17.6')) AND '$(DisableRestoreUseStaticGraphEvaluation)' != 'true'">true</RestoreUseStaticGraphEvaluation>
8181
</PropertyGroup>
8282

83-
</Project>
83+
</Project>

vnext/ReactWindows-Desktop.sln

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx",
105105
EndProject
106106
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactCommon.UnitTests", "ReactCommon.UnitTests\ReactCommon.UnitTests.vcxproj", "{B0941079-7441-4A69-868C-FE5EC62C2E9E}"
107107
EndProject
108+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ReactNative.Test.Website", "TestWebsite\Microsoft.ReactNative.Test.Website.csproj", "{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}"
109+
EndProject
108110
Global
109111
GlobalSection(SolutionConfigurationPlatforms) = preSolution
110112
Debug|x64 = Debug|x64
@@ -285,6 +287,18 @@ Global
285287
{B0941079-7441-4A69-868C-FE5EC62C2E9E}.Release|x64.ActiveCfg = Release|x64
286288
{B0941079-7441-4A69-868C-FE5EC62C2E9E}.Release|x64.Build.0 = Release|x64
287289
{B0941079-7441-4A69-868C-FE5EC62C2E9E}.Release|x86.ActiveCfg = Release|x64
290+
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Debug|ARM64.ActiveCfg = Debug|Any CPU
291+
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Debug|ARM64.Build.0 = Debug|Any CPU
292+
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Debug|x64.ActiveCfg = Debug|Any CPU
293+
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Debug|x64.Build.0 = Debug|Any CPU
294+
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Debug|x86.ActiveCfg = Debug|Any CPU
295+
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Debug|x86.Build.0 = Debug|Any CPU
296+
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Release|ARM64.ActiveCfg = Release|Any CPU
297+
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Release|ARM64.Build.0 = Release|Any CPU
298+
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Release|x64.ActiveCfg = Release|Any CPU
299+
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Release|x64.Build.0 = Release|Any CPU
300+
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Release|x86.ActiveCfg = Release|Any CPU
301+
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Release|x86.Build.0 = Release|Any CPU
288302
EndGlobalSection
289303
GlobalSection(SolutionProperties) = preSolution
290304
HideSolutionNode = FALSE

0 commit comments

Comments
 (0)