Skip to content

Commit

Permalink
Enable integration test web application (#12714)
Browse files Browse the repository at this point in the history
* Create TestServices project
  • Loading branch information
JunielKatarn authored Mar 20, 2024
1 parent 86bf444 commit a09af28
Show file tree
Hide file tree
Showing 30 changed files with 725 additions and 204 deletions.
116 changes: 111 additions & 5 deletions .ado/jobs/desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ parameters:
- X86ReleaseFabric:
BuildConfiguration: Release
BuildPlatform: x86
UseFabric: true
UseFabric: true

jobs:
- ${{ each config in parameters.buildMatrix }}:
Expand All @@ -78,11 +78,14 @@ jobs:

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

steps:
# Set up IIS tests {
- pwsh: |
Install-WindowsFeature -Name Web-Server, Web-Scripting-Tools
displayName: Install IIS
- pwsh: |
Invoke-WebRequest `
-Uri 'https://download.visualstudio.microsoft.com/download/pr/20598243-c38f-4538-b2aa-af33bc232f80/ea9b2ca232f59a6fdc84b7a31da88464/dotnet-hosting-8.0.3-win.exe' `
-OutFile dotnet-hosting-8.0.3-win.exe
Write-Host 'Installing .NET hosting bundle'
Start-Process -Wait -FilePath .\dotnet-hosting-8.0.3-win.exe -ArgumentList '/INSTALL', '/QUIET', '/NORESTART'
Write-Host 'Installed .NET hosting bundle'
Invoke-WebRequest `
-Uri 'https://download.visualstudio.microsoft.com/download/pr/f2ec926e-0d98-4a8b-8c70-722ccc2ca0e5/b59941b0c60f16421679baafdb7e9338/dotnet-sdk-7.0.407-win-x64.exe' `
-OutFile dotnet-sdk-7.0.407-win-x64.exe
Write-Host 'Installing .NET 7 SDK'
Start-Process -Wait -FilePath .\dotnet-sdk-7.0.407-win-x64.exe -ArgumentList '/INSTALL', '/QUIET', '/NORESTART'
Write-Host 'Installed .NET 7 SDK'
displayName: Install the .NET Core Hosting Bundle
# } Set up IIS tests

- template: ../templates/checkout-shallow.yml

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

- task: DotNetCoreCLI@2
displayName: Publish Test Website
inputs:
command: publish
publishWebProjects: false
zipAfterPublish: false
projects: $(Build.SourcesDirectory)\vnext\TestWebsite\Microsoft.ReactNative.Test.Website.csproj
arguments: --configuration ${{ matrix.BuildConfiguration }}

- pwsh: |
# Create and make available to IIS
$cert = New-SelfSignedCertificate `
-Type SSLServerAuthentication `
-KeyExportPolicy Exportable `
-Subject 'CN=localhost' `
-NotAfter ([DateTime]::Now).AddHours(2) `
-CertStoreLocation Cert:\LocalMachine\My\
$certPath = "${env:TEMP}\localhost.pfx"
$certPass = -join ('a'..'z' | Get-Random -Count 32) | ConvertTo-SecureString -AsPlainText -Force
$certHash = $cert.Thumbprint
Write-Host "##vso[task.setvariable variable=TestWebsiteCertificateThumbprint]$certHash"
# Export PFX
$cert | Export-PfxCertificate -FilePath $certPath -Password $certPass
# Trust globally
Import-PfxCertificate `
-Exportable `
-FilePath $certPath `
-Password $certPass `
-CertStoreLocation Cert:\LocalMachine\Root\
displayName: Install SSL Certificate
- task: IISWebAppManagementOnMachineGroup@0
displayName: Create Test Website
inputs:
EnableIIS: false
IISDeploymentType: IISWebsite
ActionIISWebsite: CreateOrUpdateWebsite
SSLCertThumbPrint: $(TestWebsiteCertificateThumbprint)
# IIS Website
WebsiteName: RNW Test Website
WebsitePhysicalPath: $(Build.SourcesDirectory)\vnext\target\${{ matrix.BuildPlatform }}\${{ matrix.BuildConfiguration }}\Microsoft.ReactNative.Test.Website\Publish
WebsitePhysicalPathAuth: WebsiteUserPassThrough
CreateOrUpdateAppPoolForWebsite: false
ConfigureAuthenticationForWebsite: false
# IIS Application pool
AppPoolNameForWebsite: DefaultAppPool
# IIS Bindings
# See https://stackoverflow.com/questions/60089756
AddBinding: true
Bindings: |
{
bindings: [
{
"protocol": "http",
"ipAddress": "*",
"port": "5555",
"sslThumbprint": "",
"sniFlag": false
},
{
"protocol": "https",
"ipAddress": "*",
"port": "5543",
"sslThumbprint": "$(TestWebsiteCertificateThumbprint)",
"sniFlag": false
}
]
}
- task: PowerShell@2
displayName: Check the metro bundle server
displayName: Ensure servers readiness
inputs:
targetType: 'inline'
script: Invoke-WebRequest -UseBasicParsing -Uri "http://localhost:8081/IntegrationTests/IntegrationTestsApp.bundle?platform=windows&dev=true"
script: |
# Test website
Invoke-WebRequest -Uri 'http://localhost:5555'
Invoke-WebRequest -Uri 'https://localhost:5543'
# Bundler
Invoke-WebRequest -UseBasicParsing -Uri "http://localhost:8081/IntegrationTests/IntegrationTestsApp.bundle?platform=windows&dev=true"
- task: VSTest@2
displayName: Run Desktop Integration Tests
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ package-lock.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
vnext/.vscode/*

#JavaScript files
*.jsbundle
Expand Down
4 changes: 3 additions & 1 deletion Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
>
<Output TaskParameter="ExitCode" ItemName="_ReactNativeYarnExitCode"/>
</Exec>

<Message Text="yarn install: Succeeded" Condition="'%(_ReactNativeYarnExitCode.Identity)' == '0'" Importance="High" />
<Message Text="yarn install: Failed" Condition="'%(_ReactNativeYarnExitCode.Identity)' != '0'" Importance="High" />

Expand Down Expand Up @@ -91,8 +91,10 @@
VCTargetsPath;
UserRootDir;
ProjectHome;
BaseIntermediateOutputPath;
IntermediateOutputPath;
OutputPath;
OS;
" />
<_VarsCustom Include="
RootDir;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Implement test website",
"packageName": "react-native-windows",
"email": "[email protected]",
"dependentChangeType": "patch"
}
40 changes: 40 additions & 0 deletions vnext/Desktop.IntegrationTests/HttpOriginPolicyIntegrationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,46 @@ TEST_CLASS(HttpOriginPolicyIntegrationTest)
TestOriginPolicy(serverArgs, clientArgs, s_shouldFail);
} // FullCors304ForSimpleGetFails

TEST_METHOD(OfficeDev_OfficeJS_4144)
{
SetRuntimeOptionString("Http.GlobalOrigin", "http://orig.in");
SetRuntimeOptionInt("Http.OriginPolicy", static_cast<int32_t>(OriginPolicy::CrossOriginResourceSharing));

ClientParams clientArgs(http::verb::get, {} /*headers*/);
auto resource = IHttpResource::Make();
resource->SetOnResponse([&clientArgs](int64_t, IHttpResource::Response&& res)
{
clientArgs.Response = std::move(res);
});
resource->SetOnData([&clientArgs](int64_t, string&& content)
{
clientArgs.ResponseContent = std::move(content);
clientArgs.ContentPromise.set_value();
});
resource->SetOnError([&clientArgs](int64_t, string&& message, bool)
{
clientArgs.ErrorMessage = std::move(message);
clientArgs.ContentPromise.set_value();
});

resource->SendRequest(
string { http::to_string(clientArgs.Method).data() },
string { "http://localhost:5555/officedev/office-js/issues/4144"},
0, /*requestId*/
std::move(clientArgs.RequestHeaders),
{ { "string", "" } }, /*data*/
"text",
false, /*useIncrementalUpdates*/
0, /*timeout*/
clientArgs.WithCredentials, /*withCredentials*/
[](int64_t) {} /*reactCallback*/
);

clientArgs.ContentPromise.get_future().wait();

Assert::AreEqual({}, clientArgs.ErrorMessage);
}

TEST_METHOD(FullCorsPreflightSucceeds)
{
ServerParams serverArgs(s_port);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ TEST_CLASS (RNTesterIntegrationTests) {
}

BEGIN_TEST_METHOD_ATTRIBUTE(WebSocketBlob)
TEST_IGNORE()
END_TEST_METHOD_ATTRIBUTE()
TEST_METHOD(WebSocketBlob) {
auto result = m_runner.RunTest("IntegrationTests/WebSocketBlobTest", "WebSocketBlobTest");
Expand Down
39 changes: 38 additions & 1 deletion vnext/Desktop.IntegrationTests/WebSocketIntegrationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,44 @@ TEST_CLASS (WebSocketIntegrationTest)
END_TEST_METHOD_ATTRIBUTE()
TEST_METHOD(SendReceiveSsl)
{
SendReceiveCloseBase(/*isSecure*/ true);
auto ws = IWebSocketResource::Make();
promise<size_t> sentSizePromise;
ws->SetOnSend([&sentSizePromise](size_t size)
{
sentSizePromise.set_value(size);
});
promise<string> receivedPromise;
ws->SetOnMessage([&receivedPromise](size_t size, const string& message, bool isBinary)
{
receivedPromise.set_value(message);
});
string clientError{};
ws->SetOnError([&clientError, &sentSizePromise, &receivedPromise](Error err)
{
clientError = err.Message;
sentSizePromise.set_value(0);
receivedPromise.set_value("");
});

string sent = "prefix";
auto expectedSize = sent.size();
ws->Connect("wss://localhost:5543/rnw/websockets/echosuffix");
ws->Send(std::move(sent));

// Block until response is received. Fail in case of a remote endpoint failure.
auto sentSizeFuture = sentSizePromise.get_future();
sentSizeFuture.wait();
auto sentSize = sentSizeFuture.get();
auto receivedFuture = receivedPromise.get_future();
receivedFuture.wait();
string received = receivedFuture.get();
Assert::AreEqual({}, clientError);

ws->Close(CloseCode::Normal, "Closing after reading");

Assert::AreEqual({}, clientError);
Assert::AreEqual(expectedSize, sentSize);
Assert::AreEqual({"prefix_response"}, received);
}

BEGIN_TEST_METHOD_ATTRIBUTE(SendBinary)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ TEST_CLASS (WebSocketResourcePerformanceTest) {
/// several times, then measure the amount of allocated threads. Important. This
/// test must be run in isolation (no other tests running concurrently).
///
BEGIN_TEST_METHOD_ATTRIBUTE(ProcessThreadsPerResource)
TEST_IGNORE()
END_TEST_METHOD_ATTRIBUTE()
TEST_METHOD(ProcessThreadsPerResource) {
// About 3 seconds total running time.
// 6, if we increase this value to 100.
Expand All @@ -71,11 +74,6 @@ TEST_CLASS (WebSocketResourcePerformanceTest) {
bool errorFound = false;
string errorMessage;

auto server = std::make_shared<Test::WebSocketServer>(s_port);
server->SetMessageFactory([](string &&message) { return message + "_response"; });
// TODO: #4493 - Allow TestWebSocketServer to handle multiple incoming messages.
// server->Start();

// WebSocket resources scope.
{
vector<shared_ptr<IWebSocketResource>> resources;
Expand Down Expand Up @@ -112,7 +110,7 @@ TEST_CLASS (WebSocketResourcePerformanceTest) {
errorFound = true;
errorMessage = error.Message;
});
ws->Connect("ws://localhost:" + std::to_string(s_port));
ws->Connect("ws://localhost:5555");

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

int32_t finalThreadCount = threadCount.load();
int64_t threadsPerResource = (finalThreadCount - startThreadCount) / resourceTotal;
Expand Down
4 changes: 2 additions & 2 deletions vnext/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
[MSBuild]::NormalizeDirectory on relative paths since cwd is not always correct. This logic should prefer to operate
on full paths and avoid extra normalization.
-->
<PropertyGroup>
<PropertyGroup Label="NodeNativeDeps" Condition="'$(IgnoreNodeNativeDeps)' != 'true'">
<ReactNativeWindowsDir Condition="'$(ReactNativeWindowsDir)' == ''">$(MSBuildThisFileDirectory)</ReactNativeWindowsDir>

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

</Project>
</Project>
14 changes: 14 additions & 0 deletions vnext/ReactWindows-Desktop.sln
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx",
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactCommon.UnitTests", "ReactCommon.UnitTests\ReactCommon.UnitTests.vcxproj", "{B0941079-7441-4A69-868C-FE5EC62C2E9E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ReactNative.Test.Website", "TestWebsite\Microsoft.ReactNative.Test.Website.csproj", "{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Expand Down Expand Up @@ -285,6 +287,18 @@ Global
{B0941079-7441-4A69-868C-FE5EC62C2E9E}.Release|x64.ActiveCfg = Release|x64
{B0941079-7441-4A69-868C-FE5EC62C2E9E}.Release|x64.Build.0 = Release|x64
{B0941079-7441-4A69-868C-FE5EC62C2E9E}.Release|x86.ActiveCfg = Release|x64
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Debug|ARM64.Build.0 = Debug|Any CPU
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Debug|x64.ActiveCfg = Debug|Any CPU
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Debug|x64.Build.0 = Debug|Any CPU
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Debug|x86.ActiveCfg = Debug|Any CPU
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Debug|x86.Build.0 = Debug|Any CPU
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Release|ARM64.ActiveCfg = Release|Any CPU
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Release|ARM64.Build.0 = Release|Any CPU
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Release|x64.ActiveCfg = Release|Any CPU
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Release|x64.Build.0 = Release|Any CPU
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Release|x86.ActiveCfg = Release|Any CPU
{C5B2EB41-849E-41F7-BC70-DEFF26F09E5E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Loading

0 comments on commit a09af28

Please sign in to comment.