Skip to content

Commit dc1df46

Browse files
authored
Merge pull request #21 from linkdotnet/feature/about-me-page
Feature/about me page - Part 1
2 parents 55178ab + cada15d commit dc1df46

File tree

53 files changed

+846
-88
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+846
-88
lines changed

LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/Sql/SqlRepositoryTests.cs renamed to LinkDotNet.Blog.IntegrationTests/Infrastructure/Persistence/Sql/BlogPostRepositoryTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace LinkDotNet.Blog.IntegrationTests.Infrastructure.Persistence.Sql
1010
{
11-
public sealed class SqlRepositoryTests : SqlDatabaseTestBase
11+
public sealed class BlogPostRepositoryTests : SqlDatabaseTestBase
1212
{
1313
[Fact]
1414
public async Task ShouldLoadBlogPost()
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System.Threading.Tasks;
2+
using FluentAssertions;
3+
using LinkDotNet.Blog.TestUtilities;
4+
using Xunit;
5+
6+
namespace LinkDotNet.Blog.IntegrationTests.Infrastructure.Persistence.Sql
7+
{
8+
public class ProfileRepositoryTests : SqlDatabaseTestBase
9+
{
10+
[Fact]
11+
public async Task ShouldSaveAndRetrieveAllEntries()
12+
{
13+
var item1 = new ProfileInformationEntryBuilder().WithContent("key1").Build();
14+
var item2 = new ProfileInformationEntryBuilder().WithContent("key2").Build();
15+
await ProfileRepository.AddAsync(item1);
16+
await ProfileRepository.AddAsync(item2);
17+
18+
var items = await ProfileRepository.GetAllAsync();
19+
20+
items[0].Content.Should().Be("key1");
21+
items[1].Content.Should().Be("key2");
22+
}
23+
24+
[Fact]
25+
public async Task ShouldDelete()
26+
{
27+
var item1 = new ProfileInformationEntryBuilder().WithContent("key1").Build();
28+
var item2 = new ProfileInformationEntryBuilder().WithContent("key2").Build();
29+
await ProfileRepository.AddAsync(item1);
30+
await ProfileRepository.AddAsync(item2);
31+
32+
await ProfileRepository.DeleteAsync(item1.Id);
33+
34+
var items = await ProfileRepository.GetAllAsync();
35+
items.Should().HaveCount(1);
36+
items[0].Id.Should().Be(item2.Id);
37+
}
38+
39+
[Fact]
40+
public async Task NoopOnDeleteWhenEntryNotFound()
41+
{
42+
var item = new ProfileInformationEntryBuilder().WithContent("key1").Build();
43+
await ProfileRepository.AddAsync(item);
44+
45+
await ProfileRepository.DeleteAsync("SomeIdWhichHopefullyDoesNotExist");
46+
47+
(await ProfileRepository.GetAllAsync()).Should().HaveCount(1);
48+
}
49+
}
50+
}

LinkDotNet.Blog.IntegrationTests/SqlDatabaseTestBase.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@ protected SqlDatabaseTestBase()
1515
var options = new DbContextOptionsBuilder()
1616
.UseSqlite(CreateInMemoryConnection())
1717
.Options;
18-
DbContext = new BlogPostContext(options);
19-
BlogPostRepository = new BlogPostRepository(new BlogPostContext(options));
18+
DbContext = new BlogDbContext(options);
19+
BlogPostRepository = new BlogPostRepository(new BlogDbContext(options));
20+
ProfileRepository = new ProfileRepository(new BlogDbContext(options));
2021
}
2122

2223
protected BlogPostRepository BlogPostRepository { get; }
2324

24-
protected BlogPostContext DbContext { get; }
25+
protected ProfileRepository ProfileRepository { get; }
26+
27+
protected BlogDbContext DbContext { get; }
2528

2629
public Task InitializeAsync()
2730
{

LinkDotNet.Blog.IntegrationTests/Web/Pages/Admin/DraftBlogPostPageTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public async Task ShouldOnlyShowPublishedPosts()
2222
await BlogPostRepository.StoreAsync(unpublishedPost);
2323
using var ctx = new TestContext();
2424
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
25-
ctx.Services.AddScoped<IRepository>(_ => BlogPostRepository);
25+
ctx.Services.AddScoped<IBlogPostRepository>(_ => BlogPostRepository);
2626
var cut = ctx.RenderComponent<DraftBlogPosts>();
2727
cut.WaitForState(() => cut.FindAll(".blog-card").Any());
2828

LinkDotNet.Blog.IntegrationTests/Web/Pages/BlogPostPageTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public async Task ShouldSetTagsWhenAvailable()
8080

8181
private void RegisterComponents(TestContextBase ctx, ILocalStorageService localStorageService = null)
8282
{
83-
ctx.Services.AddScoped<IRepository>(_ => BlogPostRepository);
83+
ctx.Services.AddScoped<IBlogPostRepository>(_ => BlogPostRepository);
8484
ctx.Services.AddScoped(_ => localStorageService ?? new Mock<ILocalStorageService>().Object);
8585
ctx.Services.AddScoped(_ => new Mock<IToastService>().Object);
8686
ctx.Services.AddScoped(_ => new Mock<IHeadElementHelper>().Object);

LinkDotNet.Blog.IntegrationTests/Web/Pages/IndexTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ private async Task CreatePublishedBlogPosts(int amount)
132132

133133
private void RegisterComponents(TestContextBase ctx)
134134
{
135-
ctx.Services.AddScoped<IRepository>(_ => BlogPostRepository);
135+
ctx.Services.AddScoped<IBlogPostRepository>(_ => BlogPostRepository);
136136
ctx.Services.AddScoped(_ => CreateSampleAppConfiguration());
137137
ctx.Services.AddScoped(_ => new Mock<IHeadElementHelper>().Object);
138138
}

LinkDotNet.Blog.IntegrationTests/Web/Pages/SearchByTagTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public async Task ShouldOnlyDisplayTagsGivenByParameter()
2222
await AddBlogPostWithTagAsync("Tag 1");
2323
await AddBlogPostWithTagAsync("Tag 1");
2424
await AddBlogPostWithTagAsync("Tag 2");
25-
ctx.Services.AddScoped<IRepository>(_ => BlogPostRepository);
25+
ctx.Services.AddScoped<IBlogPostRepository>(_ => BlogPostRepository);
2626
ctx.Services.AddScoped(_ => new Mock<IHeadElementHelper>().Object);
2727
var cut = ctx.RenderComponent<SearchByTag>(p => p.Add(s => s.Tag, "Tag 1"));
2828
cut.WaitForState(() => cut.FindAll(".blog-card").Any());
@@ -37,7 +37,7 @@ public async Task ShouldHandleSpecialCharacters()
3737
{
3838
using var ctx = new TestContext();
3939
await AddBlogPostWithTagAsync("C#");
40-
ctx.Services.AddScoped<IRepository>(_ => BlogPostRepository);
40+
ctx.Services.AddScoped<IBlogPostRepository>(_ => BlogPostRepository);
4141
ctx.Services.AddScoped(_ => new Mock<IHeadElementHelper>().Object);
4242
var cut = ctx.RenderComponent<SearchByTag>(p => p.Add(s => s.Tag, Uri.EscapeDataString("C#")));
4343
cut.WaitForState(() => cut.FindAll(".blog-card").Any());

LinkDotNet.Blog.IntegrationTests/Web/Pages/SearchTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public async Task ShouldFindBlogPostWhenTitleMatches()
2121
await BlogPostRepository.StoreAsync(blogPost1);
2222
await BlogPostRepository.StoreAsync(blogPost2);
2323
using var ctx = new TestContext();
24-
ctx.Services.AddScoped<IRepository>(_ => BlogPostRepository);
24+
ctx.Services.AddScoped<IBlogPostRepository>(_ => BlogPostRepository);
2525

2626
var cut = ctx.RenderComponent<Search>(p => p.Add(s => s.SearchTerm, "Title 1"));
2727

@@ -39,7 +39,7 @@ public async Task ShouldFindBlogPostWhenTagMatches()
3939
await BlogPostRepository.StoreAsync(blogPost1);
4040
await BlogPostRepository.StoreAsync(blogPost2);
4141
using var ctx = new TestContext();
42-
ctx.Services.AddScoped<IRepository>(_ => BlogPostRepository);
42+
ctx.Services.AddScoped<IBlogPostRepository>(_ => BlogPostRepository);
4343

4444
var cut = ctx.RenderComponent<Search>(p => p.Add(s => s.SearchTerm, "Cat"));
4545

@@ -55,7 +55,7 @@ public async Task ShouldUnescapeQuery()
5555
var blogPost1 = new BlogPostBuilder().WithTitle("Title 1").Build();
5656
await BlogPostRepository.StoreAsync(blogPost1);
5757
using var ctx = new TestContext();
58-
ctx.Services.AddScoped<IRepository>(_ => BlogPostRepository);
58+
ctx.Services.AddScoped<IBlogPostRepository>(_ => BlogPostRepository);
5959

6060
var cut = ctx.RenderComponent<Search>(p => p.Add(s => s.SearchTerm, "Title%201"));
6161

LinkDotNet.Blog.IntegrationTests/Web/Shared/Admin/BlogPostAdminActionsTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class BlogPostAdminActionsTests
1717
public void ShouldDeleteBlogPostWhenOkClicked()
1818
{
1919
const string blogPostId = "2";
20-
var repositoryMock = new Mock<IRepository>();
20+
var repositoryMock = new Mock<IBlogPostRepository>();
2121
using var ctx = new TestContext();
2222
ctx.AddTestAuthorization().SetAuthorized("s");
2323
ctx.Services.AddSingleton(repositoryMock.Object);
@@ -34,7 +34,7 @@ public void ShouldDeleteBlogPostWhenOkClicked()
3434
public void ShouldNotDeleteBlogPostWhenCancelClicked()
3535
{
3636
const string blogPostId = "2";
37-
var repositoryMock = new Mock<IRepository>();
37+
var repositoryMock = new Mock<IBlogPostRepository>();
3838
using var ctx = new TestContext();
3939
ctx.AddTestAuthorization().SetAuthorized("s");
4040
ctx.Services.AddSingleton(repositoryMock.Object);
@@ -51,7 +51,7 @@ public void ShouldNotDeleteBlogPostWhenCancelClicked()
5151
public void ShouldGoToEditPageOnEditClick()
5252
{
5353
const string blogPostId = "2";
54-
var repositoryMock = new Mock<IRepository>();
54+
var repositoryMock = new Mock<IBlogPostRepository>();
5555
using var ctx = new TestContext();
5656
ctx.AddTestAuthorization().SetAuthorized("s");
5757
ctx.Services.AddSingleton(repositoryMock.Object);
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Bunit;
4+
using FluentAssertions;
5+
using LinkDotNet.Blog.TestUtilities;
6+
using LinkDotNet.Blog.Web;
7+
using LinkDotNet.Blog.Web.Shared;
8+
using LinkDotNet.Domain;
9+
using LinkDotNet.Infrastructure.Persistence;
10+
using Microsoft.Extensions.DependencyInjection;
11+
using Moq;
12+
using Xunit;
13+
14+
namespace LinkDotNet.Blog.IntegrationTests.Web.Shared
15+
{
16+
public class ProfileTests : TestContext
17+
{
18+
[Fact]
19+
public void ShouldRenderAllItemsSortedByCreated()
20+
{
21+
var entry1 = new ProfileInformationEntryBuilder().WithContent("key 1").WithCreatedDate(new DateTime(1)).Build();
22+
var entry2 = new ProfileInformationEntryBuilder().WithContent("key 2").WithCreatedDate(new DateTime(2)).Build();
23+
var repoMock = RegisterServices();
24+
repoMock.Setup(r => r.GetAllAsync())
25+
.ReturnsAsync(new List<ProfileInformationEntry> { entry1, entry2 });
26+
var cut = RenderComponent<Profile>();
27+
28+
var items = cut.FindAll(".profile-keypoints li");
29+
30+
items.Should().HaveCount(2);
31+
items[0].TextContent.Should().Contain("key 1");
32+
items[1].TextContent.Should().Contain("key 2");
33+
}
34+
35+
[Fact]
36+
public void ShouldNotShowAdminActions()
37+
{
38+
RegisterServices();
39+
RenderComponent<Profile>().FindComponents<AddProfileShortItem>().Should().HaveCount(0);
40+
}
41+
42+
[Fact]
43+
public void ShouldShowAdminActionsWhenLoggedIn()
44+
{
45+
RegisterServices();
46+
RenderComponent<Profile>(p => p.Add(s => s.IsAuthenticated, true))
47+
.FindComponents<AddProfileShortItem>().Should().HaveCount(1);
48+
}
49+
50+
[Fact]
51+
public void ShouldAddEntry()
52+
{
53+
var repo = RegisterServices();
54+
ProfileInformationEntry entryToDb = null;
55+
repo.Setup(p => p.AddAsync(It.IsAny<ProfileInformationEntry>()))
56+
.Callback<ProfileInformationEntry>(p => entryToDb = p);
57+
var cut = RenderComponent<Profile>(p => p.Add(s => s.IsAuthenticated, true));
58+
var addShortItemComponent = cut.FindComponent<AddProfileShortItem>();
59+
addShortItemComponent.Find("input").Change("key");
60+
61+
addShortItemComponent.Find("button").Click();
62+
63+
entryToDb.Should().NotBeNull();
64+
entryToDb.Content.Should().Be("key");
65+
}
66+
67+
[Fact]
68+
public void ShouldDeleteEntryWhenConfirmed()
69+
{
70+
var entryToDelete = new ProfileInformationEntryBuilder().WithContent("key 2").WithCreatedDate(new DateTime(2)).Build();
71+
entryToDelete.Id = "SomeId";
72+
var repoMock = RegisterServices();
73+
repoMock.Setup(r => r.GetAllAsync()).ReturnsAsync(new[] { entryToDelete });
74+
var cut = RenderComponent<Profile>(p => p.Add(s => s.IsAuthenticated, true));
75+
cut.Find(".profile-keypoints li button").Click();
76+
77+
cut.FindComponent<ConfirmDialog>().Find("#ok").Click();
78+
79+
repoMock.Verify(r => r.DeleteAsync("SomeId"), Times.Once);
80+
}
81+
82+
[Fact]
83+
public void ShouldNotDeleteEntryWhenNotConfirmed()
84+
{
85+
var entryToDelete = new ProfileInformationEntryBuilder().WithContent("key 2").WithCreatedDate(new DateTime(2)).Build();
86+
entryToDelete.Id = "SomeId";
87+
var repoMock = RegisterServices();
88+
repoMock.Setup(r => r.GetAllAsync()).ReturnsAsync(new[] { entryToDelete });
89+
var cut = RenderComponent<Profile>(p => p.Add(s => s.IsAuthenticated, true));
90+
cut.Find(".profile-keypoints li button").Click();
91+
92+
cut.FindComponent<ConfirmDialog>().Find("#cancel").Click();
93+
94+
repoMock.Verify(r => r.DeleteAsync("SomeId"), Times.Never);
95+
}
96+
97+
private static AppConfiguration CreateEmptyConfiguration()
98+
{
99+
return new()
100+
{
101+
ProfileInformation = new ProfileInformation(),
102+
};
103+
}
104+
105+
private Mock<IProfileRepository> RegisterServices()
106+
{
107+
var repoMock = new Mock<IProfileRepository>();
108+
Services.AddScoped(_ => CreateEmptyConfiguration());
109+
Services.AddScoped(_ => repoMock.Object);
110+
repoMock.Setup(r => r.GetAllAsync()).ReturnsAsync(new List<ProfileInformationEntry>());
111+
return repoMock;
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)