diff --git a/LibraryManager.sln b/LibraryManager.sln
index b453e8c1..2eaccbc7 100644
--- a/LibraryManager.sln
+++ b/LibraryManager.sln
@@ -62,6 +62,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Web.LibraryManage
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Web.LibraryManager.Build.Test", "test\LibraryManager.Build.Test\Microsoft.Web.LibraryManager.Build.Test.csproj", "{C8979E80-F4D3-3F21-D966-1B317CA64C23}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "libman.IntegrationTest", "test\libman.IntegrationTest\libman.IntegrationTest.csproj", "{BE84C694-91F1-C170-7366-7B86EBC9B033}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Web.LibraryManager.Build.IntegrationTest", "test\LibraryManager.Build.IntegrationTest\Microsoft.Web.LibraryManager.Build.IntegrationTest.csproj", "{EC152F12-CB03-4405-D8AE-EE7FC6E32666}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -216,6 +220,30 @@ Global
{C8979E80-F4D3-3F21-D966-1B317CA64C23}.Release|x64.Build.0 = Release|Any CPU
{C8979E80-F4D3-3F21-D966-1B317CA64C23}.Release|x86.ActiveCfg = Release|Any CPU
{C8979E80-F4D3-3F21-D966-1B317CA64C23}.Release|x86.Build.0 = Release|Any CPU
+ {BE84C694-91F1-C170-7366-7B86EBC9B033}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BE84C694-91F1-C170-7366-7B86EBC9B033}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BE84C694-91F1-C170-7366-7B86EBC9B033}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {BE84C694-91F1-C170-7366-7B86EBC9B033}.Debug|x64.Build.0 = Debug|Any CPU
+ {BE84C694-91F1-C170-7366-7B86EBC9B033}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BE84C694-91F1-C170-7366-7B86EBC9B033}.Debug|x86.Build.0 = Debug|Any CPU
+ {BE84C694-91F1-C170-7366-7B86EBC9B033}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BE84C694-91F1-C170-7366-7B86EBC9B033}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BE84C694-91F1-C170-7366-7B86EBC9B033}.Release|x64.ActiveCfg = Release|Any CPU
+ {BE84C694-91F1-C170-7366-7B86EBC9B033}.Release|x64.Build.0 = Release|Any CPU
+ {BE84C694-91F1-C170-7366-7B86EBC9B033}.Release|x86.ActiveCfg = Release|Any CPU
+ {BE84C694-91F1-C170-7366-7B86EBC9B033}.Release|x86.Build.0 = Release|Any CPU
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666}.Debug|x64.Build.0 = Debug|Any CPU
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666}.Debug|x86.Build.0 = Debug|Any CPU
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666}.Release|x64.ActiveCfg = Release|Any CPU
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666}.Release|x64.Build.0 = Release|Any CPU
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666}.Release|x86.ActiveCfg = Release|Any CPU
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -234,6 +262,8 @@ Global
{F01D971D-AA93-60A2-2D84-05AF9AB1566F} = {FFCD12F4-5CE2-4CC2-A2C4-EACC8F387D7A}
{E199195A-7EE8-9273-37B1-3726BCEAB87A} = {FFCD12F4-5CE2-4CC2-A2C4-EACC8F387D7A}
{C8979E80-F4D3-3F21-D966-1B317CA64C23} = {FFCD12F4-5CE2-4CC2-A2C4-EACC8F387D7A}
+ {BE84C694-91F1-C170-7366-7B86EBC9B033} = {FFCD12F4-5CE2-4CC2-A2C4-EACC8F387D7A}
+ {EC152F12-CB03-4405-D8AE-EE7FC6E32666} = {FFCD12F4-5CE2-4CC2-A2C4-EACC8F387D7A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {720C6A7F-67F7-4D00-881F-D3CDEA7ABE69}
diff --git a/test/LibraryManager.Build.IntegrationTest/BuildTestBase.cs b/test/LibraryManager.Build.IntegrationTest/BuildTestBase.cs
new file mode 100644
index 00000000..0bd808f5
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/BuildTestBase.cs
@@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Web.LibraryManager.Build.IntegrationTest;
+
+[TestClass]
+[DeploymentItem(@"TestPackages", "TestPackages")]
+[DeploymentItem(@"TestSolution", "TestSolution")]
+public class BuildTestBase
+{
+ private const string BuildPackageName = "Microsoft.Web.LibraryManager.Build";
+ private const string ManifestFileName = "libman.json";
+ private const string TestProjectFolderName = "Libman.Build.TestApp";
+ private readonly string PackagesFolderPath = Path.Combine(Environment.CurrentDirectory, "TestPackages");
+ protected string TestProjectDirectory { get; private set; } = "";
+
+ [TestInitialize]
+ public async Task TestInitialize()
+ {
+ // test solution should be deployed with every test?
+ TestProjectDirectory = Path.Combine(Path.GetFullPath("TestSolution"), TestProjectFolderName);
+
+ // create an empty nuget.config with only our package source
+ await AddPackageReferenceAsync();
+ }
+
+ [TestCleanup]
+ public void TestCleanup()
+ {
+ if (Directory.Exists(TestProjectDirectory))
+ {
+ Directory.Delete(TestProjectDirectory, true);
+ }
+ }
+
+ protected async Task RunDotnetCommandLineAsync(string arguments, string? workingDirectory)
+ {
+ var processStartInfo = new ProcessStartInfo("dotnet", arguments)
+ {
+ RedirectStandardInput = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ WorkingDirectory = workingDirectory,
+ };
+
+ using (var process = Process.Start(processStartInfo))
+ {
+ await process.WaitForExitAsync();
+ if (process.ExitCode != 0)
+ {
+ string output = await process.StandardError.ReadToEndAsync() + await process.StandardOutput.ReadToEndAsync();
+ throw new InvalidOperationException($"CLI tool execution failed with arguments: {arguments}.\r\nOutput: {output}");
+ }
+ }
+ }
+
+ private async Task AddPackageReferenceAsync()
+ {
+ await RunDotnetCommandLineAsync($"nuget add source \"{PackagesFolderPath}\"", TestProjectDirectory);
+
+ await RunDotnetCommandLineAsync($"add package {BuildPackageName} --version {GetBuildPackageVersion()}", TestProjectDirectory);
+ }
+
+ private static string GetBuildPackageVersion()
+ {
+ return Directory.GetFiles("TestPackages", $"{BuildPackageName}.*.nupkg")
+ .Select(Path.GetFileNameWithoutExtension)
+ .Select(name => name.Substring(BuildPackageName.Length + 1))
+ .OrderByDescending(version => version)
+ .First();
+ }
+
+ protected async Task CreateManifestFileAsync(string content)
+ {
+ string manifestFilePath = Path.Combine(TestProjectDirectory, ManifestFileName);
+ await Task.Run(() => File.WriteAllText(manifestFilePath, content));
+ }
+
+ protected void AssertFileExists(string relativeFilePath)
+ {
+ string filePath = Path.Combine(TestProjectDirectory, relativeFilePath);
+ Assert.IsTrue(File.Exists(filePath), $"Expected file '{filePath}' does not exist.");
+ }
+
+}
diff --git a/test/LibraryManager.Build.IntegrationTest/Microsoft.Web.LibraryManager.Build.IntegrationTest.csproj b/test/LibraryManager.Build.IntegrationTest/Microsoft.Web.LibraryManager.Build.IntegrationTest.csproj
new file mode 100644
index 00000000..e7834b84
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/Microsoft.Web.LibraryManager.Build.IntegrationTest.csproj
@@ -0,0 +1,44 @@
+
+
+ $(NetCoreTFM)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/LibraryManager.Build.IntegrationTest/RestoreTests.cs b/test/LibraryManager.Build.IntegrationTest/RestoreTests.cs
new file mode 100644
index 00000000..53a65ef0
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/RestoreTests.cs
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.Web.LibraryManager.Build.IntegrationTest;
+
+[TestClass]
+public class RestoreTests : BuildTestBase
+{
+ [TestMethod]
+ public async Task Restore_LibraryWithFileMapping_NamedFiles()
+ {
+ string manifest = """
+ {
+ "version": "3.0",
+ "defaultProvider": "jsdelivr",
+ "libraries": [
+ {
+ "library": "jquery@3.6.0",
+ "destination": "wwwroot/lib/jquery",
+ "fileMappings": [
+ {
+ "files": [
+ "dist/jquery.min.js",
+ "dist/jquery.min.map"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ """;
+ await CreateManifestFileAsync(manifest);
+
+ await RunDotnetCommandLineAsync("build", TestProjectDirectory);
+
+ AssertFileExists("wwwroot/lib/jquery/dist/jquery.min.js");
+ AssertFileExists("wwwroot/lib/jquery/dist/jquery.min.map");
+ }
+}
diff --git a/test/LibraryManager.Build.IntegrationTest/TestSolution/Directory.Build.props b/test/LibraryManager.Build.IntegrationTest/TestSolution/Directory.Build.props
new file mode 100644
index 00000000..fca27833
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/TestSolution/Directory.Build.props
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/test/LibraryManager.Build.IntegrationTest/TestSolution/Directory.Build.targets b/test/LibraryManager.Build.IntegrationTest/TestSolution/Directory.Build.targets
new file mode 100644
index 00000000..cdf451da
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/TestSolution/Directory.Build.targets
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/test/LibraryManager.Build.IntegrationTest/TestSolution/Directory.Packages.props b/test/LibraryManager.Build.IntegrationTest/TestSolution/Directory.Packages.props
new file mode 100644
index 00000000..fca27833
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/TestSolution/Directory.Packages.props
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp.sln b/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp.sln
new file mode 100644
index 00000000..955000f6
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libman.Build.TestApp", "Libman.Build.TestApp\Libman.Build.TestApp.csproj", "{DB512B50-3DA7-4082-99E4-99C0ED151545}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DB512B50-3DA7-4082-99E4-99C0ED151545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DB512B50-3DA7-4082-99E4-99C0ED151545}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DB512B50-3DA7-4082-99E4-99C0ED151545}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DB512B50-3DA7-4082-99E4-99C0ED151545}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/Libman.Build.TestApp.csproj b/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/Libman.Build.TestApp.csproj
new file mode 100644
index 00000000..1b28a01c
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/Libman.Build.TestApp.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/Program.cs b/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/Program.cs
new file mode 100644
index 00000000..1760df1d
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/Program.cs
@@ -0,0 +1,6 @@
+var builder = WebApplication.CreateBuilder(args);
+var app = builder.Build();
+
+app.MapGet("/", () => "Hello World!");
+
+app.Run();
diff --git a/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/Properties/launchSettings.json b/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/Properties/launchSettings.json
new file mode 100644
index 00000000..3d5c30f9
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/Properties/launchSettings.json
@@ -0,0 +1,38 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:13287",
+ "sslPort": 44380
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:5264",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:7009;http://localhost:5264",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/appsettings.Development.json b/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/appsettings.Development.json
new file mode 100644
index 00000000..0c208ae9
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/appsettings.json b/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/appsettings.json
new file mode 100644
index 00000000..10f68b8c
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/TestSolution/Libman.Build.TestApp/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/test/LibraryManager.Build.IntegrationTest/TestSolution/nuget.config b/test/LibraryManager.Build.IntegrationTest/TestSolution/nuget.config
new file mode 100644
index 00000000..6ce97590
--- /dev/null
+++ b/test/LibraryManager.Build.IntegrationTest/TestSolution/nuget.config
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/test/libman.IntegrationTest/CliBaseTest.cs b/test/libman.IntegrationTest/CliBaseTest.cs
new file mode 100644
index 00000000..94c9736a
--- /dev/null
+++ b/test/libman.IntegrationTest/CliBaseTest.cs
@@ -0,0 +1,120 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace Microsoft.Web.LibraryManager.Cli.IntegrationTest;
+
+[TestClass]
+[DeploymentItem(@"TestPackages", "TestPackages")]
+public class CliTestBase
+{
+ private const string CliPackageName = "Microsoft.Web.LibraryManager.Cli";
+ private const string ToolInstallPath = "./TestInstallPath";
+ private const string ManifestFileName = "libman.json";
+ private string _testDirectory;
+
+ [TestInitialize]
+ public async Task TestInitialize()
+ {
+ _testDirectory = Path.Combine(Path.GetTempPath(), "LibmanTest" + Guid.NewGuid().ToString());
+ Directory.CreateDirectory(_testDirectory);
+
+ // create an empty nuget.config with only our package source
+ await RunDotnetCommandLineAsync("new nugetconfig");
+ await RunDotnetCommandLineAsync("nuget remove source nuget");
+ await RunDotnetCommandLineAsync("nuget add source ./TestPackages");
+
+ await InstallCliToolAsync();
+ }
+
+ [TestCleanup]
+ public void TestCleanup()
+ {
+ if (Directory.Exists(_testDirectory))
+ {
+ Directory.Delete(_testDirectory, true);
+ }
+
+ if (File.Exists("nuget.config"))
+ {
+ File.Delete("nuget.config");
+ }
+ }
+
+ private async Task RunDotnetCommandLineAsync(string arguments)
+ {
+ var processStartInfo = new ProcessStartInfo("dotnet", arguments)
+ {
+ RedirectStandardInput = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ };
+
+ using (var process = Process.Start(processStartInfo))
+ {
+ await WaitForExitAsync(process);
+ if (process.ExitCode != 0)
+ {
+ string output = await process.StandardError.ReadToEndAsync() + await process.StandardOutput.ReadToEndAsync();
+ throw new InvalidOperationException($"Failed to run command line `dotnet {arguments}`.\r\nOutput: {output}");
+ }
+ }
+ }
+
+ private async Task InstallCliToolAsync()
+ {
+ await RunDotnetCommandLineAsync($"tool install {CliPackageName} --no-cache --prerelease --tool-path {ToolInstallPath}");
+ }
+
+ protected async Task CreateManifestFileAsync(string content)
+ {
+ string manifestFilePath = Path.Combine(_testDirectory, ManifestFileName);
+ await Task.Run(() => File.WriteAllText(manifestFilePath, content));
+ }
+
+ protected async Task ExecuteCliToolAsync(string arguments)
+ {
+ var processStartInfo = new ProcessStartInfo($"{ToolInstallPath}\\libman.exe", arguments)
+ {
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ WorkingDirectory = _testDirectory,
+ };
+
+ using (var process = Process.Start(processStartInfo))
+ {
+ await WaitForExitAsync(process);
+ if (process.ExitCode != 0)
+ {
+ string output = await process.StandardError.ReadToEndAsync() + await process.StandardOutput.ReadToEndAsync();
+ throw new InvalidOperationException($"CLI tool execution failed with arguments: {arguments}.\r\nOutput: {output}");
+ }
+ }
+ }
+
+ private Task WaitForExitAsync(Process process)
+ {
+ var tcs = new TaskCompletionSource();
+ process.Exited += (sender, args) => tcs.SetResult(true);
+ process.EnableRaisingEvents = true;
+ if (process.HasExited && !tcs.Task.IsCompleted)
+ {
+ tcs.SetResult(true);
+ }
+ return tcs.Task;
+ }
+
+ protected void AssertFileExists(string relativeFilePath)
+ {
+ string filePath = Path.Combine(_testDirectory, relativeFilePath);
+ Assert.IsTrue(File.Exists(filePath), $"Expected file '{relativeFilePath}' does not exist.");
+ }
+}
diff --git a/test/libman.IntegrationTest/InstallTests.cs b/test/libman.IntegrationTest/InstallTests.cs
new file mode 100644
index 00000000..3cb8d852
--- /dev/null
+++ b/test/libman.IntegrationTest/InstallTests.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Threading.Tasks;
+
+namespace Microsoft.Web.LibraryManager.Cli.IntegrationTest;
+
+[TestClass]
+public class InstallTests : CliTestBase
+{
+ [TestMethod]
+ public async Task Install_FileSpecified()
+ {
+ await ExecuteCliToolAsync("install jquery@3.6.0 --provider cdnjs --destination test/jquery --files jquery.min.js");
+
+ AssertFileExists("test/jquery/jquery.min.js");
+ }
+}
diff --git a/test/libman.IntegrationTest/RestoreTests.cs b/test/libman.IntegrationTest/RestoreTests.cs
new file mode 100644
index 00000000..a2274dcc
--- /dev/null
+++ b/test/libman.IntegrationTest/RestoreTests.cs
@@ -0,0 +1,102 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Threading.Tasks;
+
+namespace Microsoft.Web.LibraryManager.Cli.IntegrationTest;
+
+[TestClass]
+public class RestoreTests : CliTestBase
+{
+ [TestMethod]
+ public async Task Restore_WithFileMapping_ConcreteFileNames()
+ {
+ string manifest = """
+ {
+ "version": "3.0",
+ "defaultProvider": "jsdelivr",
+ "libraries": [
+ {
+ "library": "jquery@3.6.0",
+ "destination": "wwwroot/lib/jquery",
+ "fileMappings": [
+ {
+ "files": [
+ "dist/jquery.min.js",
+ "dist/jquery.min.map"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ """;
+ await CreateManifestFileAsync(manifest);
+
+ await ExecuteCliToolAsync("restore");
+
+ AssertFileExists("wwwroot/lib/jquery/dist/jquery.min.js");
+ AssertFileExists("wwwroot/lib/jquery/dist/jquery.min.map");
+ }
+
+ [TestMethod]
+ public async Task Restore_WithFileMapping_FileGlobs()
+ {
+ string manifest = """
+ {
+ "version": "3.0",
+ "defaultProvider": "jsdelivr",
+ "libraries": [
+ {
+ "library": "jquery@3.6.0",
+ "destination": "wwwroot/lib/jquery",
+ "fileMappings": [
+ {
+ "files": [
+ "dist/jquery.min.*",
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ """;
+ await CreateManifestFileAsync(manifest);
+
+ await ExecuteCliToolAsync("restore");
+
+ AssertFileExists("wwwroot/lib/jquery/dist/jquery.min.js");
+ AssertFileExists("wwwroot/lib/jquery/dist/jquery.min.map");
+ }
+
+ [TestMethod]
+ public async Task Restore_WithFileMapping_FileGlobs_SetRootPath()
+ {
+ string manifest = """
+ {
+ "version": "3.0",
+ "defaultProvider": "jsdelivr",
+ "libraries": [
+ {
+ "library": "jquery@3.6.0",
+ "destination": "wwwroot/lib/jquery",
+ "fileMappings": [
+ {
+ "root": "dist",
+ "files": [
+ "jquery.min.*",
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ """;
+ await CreateManifestFileAsync(manifest);
+
+ await ExecuteCliToolAsync("restore");
+
+ AssertFileExists("wwwroot/lib/jquery/jquery.min.js");
+ AssertFileExists("wwwroot/lib/jquery/jquery.min.map");
+ }
+}
diff --git a/test/libman.IntegrationTest/libman.IntegrationTest.csproj b/test/libman.IntegrationTest/libman.IntegrationTest.csproj
new file mode 100644
index 00000000..09d98f59
--- /dev/null
+++ b/test/libman.IntegrationTest/libman.IntegrationTest.csproj
@@ -0,0 +1,34 @@
+
+
+ $(NetCoreTFM)
+ Microsoft.Web.LibraryManager.Cli.IntegrationTest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+