diff --git a/WotBlitzStatisticsPro.Blazor.Tests/Pages/LoginPageTests.cs b/WotBlitzStatisticsPro.Blazor.Tests/Pages/LoginPageTests.cs new file mode 100644 index 0000000..5cbd0d5 --- /dev/null +++ b/WotBlitzStatisticsPro.Blazor.Tests/Pages/LoginPageTests.cs @@ -0,0 +1,31 @@ +using Bunit; +using Moq; +using NUnit.Framework; +using System.Threading; +using WotBlitzStatisticsPro.Blazor.Messages; +using WotBlitzStatisticsPro.Blazor.Pages; + +namespace WotBlitzStatisticsPro.Blazor.Tests.Pages +{ + public class LoginPageTests: TestContextBase + { + [Test] + public void ShouldParseLoginDataAndSendRedirectFromWgLoginMessage() + { + string accessToken = "fake_token"; + string nickname = "fake_nick"; + long accountId = 123456789; + long expiration = 987654321; + NavigationManagerMock.NavigateTo($"/login?&status=ok&access_token={accessToken}&nickname={nickname}&account_id={accountId}&expires_at={expiration}"); + + var component = TestContext.RenderComponent(); + + MediatorMock.Verify(m => m.Publish( + It.Is(m => m.AccessToken == accessToken && + m.NickName == nickname && + m.AccountId == accountId && + m.ExpiresAt == expiration), + It.IsAny()), Times.Once); + } + } +} diff --git a/WotBlitzStatisticsPro.Blazor.Tests/Pages/WgLoginDialogTests.cs b/WotBlitzStatisticsPro.Blazor.Tests/Pages/WgLoginDialogTests.cs new file mode 100644 index 0000000..b26ebbd --- /dev/null +++ b/WotBlitzStatisticsPro.Blazor.Tests/Pages/WgLoginDialogTests.cs @@ -0,0 +1,94 @@ +using Bunit; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using NUnit.Framework; +using System.Threading; +using System.Threading.Tasks; +using WotBlitzStatisticsPro.Blazor.GraphQl; +using WotBlitzStatisticsPro.Blazor.Messages; +using WotBlitzStatisticsPro.Blazor.Model; +using WotBlitzStatisticsPro.Blazor.Pages; +using WotBlitzStatisticsPro.Blazor.Services; + +namespace WotBlitzStatisticsPro.Blazor.Tests.Pages +{ + public class WgLoginDialogTests: TestContextBase + { + private UserSettings _settings; + private IRenderedComponent _component; + private Mock _localStorageServiceMock; + + + [SetUp] + public void Setup() + { + _settings = new(); + + _localStorageServiceMock = new(); + _localStorageServiceMock.Setup(s => s.ReadSettings()).ReturnsAsync(_settings); + + TestContext?.Services.AddSingleton(_localStorageServiceMock.Object); + + TestContext?.JSInterop.SetupVoid("Radzen.preventArrows", _ => true).SetVoidResult(); + TestContext?.JSInterop.SetupVoid("Radzen.closePopup", _ => true).SetVoidResult(); + + _component = TestContext?.RenderComponent(); + } + + + [Test] + public void ShouldChangeRealmPropertiesWhenDropDownSelectedChanged() + { + // Default realm is Eu + _component.Instance.CurrentRealmType.Should().Be(RealmType.Eu); + + // Find 4 dropdown items + var items = _component.FindAll(".rz-dropdown-item"); + items.Should().NotBeNull(); + items.Count.Should().Be(4); + + // Act + // Asia is the last dropdown item + var asiaItem = items[3]; + asiaItem.Click(); + + // Assert + _component.Instance.CurrentRealmType.Should().Be(RealmType.Asia); + } + + [Test] + public void ShouldNotSendLoginMessageWhenCancelClicked() + { + var buttons = _component.FindAll(".rz-button"); + buttons.Should().NotBeNull(); + buttons.Count.Should().Be(2); + + // Cancel is first + buttons[0].Click(); + + // Should not send any messages to mediator + MediatorMock.Verify(m => m.Publish( + It.IsAny(), + It.IsAny()), Times.Never); + } + + [Test] + public void ShouldSendRedirectToWgMessageWhenOkClicked() + { + var realm = RealmType.Na; + _component.Instance.CurrentRealmType = realm; + var buttons = _component.FindAll(".rz-button"); + buttons.Should().NotBeNull(); + buttons.Count.Should().Be(2); + + // OK is the second one + buttons[1].Click(); + + // Should send messages to mediator with appropriate Realm + MediatorMock.Verify(m => m.Publish( + It.Is(m => m.Realm == realm), + It.IsAny()), Times.Once); + } + } +} diff --git a/WotBlitzStatisticsPro.Blazor.Tests/Services/LocalStorageServiceTests.cs b/WotBlitzStatisticsPro.Blazor.Tests/Services/LocalStorageServiceTests.cs index 419676e..dc4e201 100644 --- a/WotBlitzStatisticsPro.Blazor.Tests/Services/LocalStorageServiceTests.cs +++ b/WotBlitzStatisticsPro.Blazor.Tests/Services/LocalStorageServiceTests.cs @@ -3,8 +3,6 @@ using System.Threading; using System.Threading.Tasks; using Bunit; using FluentAssertions; -using Microsoft.AspNetCore.Components; -using Moq; using NUnit.Framework; using WotBlitzStatisticsPro.Blazor.GraphQl; using WotBlitzStatisticsPro.Blazor.Messages; diff --git a/WotBlitzStatisticsPro.Blazor.Tests/Services/WargamingLoginServiceTests.cs b/WotBlitzStatisticsPro.Blazor.Tests/Services/WargamingLoginServiceTests.cs new file mode 100644 index 0000000..e1ba634 --- /dev/null +++ b/WotBlitzStatisticsPro.Blazor.Tests/Services/WargamingLoginServiceTests.cs @@ -0,0 +1,100 @@ +using FluentAssertions; +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.Localization; +using Moq; +using NUnit.Framework; +using Radzen; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using WotBlitzStatisticsPro.Blazor.GraphQl; +using WotBlitzStatisticsPro.Blazor.Messages; +using WotBlitzStatisticsPro.Blazor.Model; +using WotBlitzStatisticsPro.Blazor.Services; + +namespace WotBlitzStatisticsPro.Blazor.Tests.Services +{ + public class WargamingLoginServiceTests: TestContextBase + { + private Mock _graphQlBackendMock; + private Mock _localStorageMock; + + private WargamingLoginService _service; + + + [SetUp] + public void Setup() + { + _graphQlBackendMock = new(); + _localStorageMock = new(); + + _service = new WargamingLoginService( + _graphQlBackendMock.Object, + TestContext.Services.GetService(), + TestContext.Services.GetService>(), + NavigationManagerMock, + _localStorageMock.Object, + MediatorMock.Object + ); + } + + [Test] + public async Task ShouldRequestRedirectUrlFromBackendWhenRedirectToWgLoginMessageArrives() + { + var realm = RealmType.Eu; + string url = "/fakeUrl"; + _graphQlBackendMock.Setup(b => b.GetWgLoginUrl(realm)).ReturnsAsync(url); + await _service.Handle(new RedirectToWgLoginMessage(realm), CancellationToken.None); + + _graphQlBackendMock.Verify(b => b.GetWgLoginUrl(realm), Times.Once); + } + + [Test] + public async Task ShouldNavigateToAppropriateUrlWhenRedirectToWgLoginMessageArrives() + { + var realm = RealmType.Eu; + string url = "/fakeUrl"; + _graphQlBackendMock.Setup(b => b.GetWgLoginUrl(It.IsAny())).ReturnsAsync(url); + await _service.Handle(new RedirectToWgLoginMessage(realm), CancellationToken.None); + + NavigationManagerMock.NavigationCount.Should().Be(1); + NavigationManagerMock.Uri.Should().Be($"https://unit-test.example{url}&redirect_uri=https://unit-test.example/login/{realm}"); + } + + [Test] + public async Task ShouldStoreAppropriateDataToLocalStorageWhenRedirectFromWgLoginMessageArrives() + { + var realm = RealmType.Asia; + string nick = "fakeNick"; + string token = "fakeToken"; + long accountId = 123456789; + long expiration = 987654321; + + await _service.Handle(new RedirectFromWgLoginMessage(realm, nick, accountId, token, expiration), CancellationToken.None); + + _localStorageMock.Verify(s => s.SetItemAsync( + Constants.LoginInfoLocalStorageKey, + It.Is(l => l.Realm == realm && l.NickName == nick && l.AccountId == accountId && l.AccessToken == token && l.ExpiresAt == expiration)) + , Times.Once); + } + + [Test] + public async Task ShouldFireOnPlayerInfoMessageWhenRedirectFromWgLoginMessageArrives() + { + var realm = RealmType.Asia; + string nick = "fakeNick"; + string token = "fakeToken"; + long accountId = 123456789; + long expiration = 987654321; + + await _service.Handle(new RedirectFromWgLoginMessage(realm, nick, accountId, token, expiration), CancellationToken.None); + + MediatorMock.Verify(m => m.Publish( + It.Is(m => m.AccountId == accountId && m.IsLoggedIn), + It.IsAny()), Times.Once); + } + } +} diff --git a/WotBlitzStatisticsPro.Blazor/Constants.cs b/WotBlitzStatisticsPro.Blazor/Constants.cs index 6db0e75..2fadd31 100644 --- a/WotBlitzStatisticsPro.Blazor/Constants.cs +++ b/WotBlitzStatisticsPro.Blazor/Constants.cs @@ -39,5 +39,7 @@ public const string CountryOther = "other"; public const string CountryEuropean = "european"; + + public const string LoginInfoLocalStorageKey = "login_info"; } } \ No newline at end of file diff --git a/WotBlitzStatisticsPro.Blazor/Model/LoginInfo.cs b/WotBlitzStatisticsPro.Blazor/Model/LoginInfo.cs new file mode 100644 index 0000000..2645e76 --- /dev/null +++ b/WotBlitzStatisticsPro.Blazor/Model/LoginInfo.cs @@ -0,0 +1,13 @@ +using WotBlitzStatisticsPro.Blazor.GraphQl; + +namespace WotBlitzStatisticsPro.Blazor.Model +{ + public class LoginInfo + { + public RealmType Realm { get; set; } + public string NickName { get; set; } + public long AccountId { get; set; } + public string AccessToken { get; set; } + public long ExpiresAt { get; set; } + } +} diff --git a/WotBlitzStatisticsPro.Blazor/Pages/WgLoginDialog.razor b/WotBlitzStatisticsPro.Blazor/Pages/WgLoginDialog.razor index 80c7ad5..b605bd2 100644 --- a/WotBlitzStatisticsPro.Blazor/Pages/WgLoginDialog.razor +++ b/WotBlitzStatisticsPro.Blazor/Pages/WgLoginDialog.razor @@ -15,7 +15,7 @@
- @code { - RealmType _currentRealmType = RealmType.Eu; + public RealmType CurrentRealmType { get; set; } = RealmType.Eu; readonly List _realms = new() { @@ -47,7 +47,7 @@ protected override async Task OnInitializedAsync() { var settings = await LocalStorageService.ReadSettings(); - _currentRealmType = settings.RealmType; + CurrentRealmType = settings.RealmType; } public async Task OnRealmChanged(object value) @@ -60,7 +60,7 @@ public async Task RedirectToWg() { - await Mediator.Publish(new RedirectToWgLoginMessage(_currentRealmType)); + await Mediator.Publish(new RedirectToWgLoginMessage(CurrentRealmType)); DialogService.Close(true); } diff --git a/WotBlitzStatisticsPro.Blazor/Services/WargamingLoginService.cs b/WotBlitzStatisticsPro.Blazor/Services/WargamingLoginService.cs index 9b70bb1..83eaf44 100644 --- a/WotBlitzStatisticsPro.Blazor/Services/WargamingLoginService.cs +++ b/WotBlitzStatisticsPro.Blazor/Services/WargamingLoginService.cs @@ -1,11 +1,11 @@ -using System.Collections.Generic; -using System.Threading; +using System.Threading; using System.Threading.Tasks; using MediatR; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Localization; using Radzen; using WotBlitzStatisticsPro.Blazor.Messages; +using WotBlitzStatisticsPro.Blazor.Model; using WotBlitzStatisticsPro.Blazor.Pages; namespace WotBlitzStatisticsPro.Blazor.Services @@ -19,17 +19,23 @@ namespace WotBlitzStatisticsPro.Blazor.Services private readonly DialogService _dialogService; private readonly IStringLocalizer _localizer; private readonly NavigationManager _navigationManager; + private readonly ILocalStorageService _localStorage; + private readonly IMediator _mediator; public WargamingLoginService( IGraphQlBackendService graphQlBackendService, DialogService dialogService, IStringLocalizer localizer, - NavigationManager navigationManager) + NavigationManager navigationManager, + ILocalStorageService localStorage, + IMediator mediator) { _graphQlBackendService = graphQlBackendService; _dialogService = dialogService; _localizer = localizer; _navigationManager = navigationManager; + _localStorage = localStorage; + _mediator = mediator; } public async Task Handle(LoginToWgMessage notification, CancellationToken cancellationToken) @@ -49,11 +55,18 @@ namespace WotBlitzStatisticsPro.Blazor.Services _navigationManager.NavigateTo(url); } - public Task Handle(RedirectFromWgLoginMessage notification, CancellationToken cancellationToken) + public async Task Handle(RedirectFromWgLoginMessage notification, CancellationToken cancellationToken) { - // ToDo: Store login info to local storage - // ToDo: Send OpenPlayerInfoMessage with isLoggedIn = true - return Task.CompletedTask; + // Store login info to local storage + await _localStorage.SetItemAsync(Constants.LoginInfoLocalStorageKey, new LoginInfo { + Realm = notification.Realm, + AccessToken = notification.AccessToken, + AccountId = notification.AccountId, + ExpiresAt = notification.ExpiresAt, + NickName = notification.NickName, }); + + // Send OpenPlayerInfoMessage with isLoggedIn = true + await _mediator.Publish(new OpenPlayerInfoMessage(notification.AccountId, true)); } } } \ No newline at end of file