Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,7 @@ jobs:
run: dotnet restore
- name: Build
run: dotnet build --no-restore -c Release
- name: Ensure browsers are installed
run: npx playwright install chromium --with-deps
- name: Test
run: dotnet test -c Release --no-build -p:VSTestUseMSBuildOutput=false
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
<PackageReference Include="Microsoft.Playwright" />
<PackageReference Include="RavenDB.TestDriver" />
</ItemGroup>

Expand Down
119 changes: 119 additions & 0 deletions tests/LinkDotNet.Blog.IntegrationTests/PlaywrightSmokeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System;
using System.Threading.Tasks;
using LinkDotNet.Blog.TestUtilities;
using Microsoft.Playwright;

namespace LinkDotNet.Blog.IntegrationTests;

[Collection("Sequential")]
public sealed class PlaywrightSmokeTests : IClassFixture<PlaywrightWebApplicationFactory>, IAsyncDisposable
{
private readonly PlaywrightWebApplicationFactory factory;
private readonly Lazy<Task<IPlaywright>> playwrightTask;
private readonly Lazy<Task<IBrowser>> browserTask;

public PlaywrightSmokeTests(PlaywrightWebApplicationFactory factory)
{
this.factory = factory;
_ = factory.CreateClient();
playwrightTask = new Lazy<Task<IPlaywright>>(Playwright.CreateAsync);
browserTask = new Lazy<Task<IBrowser>>(async () =>
{
var playwright = await playwrightTask.Value;
return await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Headless = true });
});
}

[Fact]
public async Task ShouldNavigateToHomePageAndShowBlogPosts()
{
var browser = await browserTask.Value;
var page = await browser.NewPageAsync();

await page.GotoAsync(factory.ServerAddress, new PageGotoOptions { WaitUntil = WaitUntilState.NetworkIdle });

var blogPostElements = await page.QuerySelectorAllAsync("article");
blogPostElements.Count.ShouldBeGreaterThan(0);

var title = await page.TitleAsync();
title.ShouldBe("Integration Tests Blog");
}

[Fact]
public async Task ShouldNavigateToBlogPostAndShowContent()
{
var browser = await browserTask.Value;
var page = await browser.NewPageAsync();

await page.GotoAsync(factory.ServerAddress, new PageGotoOptions { WaitUntil = WaitUntilState.NetworkIdle });

var blogPostLink = await page.QuerySelectorAsync("article a[href*='/blogPost/']");
blogPostLink.ShouldNotBeNull();

var href = await blogPostLink.GetAttributeAsync("href");
href.ShouldNotBeNull();

await page.GotoAsync($"{factory.ServerAddress.TrimEnd('/')}{href}", new PageGotoOptions { WaitUntil = WaitUntilState.NetworkIdle });

var heading = await page.QuerySelectorAsync("h1");
heading.ShouldNotBeNull();
var headingText = await heading.TextContentAsync();
headingText.ShouldNotBeNullOrWhiteSpace();
}

[Fact]
public async Task ShouldNavigateToSecondPageAndShowBlogPosts()
{
var browser = await browserTask.Value;
var page = await browser.NewPageAsync();

await page.GotoAsync(factory.ServerAddress, new PageGotoOptions { WaitUntil = WaitUntilState.NetworkIdle });

var page2Link = await page.QuerySelectorAsync("a[href='/2']");
page2Link.ShouldNotBeNull();
await page2Link.ClickAsync();
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);

var currentUrl = page.Url;
currentUrl.ShouldContain("/2");

var blogPostElements = await page.QuerySelectorAllAsync("article");
blogPostElements.Count.ShouldBeGreaterThan(0);
}

[Fact]
public async Task ShouldClickOnBlogPostLinkAndNavigate()
{
var browser = await browserTask.Value;
var page = await browser.NewPageAsync();

await page.GotoAsync(factory.ServerAddress, new PageGotoOptions { WaitUntil = WaitUntilState.NetworkIdle });

var blogPostLink = await page.QuerySelectorAsync("article a[href*='/blogPost/']");
blogPostLink.ShouldNotBeNull();

var expectedHref = await blogPostLink.GetAttributeAsync("href");
expectedHref.ShouldNotBeNull();

await blogPostLink.ClickAsync();
await page.WaitForLoadStateAsync(LoadState.NetworkIdle);

var currentUrl = page.Url;
currentUrl.ShouldContain("/blogPost/");
}

public async ValueTask DisposeAsync()
{
if (browserTask.IsValueCreated)
{
var browser = await browserTask.Value;
await browser.DisposeAsync();
}

if (playwrightTask.IsValueCreated)
{
var playwright = await playwrightTask.Value;
playwright.Dispose();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Linq;
using LinkDotNet.Blog.Infrastructure.Persistence;
using LinkDotNet.Blog.Web;
using LinkDotNet.Blog.Web.Features.DummyData;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace LinkDotNet.Blog.IntegrationTests;

public sealed class PlaywrightWebApplicationFactory : WebApplicationFactory<Program>
{
private IHost? host;

public string ServerAddress => ClientOptions.BaseAddress.ToString();

public override IServiceProvider Services => host?.Services
?? throw new InvalidOperationException("Create the Client first before retrieving instances from the container");

protected override IHost CreateHost(IHostBuilder builder)
{
var testHost = builder.Build();

builder = builder.ConfigureWebHost(b =>
{
b.UseSetting("Logging:LogLevel:Default", "Error");
b.UseSetting("BlogName", "Integration Tests Blog");
b.ConfigureServices(services =>
{
services.UseDummyData(new DummyDataOptions { NumberOfBlogPosts = 25 });
});
b.UseKestrel();
});

host?.Dispose();
host = builder.Build();
host.Start();

var server = host!.Services.GetRequiredService<IServer>();
var addresses = server.Features.Get<IServerAddressesFeature>();

ClientOptions.BaseAddress = addresses!.Addresses
.Select(x => new Uri(x))
.Last();

testHost.Start();
return testHost;
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
host?.Dispose();
}

base.Dispose(disposing);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace LinkDotNet.Blog.IntegrationTests;

[CollectionDefinition("Sequential", DisableParallelization = true)]
public class SequentialCollection
{
}
Loading