// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Clockwise;
using FluentAssertions;
using FluentAssertions.Extensions;
using HtmlAgilityPack;
using Microsoft.DotNet.Interactive.Utility;
using Microsoft.DotNet.Try.Markdown;
using MLS.Agent.CommandLine;
using MLS.Agent.Tools;
using MLS.Agent.Tools.Tests;
using Recipes;
using WorkspaceServer;
using WorkspaceServer.Packaging;
using WorkspaceServer.Tests;
using Xunit;
namespace MLS.Agent.Tests
{
public class DocumentationAPITests
{
[Fact]
public async Task Request_for_non_existent_markdown_file_returns_404()
{
using (var agent = new AgentService(new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole))))
{
var response = await agent.GetAsync(@"/DOESNOTEXIST");
response.Should().BeNotFound();
}
}
[Fact]
public async Task Return_html_for_an_existing_markdown_file()
{
using (var agent = new AgentService(new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole))))
{
var response = await agent.GetAsync(@"Readme.md");
response.Should().BeSuccessful();
var result = await response.Content.ReadAsStringAsync();
response.Content.Headers.ContentType.MediaType.Should().Be("text/html");
result.Should().Contain("markdown file");
}
}
[Fact]
public async Task Return_html_for_existing_markdown_files_in_a_subdirectory()
{
using (var agent = new AgentService(new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole))))
{
var response = await agent.GetAsync(@"Subdirectory/Tutorial.md");
response.Should().BeSuccessful();
var result = await response.Content.ReadAsStringAsync();
result.Should().Contain("tutorial file");
}
}
[Fact]
public async Task Lists_markdown_files_when_a_folder_is_requested()
{
using (var agent = new AgentService(new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole))))
{
var response = await agent.GetAsync(@"/");
response.Should().BeSuccessful();
var html = await response.Content.ReadAsStringAsync();
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(html);
var links = htmlDoc.DocumentNode
.SelectNodes("//a")
.Select(a => a.Attributes["href"].Value)
.ToArray();
links.Should().Contain("./Readme.md");
links.Should().Contain("./Subdirectory/Tutorial.md");
}
}
[Fact]
public async Task Scaffolding_HTML_includes_trydotnet_js_script_link()
{
using (var agent = new AgentService(new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole))))
{
var response = await agent.GetAsync(@"Subdirectory/Tutorial.md");
response.Should().BeSuccessful();
var html = await response.Content.ReadAsStringAsync();
var document = new HtmlDocument();
document.LoadHtml(html);
var script = document.DocumentNode
.Descendants("head")
.Single()
.Descendants("script")
.FirstOrDefault();
script.Attributes["src"].Value.Should().StartWith("/api/trydotnet.min.js?v=");
}
}
[Fact]
public async Task Scaffolding_HTML_includes_trydotnet_js_autoEnable_invocation_with_useBlazor_defaulting_to_false()
{
using (var agent = new AgentService(new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole))))
{
var response = await agent.GetAsync(@"Subdirectory/Tutorial.md");
response.Should().BeSuccessful();
var html = await response.Content.ReadAsStringAsync();
var document = new HtmlDocument();
document.LoadHtml(html);
var scripts = document.DocumentNode
.Descendants("body")
.Single()
.Descendants("script")
.Select(s => s.InnerHtml);
scripts.Should()
.Contain(s => s.Contains(@"trydotnet.autoEnable({ apiBaseAddress: new URL(""http://localhost""), useWasmRunner: false });"));
}
}
[Fact]
public async Task Scaffolding_HTML_trydotnet_js_autoEnable_useBlazor_is_true_when_package_is_specified_and_supports_wasmrunner()
{
var (name, addSource) = await Create.NupkgWithBlazorEnabled("packageName");
var startupOptions = new StartupOptions(
rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole),
addPackageSource: new PackageSource(addSource.FullName),
package: name);
using (var agent = new AgentService(startupOptions))
{
var response = await agent.GetAsync(@"Subdirectory/Tutorial.md");
response.Should().BeSuccessful();
var html = await response.Content.ReadAsStringAsync();
var document = new HtmlDocument();
document.LoadHtml(html);
var scripts = document.DocumentNode
.Descendants("body")
.Single()
.Descendants("script")
.Select(s => s.InnerHtml);
scripts.Should()
.Contain(s => s.Contains(@"trydotnet.autoEnable({ apiBaseAddress: new URL(""http://localhost""), useWasmRunner: true });"));
}
}
[Fact]
public async Task Scaffolding_HTML_trydotnet_js_autoEnable_useBlazor_is_true_when_package_is_not_specified_and_supports_wasmrunner()
{
var (name, addSource) = await Create.NupkgWithBlazorEnabled("packageName");
using (var dir = DisposableDirectory.Create())
{
var text = $@"
```cs --package {name}
```";
var path = Path.Combine(dir.Directory.FullName, "BlazorTutorial.md");
File.WriteAllText(path, text);
var startupOptions = new StartupOptions(
rootDirectory: new FileSystemDirectoryAccessor(dir.Directory),
addPackageSource: new PackageSource(addSource.FullName));
using (var agent = new AgentService(startupOptions))
{
var response = await agent.GetAsync(@"/BlazorTutorial.md");
response.Should().BeSuccessful();
var html = await response.Content.ReadAsStringAsync();
var document = new HtmlDocument();
document.LoadHtml(html);
var scripts = document.DocumentNode
.Descendants("body")
.Single()
.Descendants("script")
.Select(s => s.InnerHtml);
scripts.Should()
.Contain(s => s.Contains(@"trydotnet.autoEnable({ apiBaseAddress: new URL(""http://localhost""), useWasmRunner: true });"));
}
}
}
[Fact]
public async Task When_relative_uri_is_specified_then_it_opens_to_that_page()
{
var launchUri = new Uri("something.md", UriKind.Relative);
using (var clock = VirtualClock.Start())
using (var agent = new AgentService(new StartupOptions(
rootDirectory: new FileSystemDirectoryAccessor(TestAssets.SampleConsole),
uri: launchUri)))
{
await clock.Wait(5.Seconds());
agent.BrowserLauncher
.LaunchedUri
.ToString()
.Should()
.Match("https://localhost:*/something.md");
}
}
[Fact]
public async Task When_readme_file_is_on_root_browser_opens_there()
{
var directoryAccessor = new InMemoryDirectoryAccessor
{
("./readme.md", ""),
("./subfolder/part1.md", ""),
("./subfolder/part2.md", "")
};
var root = directoryAccessor.GetFullyQualifiedPath(new RelativeDirectoryPath(".")) as DirectoryInfo;
var options = new StartupOptions(rootDirectory: new FileSystemDirectoryAccessor(root));
using (var clock = VirtualClock.Start())
using (var agent = new AgentService(options: options, directoryAccessor: directoryAccessor))
{
await clock.Wait(5.Seconds());
agent.BrowserLauncher
.LaunchedUri
.ToString()
.Should()
.Match("https://localhost:*/readme.md");
}
}
}
}