-
-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from linkdotnet/feature/give-kudos
Feature/give kudos
- Loading branch information
Showing
9 changed files
with
256 additions
and
1 deletion.
There are no files selected for viewing
67 changes: 67 additions & 0 deletions
67
LinkDotNet.Blog.IntegrationTests/Web/Pages/BlogPostPageTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
using System.Threading.Tasks; | ||
using Blazored.LocalStorage; | ||
using Blazored.Toast.Services; | ||
using Bunit; | ||
using Bunit.TestDoubles; | ||
using FluentAssertions; | ||
using LinkDotNet.Blog.TestUtilities; | ||
using LinkDotNet.Blog.Web.Pages; | ||
using LinkDotNet.Blog.Web.Shared; | ||
using LinkDotNet.Infrastructure.Persistence; | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Moq; | ||
using Xunit; | ||
|
||
namespace LinkDotNet.Blog.IntegrationTests.Web.Pages | ||
{ | ||
public class BlogPostPageTests : SqlDatabaseTestBase | ||
{ | ||
[Fact] | ||
public async Task ShouldAddLikeOnEvent() | ||
{ | ||
var publishedPost = new BlogPostBuilder().WithLikes(2).IsPublished().Build(); | ||
await BlogPostRepository.StoreAsync(publishedPost); | ||
using var ctx = new TestContext(); | ||
ctx.JSInterop.Mode = JSRuntimeMode.Loose; | ||
ctx.Services.AddScoped<IRepository>(_ => BlogPostRepository); | ||
ctx.Services.AddScoped(_ => new Mock<ILocalStorageService>().Object); | ||
ctx.Services.AddScoped(_ => new Mock<IToastService>().Object); | ||
ctx.AddTestAuthorization().SetAuthorized("s"); | ||
var cut = ctx.RenderComponent<BlogPostPage>( | ||
p => p.Add(b => b.BlogPostId, publishedPost.Id)); | ||
var likeComponent = cut.FindComponent<Like>(); | ||
likeComponent.SetParametersAndRender(c => c.Add(p => p.BlogPost, publishedPost)); | ||
|
||
likeComponent.Find("button").Click(); | ||
|
||
var fromDb = await DbContext.BlogPosts.AsNoTracking().SingleAsync(d => d.Id == publishedPost.Id); | ||
fromDb.Likes.Should().Be(3); | ||
} | ||
|
||
[Fact] | ||
public async Task ShouldSubtractLikeOnEvent() | ||
{ | ||
var publishedPost = new BlogPostBuilder().WithLikes(2).IsPublished().Build(); | ||
await BlogPostRepository.StoreAsync(publishedPost); | ||
using var ctx = new TestContext(); | ||
var localStorage = new Mock<ILocalStorageService>(); | ||
localStorage.Setup(l => l.ContainKeyAsync("hasLiked", default)).ReturnsAsync(true); | ||
localStorage.Setup(l => l.GetItemAsync<bool>("hasLiked", default)).ReturnsAsync(true); | ||
ctx.JSInterop.Mode = JSRuntimeMode.Loose; | ||
ctx.Services.AddScoped<IRepository>(_ => BlogPostRepository); | ||
ctx.Services.AddScoped(_ => localStorage.Object); | ||
ctx.Services.AddScoped(_ => new Mock<IToastService>().Object); | ||
ctx.AddTestAuthorization().SetAuthorized("s"); | ||
var cut = ctx.RenderComponent<BlogPostPage>( | ||
p => p.Add(b => b.BlogPostId, publishedPost.Id)); | ||
var likeComponent = cut.FindComponent<Like>(); | ||
likeComponent.SetParametersAndRender(c => c.Add(p => p.BlogPost, publishedPost)); | ||
|
||
likeComponent.Find("button").Click(); | ||
|
||
var fromDb = await DbContext.BlogPosts.AsNoTracking().SingleAsync(d => d.Id == publishedPost.Id); | ||
fromDb.Likes.Should().Be(1); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
using Blazored.LocalStorage; | ||
using Bunit; | ||
using FluentAssertions; | ||
using LinkDotNet.Blog.TestUtilities; | ||
using LinkDotNet.Blog.Web.Shared; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Moq; | ||
using Xunit; | ||
|
||
namespace LinkDotNet.Blog.UnitTests.Web.Shared | ||
{ | ||
public class LikeTests : TestContext | ||
{ | ||
[Theory] | ||
[InlineData(0, "0 Likes")] | ||
[InlineData(1, "1 Like")] | ||
[InlineData(2, "2 Likes")] | ||
public void ShouldDisplayLikes(int likes, string expectedText) | ||
{ | ||
Services.AddScoped(_ => new Mock<ILocalStorageService>().Object); | ||
var blogPost = new BlogPostBuilder().WithLikes(likes).Build(); | ||
var cut = RenderComponent<Like>( | ||
p => p.Add(l => l.BlogPost, blogPost)); | ||
|
||
var label = cut.Find("small").TextContent; | ||
|
||
label.Should().Be(expectedText); | ||
} | ||
|
||
[Fact] | ||
public void ShouldInvokeEventWhenButtonClicked() | ||
{ | ||
Services.AddScoped(_ => new Mock<ILocalStorageService>().Object); | ||
var blogPost = new BlogPostBuilder().Build(); | ||
var wasClicked = false; | ||
var wasLike = false; | ||
var cut = RenderComponent<Like>( | ||
p => p.Add(l => l.BlogPost, blogPost) | ||
.Add(l => l.OnBlogPostLiked, b => | ||
{ | ||
wasClicked = true; | ||
wasLike = b; | ||
})); | ||
|
||
cut.Find("button").Click(); | ||
|
||
wasClicked.Should().BeTrue(); | ||
wasLike.Should().BeTrue(); | ||
} | ||
|
||
[Fact] | ||
public void ShouldSetLocalStorageVariableOnClick() | ||
{ | ||
var localStorage = new Mock<ILocalStorageService>(); | ||
Services.AddScoped(_ => localStorage.Object); | ||
var blogPost = new BlogPostBuilder().Build(); | ||
var cut = RenderComponent<Like>( | ||
p => p.Add(l => l.BlogPost, blogPost)); | ||
|
||
cut.Find("button").Click(); | ||
|
||
localStorage.Verify(l => l.SetItemAsync("hasLiked", true, default), Times.Once); | ||
} | ||
|
||
[Fact] | ||
public void ShouldCheckLocalStorageOnInit() | ||
{ | ||
var localStorage = new Mock<ILocalStorageService>(); | ||
localStorage.Setup(l => l.ContainKeyAsync("hasLiked", default)).ReturnsAsync(true); | ||
localStorage.Setup(l => l.GetItemAsync<bool>("hasLiked", default)).ReturnsAsync(true); | ||
Services.AddScoped(_ => localStorage.Object); | ||
var blogPost = new BlogPostBuilder().Build(); | ||
var wasLike = true; | ||
var cut = RenderComponent<Like>( | ||
p => p.Add(l => l.BlogPost, blogPost) | ||
.Add(l => l.OnBlogPostLiked, b => wasLike = b)); | ||
|
||
cut.Find("button").Click(); | ||
|
||
wasLike.Should().BeFalse(); | ||
} | ||
|
||
[Fact] | ||
public void ShouldCheckStorageOnClickAgainAndDoNothingOnMismatch() | ||
{ | ||
var localStorage = new Mock<ILocalStorageService>(); | ||
Services.AddScoped(_ => localStorage.Object); | ||
var blogPost = new BlogPostBuilder().Build(); | ||
var wasClicked = false; | ||
var cut = RenderComponent<Like>( | ||
p => p.Add(l => l.BlogPost, blogPost) | ||
.Add(l => l.OnBlogPostLiked, _ => wasClicked = true)); | ||
localStorage.Setup(l => l.ContainKeyAsync("hasLiked", default)).ReturnsAsync(true); | ||
localStorage.Setup(l => l.GetItemAsync<bool>("hasLiked", default)).ReturnsAsync(true); | ||
|
||
cut.Find("button").Click(); | ||
|
||
wasClicked.Should().BeFalse(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
@using LinkDotNet.Domain | ||
@using Blazored.LocalStorage | ||
@using LinkDotNet.Infrastructure.Persistence | ||
@inject ILocalStorageService _localStorage | ||
<div class="like-container"> | ||
<small>@BlogPost.Likes @LikeText</small> | ||
<button class="btn @BtnClass" @onclick="LikeBlogPost"><i class="far fa-thumbs-up"></i> @LikeTextButton</button> | ||
</div> | ||
|
||
@code { | ||
[Parameter] | ||
public BlogPost BlogPost { get; set; } | ||
|
||
[Parameter] | ||
public EventCallback<bool> OnBlogPostLiked { get; set; } | ||
|
||
private bool HasLiked { get; set; } | ||
|
||
private string BtnClass => HasLiked ? "btn-secondary" : "btn-primary"; | ||
|
||
private string LikeTextButton => HasLiked ? "Unlike" : "Like"; | ||
|
||
private string LikeText => BlogPost.Likes == 1 ? "Like" : "Likes"; | ||
|
||
protected override async Task OnAfterRenderAsync(bool firstRender) | ||
{ | ||
if (firstRender) | ||
{ | ||
HasLiked = await GetHasLiked(); | ||
StateHasChanged(); | ||
} | ||
} | ||
|
||
private async Task LikeBlogPost() | ||
{ | ||
// Prevent multiple open sites to like / unlike multiple times | ||
var hasLikedFromLocalStorage = await GetHasLiked(); | ||
if (HasLiked != hasLikedFromLocalStorage) | ||
{ | ||
return; | ||
} | ||
|
||
HasLiked = !HasLiked; | ||
await OnBlogPostLiked.InvokeAsync(HasLiked); | ||
await _localStorage.SetItemAsync("hasLiked", HasLiked); | ||
} | ||
|
||
private async Task<bool> GetHasLiked() | ||
{ | ||
if (await _localStorage.ContainKeyAsync("hasLiked")) | ||
{ | ||
return await _localStorage.GetItemAsync<bool>("hasLiked"); | ||
} | ||
|
||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
.like-container { | ||
float: right; | ||
margin-top: 20px; | ||
} | ||
|
||
.like-container button { | ||
margin-left: 10px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters